From efa23003157b8494f6d59afbbcb5ecdb47f535c3 Mon Sep 17 00:00:00 2001 From: Caleb Spradlin <72508718+cssprad1@users.noreply.github.com> Date: Fri, 18 Aug 2023 14:41:00 -0400 Subject: [PATCH 01/12] Update ci.yml --- .github/workflows/ci.yml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d065406..c2d02e4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,12 +26,6 @@ jobs: sudo apt-get install -y gdal-bin libgdal-dev build-essential python -m pip install --upgrade pip pip install https://github.com/rasterio/rasterio/archive/master.zip - pip install .[test] - pip install coveralls - - name: Run tests - run: coverage run -m pytest -v - - name: Submit coveralls - if: github.event_name != 'pull_request' - run: coveralls --service=github - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Run CI tests + run: bash test.sh + shell: bash From 33ab9ea7a9d95a04aece954c6c8eb96d2534ac04 Mon Sep 17 00:00:00 2001 From: jordancaraballo Date: Mon, 21 Aug 2023 11:31:03 -0400 Subject: [PATCH 02/12] Adding pypi and readthedocs --- .github/workflows/build_docs.yml | 61 ++++++++++++++ .github/workflows/pypi-publish.yml | 37 +++------ README.rst | 127 +++++++++++++++++++++++++++++ docs/Makefile | 20 +++++ docs/conf.py | 75 +++++++++++++++++ docs/examples.rst | 3 + docs/index.rst | 22 +++++ docs/make.bat | 35 ++++++++ docs/modules.rst | 44 ++++++++++ docs/pytorch_caney.rst | 31 +++++++ docs/readme.rst | 1 + docs/requirements.txt | 4 + docs/source/conf.py | 30 +++++++ docs/source/index.rst | 20 +++++ docs/static/DSG_LOGO_REDESIGN.png | Bin 0 -> 51468 bytes 15 files changed, 485 insertions(+), 25 deletions(-) create mode 100644 .github/workflows/build_docs.yml create mode 100644 README.rst create mode 100644 docs/Makefile create mode 100644 docs/conf.py create mode 100644 docs/examples.rst create mode 100644 docs/index.rst create mode 100644 docs/make.bat create mode 100644 docs/modules.rst create mode 100644 docs/pytorch_caney.rst create mode 100644 docs/readme.rst create mode 100644 docs/requirements.txt create mode 100644 docs/source/conf.py create mode 100644 docs/source/index.rst create mode 100644 docs/static/DSG_LOGO_REDESIGN.png diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml new file mode 100644 index 0000000..e4a6b4a --- /dev/null +++ b/.github/workflows/build_docs.yml @@ -0,0 +1,61 @@ +name: Publish Docs + +on: + push: + branches: [ main ] + release: + types: [ created ] + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true + +jobs: + docs: + name: Publish Docs + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + persist-credentials: false + + - name: Setup Conda + uses: s-weigand/setup-conda@v1 + with: + python-version: 3.9 + conda-channels: conda-forge + + - name: Install and Build + shell: bash + run: | + conda config --prepend channels conda-forge + conda config --set channel_priority strict + conda create -n docs python=3.9 rasterio xarray scipy pyproj pandoc sphinx sphinx-autodoc-typehints jupyter_sphinx sphinx_rtd_theme sphinx-click nbsphinx myst-nb + source activate docs + python -m pip install -e .[doc] + sphinx-build -b html docs/ docs/_build/ + #cd docs/ + #make html + + - name: Deploy 🚀 + uses: JamesIves/github-pages-deploy-action@v4 + if: ${{ github.event_name == 'release' }} + with: + token: ${{ secrets.GITHUB_TOKEN }} + branch: gh-pages + folder: docs/_build/ + #folder: docs/_build/html + clean: false + target-folder: ${{ github.ref }} + + - name: Deploy 🚀 + uses: JamesIves/github-pages-deploy-action@v4 + if: ${{ github.event_name == 'push' }} + with: + token: ${{ secrets.GITHUB_TOKEN }} + branch: gh-pages + folder: docs/_build/ + #folder: docs/_build/html + clean: false + target-folder: latest diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index 51249fe..5ffc8f5 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -1,31 +1,18 @@ -name: Publish package - +name: Publish to PyPI.org on: + workflow_dispatch: release: types: [published] - jobs: - pypi-publish: + pypi: runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - - name: Install build package - run: | - python -m pip install --upgrade pip - python -m pip install build --user - - name: Build distribution - run: | - python -m build --sdist --wheel --outdir dist/ . - - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@master - with: - password: ${{ secrets.PYPI_API_TOKEN }} \ No newline at end of file + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - run: python3 -m pip install --upgrade build && python3 -m build + - name: Publish package + uses: pypa/gh-action-pypi-publish@release/v1 + with: + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..271fe0a --- /dev/null +++ b/README.rst @@ -0,0 +1,127 @@ +================ +pytorch-caney +================ + +Python package for lots of Pytorch tools for geospatial science problems. + +.. image:: https://zenodo.org/badge/472450059.svg + :target: https://zenodo.org/badge/latestdoi/472450059 + +Objectives +------------ + +- Library to process remote sensing imagery using GPU and CPU parallelization. +- Machine Learning and Deep Learning image classification and regression. +- Agnostic array and vector-like data structures. +- User interface environments via Notebooks for easy to use AI/ML projects. +- Example notebooks for quick AI/ML start with your own data. + +Installation +---------------- + +The following library is intended to be used to accelerate the development of data science products +for remote sensing satellite imagery, or any other applications. pytorch-caney can be installed +by itself, but instructions for installing the full environments are listed under the requirements +directory so projects, examples, and notebooks can be run. + +Note: PIP installations do not include CUDA libraries for GPU support. Make sure NVIDIA libraries +are installed locally in the system if not using conda/mamba. + +```bash +module load singularity # if a module needs to be loaded +singularity build --sandbox pytorch-caney-container docker://nasanccs/pytorch-caney:latest +``` + +Why Caney? +--------------- + +"Caney" means longhouse in Taíno. + +Contributors +------------- + +- Jordan Alexis Caraballo-Vega, jordan.a.caraballo-vega@nasa.gov +- Caleb Spradlin, caleb.s.spradlin@nasa.gov + +Contributing +------------- + +Please see our [guide for contributing to pytorch-caney](CONTRIBUTING.md). + +SatVision +------------ + +| name | pretrain | resolution | #params | +| :---: | :---: | :---: | :---: | +| SatVision-B | MODIS-1.9-M | 192x192 | 84.5M | + +SatVision Datasets +----------------------- + +| name | bands | resolution | #chips | +| :---: | :---: | :---: | :---: | +| MODIS-Small | 7 | 128x128 | 1,994,131 | + +Pre-training with Masked Image Modeling +----------------------------------------- + +To pre-train the swinv2 base model with masked image modeling pre-training, run: +```bash +torchrun --nproc_per_node pytorch-caney/pytorch_caney/pipelines/pretraining/mim.py --cfg --dataset --data-paths --batch-size --output --enable-amp +``` + +For example to run on a compute node with 4 GPUs and a batch size of 128 on the MODIS SatVision pre-training dataset with a base swinv2 model, run: + +```bash +singularity shell --nv -B /path/to/container/pytorch-caney-container +Singularity> export PYTHONPATH=$PWD:$PWD/pytorch-caney +Singularity> torchrun --nproc_per_node 4 pytorch-caney/pytorch_caney/pipelines/pretraining/mim.py --cfg pytorch-caney/examples/satvision/mim_pretrain_swinv2_satvision_base_192_window12_800ep.yaml --dataset MODIS --data-paths /explore/nobackup/projects/ilab/data/satvision/pretraining/training_* --batch-size 128 --output . --enable-amp +``` + +This example script runs the exact configuration used to make the SatVision-base model pre-training with MiM and the MODIS pre-training dataset. +```bash +singularity shell --nv -B /path/to/container/pytorch-caney-container +Singularity> cd pytorch-caney/examples/satvision +Singularity> ./run_satvision_pretrain.sh +``` + +Fine-tuning Satvision-base +----------------------------- + +To fine-tune the satvision-base pre-trained model, run: +```bash +torchrun --nproc_per_node pytorch-caney/pytorch_caney/pipelines/finetuning/finetune.py --cfg --pretrained --dataset --data-paths --batch-size --output --enable-amp +``` + +See example config files pytorch-caney/examples/satvision/finetune_satvision_base_*.yaml to see how to structure your config file for fine-tuning. + + +Testing +------------ + +For unittests, run this bash command to run linting and unit test runs. This will execute unit tests and linting in a temporary venv environment only used for testing. +```bash +git clone git@github.com:nasa-nccs-hpda/pytorch-caney.git +cd pytorch-caney; bash test.sh +``` +or run unit tests directly with container or anaconda env + +```bash +git clone git@github.com:nasa-nccs-hpda/pytorch-caney.git +singularity build --sandbox pytorch-caney-container docker://nasanccs/pytorch-caney:latest +singularity shell --nv -B /path/to/container/pytorch-caney-container +cd pytorch-caney; python -m unittest discover pytorch_caney/tests +``` + +```bash +git clone git@github.com:nasa-nccs-hpda/pytorch-caney.git +cd pytorch-caney; conda env create -f requirements/environment_gpu.yml; +conda activate pytorch-caney +python -m unittest discover pytorch_caney/tests +``` +References +------------ + +- `Pytorch Lightning `_ +- `Swin Transformer `_ +- `SimMIM `_ diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +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/conf.py b/docs/conf.py new file mode 100644 index 0000000..7d7639e --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,75 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html +import os +import sys + +sys.path.insert(0, os.path.abspath('..')) + +import pytorch_caney + +# package_path = os.path.abspath('..') +# os.environ['PYTHONPATH'] = ':'.join((package_path, os.environ.get('PYTHONPATH', ''))) + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = 'pytorch-caney' +copyright = '2023, Jordan A. Caraballo-Vega' +author = 'Jordan A. Caraballo-Vega' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx_autodoc_typehints', + 'jupyter_sphinx.execute', + "sphinx.ext.intersphinx", + "sphinx.ext.viewcode", + "sphinx.ext.napoleon", + "sphinx_click.ext", + "sphinx.ext.githubpages", + "nbsphinx", +] + +intersphinx_mapping = { + "pyproj": ("https://pyproj4.github.io/pyproj/stable/", None), + "rasterio": ("https://rasterio.readthedocs.io/en/stable/", None), + "xarray": ("http://xarray.pydata.org/en/stable/", None), +} + +templates_path = ['_templates'] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +#source_suffix ={ +# '.rst': 'restructuredtext', +# '.txt': 'markdown', +# '.md': 'markdown', +# '.ipynb': 'myst-nb' +#} +master_doc = "index" + +version = release = pytorch_caney.__version__ + +pygments_style = "sphinx" + +todo_include_todos = False + +html_theme = 'sphinx_rtd_theme' +html_logo = 'static/DSG_LOGO_REDESIGN.png' + +# html_static_path = ['_static/'] + +myst_enable_extensions = [ + "amsmath", + "colon_fence", + "deflist", + "dollarmath", + "html_image", +] +myst_url_schemes = ("http", "https", "mailto") diff --git a/docs/examples.rst b/docs/examples.rst new file mode 100644 index 0000000..4c49555 --- /dev/null +++ b/docs/examples.rst @@ -0,0 +1,3 @@ +.. toctree:: + :maxdepth: 2 + :caption: Contents: diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..0e6edb0 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,22 @@ +.. pytorch-caney's documentation master file, created by + sphinx-quickstart on Fri Jun 23 11:32:18 2023. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to pytorch-caney's documentation! +========================================= + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + readme + examples + modules + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..32bb245 --- /dev/null +++ b/docs/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 + +%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.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/modules.rst b/docs/modules.rst new file mode 100644 index 0000000..c516a87 --- /dev/null +++ b/docs/modules.rst @@ -0,0 +1,44 @@ +pytorch-caney package +======================== + +pytorch_caney.lr_scheduler +---------------------- + +.. automodule:: pytorch_caney.lr_scheduler + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.metrics +---------------------- + +.. automodule:: pytorch_caney.metrics + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.processing +---------------------- + +.. automodule:: pytorch_caney.processing + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.ptc_logging +---------------------- + +.. automodule:: pytorch_caney.ptc_logging + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.utils +---------------------- + +.. automodule:: pytorch_caney.utils + :members: + :undoc-members: + :show-inheritance: + + diff --git a/docs/pytorch_caney.rst b/docs/pytorch_caney.rst new file mode 100644 index 0000000..ec3ad23 --- /dev/null +++ b/docs/pytorch_caney.rst @@ -0,0 +1,31 @@ +pytorch-caney +------------------ + +Python package for lots of Pytorch tools for geospatial science problems. + +Installation +----------------- + +Install with pip +:: + + pip install pytorch-caney + +API +---- + +.. automodule:: pytorch_caney + :members: + +Authors +---------- + +Jordan A. Caraballo-Vega, jordan.a.caraballo-vega@nasa.gov +Caleb S. Spradlin, caleb.s.spradlin@nasa.gov +Jian Li, jian.li@nasa.gov + +License +--------------- + +The package is released under the `MIT +License `__. diff --git a/docs/readme.rst b/docs/readme.rst new file mode 100644 index 0000000..72a3355 --- /dev/null +++ b/docs/readme.rst @@ -0,0 +1 @@ +.. include:: ../README.rst diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..4fc9df0 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,4 @@ +sphinx +sphinx-autodoc-typehints +jupyter-sphinx +sphinx-rtd-theme diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000..582498c --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,30 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information +import os +import sys +sys.path.insert(0, os.path.abspath('../../tensorflow-caney/')) + +project = 'tensorflow-caney' +copyright = '2023, Jordan A Caraballo-Vega' +author = 'Jordan A Caraballo-Vega' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [] + +templates_path = ['_templates'] +exclude_patterns = [] + + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = 'sphinx_rtd_theme' +html_static_path = ['_static'] diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000..963fbcc --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,20 @@ +.. tensorflow-caney documentation master file, created by + sphinx-quickstart on Fri Jan 13 06:59:19 2023. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to tensorflow-caney's documentation! +============================================ + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/static/DSG_LOGO_REDESIGN.png b/docs/static/DSG_LOGO_REDESIGN.png new file mode 100644 index 0000000000000000000000000000000000000000..79e45381c3f73ffc7c33aeeec0571d9087abb4e6 GIT binary patch literal 51468 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 KSfz+T!2bd=bwc6* literal 0 HcmV?d00001 From def51818c5bafa702b667e277539557160633e38 Mon Sep 17 00:00:00 2001 From: jordancaraballo Date: Mon, 21 Aug 2023 11:33:08 -0400 Subject: [PATCH 03/12] Adding pypi and readthedocs --- README.rst | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/README.rst b/README.rst index 271fe0a..69cd1b1 100644 --- a/README.rst +++ b/README.rst @@ -89,9 +89,10 @@ Fine-tuning Satvision-base ----------------------------- To fine-tune the satvision-base pre-trained model, run: -```bash + +.. code-block:: bash + torchrun --nproc_per_node pytorch-caney/pytorch_caney/pipelines/finetuning/finetune.py --cfg --pretrained --dataset --data-paths --batch-size --output --enable-amp -``` See example config files pytorch-caney/examples/satvision/finetune_satvision_base_*.yaml to see how to structure your config file for fine-tuning. @@ -100,25 +101,31 @@ Testing ------------ For unittests, run this bash command to run linting and unit test runs. This will execute unit tests and linting in a temporary venv environment only used for testing. -```bash + +.. code-block:: bash + git clone git@github.com:nasa-nccs-hpda/pytorch-caney.git cd pytorch-caney; bash test.sh -``` + + or run unit tests directly with container or anaconda env -```bash +.. code-block:: bash + git clone git@github.com:nasa-nccs-hpda/pytorch-caney.git singularity build --sandbox pytorch-caney-container docker://nasanccs/pytorch-caney:latest singularity shell --nv -B /path/to/container/pytorch-caney-container cd pytorch-caney; python -m unittest discover pytorch_caney/tests -``` -```bash + +.. code-block:: bash + git clone git@github.com:nasa-nccs-hpda/pytorch-caney.git cd pytorch-caney; conda env create -f requirements/environment_gpu.yml; conda activate pytorch-caney python -m unittest discover pytorch_caney/tests -``` + + References ------------ From 39e73f28349a0df76b87cd738d44b736a02240b4 Mon Sep 17 00:00:00 2001 From: jordancaraballo Date: Mon, 21 Aug 2023 11:58:23 -0400 Subject: [PATCH 04/12] Adding packages metadata and documentation --- .readthedocs.yaml | 9 +++++++ README.rst | 21 ++++++++++------- docs/conf.py | 24 ------------------- pyproject.toml | 6 +++++ pytorch_caney/__init__.py | 1 + setup.cfg | 49 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 78 insertions(+), 32 deletions(-) create mode 100644 .readthedocs.yaml create mode 100644 pyproject.toml create mode 100644 setup.cfg diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..3859357 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,9 @@ +version: 2 + +build: + os: "ubuntu-20.04" + tools: + python: "3.8" + +sphinx: + fail_on_warning: true diff --git a/README.rst b/README.rst index 69cd1b1..630138e 100644 --- a/README.rst +++ b/README.rst @@ -27,10 +27,11 @@ directory so projects, examples, and notebooks can be run. Note: PIP installations do not include CUDA libraries for GPU support. Make sure NVIDIA libraries are installed locally in the system if not using conda/mamba. -```bash +.. code-block:: bash + module load singularity # if a module needs to be loaded singularity build --sandbox pytorch-caney-container docker://nasanccs/pytorch-caney:latest -``` + Why Caney? --------------- @@ -66,24 +67,28 @@ Pre-training with Masked Image Modeling ----------------------------------------- To pre-train the swinv2 base model with masked image modeling pre-training, run: -```bash + +.. code-block:: bash + torchrun --nproc_per_node pytorch-caney/pytorch_caney/pipelines/pretraining/mim.py --cfg --dataset --data-paths --batch-size --output --enable-amp -``` For example to run on a compute node with 4 GPUs and a batch size of 128 on the MODIS SatVision pre-training dataset with a base swinv2 model, run: -```bash +.. code-block:: bash + singularity shell --nv -B /path/to/container/pytorch-caney-container Singularity> export PYTHONPATH=$PWD:$PWD/pytorch-caney Singularity> torchrun --nproc_per_node 4 pytorch-caney/pytorch_caney/pipelines/pretraining/mim.py --cfg pytorch-caney/examples/satvision/mim_pretrain_swinv2_satvision_base_192_window12_800ep.yaml --dataset MODIS --data-paths /explore/nobackup/projects/ilab/data/satvision/pretraining/training_* --batch-size 128 --output . --enable-amp -``` + This example script runs the exact configuration used to make the SatVision-base model pre-training with MiM and the MODIS pre-training dataset. -```bash + +.. code-block:: bash + singularity shell --nv -B /path/to/container/pytorch-caney-container Singularity> cd pytorch-caney/examples/satvision Singularity> ./run_satvision_pretrain.sh -``` + Fine-tuning Satvision-base ----------------------------- diff --git a/docs/conf.py b/docs/conf.py index 7d7639e..a86bb2f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,7 +1,3 @@ -# Configuration file for the Sphinx documentation builder. -# -# For the full list of built-in configuration values, see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html import os import sys @@ -9,19 +5,10 @@ import pytorch_caney -# package_path = os.path.abspath('..') -# os.environ['PYTHONPATH'] = ':'.join((package_path, os.environ.get('PYTHONPATH', ''))) - -# -- Project information ----------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information - project = 'pytorch-caney' copyright = '2023, Jordan A. Caraballo-Vega' author = 'Jordan A. Caraballo-Vega' -# -- General configuration --------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration - extensions = [ 'sphinx.ext.autodoc', 'sphinx_autodoc_typehints', @@ -43,15 +30,6 @@ templates_path = ['_templates'] exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] -# -- Options for HTML output ------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output - -#source_suffix ={ -# '.rst': 'restructuredtext', -# '.txt': 'markdown', -# '.md': 'markdown', -# '.ipynb': 'myst-nb' -#} master_doc = "index" version = release = pytorch_caney.__version__ @@ -63,8 +41,6 @@ html_theme = 'sphinx_rtd_theme' html_logo = 'static/DSG_LOGO_REDESIGN.png' -# html_static_path = ['_static/'] - myst_enable_extensions = [ "amsmath", "colon_fence", diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..931e61b --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,6 @@ +[build-system] +# Minimum requirements for the build system to execute. +requires = ["setuptools", "wheel"] + +[tool.black] +target_version = ['py39'] diff --git a/pytorch_caney/__init__.py b/pytorch_caney/__init__.py index e69de29..3dc1f76 100755 --- a/pytorch_caney/__init__.py +++ b/pytorch_caney/__init__.py @@ -0,0 +1 @@ +__version__ = "0.1.0" diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..18d44ca --- /dev/null +++ b/setup.cfg @@ -0,0 +1,49 @@ +[metadata] +name = pytorch-caney +version = attr: pytorch_caney.__version__ +description = Methods for pytorch deep learning applications +long_description = file: README.md +long_description_content_type = text/markdown +keywords = pytorch-caney, deep-learning, machine-learning +url = https://github.com/nasa-nccs-hpda/pytorch-caney +author = jordancaraballo +author_email = jordan.a.caraballo-vega@nasa.gov +license = MIT +license_file = LICENSE.md +classifiers = + Development Status :: 4 - Beta + Intended Audience :: Developers + Intended Audience :: Science/Research + Topic :: Software Development :: Libraries :: Python Modules + License :: OSI Approved :: MIT License + Programming Language :: Python :: 3 :: Only +project_urls = + Documentation = https://github.com/nasa-nccs-hpda/pytorch-caney + Source = https://github.com/nasa-nccs-hpda/pytorch-caney + Issues = https://github.com/nasa-nccs-hpda/pytorch-caney/issues + +[options] +packages = find: +zip_safe = True +include_package_data = True +platforms = any +python_requires = >= 3.7 +install_requires = + omegaconf + numpy + pandas + tqdm + xarray + rioxarray + numba + +[options.extras_require] +test = + pytest + coverage[toml] + black +docs = + pdoc==8.0.1 +all = + %(docs)s + %(test)s From f94117b4f6cc3c9a87d2b13d7aebae7267b6c642 Mon Sep 17 00:00:00 2001 From: jordancaraballo Date: Mon, 21 Aug 2023 13:26:05 -0400 Subject: [PATCH 05/12] Removing unncesary conditions from docs --- docs/conf.py | 2 ++ docs/source/conf.py | 30 ------------------------------ 2 files changed, 2 insertions(+), 30 deletions(-) delete mode 100644 docs/source/conf.py diff --git a/docs/conf.py b/docs/conf.py index a86bb2f..887536d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -48,4 +48,6 @@ "dollarmath", "html_image", ] + myst_url_schemes = ("http", "https", "mailto") + diff --git a/docs/source/conf.py b/docs/source/conf.py deleted file mode 100644 index 582498c..0000000 --- a/docs/source/conf.py +++ /dev/null @@ -1,30 +0,0 @@ -# Configuration file for the Sphinx documentation builder. -# -# For the full list of built-in configuration values, see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Project information ----------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information -import os -import sys -sys.path.insert(0, os.path.abspath('../../tensorflow-caney/')) - -project = 'tensorflow-caney' -copyright = '2023, Jordan A Caraballo-Vega' -author = 'Jordan A Caraballo-Vega' - -# -- General configuration --------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration - -extensions = [] - -templates_path = ['_templates'] -exclude_patterns = [] - - - -# -- Options for HTML output ------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output - -html_theme = 'sphinx_rtd_theme' -html_static_path = ['_static'] From c7f0df3b566bce90ed03e0748d05b3529462d35d Mon Sep 17 00:00:00 2001 From: jordancaraballo Date: Mon, 21 Aug 2023 13:46:30 -0400 Subject: [PATCH 06/12] Changes to README --- README.md | 4 +++ README.rst | 71 +++++++++++++++++++++++++++++++++--------------------- 2 files changed, 48 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index d4686d9..702e80a 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,10 @@ Python package for lots of Pytorch tools. [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![Coverage Status](https://coveralls.io/repos/github/nasa-nccs-hpda/pytorch-caney/badge.svg?branch=main)](https://coveralls.io/github/nasa-nccs-hpda/pytorch-caney?branch=main) +## Documentation + +- Latest: https://nasa-nccs-hpda.github.io/pytorch-caney/latest + ## Objectives - Library to process remote sensing imagery using GPU and CPU parallelization. diff --git a/README.rst b/README.rst index 630138e..e1ffaee 100644 --- a/README.rst +++ b/README.rst @@ -29,8 +29,8 @@ are installed locally in the system if not using conda/mamba. .. code-block:: bash -module load singularity # if a module needs to be loaded -singularity build --sandbox pytorch-caney-container docker://nasanccs/pytorch-caney:latest + module load singularity # if a module needs to be loaded + singularity build --sandbox pytorch-caney-container docker://nasanccs/pytorch-caney:latest Why Caney? @@ -52,16 +52,34 @@ Please see our [guide for contributing to pytorch-caney](CONTRIBUTING.md). SatVision ------------ -| name | pretrain | resolution | #params | -| :---: | :---: | :---: | :---: | -| SatVision-B | MODIS-1.9-M | 192x192 | 84.5M | +.. list-table:: Pre-trained Vision Transformers + :widths: 25 25 50 + :header-rows: 1 + + * - Name + - Pretrain + - Resolution + - Parameters + * - SatVision-B + - MODIS-1.9-M + - 192x192 + - 84.5M SatVision Datasets ----------------------- -| name | bands | resolution | #chips | -| :---: | :---: | :---: | :---: | -| MODIS-Small | 7 | 128x128 | 1,994,131 | +.. list-table:: Pre-trained Vision Transformers + :widths: 25 25 50 + :header-rows: 1 + + * - Name + - Bands + - Resolution + - Image Chips + * - MODIS-Small + - 7 + - 128x128 + - 1,994,131 Pre-training with Masked Image Modeling ----------------------------------------- @@ -70,24 +88,24 @@ To pre-train the swinv2 base model with masked image modeling pre-training, run: .. code-block:: bash -torchrun --nproc_per_node pytorch-caney/pytorch_caney/pipelines/pretraining/mim.py --cfg --dataset --data-paths --batch-size --output --enable-amp + torchrun --nproc_per_node pytorch-caney/pytorch_caney/pipelines/pretraining/mim.py --cfg --dataset --data-paths --batch-size --output --enable-amp For example to run on a compute node with 4 GPUs and a batch size of 128 on the MODIS SatVision pre-training dataset with a base swinv2 model, run: .. code-block:: bash -singularity shell --nv -B /path/to/container/pytorch-caney-container -Singularity> export PYTHONPATH=$PWD:$PWD/pytorch-caney -Singularity> torchrun --nproc_per_node 4 pytorch-caney/pytorch_caney/pipelines/pretraining/mim.py --cfg pytorch-caney/examples/satvision/mim_pretrain_swinv2_satvision_base_192_window12_800ep.yaml --dataset MODIS --data-paths /explore/nobackup/projects/ilab/data/satvision/pretraining/training_* --batch-size 128 --output . --enable-amp + singularity shell --nv -B /path/to/container/pytorch-caney-container + Singularity> export PYTHONPATH=$PWD:$PWD/pytorch-caney + Singularity> torchrun --nproc_per_node 4 pytorch-caney/pytorch_caney/pipelines/pretraining/mim.py --cfg pytorch-caney/examples/satvision/mim_pretrain_swinv2_satvision_base_192_window12_800ep.yaml --dataset MODIS --data-paths /explore/nobackup/projects/ilab/data/satvision/pretraining/training_* --batch-size 128 --output . --enable-amp This example script runs the exact configuration used to make the SatVision-base model pre-training with MiM and the MODIS pre-training dataset. .. code-block:: bash -singularity shell --nv -B /path/to/container/pytorch-caney-container -Singularity> cd pytorch-caney/examples/satvision -Singularity> ./run_satvision_pretrain.sh + singularity shell --nv -B /path/to/container/pytorch-caney-container + Singularity> cd pytorch-caney/examples/satvision + Singularity> ./run_satvision_pretrain.sh Fine-tuning Satvision-base @@ -97,7 +115,7 @@ To fine-tune the satvision-base pre-trained model, run: .. code-block:: bash -torchrun --nproc_per_node pytorch-caney/pytorch_caney/pipelines/finetuning/finetune.py --cfg --pretrained --dataset --data-paths --batch-size --output --enable-amp + torchrun --nproc_per_node pytorch-caney/pytorch_caney/pipelines/finetuning/finetune.py --cfg --pretrained --dataset --data-paths --batch-size --output --enable-amp See example config files pytorch-caney/examples/satvision/finetune_satvision_base_*.yaml to see how to structure your config file for fine-tuning. @@ -109,26 +127,25 @@ For unittests, run this bash command to run linting and unit test runs. This wil .. code-block:: bash -git clone git@github.com:nasa-nccs-hpda/pytorch-caney.git -cd pytorch-caney; bash test.sh + git clone git@github.com:nasa-nccs-hpda/pytorch-caney.git + cd pytorch-caney; bash test.sh or run unit tests directly with container or anaconda env .. code-block:: bash -git clone git@github.com:nasa-nccs-hpda/pytorch-caney.git -singularity build --sandbox pytorch-caney-container docker://nasanccs/pytorch-caney:latest -singularity shell --nv -B /path/to/container/pytorch-caney-container -cd pytorch-caney; python -m unittest discover pytorch_caney/tests - + git clone git@github.com:nasa-nccs-hpda/pytorch-caney.git + singularity build --sandbox pytorch-caney-container docker://nasanccs/pytorch-caney:latest + singularity shell --nv -B /path/to/container/pytorch-caney-container + cd pytorch-caney; python -m unittest discover pytorch_caney/tests .. code-block:: bash -git clone git@github.com:nasa-nccs-hpda/pytorch-caney.git -cd pytorch-caney; conda env create -f requirements/environment_gpu.yml; -conda activate pytorch-caney -python -m unittest discover pytorch_caney/tests + git clone git@github.com:nasa-nccs-hpda/pytorch-caney.git + cd pytorch-caney; conda env create -f requirements/environment_gpu.yml; + conda activate pytorch-caney + python -m unittest discover pytorch_caney/tests References From 0793eb77ec1ac1d7bd1ce56bcc046e9a3b1ee58e Mon Sep 17 00:00:00 2001 From: jordancaraballo Date: Mon, 21 Aug 2023 14:57:20 -0400 Subject: [PATCH 07/12] Fixing additional documentation --- README.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index e1ffaee..f805feb 100644 --- a/README.rst +++ b/README.rst @@ -43,16 +43,17 @@ Contributors - Jordan Alexis Caraballo-Vega, jordan.a.caraballo-vega@nasa.gov - Caleb Spradlin, caleb.s.spradlin@nasa.gov +- Jian Li, jian.li@nasa.gov Contributing ------------- -Please see our [guide for contributing to pytorch-caney](CONTRIBUTING.md). +Please see our `guide for contributing to pytorch-caney `_. SatVision ------------ -.. list-table:: Pre-trained Vision Transformers +.. list-table:: Pre-trained Vision Transformer Models :widths: 25 25 50 :header-rows: 1 @@ -68,8 +69,8 @@ SatVision SatVision Datasets ----------------------- -.. list-table:: Pre-trained Vision Transformers - :widths: 25 25 50 +.. list-table:: Pre-trained Vision Transformer Datasets + :widths: 25 25 50 :header-rows: 1 * - Name From 60265605dd54d3c98bbf600066b12b2d70b13ebb Mon Sep 17 00:00:00 2001 From: jordancaraballo Date: Wed, 23 Aug 2023 13:53:03 -0400 Subject: [PATCH 08/12] Adding documentation tags --- docs/modules.rst | 240 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) diff --git a/docs/modules.rst b/docs/modules.rst index c516a87..b687256 100644 --- a/docs/modules.rst +++ b/docs/modules.rst @@ -1,6 +1,102 @@ pytorch-caney package ======================== +pytorch_caney.config +---------------------- + +.. automodule:: pytorch_caney.config + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.data.datamodules.finetune_datamodule +---------------------- + +.. automodule:: pytorch_caney.data.datamodules.finetune_datamodule + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.data.datamodules.mim_datamodule +---------------------- + +.. automodule:: pytorch_caney.data.datamodules.mim_datamodule + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.data.datamodules.segmentation_datamodule +---------------------- + +.. automodule:: pytorch_caney.data.datamodules.segmentation_datamodule + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.data.datamodules.simmim_datamodule +---------------------- + +.. automodule:: pytorch_caney.data.datamodules.simmim_datamodule + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.data.datasets.classification_dataset +---------------------- + +.. automodule:: pytorch_caney.data.datasets.classification_dataset + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.data.datasets.modis_dataset +---------------------- + +.. automodule:: pytorch_caney.data.datasets.modis_dataset + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.data.transforms +---------------------- + +.. automodule:: pytorch_caney.data.transforms + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.data.utils +---------------------- + +.. automodule:: pytorch_caney.data.utils + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.inference +---------------------- + +.. automodule:: pytorch_caney.inference + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.loss.build +---------------------- + +.. automodule:: pytorch_caney.loss.build + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.loss.utils +---------------------- + +.. automodule:: pytorch_caney.loss.utils + :members: + :undoc-members: + :show-inheritance: + pytorch_caney.lr_scheduler ---------------------- @@ -17,6 +113,110 @@ pytorch_caney.metrics :undoc-members: :show-inheritance: +pytorch_caney.models.decoders.unet_decoder +---------------------- + +.. automodule:: pytorch_caney.models.decoders.unet_decoder + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.models.mim.mim +---------------------- + +.. automodule:: pytorch_caney.models.mim.mim + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.models.simmim.simmim +---------------------- + +.. automodule:: pytorch_caney.models.simmim.simmim + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.models.build +---------------------- + +.. automodule:: pytorch_caney.models.build + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.models.maskrcnn_model +---------------------- + +.. automodule:: pytorch_caney.models.maskrcnn_model + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.models.swinv2_model +---------------------- + +.. automodule:: pytorch_caney.models.swinv2_model + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.models.unet_model +---------------------- + +.. automodule:: pytorch_caney.models.unet_model + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.models.unet_swin_model +---------------------- + +.. automodule:: pytorch_caney.models.unet_swin_model + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.network.attention +---------------------- + +.. automodule:: pytorch_caney.network.attention + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.network.mlp +---------------------- + +.. automodule:: pytorch_caney.network.mlp + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.pipelines.finetuning.finetune +---------------------- + +.. automodule:: pytorch_caney.pipelines.finetuning.finetune + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.pipelines.pretraining.mim +---------------------- + +.. automodule:: pytorch_caney.pipelines.pretraining.mim + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.pipelines.modis_segmentation +---------------------- + +.. automodule:: pytorch_caney.pipelines.modis_segmentation + :members: + :undoc-members: + :show-inheritance: + pytorch_caney.processing ---------------------- @@ -33,6 +233,46 @@ pytorch_caney.ptc_logging :undoc-members: :show-inheritance: +pytorch_caney.training.fine_tuning +---------------------- + +.. automodule:: pytorch_caney.training.fine_tuning + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.training.mim_utils +---------------------- + +.. automodule:: pytorch_caney.training.mim_utils + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.training.pre_training +---------------------- + +.. automodule:: pytorch_caney.training.pre_training + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.training.simmim_utils +---------------------- + +.. automodule:: pytorch_caney.training.simmim_utils + :members: + :undoc-members: + :show-inheritance: + +pytorch_caney.training.utils +---------------------- + +.. automodule:: pytorch_caney.training.utils + :members: + :undoc-members: + :show-inheritance: + pytorch_caney.utils ---------------------- From cd2e673cc437fa6d8aec944108c48a80db0c3267 Mon Sep 17 00:00:00 2001 From: jordancaraballo Date: Wed, 23 Aug 2023 13:57:50 -0400 Subject: [PATCH 09/12] Cleaning up documentation --- docs/conf.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 887536d..7c50540 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -3,7 +3,7 @@ sys.path.insert(0, os.path.abspath('..')) -import pytorch_caney +import pytorch_caney # noqa: E402 project = 'pytorch-caney' copyright = '2023, Jordan A. Caraballo-Vega' @@ -50,4 +50,3 @@ ] myst_url_schemes = ("http", "https", "mailto") - From 3b3886f968527d00eb553eb5b36c3abe5c6336a3 Mon Sep 17 00:00:00 2001 From: jordancaraballo Date: Wed, 23 Aug 2023 14:22:26 -0400 Subject: [PATCH 10/12] Adding MODIS details --- README.md | 14 ++++++++++++++ README.rst | 55 ++++++++++++++++++++++++++++++------------------------ 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 702e80a..344f546 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,21 @@ Please see our [guide for contributing to pytorch-caney](CONTRIBUTING.md). | name | bands | resolution | #chips | | :---: | :---: | :---: | :---: | | MODIS-Small | 7 | 128x128 | 1,994,131 | + +## MODIS Surface Reflectance (MOD09GA) Band Details + +| Band Name | Bandwidth | +| :------------: | :-----------: | +| sur_refl_b01_1 | 0.620 - 0.670 | +| sur_refl_b02_1 | 0.841 - 0.876 | +| sur_refl_b03_1 | 0.459 - 0.479 | +| sur_refl_b04_1 | 0.545 - 0.565 | +| sur_refl_b05_1 | 1.230 - 1.250 | +| sur_refl_b06_1 | 1.628 - 1.652 | +| sur_refl_b07_1 | 2.105 - 2.155 | + ## Pre-training with Masked Image Modeling + To pre-train the swinv2 base model with masked image modeling pre-training, run: ```bash torchrun --nproc_per_node pytorch-caney/pytorch_caney/pipelines/pretraining/mim.py --cfg --dataset --data-paths --batch-size --output --enable-amp diff --git a/README.rst b/README.rst index f805feb..9125193 100644 --- a/README.rst +++ b/README.rst @@ -53,34 +53,41 @@ Please see our `guide for contributing to pytorch-caney `_. SatVision ------------ -.. list-table:: Pre-trained Vision Transformer Models - :widths: 25 25 50 - :header-rows: 1 - - * - Name - - Pretrain - - Resolution - - Parameters - * - SatVision-B - - MODIS-1.9-M - - 192x192 - - 84.5M ++---------------+--------------+------------+------------+ +| Name | Pretrain | Resolution | Parameters | ++===============+==============+============+============+ +| SatVision-B | MODIS-1.9-M | 192x192 | 84.5M | ++---------------+--------------+------------+------------+ SatVision Datasets ----------------------- -.. list-table:: Pre-trained Vision Transformer Datasets - :widths: 25 25 50 - :header-rows: 1 - - * - Name - - Bands - - Resolution - - Image Chips - * - MODIS-Small - - 7 - - 128x128 - - 1,994,131 ++---------------+-----------+------------+-------------+ +| Name | Bands | Resolution | Image Chips | ++===============+===========+============+=============+ +| MODIS-Small | 7 | 128x128 | 1,994,131 | ++---------------+-----------+------------+-------------+ + +MODIS Surface Reflectance (MOD09GA) Band Details +------------------------------------------------------ + ++-----------------+---------------+ +| Band Name | Bandwidth | ++=================+===============+ +| sur_refl_b01_1 | 0.620 - 0.670 | ++-----------------+---------------+ +| sur_refl_b02_1 | 0.841 - 0.876 | ++-----------------+---------------+ +| sur_refl_b03_1 | 0.459 - 0.479 | ++-----------------+---------------+ +| sur_refl_b04_1 | 0.545 - 0.565 | ++-----------------+---------------+ +| sur_refl_b05_1 | 1.230 - 1.250 | ++-----------------+---------------+ +| sur_refl_b06_1 | 1.628 - 1.652 | ++-----------------+---------------+ +| sur_refl_b07_1 | 2.105 - 2.155 | ++-----------------+---------------+ Pre-training with Masked Image Modeling ----------------------------------------- From 98fa6f3f58ee11588ad0c996551278b4d9fcedec Mon Sep 17 00:00:00 2001 From: jordancaraballo Date: Wed, 23 Aug 2023 14:27:40 -0400 Subject: [PATCH 11/12] Adding pytorch-caney package to production container --- requirements/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements/Dockerfile b/requirements/Dockerfile index d68964b..5f6eb68 100644 --- a/requirements/Dockerfile +++ b/requirements/Dockerfile @@ -108,7 +108,8 @@ RUN pip --no-cache-dir install omegaconf \ sphinx_rtd_theme \ yacs \ termcolor \ - segmentation-models-pytorch \ + segmentation-models-pytorch \ + pytorch-caney \ GDAL==`ogrinfo --version | grep -Eo '[0-9]\.[0-9]\.[0-9]+'` HEALTHCHECK NONE From 4e822b28d54b647f9aaa4d407bb937271ed86b94 Mon Sep 17 00:00:00 2001 From: cssprad1 Date: Fri, 15 Mar 2024 16:32:47 -0400 Subject: [PATCH 12/12] updated notebook --- notebooks/satvision-toa-reconstruction.ipynb | 65 +++++++++++++------- 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/notebooks/satvision-toa-reconstruction.ipynb b/notebooks/satvision-toa-reconstruction.ipynb index b4b8f1e..2f98f75 100644 --- a/notebooks/satvision-toa-reconstruction.ipynb +++ b/notebooks/satvision-toa-reconstruction.ipynb @@ -7,7 +7,7 @@ "source": [ "# Satvision-TOA Reconstruction Notebook\n", "\n", - "Version: 02.20.24\n", + "Version: 03.15.24\n", "\n", "Env: `Python [conda env:ilab-pytorch]`" ] @@ -59,13 +59,11 @@ "\n", "from pytorch_caney.config import get_config\n", "\n", - "from pytorch_caney.training.mim_utils import load_checkpoint, load_pretrained\n", - "\n", "from pytorch_caney.models.build import build_model\n", "\n", "from pytorch_caney.ptc_logging import create_logger\n", "\n", - "from pytorch_caney.data.datamodules import mim_webdataset_datamodule\n", + "from pytorch_caney.data.datasets.mim_modis_22m_dataset import MODIS22MDataset\n", "\n", "from pytorch_caney.data.transforms import SimmimTransform, SimmimMaskGenerator\n", "\n", @@ -93,10 +91,21 @@ "\n", "git lfs install\n", "\n", - "git clone git@hf.co:nasa-cisto-data-science-group/satvision-toa-base\n", + "git clone git@hf.co:nasa-cisto-data-science-group/satvision-toa-huge\n", "```\n", "\n", - "Note: If using git w/ ssh, make sure you have ssh keys enabled to clone using ssh auth. " + "Note: If using git w/ ssh, make sure you have ssh keys enabled to clone using ssh auth. \n", + "\n", + "If experiencing ssh-related authentication issues:\n", + "```bash\n", + "eval `ssh-agent -s` # starts ssh-agent\n", + "\n", + "ssh-add -l # is your ssh key added to the agent?\n", + "\n", + "ssh-add ~/.ssh/id_xxxx # adds ssh ID to ssh-agent\n", + "\n", + "ssh -T git@hf.co # Should return \"Hi , welcome to Hugging Face.\"\n", + "```" ] }, { @@ -106,10 +115,10 @@ "metadata": {}, "outputs": [], "source": [ - "MODEL_PATH: str = '../../satvision-toa-base/satvision-toa_84M_2M_100.pth'\n", - "CONFIG_PATH: str = '../../satvision-toa-base/mim_pretrain_swinv2_satvision-toa_base_192_window12_800ep.yaml'\n", + "MODEL_PATH: str = '../../satvision-toa-huge/ckpt_epoch_100.pth'\n", + "CONFIG_PATH: str = '../../satvision-toa-huge/mim_pretrain_swinv2_satvision_huge_192_window12_200ep.yaml'\n", "\n", - "BATCH_SIZE: int = 64 # Want to report loss on every image? Change to 1.\n", + "BATCH_SIZE: int = 1 # Want to report loss on every image? Change to 1.\n", "OUTPUT: str = '.'\n", "TAG: str = 'satvision-base-toa-reconstruction'\n", "DATA_PATH: str = '/explore/nobackup/projects/ilab/projects/3DClouds/data/mosaic-v3/webdatasets'\n", @@ -191,7 +200,19 @@ "metadata": {}, "outputs": [], "source": [ - "dataloader = mim_webdataset_datamodule.build_mim_dataloader(config, logger)" + "dataset = MODIS22MDataset(config,\n", + " config.DATA.DATA_PATHS,\n", + " split=\"train\",\n", + " img_size=config.DATA.IMG_SIZE,\n", + " transform=SimmimTransform(config),\n", + " batch_size=config.DATA.BATCH_SIZE).dataset()\n", + "\n", + "dataloader = torch.utils.data.DataLoader(\n", + " dataset,\n", + " batch_size=None, # Change if not using webdataset as underlying dataset type\n", + " num_workers=15,\n", + " shuffle=False,\n", + " pin_memory=True,)" ] }, { @@ -238,7 +259,7 @@ " inputs.extend(img.cpu())\n", " masks.extend(mask.cpu())\n", " outputs.extend(img_recon.cpu())\n", - " losses.append(losses)\n", + " losses.append(loss.cpu())\n", " \n", " return inputs, outputs, masks, losses\n", "\n", @@ -261,7 +282,6 @@ "\n", "\n", "def process_prediction(image, img_recon, mask, rgb_index):\n", - " img_normed = minmax_norm(image.numpy())\n", "\n", " mask = process_mask(mask)\n", " \n", @@ -269,16 +289,19 @@ " blue_idx = rgb_index[1]\n", " green_idx = rgb_index[2]\n", "\n", - " rgb_image = np.stack((img_normed[red_idx, :, :],\n", - " img_normed[blue_idx, :, :],\n", - " img_normed[green_idx, :, :]),\n", - " axis=-1)\n", + " image = image.numpy()\n", + " rgb_image = np.stack((image[red_idx, :, :],\n", + " image[blue_idx, :, :],\n", + " image[green_idx, :, :]),\n", + " axis=-1)\n", + " rgb_image = minmax_norm(rgb_image)\n", "\n", - " img_recon = minmax_norm(img_recon.numpy())\n", + " img_recon = img_recon.numpy()\n", " rgb_image_recon = np.stack((img_recon[red_idx, :, :],\n", " img_recon[blue_idx, :, :],\n", " img_recon[green_idx, :, :]),\n", " axis=-1)\n", + " rgb_image_recon = minmax_norm(rgb_image_recon)\n", "\n", " rgb_masked = np.where(mask == 0, rgb_image, rgb_image_recon)\n", " rgb_image_masked = np.where(mask == 1, 0, rgb_image)\n", @@ -336,7 +359,7 @@ "source": [ "%%time\n", "\n", - "inputs, outputs, masks, losses = predict(model, dataloader, num_batches=5)" + "inputs, outputs, masks, losses = predict(model, dataloader, num_batches=64)" ] }, { @@ -354,9 +377,9 @@ "metadata": {}, "outputs": [], "source": [ - "pdf_path = '../../satvision-toa-reconstruction-pdf-02.20.pdf'\n", - "num_samples = 10 # Number of random samples from the predictions\n", - "rgb_index = [0, 3, 2] # Indices of [Red band, Blue band, Green band]\n", + "pdf_path = '../../satvision-toa-reconstruction-pdf-03.15.16patch.huge.001.pdf'\n", + "num_samples = 25 # Number of random samples from the predictions\n", + "rgb_index = [0, 2, 1] # Indices of [Red band, Blue band, Green band]\n", "\n", "plot_export_pdf(pdf_path, num_samples, inputs, outputs, masks, rgb_index)" ]