From ce3c0580c1e58bda922ac7522ace7d25c9f8eb17 Mon Sep 17 00:00:00 2001 From: Plutonium-239 Date: Tue, 27 Aug 2024 17:26:13 +0530 Subject: [PATCH] docs revamp --- docs_src/_ext/dict2table.py | 52 +++-- docs_src/_static/favicon.png | Bin 0 -> 6570 bytes docs_src/_static/memsave_torch_banner.svg | 116 ++++++++++ docs_src/_static/memsave_torch_logo_inv.svg | 116 ++++++++++ .../memsave_torch_logo_inv_text_256x256.png | Bin 0 -> 41243 bytes docs_src/_static/style.css | 215 +++++++++++++++++- docs_src/_templates/partials/webfonts.html | 11 + docs_src/_templates/repo-stats-custom.html | 30 +++ docs_src/_templates/sidebar_logo.html | 172 ++++++++++++++ .../{util => experiments}/collect_results.rst | 0 .../api/{util => experiments}/estimate.rst | 0 docs_src/api/{util => experiments}/index.rst | 5 +- docs_src/api/experiments/measurements.rst | 7 + docs_src/api/experiments/models.rst | 29 +++ docs_src/api/index.rst | 2 +- docs_src/api/util/measurements.rst | 0 docs_src/api/util/models.rst | 16 -- docs_src/basic_usage.rst | 26 ++- docs_src/conf.py | 39 +++- docs_src/index.rst | 125 +++++++++- docs_src/requirements.txt | 4 +- 21 files changed, 911 insertions(+), 54 deletions(-) create mode 100644 docs_src/_static/favicon.png create mode 100644 docs_src/_static/memsave_torch_banner.svg create mode 100644 docs_src/_static/memsave_torch_logo_inv.svg create mode 100644 docs_src/_static/memsave_torch_logo_inv_text_256x256.png create mode 100644 docs_src/_templates/partials/webfonts.html create mode 100644 docs_src/_templates/repo-stats-custom.html create mode 100644 docs_src/_templates/sidebar_logo.html rename docs_src/api/{util => experiments}/collect_results.rst (100%) rename docs_src/api/{util => experiments}/estimate.rst (100%) rename docs_src/api/{util => experiments}/index.rst (90%) create mode 100644 docs_src/api/experiments/measurements.rst create mode 100644 docs_src/api/experiments/models.rst delete mode 100644 docs_src/api/util/measurements.rst delete mode 100644 docs_src/api/util/models.rst diff --git a/docs_src/_ext/dict2table.py b/docs_src/_ext/dict2table.py index 62f21f2..02ee751 100644 --- a/docs_src/_ext/dict2table.py +++ b/docs_src/_ext/dict2table.py @@ -10,14 +10,24 @@ from importlib import import_module from typing import Dict, List, Tuple + def format_function(x): - if x.__name__ == '': + if "functools.partial" in str(x.__class__): + return str(x) + if getattr(x, "__name__", None) == "": return x + if "experiments.util.models._HF_model" in str(x.__class__): + classname = str(x.model_cls)[:-2].split(".")[-1] + link_to_hf = f"`{x.hf_name} `_" + # return link_to_hf + return f":class:`{classname} ` , {link_to_hf}" + # return f':class:`{classname}` ``.from_pretrained(`` ' + link_to_hf + ' ``)``' module = x.__module__ - if module.startswith('torchvision'): - module = '.'.join(x.__module__.split('.')[:-1]) - content = module + "." + x.__name__ - return f':func:`{content}`' + if module.startswith("torchvision"): + module = ".".join(x.__module__.split(".")[:-1]) + content = module + "." + x.__name__ + return f":func:`{content}`" + class Dict2Table(SphinxDirective): has_content = True @@ -26,6 +36,8 @@ class Dict2Table(SphinxDirective): "filter": directives.unchanged_required, "filter-out": directives.unchanged_required, "caption": directives.unchanged_required, + "headers": directives.unchanged_required, + "widths": directives.unchanged_required, } def run(self): @@ -33,30 +45,34 @@ def run(self): member_data = getattr(import_module(module_path), member_name) assert isinstance(member_data, dict) if "filter" in self.options: - member_data = { - k: v for k, v in member_data.items() if self.options.get("filter") in k - } + filter_in = self.options["filter"].split(",") + for k, v in member_data.copy().items(): + if not any([f in k for f in filter_in]): + del member_data[k] + if "filter-out" in self.options: - member_data = { - k: v - for k, v in member_data.items() - if self.options.get("filter-out") not in k - } + filter_out = self.options["filter-out"].split(",") + for k, v in member_data.copy().items(): + if any([f in k for f in filter_out]): + del member_data[k] table_head = ( f'.. csv-table:: {self.options.get("caption")}\n' - + ' :header: "Model Name", "Model Function"\n' - + " :widths: 4, 6\n\n" + + f" :header: {self.options.get('headers')} \n" + + f" :widths: {self.options.get('widths')} \n" + + "\n" ) - table_str = "\n".join([f' "{k}", "{format_function(v)}"' for k, v in member_data.items()]) + table_str = "\n".join( + [f' ``"{k}"``, {format_function(v)}' for k, v in member_data.items()] + ) table_str = table_head + table_str - columns = {"model_name": 4, "function": 6} + # columns = {"model_name": 4, "function": 6} # table, tablegroup = self._PrepareTable(member_data, ) # import pdb; pdb.set_trace() main_node = nodes.paragraph() - self.state.nested_parse(StringList(table_str.split('\n')), 0, main_node) + self.state.nested_parse(StringList(table_str.split("\n")), 0, main_node) # return self.parse_rst(table_str) diff --git a/docs_src/_static/favicon.png b/docs_src/_static/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..5c6777fad7725ea826cf32a5eb74ac3dadd34e3d GIT binary patch literal 6570 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 literal 0 HcmV?d00001 diff --git a/docs_src/_static/memsave_torch_banner.svg b/docs_src/_static/memsave_torch_banner.svg new file mode 100644 index 0000000..1d4e82e --- /dev/null +++ b/docs_src/_static/memsave_torch_banner.svg @@ -0,0 +1,116 @@ + + + + + + + + +memsavetorch diff --git a/docs_src/_static/memsave_torch_logo_inv.svg b/docs_src/_static/memsave_torch_logo_inv.svg new file mode 100644 index 0000000..327904f --- /dev/null +++ b/docs_src/_static/memsave_torch_logo_inv.svg @@ -0,0 +1,116 @@ + + + + + + + + + diff --git a/docs_src/_static/memsave_torch_logo_inv_text_256x256.png b/docs_src/_static/memsave_torch_logo_inv_text_256x256.png new file mode 100644 index 0000000000000000000000000000000000000000..c1fb3930bc5eefaf96abf8aeb78b2967651f8789 GIT binary patch literal 41243 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 z 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 literal 0 HcmV?d00001 diff --git a/docs_src/_static/style.css b/docs_src/_static/style.css index 99e9781..403e97a 100644 --- a/docs_src/_static/style.css +++ b/docs_src/_static/style.css @@ -1,19 +1,224 @@ html.light { - --sy-c-divider: var(--pink-9); - --sy-c-foot-divider: var(--pink-9); + --sy-c-divider: var(--teal-9); + --sy-c-foot-divider: var(--teal-9); } html.dark { - --sy-c-divider: var(--pink-9); - --sy-c-foot-divider: var(--pink-9); + --sy-c-divider: var(--teal-9); + --sy-c-foot-divider: var(--teal-9); /* --sy-c-link-active: var(--pink-a11); */ } +/* :root{ + --sy-f-text: "Readex Pro Deca"; + --sy-f-mono: "Hack"; +} */ +.logo_head { + color: var(--teal-9); +} + +details { + background: var(--accent-3); + padding: 1em; + border-radius: 6px; + +/* & summary { + list-style-type: url("data:image/svg+xml;utf8,"); + } */ + & summary:focus-visible { + outline: none; + } +} + .localtoc { overflow-wrap: break-word; } +#memsave_logo_sidebar > svg { + width: 100%; + height: auto; +} + +.yue table { + font-size: unset; +} + +.sphinx-contributors--avatars .sphinx-contributors_contributor__image { + width: 80px; + border-radius: 100%; +} +.sphinx-contributors_contributor { + align-items: center; +} +.sphinx-contributors--avatars .sphinx-contributors_list { + justify-content: flex-start; + gap: 10em; +} + +.sy-head-brand .dark-logo, .sy-head-brand .light-logo { + height: var(--sy-s-navbar-height); +} + +li.link { + background: linear-gradient(135deg, #00cfff, #00ff89); + background-clip: text; + color: transparent; +} + +/* @media (min-width: 1280px) { + .xl\:px-12 { + padding-left: 1.5em; + padding-right: 1.5em; + } +} */ + .dark, .dark-theme { + --teal-1: #0b1312; + --teal-2: #0f1b1a; + --teal-3: #082d2a; + --teal-4: #003b36; + --teal-5: #004843; + --teal-6: #055751; + --teal-7: #106962; + --teal-8: #0d7f77; + --teal-9: #00e1d2; + --teal-10: #00d6c7; + --teal-11: #00d8c9; + --teal-12: #aef3eb; + + --teal-a1: #00bb6603; + --teal-a2: #00f9e20b; + --teal-a3: #00ffe61e; + --teal-a4: #00ffe32d; + --teal-a5: #00ffea3b; + --teal-a6: #00ffeb4b; + --teal-a7: #0ffdea5f; + --teal-a8: #09ffee76; + --teal-a9: #00ffeedf; + --teal-a10: #00feecd4; + --teal-a11: #00feecd6; + --teal-a12: #b6fef6f3; + + --teal-contrast: #042825; + --teal-surface: #0e252380; + --teal-indicator: #00e1d2; + --teal-track: #00e1d2; +} + +@supports (color: color(display-p3 1 1 1)) { + @media (color-gamut: p3) { + .dark, .dark-theme { + --teal-1: oklch(17.8% 0.0128 186.3); + --teal-2: oklch(21.2% 0.0181 186.3); + --teal-3: oklch(27.1% 0.0409 186.3); + --teal-4: oklch(31.5% 0.06 186.3); + --teal-5: oklch(36.1% 0.0659 186.3); + --teal-6: oklch(41.2% 0.0705 186.3); + --teal-7: oklch(47.2% 0.0785 186.3); + --teal-8: oklch(53.9% 0.0918 186.3); + --teal-9: oklch(81.9% 0.1434 186.3); + --teal-10: oklch(78.6% 0.1434 186.3); + --teal-11: oklch(79.2% 0.1434 186.3); + --teal-12: oklch(91.7% 0.0696 186.3); + + --teal-a1: color(display-p3 0 0.9412 0.5059 / 0.009); + --teal-a2: color(display-p3 0.1608 0.9804 0.8902 / 0.043); + --teal-a3: color(display-p3 0.1373 0.9961 0.9294 / 0.114); + --teal-a4: color(display-p3 0.1804 0.9961 0.9059 / 0.173); + --teal-a5: color(display-p3 0.2588 1 0.9333 / 0.227); + --teal-a6: color(display-p3 0.3373 1 0.9333 / 0.29); + --teal-a7: color(display-p3 0.4039 1 0.9451 / 0.362); + --teal-a8: color(display-p3 0.4157 1 0.949 / 0.454); + --teal-a9: color(display-p3 0.4549 1 0.9451 / 0.862); + --teal-a10: color(display-p3 0.451 1 0.9412 / 0.816); + --teal-a11: color(display-p3 0.451 1 0.9412 / 0.824); + --teal-a12: color(display-p3 0.7843 1 0.9725 / 0.942); + + --teal-contrast: #042825; + --teal-surface: color(display-p3 0.0706 0.1412 0.1333 / 0.5); + --teal-indicator: oklch(81.9% 0.1434 186.3); + --teal-track: oklch(81.9% 0.1434 186.3); + } + } +} + +.dark, .dark-theme { + --gray-1: #111113; + --gray-2: #19191b; + --gray-3: #222325; + --gray-4: #292a2e; + --gray-5: #303136; + --gray-6: #393a40; + --gray-7: #46484f; + --gray-8: #5f606a; + --gray-9: #6c6e79; + --gray-10: #797b86; + --gray-11: #b2b3bd; + --gray-12: #eeeef0; + + --gray-a1: #1111bb03; + --gray-a2: #cbcbf90b; + --gray-a3: #d6e2f916; + --gray-a4: #d1d9f920; + --gray-a5: #d7ddfd28; + --gray-a6: #d9defc33; + --gray-a7: #dae2fd43; + --gray-a8: #e0e3fd60; + --gray-a9: #e0e4fd70; + --gray-a10: #e3e7fd7e; + --gray-a11: #eff0feb9; + --gray-a12: #fdfdffef; + + --gray-contrast: #FFFFFF; + --gray-surface: rgba(0, 0, 0, 0.05); + --gray-indicator: #6c6e79; + --gray-track: #6c6e79; +} + +@supports (color: color(display-p3 1 1 1)) { + @media (color-gamut: p3) { + .dark, .dark-theme { + --gray-1: oklch(17.8% 0.0042 277.7); + --gray-2: oklch(21.5% 0.004 277.7); + --gray-3: oklch(25.5% 0.0055 277.7); + --gray-4: oklch(28.4% 0.0075 277.7); + --gray-5: oklch(31.4% 0.0089 277.7); + --gray-6: oklch(35% 0.01 277.7); + --gray-7: oklch(40.2% 0.0121 277.7); + --gray-8: oklch(49.2% 0.0157 277.7); + --gray-9: oklch(54% 0.0167 277.7); + --gray-10: oklch(58.6% 0.0165 277.7); + --gray-11: oklch(77% 0.0138 277.7); + --gray-12: oklch(94.9% 0.0026 277.7); + + --gray-a1: color(display-p3 0.0667 0.0667 0.9412 / 0.009); + --gray-a2: color(display-p3 0.8 0.8 0.9804 / 0.043); + --gray-a3: color(display-p3 0.851 0.898 0.9882 / 0.085); + --gray-a4: color(display-p3 0.8392 0.8706 1 / 0.122); + --gray-a5: color(display-p3 0.8471 0.8745 1 / 0.156); + --gray-a6: color(display-p3 0.8784 0.898 1 / 0.194); + --gray-a7: color(display-p3 0.8745 0.9059 0.9961 / 0.257); + --gray-a8: color(display-p3 0.8941 0.9059 1 / 0.37); + --gray-a9: color(display-p3 0.8902 0.9098 1 / 0.433); + --gray-a10: color(display-p3 0.902 0.9176 1 / 0.488); + --gray-a11: color(display-p3 0.9451 0.949 1 / 0.719); + --gray-a12: color(display-p3 0.9922 0.9922 1 / 0.937); + + --gray-contrast: #FFFFFF; + --gray-surface: color(display-p3 0 0 0 / 5%); + --gray-indicator: oklch(54% 0.0167 277.7); + --gray-track: oklch(54% 0.0167 277.7); + } + } +} + +.dark, .dark-theme, :is(.dark, .dark-theme) :where(.radix-themes:not(.light, .light-theme)) { + --color-background: #111; +} + + + +/* .dark, .dark-theme { --pink-1: #180e11; --pink-2: #211216; --pink-3: #3c1121; @@ -81,4 +286,4 @@ html.dark { --pink-track: oklch(65.5% 0.253 1.135); } } -} \ No newline at end of file +} */ \ No newline at end of file diff --git a/docs_src/_templates/partials/webfonts.html b/docs_src/_templates/partials/webfonts.html new file mode 100644 index 0000000..30aeeab --- /dev/null +++ b/docs_src/_templates/partials/webfonts.html @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/docs_src/_templates/repo-stats-custom.html b/docs_src/_templates/repo-stats-custom.html new file mode 100644 index 0000000..965e9d1 --- /dev/null +++ b/docs_src/_templates/repo-stats-custom.html @@ -0,0 +1,30 @@ +{%- if source_type and source_user and source_repo and source_type in ("github", "gitlab") -%} + +{%- if source_type == "github" -%} + {% set source_url = "https://github.com/{}/{}".format(source_user, source_repo) %} +{%- else -%} + {% set source_url = "https://gitlab.com/{}/{}".format(source_user, source_repo) %} +{%- endif -%} + + + + + + + {{ source_user }} + / + {{ source_repo }} + + + + 0 + + + + 0 + + + + +{%- endif -%} diff --git a/docs_src/_templates/sidebar_logo.html b/docs_src/_templates/sidebar_logo.html new file mode 100644 index 0000000..9370b9b --- /dev/null +++ b/docs_src/_templates/sidebar_logo.html @@ -0,0 +1,172 @@ +
+ + + + + + +memsavetorch + +
\ No newline at end of file diff --git a/docs_src/api/util/collect_results.rst b/docs_src/api/experiments/collect_results.rst similarity index 100% rename from docs_src/api/util/collect_results.rst rename to docs_src/api/experiments/collect_results.rst diff --git a/docs_src/api/util/estimate.rst b/docs_src/api/experiments/estimate.rst similarity index 100% rename from docs_src/api/util/estimate.rst rename to docs_src/api/experiments/estimate.rst diff --git a/docs_src/api/util/index.rst b/docs_src/api/experiments/index.rst similarity index 90% rename from docs_src/api/util/index.rst rename to docs_src/api/experiments/index.rst index 43d546b..23cd800 100644 --- a/docs_src/api/util/index.rst +++ b/docs_src/api/experiments/index.rst @@ -1,5 +1,5 @@ -util |:warning:| -=================== +experiments.util |:warning:| +=============================== A collection of scripts/utilities to measure the memory and time consumed during the forward and backward passes. @@ -22,6 +22,7 @@ A collection of scripts/utilities to measure the memory and time consumed during :maxdepth: 3 :caption: Utility classes and scripts + measurements collect_results estimate models diff --git a/docs_src/api/experiments/measurements.rst b/docs_src/api/experiments/measurements.rst new file mode 100644 index 0000000..541bf0f --- /dev/null +++ b/docs_src/api/experiments/measurements.rst @@ -0,0 +1,7 @@ +Measurements +=================== + +The classes :class:`RuntimeMeasurement` and :class:`MemoryMeasurement` measure the runtime and memory respectively. + +.. automodule:: experiments.util.measurements + :members: diff --git a/docs_src/api/experiments/models.rst b/docs_src/api/experiments/models.rst new file mode 100644 index 0000000..67f7ce5 --- /dev/null +++ b/docs_src/api/experiments/models.rst @@ -0,0 +1,29 @@ +Models +=================== + +This module defines mappings from strings to models. This was necessary to isolate the model being run in :mod:`memsave_torch.util.estimate` and having a separate torch runtime for every single run. Otherwise, CUDA does not clear memory unless absolutely required, even on calling the :func:`torch.cuda.empty_cache()` function, which makes memory measurements very difficult. + +.. automodule:: experiments.util.models + :members: prefix_in_pairs + +.. currentmodule:: experiments.util.models + +.. attribute:: conv_model_fns + + All listed models have their MemSave variants accessible as ``'memsave_'`` + + .. dict2table:: experiments.util.models.conv_model_fns + :caption: All conv models defined to be used as strings in :mod:`experiments.paper_demo` script + :filter-out: memsave_,model + :headers: "Model Name (:class:`str`)", "Model Function" + :widths: 4, 8 + +.. attribute:: hf_transformers_models_map + + All listed models have their MemSave variants accessible as ``'memsave_'`` + + .. dict2table:: experiments.util.models.hf_transformers_models_map + :caption: All transformer models defined to be used as strings in :mod:`experiments.paper_demo` script + :filter-out: memsave_ + :headers: "Model Name (:class:`str`)", "Model Class", "Model Page" + :widths: 4, 3, 7 diff --git a/docs_src/api/index.rst b/docs_src/api/index.rst index 0d10638..3d7f100 100644 --- a/docs_src/api/index.rst +++ b/docs_src/api/index.rst @@ -6,7 +6,7 @@ API Reference :caption: Available Modules nn/index - util/index + experiments/index .. automodule:: memsave_torch diff --git a/docs_src/api/util/measurements.rst b/docs_src/api/util/measurements.rst deleted file mode 100644 index e69de29..0000000 diff --git a/docs_src/api/util/models.rst b/docs_src/api/util/models.rst deleted file mode 100644 index 8895a38..0000000 --- a/docs_src/api/util/models.rst +++ /dev/null @@ -1,16 +0,0 @@ -Models -=================== - -This module defines mappings from strings to models. This was necessary to isolate the model being run in :mod:`memsave_torch.util.estimate` and having a separate torch runtime for every single run. Otherwise, CUDA does not clear memory unless absolutely required, even on calling the :func:`torch.cuda.empty_cache()` function, which makes memory measurements very difficult. - -.. automodule:: experiments.util.models - :members: prefix_in_pairs - -.. currentmodule:: experiments.util.models - -.. attribute:: conv_model_fns - - .. dict2table:: experiments.util.models.conv_model_fns - :caption: All Models defined to be used as strings in :mod:`experiments.paper_demo` script - :filter-out: memsave_ - diff --git a/docs_src/basic_usage.rst b/docs_src/basic_usage.rst index 7c4af0c..6117803 100644 --- a/docs_src/basic_usage.rst +++ b/docs_src/basic_usage.rst @@ -9,14 +9,30 @@ Installation / Quickstart Replace all (valid) layers with ``MemSave`` layers -------------------------------------------------- -The :func:`convert_to_memory_saving` function from the :mod:`memsave_torch.nn` module is a handy tool to replace all layers of a model that is passed to it with their memory saving counterparts. +The :func:`convert_to_memory_saving ` function from the :mod:`memsave_torch.nn` module is a handy tool to replace all layers of a model that is passed to it with their memory saving counterparts. .. code-block:: python - :emphasize-lines: 4,5 + :emphasize-lines: 3,8 import torch - my_torch_model: torch.nn.Module - + from torchvision.models import resnet18 from memsave_torch.nn import convert_to_memory_saving - memsave_torch_model = convert_to_memory_saving(my_torch_model) + x = torch.rand(2, 3, 224, 224) + rn18 = resnet18() + + rn18 = convert_to_memory_saving(rn18) + + # Set input to be differentiable and model weights to be non-differentiable + x.requires_grad = True + rn18.requires_grad_(False) + + y = rn18(x) + loss = torch.nn.MSELoss()(y, torch.rand_like(y)) + loss.backward() + + +.. attention:: + You can't use the old model in the same python run after calling :func:`convert_to_memory_saving ` on it, because by default weights are not copied to not cause extra memory consumption. + + However, if you need to use both models together, pass the ``clone_params = True`` argument to :func:`convert_to_memory_saving `, this will cause model weights to be copied and not just referenced. \ No newline at end of file diff --git a/docs_src/conf.py b/docs_src/conf.py index a337ee3..0d91e00 100644 --- a/docs_src/conf.py +++ b/docs_src/conf.py @@ -3,6 +3,9 @@ # For the full list of built-in configuration values, see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html import os, sys +from dotenv import load_dotenv + +load_dotenv() sys.path.insert(0, os.path.abspath("../")) sys.path.append(os.path.abspath("./_ext")) @@ -16,6 +19,9 @@ # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration +docsearch_app_id = os.getenv("DOCSEARCH_APP_ID") +docsearch_api_key = os.getenv("DOCSEARCH_API_KEY") +docsearch_index_name = os.getenv("DOCSEARCH_INDEX_NAME") extensions = [ "sphinx.ext.autodoc", @@ -26,6 +32,8 @@ "sphinxemoji.sphinxemoji", "sphinx_sitemap", "dict2table", + "sphinx_contributors", + "sphinx_docsearch", ] templates_path = ["_templates"] @@ -36,7 +44,8 @@ intersphinx_mapping = { "python": ("https://docs.python.org/3", None), "torch": ("https://pytorch.org/docs/stable/", None), - "torchvision": ("https://pytorch.org/vision/stable/", None) + "torchvision": ("https://pytorch.org/vision/stable/", None), + "transformers": ("https://huggingface.co/docs/transformers/main/en/", None), } # add_module_names = False @@ -51,5 +60,31 @@ html_css_files = ["style.css"] html_theme_options = { - "accent_color": "pink", + "accent_color": "teal", + "github_url": "https://github.com/plutonium-239/memsave_torch", + "og_image_url": "https://memsave-torch.readthedocs.io/_static/memsave_torch_logo_inv_text_256x256.png", + "nav_links": [ + {"title": "memsave_torch", "url": "/index"}, + ], + "nav_links_align": "center", +} +html_logo = "_static/memsave_torch_logo_inv.svg" +html_favicon = "_static/favicon.png" + +html_context = { + "source_type": "github", + "source_user": "plutonium-239", + "source_repo": "memsave_torch", + "source_version": "main", # Optional + "source_docs_path": "/docs_src/", # Optional +} + +html_sidebars = { + "**": [ + "sidebars/localtoc.html", + "repo-stats-custom.html", + # "sidebars/repo-stats.html", + "sidebars/edit-this-page.html", + "sidebar_logo.html", + ] } diff --git a/docs_src/index.rst b/docs_src/index.rst index 87f3992..f227fee 100644 --- a/docs_src/index.rst +++ b/docs_src/index.rst @@ -1,19 +1,136 @@ +.. :layout: landing + .. MemSave PyTorch documentation master file, created by sphinx-quickstart on Tue Apr 2 17:30:57 2024. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Welcome to MemSave PyTorch's documentation! -=========================================== +``memsave_torch`` +=================== + +Lowering PyTorch's Memory Consumption for Selective Differentiation +********************************************************************** .. toctree:: + :hidden: :maxdepth: 3 - :caption: Getting started + :caption: Index - Index + Overview basic_usage api/index +.. image:: /_static/memsave_torch_banner.svg + :width: 100% + + +This package offers drop-in implementations of PyTorch :class:`torch.nn.Module` s. +They are as fast as their built-in equivalents, but more memory-efficient whenever you want to compute gradients for a sub-set of parameters (i.e. some have ``requires_grad=False``). + + +.. raw:: html + +
+ + Currently it supports the following layers: + + +.. .. hlist:: +.. :columns: 2 + +* :class:`memsave_torch.nn.MemSaveConv1d` +* :class:`memsave_torch.nn.MemSaveConv2d` +* :class:`memsave_torch.nn.MemSaveConv3d` +* :class:`memsave_torch.nn.MemSaveLinear` +* :class:`memsave_torch.nn.MemSaveReLU` +* :class:`memsave_torch.nn.MemSaveConvTranspose1d` +* :class:`memsave_torch.nn.MemSaveConvTranspose2d` +* :class:`memsave_torch.nn.MemSaveConvTranspose3d` +* :class:`memsave_torch.nn.MemSaveMaxPool2d` +* :class:`memsave_torch.nn.MemSaveBatchNorm2d` + +.. raw:: html + +
+ +Also, each layer has a ``.from_nn_(layer)`` function which allows to convert a single layer into its memory-saving equivalent. (e.g. :func:`MemSaveConv2d.from_nn_Conv2d `) + +Installation +************** + +Normal installation: + +.. code-block:: bash + + pip install git+https://github.com/plutonium-239/memsave_torch + + +Install (editable): + +.. code-block:: bash + + pip install -e git+https://github.com/plutonium-239/memsave_torch + + +Usage +******* + +Please refer to :doc:`basic_usage`. + +.. Basic Example +.. *************** + +.. .. code-block:: python +.. :emphasize-lines: 1,9 + +.. from memsave_torch.nn import convert_to_memory_saving + +.. X, y = ... + +.. model = Sequential(...) +.. loss_func = MSELoss() + +.. # replace all available layers with mem-saving layers +.. model = memsave_torch.nn.convert_to_memory_saving(model) + +.. # same as if you were using the original model +.. loss = loss_func(model(X), y) + + +Further reading +****************** + +* `Writeup `_ + + This explains the basic ideas around MemSave without diving into too many details. + +* `Our paper (WANT@ICML'24) `_ + + It is also available on `arXiv `_. + +How to cite +************* + +If this package has benefited you at some point, consider citing + +.. code:: bibtex + + @inproceedings{ + bhatia2024lowering, + title={Lowering PyTorch's Memory Consumption for Selective Differentiation}, + author={Samarth Bhatia and Felix Dangel}, + booktitle={2nd Workshop on Advancing Neural Network Training: Computational Efficiency, Scalability, and Resource Optimization (WANT@ICML 2024)}, + year={2024}, + url={https://openreview.net/forum?id=KsUUzxUK7N} + } + +Contributors +************** + +.. contributors:: plutonium-239/memsave_torch + :avatars: + :limit: 5 + :order: ASC Indices and tables diff --git a/docs_src/requirements.txt b/docs_src/requirements.txt index da16e8a..6c4f5a7 100644 --- a/docs_src/requirements.txt +++ b/docs_src/requirements.txt @@ -10,4 +10,6 @@ tqdm shibuya sphinx-copybutton sphinxemoji -sphinx-sitemap \ No newline at end of file +sphinx-sitemap +sphinx_contributors +sphinx_docsearch \ No newline at end of file