├── .dockerignore ├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── tutorial-or-example-request.md └── workflows │ ├── ci-testing.yml │ ├── code-format.yml │ ├── codeql-analysis.yml │ ├── install-pkg.yml │ ├── pypi-release.yml │ ├── sphinx-docs-build.yml │ └── sphinx-docs-deploy.yml ├── .gitignore ├── .gitmodules ├── .nojekyll ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── MANIFEST.in ├── README.md ├── SECURITY.md ├── assets └── demo.png ├── docs ├── Makefile ├── _static │ └── styles.css ├── index.rst ├── make.bat ├── requirements.txt └── source │ ├── conf.py │ ├── modules.rst │ ├── quickvision.datasets.rst │ ├── quickvision.layers.rst │ ├── quickvision.losses.rst │ ├── quickvision.metrics.rst │ ├── quickvision.models.classification.cnn.rst │ ├── quickvision.models.classification.rst │ ├── quickvision.models.components.rst │ ├── quickvision.models.detection.detr.rst │ ├── quickvision.models.detection.faster_rcnn.rst │ ├── quickvision.models.detection.retinanet.rst │ ├── quickvision.models.detection.rst │ ├── quickvision.models.rst │ ├── quickvision.models.segmentation.deeplab.rst │ ├── quickvision.models.segmentation.fcn.rst │ ├── quickvision.models.segmentation.rst │ ├── quickvision.optimizers.rst │ ├── quickvision.pretrained.rst │ ├── quickvision.rst │ └── quickvision.utils.rst ├── examples ├── README.md ├── classification │ ├── intro_to_quickvision_with_w&b.py │ ├── quantization_aware_training.py │ ├── torchvision_train.py │ ├── train_step_example.py │ ├── training_sota_cnns.py │ └── transfer_learning_with_pytorch_lightning.py ├── detection │ ├── detr_coco │ │ ├── coco.py │ │ ├── detr_train.py │ │ └── detr_transforms.py │ ├── quickvision_object_detection_tutorial.py │ └── training_detection_models_with_pytorch_lightning.py └── notebooks │ ├── Intro_to_Quickvision_with_W&B.ipynb │ ├── Quantization_Aware_Training.ipynb │ ├── Quickstart.ipynb │ ├── Quickvision_Object_Detection_Tutorial.ipynb │ ├── Training_Detection_models_with_PyTorch_Lightning.ipynb │ ├── Training_SOTA_CNNs.ipynb │ └── Transfer_Learning_with_PyTorch_Lightning.ipynb ├── quickvision ├── README.md ├── __init__.py ├── datasets │ ├── README.md │ ├── __init__.py │ ├── classification.py │ └── detection.py ├── layers │ ├── README.md │ ├── __init__.py │ ├── act_mish.py │ ├── block_mlp.py │ └── functional │ │ ├── __init__.py │ │ └── act_mish.py ├── losses │ ├── README.md │ ├── __init__.py │ ├── detr_loss.py │ ├── dice_loss.py │ └── functional │ │ ├── __init__.py │ │ └── dice_loss.py ├── metrics │ ├── README.md │ ├── __init__.py │ └── classification.py ├── models │ ├── README.md │ ├── __init__.py │ ├── classification │ │ ├── README.md │ │ ├── __init__.py │ │ └── cnn │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── engine.py │ │ │ ├── lightning_trainer.py │ │ │ ├── model_factory.py │ │ │ └── utils.py │ ├── components │ │ ├── README.md │ │ ├── __init__.py │ │ └── torchvision_backbones.py │ ├── detection │ │ ├── README.md │ │ ├── __init__.py │ │ ├── detr │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── engine.py │ │ │ ├── lightning_trainer.py │ │ │ ├── model_factory.py │ │ │ └── utils.py │ │ ├── faster_rcnn │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── engine.py │ │ │ ├── lightning_trainer.py │ │ │ └── model_factory.py │ │ ├── retinanet │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── engine.py │ │ │ ├── lightning_trainer.py │ │ │ └── model_factory.py │ │ └── utils.py │ └── segmentation │ │ ├── README.md │ │ ├── __init__.py │ │ ├── deeplab │ │ ├── README.md │ │ └── __init__.py │ │ └── fcn │ │ ├── README.md │ │ └── __init__.py ├── optimizers │ ├── README.md │ └── __init__.py ├── pretrained │ ├── README.md │ ├── __init__.py │ ├── _model_zoo.py │ ├── _pretrained_cnns.py │ ├── _pretrained_detection.py │ └── _pretrained_weights.py └── utils │ ├── README.md │ ├── __init__.py │ ├── averaging_utils.py │ ├── early_stopping_utlis.py │ ├── nested_tensor_utils.py │ ├── progress_utils.py │ └── torch_utils.py ├── requirements-test.txt ├── requirements.txt ├── setup.cfg ├── setup.py └── tests ├── README.md ├── assets ├── 10001.png ├── 10002.png ├── csv_dataset.csv └── grace_hopper_517x606.jpg ├── dataset_utils.py ├── py_utils.py ├── test_cnn.py ├── test_datasets.py ├── test_detr.py ├── test_frcnn.py ├── test_layers.py ├── test_losses.py ├── test_retinanet.py ├── test_smoke.py ├── test_torchvision_backbones.py ├── test_utils.py └── torch_utils.py /.dockerignore: -------------------------------------------------------------------------------- 1 | # git 2 | .git 3 | .gitattributes 4 | .gitignore 5 | *.github 6 | 7 | # python 8 | **/__pycache__ 9 | 10 | # Common 11 | *.md 12 | docker-compose.yml 13 | Dockerfile 14 | 15 | # JetBrains 16 | .idea 17 | 18 | # Configutration files 19 | .editorconfig 20 | 21 | # Editor files 22 | *.vscode 23 | *.vs 24 | 25 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Inspired from https://github.com/seanfisk/python-project-template/blob/master/.editorconfig 2 | # More stuff taken from https://github.com/django/django/blob/master/.editorconfig 3 | # -*- mode: conf-unix; -*- 4 | 5 | # EditorConfig is awesome: http://EditorConfig.org 6 | 7 | # top-most EditorConfig file 8 | root = true 9 | 10 | # defaults 11 | [*] 12 | end_of_line = lf 13 | trim_trailing_whitespace = true 14 | insert_final_newline = true 15 | charset = utf-8 16 | 17 | # Use 2 spaces for the HTML files 18 | [*.html] 19 | indent_size = 2 20 | 21 | # The JSON files contain newlines inconsistently 22 | [*.json] 23 | indent_size = 2 24 | insert_final_newline = ignore 25 | 26 | # Minified JavaScript files shouldn't be changed 27 | [**.min.js] 28 | indent_style = ignore 29 | insert_final_newline = ignore 30 | 31 | # Makefiles always use tabs for indentation 32 | [Makefile] 33 | indent_style = tab 34 | 35 | # Batch files use tabs for indentation 36 | [*.bat] 37 | indent_style = tab 38 | 39 | # 4 space indentation 40 | [*.{ini,py,py.tpl,rst}] 41 | indent_style = space 42 | indent_size = 4 43 | 44 | # 4-width tabbed indentation 45 | [*.{sh,bat.tpl,Makefile.tpl}] 46 | indent_style = tab 47 | indent_size = 4 48 | 49 | # 2-width space indentation for GitHub / Travis actions and yml files. 50 | [*.{yml}] 51 | indent_style = space 52 | indent_size = 2 53 | 54 | # Markdown might need whitespaces 55 | [*.md] 56 | trim_trailing_whitespace = false 57 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Basic .gitattributes for a python repo. 2 | # Taken from https://github.com/alexkaratarakis/gitattributes 3 | 4 | # Source files 5 | # ============ 6 | # *.pxd text diff=python 7 | # *.py text diff=python 8 | # *.py3 text diff=python 9 | # *.pyw text diff=python 10 | # *.pyx text diff=python 11 | # *.pyz text diff=pythoncoun 12 | # *.pyi text diff=python 13 | 14 | # Binary files 15 | # ============ 16 | # *.db binary 17 | # *.p binary 18 | # *.pkl binary 19 | # *.pickle binary 20 | # *.pyc binary 21 | # *.pyd binary 22 | # *.pyo binary 23 | 24 | # Jupyter notebook 25 | 26 | # For text count 27 | # *.ipynb text 28 | 29 | # To ignore it use below 30 | *.ipynb linguist-documentation 31 | 32 | # Note: .db, .p, and .pkl files are associated 33 | # with the python modules ``pickle``, ``dbm.*``, 34 | # ``shelve``, ``marshal``, ``anydbm``, & ``bsddb`` 35 | # (among others). 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | ## 🐛 Bug 12 | **Describe the bug** 13 | A clear and concise description of what the bug is. 14 | 15 | **To Reproduce** 16 | Steps to reproduce the behavior: 17 | 1. Go to '...' 18 | 2. Click on '....' 19 | 3. Scroll down to '....' 20 | 4. See error 21 | 22 | 23 | 24 | #### Code sample 25 | 27 | 28 | ### Expected behavior 29 | 30 | 31 | 32 | ### Environment 33 | 34 | - PyTorch Version (e.g., 1.0): 35 | - OS (e.g., Linux): 36 | - How you installed PyTorch (`conda`, `pip`, source): 37 | - Build command you used (if compiling from source): 38 | - Python version: 39 | - CUDA/cuDNN version: 40 | - GPU models and configuration: 41 | - Any other relevant information: 42 | 43 | ### Additional context 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## 🚀 Feature 11 | 12 | 13 | ### Motivation 14 | 15 | 16 | 17 | ### Pitch 18 | 19 | 20 | 21 | ### Alternatives 22 | 23 | 24 | 25 | ### Additional context 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/tutorial-or-example-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Tutorial or Example request 3 | about: 'Suggest a new usage example ' 4 | title: '' 5 | labels: documentation, example request, good first issue, help wanted 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## 📓 New 11 | 12 | **Is this a request for a tutorial or for an example?** 13 | Tutorials are in `.ipynb` format, explaining each step of the process, really detailed, not production like. 14 | 15 | Examples are be in the `.py` format, more production oriented. Ready to be run with arguments from the command line and easy to integrate with wandb sweeps and alike. 16 | 17 | -------------------------------------------------------------------------------- /.github/workflows/ci-testing.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: CI Tests 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | runs-on: ${{ matrix.os }} 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | os: [ubuntu-20.04] 19 | python-version: [3.6, 3.7, 3.8] 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v2 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | pip install flake8 pytest pytest-cov 31 | pip install -r requirements-test.txt 32 | pip install -e . 33 | - name: Lint with flake8 34 | run: | 35 | # stop the build if there are Python syntax errors or undefined names 36 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 37 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 38 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 39 | - name: Run Tests and Generate Coverage Report 40 | run: | 41 | pytest tests/ --cov=quickvision --cov-report=xml 42 | - name: Upload Coverage to CodeCov 43 | uses: codecov/codecov-action@v1 44 | with: 45 | token: ${{ secrets.CODECOV_TOKEN }} 46 | file: ./coverage.xml 47 | flags: unittests 48 | env_vars: OS,PYTHON 49 | name: codecov-umbrella 50 | fail_ci_if_error: true 51 | verbose: true 52 | 53 | -------------------------------------------------------------------------------- /.github/workflows/code-format.yml: -------------------------------------------------------------------------------- 1 | # Taken from pl-bolts. 2 | # https://github.com/PyTorchLightning/pytorch-lightning-bolts/blob/master/.github/workflows/code-format.yml 3 | 4 | name: Check Code formatting 5 | 6 | # see: https://help.github.com/en/actions/reference/events-that-trigger-workflows 7 | on: # Trigger the workflow on push or pull request, but only for the master branch 8 | push: 9 | branches: [master] 10 | pull_request: 11 | branches: [master] 12 | 13 | jobs: 14 | flake8: 15 | runs-on: ubuntu-20.04 16 | steps: 17 | - uses: actions/checkout@master 18 | - uses: actions/setup-python@v2 19 | with: 20 | python-version: 3.7 21 | 22 | - name: Install dependencies 23 | run: | 24 | # python -m pip install --upgrade --user pip 25 | pip install flake8 26 | pip --version 27 | shell: bash 28 | 29 | - name: Flake8 Code Formatting 30 | run: | 31 | flake8 . 32 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [master] 17 | # pull_request: 18 | # # The branches below must be a subset of the branches above 19 | # branches: [master] 20 | schedule: 21 | - cron: "01 10 * * *" 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-20.04 27 | 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | language: ["python"] 32 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 33 | # Learn more: 34 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 35 | 36 | steps: 37 | - name: Checkout repository 38 | uses: actions/checkout@v2 39 | 40 | # Initializes the CodeQL tools for scanning. 41 | - name: Initialize CodeQL 42 | uses: github/codeql-action/init@v1 43 | with: 44 | languages: ${{ matrix.language }} 45 | # If you wish to specify custom queries, you can do so here or in a config file. 46 | # By default, queries listed here will override any specified in a config file. 47 | # Prefix the list here with "+" to use these queries and those in the config file. 48 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 49 | 50 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 51 | # If this step fails, then you should remove it and run the build manually (see below) 52 | - name: Autobuild 53 | uses: github/codeql-action/autobuild@v1 54 | 55 | # ℹ️ Command-line programs to run using the OS shell. 56 | # 📚 https://git.io/JvXDl 57 | 58 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 59 | # and modify them (or add more) to build your code if your project 60 | # uses a compiled language 61 | 62 | #- run: | 63 | # make bootstrap 64 | # make release 65 | 66 | - name: Perform CodeQL Analysis 67 | uses: github/codeql-action/analyze@v1 68 | -------------------------------------------------------------------------------- /.github/workflows/install-pkg.yml: -------------------------------------------------------------------------------- 1 | name: Install Package 2 | 3 | # see: https://help.github.com/en/actions/reference/events-that-trigger-workflows 4 | on: 5 | # Trigger the workflow on push or pull request, 6 | # but only for the master branch 7 | push: 8 | branches: 9 | - master 10 | pull_request: 11 | branches: 12 | - master 13 | 14 | jobs: 15 | pkg-check: 16 | runs-on: ubuntu-20.04 17 | 18 | steps: 19 | - uses: actions/checkout@master 20 | - uses: actions/setup-python@v2 21 | with: 22 | python-version: 3.7 23 | 24 | - name: Create package 25 | run: | 26 | python -m pip install --upgrade pip 27 | python -m pip install setuptools wheel 28 | python setup.py sdist bdist_wheel 29 | - name: Check package 30 | run: | 31 | pip install twine==1.13.0 32 | twine check dist/* 33 | python setup.py clean 34 | 35 | pkg-install: 36 | runs-on: ${{ matrix.os }} 37 | strategy: 38 | fail-fast: false 39 | # max-parallel: 6 40 | matrix: 41 | os: [ubuntu-20.04] 42 | python-version: [3.6, 3.7, 3.8] 43 | 44 | steps: 45 | - uses: actions/checkout@master 46 | - uses: actions/setup-python@v2 47 | with: 48 | python-version: ${{ matrix.python-version }} 49 | 50 | - name: Create and Install package on Ubuntu 51 | if: runner.os == 'Linux' 52 | run: | 53 | python -m pip install --upgrade pip 54 | python -m pip install setuptools wheel 55 | python setup.py bdist_wheel 56 | pip install virtualenv 57 | virtualenv vEnv ; source vEnv/bin/activate 58 | pip install dist/* 59 | cd .. & python 60 | deactivate ; rm -rf vEnv 61 | 62 | - name: Install package on Mac 63 | if: runner.os == 'macOS' 64 | run: | 65 | python -m pip install --upgrade pip 66 | python -m pip install setuptools wheel 67 | python setup.py bdist_wheel 68 | pip install virtualenv 69 | virtualenv vEnv ; source vEnv/bin/activate 70 | pip install dist/* 71 | cd .. & python 72 | deactivate ; rm -rf vEnv 73 | 74 | - name: Install package on Windows 75 | if: runner.os == 'windows' 76 | run: | 77 | python -m pip install --upgrade pip 78 | python -m pip install setuptools wheel 79 | python setup.py bdist_wheel 80 | pip install virtualenv 81 | virtualenv vEnv ; venv\Scripts\activate.bat 82 | cd .. & python 83 | venv\Scripts\deactivate.bat ; rm -r vEnv 84 | 85 | # pip install dist/* Isn't working on windows, need a workaround. 86 | -------------------------------------------------------------------------------- /.github/workflows/pypi-release.yml: -------------------------------------------------------------------------------- 1 | name: PyPi Release 2 | 3 | # https://help.github.com/en/actions/reference/events-that-trigger-workflows 4 | on: 5 | # Trigger the workflow on push or pull request, 6 | # but only for the master branch 7 | push: 8 | branches: 9 | - master 10 | release: 11 | types: 12 | - created 13 | 14 | # based on https://github.com/pypa/gh-action-pypi-publish 15 | 16 | jobs: 17 | build: 18 | runs-on: ubuntu-20.04 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | - uses: actions/setup-python@v2 23 | with: 24 | python-version: 3.7 25 | 26 | - name: Install dependencies 27 | run: >- 28 | python -m pip install --user --upgrade setuptools wheel 29 | - name: Build 30 | run: >- 31 | python setup.py sdist bdist_wheel 32 | # We do this, since failures on test.pypi aren't that bad 33 | - name: Publish to Test PyPI 34 | if: startsWith(github.event.ref, 'refs/tags') || github.event_name == 'release' 35 | uses: pypa/gh-action-pypi-publish@master 36 | with: 37 | user: __token__ 38 | password: ${{ secrets.test_pypi_password }} 39 | repository_url: https://test.pypi.org/legacy/ 40 | 41 | - name: Publish distribution 📦 to PyPI 42 | if: startsWith(github.event.ref, 'refs/tags') || github.event_name == 'release' 43 | uses: pypa/gh-action-pypi-publish@master 44 | with: 45 | user: __token__ 46 | password: ${{ secrets.pypi_password }} 47 | -------------------------------------------------------------------------------- /.github/workflows/sphinx-docs-build.yml: -------------------------------------------------------------------------------- 1 | name: Build Sphinx Documentation 2 | 3 | on: 4 | pull_request: 5 | branches: [ master ] 6 | 7 | jobs: 8 | sphinx-check: 9 | runs-on: ubuntu-20.04 10 | steps: 11 | - uses: actions/checkout@master 12 | - uses: ammaraskar/sphinx-action@master 13 | with: 14 | # git is required to clone the docs theme 15 | # before custom requirement are resolved https://github.com/ammaraskar/sphinx-action/issues/16 16 | pre-build-command: "sudo apt-get update -y && sudo apt-get install -y git && pip install . && pip install -r docs/requirements.txt" 17 | build-command: "sphinx-build -b html source build" 18 | docs-folder: "docs/" 19 | - uses: actions/upload-artifact@master 20 | with: 21 | name: Documentation 22 | path: docs/build/ 23 | -------------------------------------------------------------------------------- /.github/workflows/sphinx-docs-deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Sphinx Docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | sphinx-check: 10 | runs-on: ubuntu-20.04 11 | steps: 12 | - uses: actions/checkout@master 13 | - uses: ammaraskar/sphinx-action@master 14 | with: 15 | # git is required to clone the docs theme 16 | # before custom requirement are resolved https://github.com/ammaraskar/sphinx-action/issues/16 17 | pre-build-command: "sudo apt-get update -y && sudo apt-get install -y git && pip install . && pip install -r docs/requirements.txt" 18 | build-command: "sphinx-build -b html source build" 19 | docs-folder: "docs/" 20 | - name: Deploy Documentation 21 | uses: ad-m/github-push-action@master 22 | with: 23 | branch: gh-pages 24 | directory: master 25 | force: true 26 | # run: | 27 | # # First run the same pipeline as Read-The-Docs 28 | # sudo apt-get install python3-sphinx 29 | # sudo apt-get install -y texlive-latex-extra dvipng texlive-pictures 30 | # sudo apt-get install -y cmake 31 | # pip install . 32 | # pip install -r docs/requirements.txt 33 | # cd docs 34 | # make html 35 | # shell: bash 36 | # - name: 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Files 2 | *.DS_Store 3 | *.vscode 4 | *.idea 5 | *.vs 6 | .vscode 7 | # dotenv 8 | .env 9 | 10 | # GitHub Codespaces Pythonenv 11 | pythonenv*/ 12 | 13 | # Docs 14 | docs/site 15 | 16 | # logs Folder 17 | data/ 18 | tests/data/ 19 | lightning_logs/ 20 | tests/lightning_logs/ 21 | checkpoints/ 22 | 23 | # Models 24 | *.h5 25 | *.hdf5 26 | *.pt 27 | *.bin 28 | *.pth 29 | *.onnx 30 | 31 | # Byte-compiled / optimized / DLL files 32 | __pycache__/ 33 | *.py[cod] 34 | *$py.class 35 | 36 | # C extensions 37 | *.so 38 | 39 | # Distribution / packaging 40 | .Python 41 | build/ 42 | develop-eggs/ 43 | dist/ 44 | downloads/ 45 | eggs/ 46 | .eggs/ 47 | lib/ 48 | lib64/ 49 | parts/ 50 | sdist/ 51 | var/ 52 | wheels/ 53 | pip-wheel-metadata/ 54 | share/python-wheels/ 55 | *.egg-info/ 56 | .installed.cfg 57 | *.egg 58 | MANIFEST 59 | 60 | # PyInstaller 61 | # Usually these files are written by a python script from a template 62 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 63 | *.manifest 64 | *.spec 65 | 66 | # Installer logs 67 | pip-log.txt 68 | pip-delete-this-directory.txt 69 | 70 | # Unit test / coverage reports 71 | htmlcov/ 72 | .tox/ 73 | .nox/ 74 | .coverage 75 | .coverage.* 76 | .cache 77 | nosetests.xml 78 | coverage.xml 79 | *.cover 80 | *.py,cover 81 | .hypothesis/ 82 | .pytest_cache/ 83 | 84 | # Translations 85 | *.mo 86 | *.pot 87 | 88 | # Django stuff: 89 | *.log 90 | local_settings.py 91 | db.sqlite3 92 | db.sqlite3-journal 93 | 94 | # Flask stuff: 95 | instance/ 96 | .webassets-cache 97 | 98 | # Scrapy stuff: 99 | .scrapy 100 | 101 | # Sphinx documentation 102 | docs/_build/ 103 | 104 | # PyBuilder 105 | target/ 106 | 107 | # Jupyter Notebook 108 | .ipynb_checkpoints 109 | 110 | # IPython 111 | profile_default/ 112 | ipython_config.py 113 | 114 | # pyenv 115 | .python-version 116 | 117 | # pipenv 118 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 119 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 120 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 121 | # install all needed dependencies. 122 | #Pipfile.lock 123 | 124 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 125 | __pypackages__/ 126 | 127 | # Celery stuff 128 | celerybeat-schedule 129 | celerybeat.pid 130 | 131 | # SageMath parsed files 132 | *.sage.py 133 | 134 | # Environments 135 | .env 136 | .venv 137 | env/ 138 | venv/ 139 | ENV/ 140 | env.bak/ 141 | venv.bak/ 142 | 143 | # Spyder project settings 144 | .spyderproject 145 | .spyproject 146 | 147 | # Rope project settings 148 | .ropeproject 149 | 150 | # mkdocs documentation 151 | /site 152 | 153 | # mypy 154 | .mypy_cache/ 155 | .dmypy.json 156 | dmypy.json 157 | 158 | # Pyre type checker 159 | .pyre/ 160 | .vscode/settings.json 161 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/.gitmodules -------------------------------------------------------------------------------- /.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/.nojekyll -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/CODEOWNERS -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting Quick-AI at this GitHub repo . All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html), version 1.4. 71 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guide 2 | 3 | **Please, follow these steps** 4 | 5 | ## Step 1: Forking and Installing quickvision 6 | 7 | ​1. Fork the repo to your own github account. Click the Fork button to 8 | create your own repo copy under your GitHub account. Once forked, you're 9 | responsible for keeping your repo copy up-to-date with the upstream 10 | quickvision repo. 11 | 12 | ​2. Download a copy of your remote `/quickvision` repo to your 13 | local machine. This is the working directory where you will make 14 | changes: 15 | 16 | ```bash 17 | $ git clone https://github.com//quickvision 18 | ``` 19 | 20 | 3. Install the requirements. You may use miniconda or conda as well. 21 | 22 | ```bash 23 | $ pip install -r requirements-test.txt 24 | ``` 25 | 26 | 4. Install this package in develop mode. Go to root of this package and run the following: 27 | 28 | ```bash 29 | $ python setup.py develop 30 | ``` 31 | 32 | ## Step 2: Set up upstream repo 33 | 34 | 1. Set the upstream to sync with this repo. This will keep you in sync 35 | with `quickvision` easily. 36 | 37 | ```bash 38 | $ git remote add upstream https://github.com/Quick-AI/quickvision 39 | ``` 40 | 41 | 2. Updating your local repo: Pull the upstream (original) repo. 42 | 43 | ```bash 44 | $ git checkout master 45 | $ git pull upstream master 46 | ``` 47 | 48 | ## Step 3: Creating a new branch 49 | 50 | ```bash 51 | $ git checkout -b 52 | $ git branch 53 | master 54 | * : 55 | ``` 56 | 57 | ## Step 4: Make changes, and commit your file changes 58 | 59 | Stage and commit your changes. 60 | 61 | ``` 62 | git add . 63 | git commit -m "Your meaningful commit message for the change." 64 | ``` 65 | 66 | Add more commits, if necessary. 67 | 68 | ## Step 5: Submitting a Pull Request 69 | 70 | #### 1. Create a pull request git 71 | 72 | Upload your local branch to your remote GitHub repo 73 | (`github.com//quickvision`) 74 | 75 | ```bash 76 | git push origin 77 | ``` 78 | 79 | After the push completes, a message may display a URL to automatically 80 | submit a pull request to the upstream repo. If not, go to the 81 | quickvision main repo and GitHub will prompt you to create a pull 82 | request. 83 | 84 | #### 2. Confirm PR was created 85 | 86 | Ensure your PR is listed 87 | [here](https://github.com/Quick-AI/quickvision/pulls) 88 | 89 | 3. Updating a PR: 90 | 91 | Same as before, normally push changes to your branch and the PR will get automatically updated. 92 | 93 | ```bash 94 | git commit -m "Updated the feature" 95 | git push origin 96 | ``` 97 | 98 | * * * * * 99 | 100 | ## Reviewing Your PR 101 | 102 | Maintainers and other contributors will review your pull request. 103 | Please participate in the discussion and make the requested changes. 104 | When your pull request is approved, it will be merged into the upstream quickvision repo. 105 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Choose the base image from to take. 2 | # Using slim images is best practice 3 | FROM ubuntu:latest 4 | 5 | ARG PYTHON_VERSION=3.6 6 | 7 | # This is one of the best practice. 8 | # This technique is known as “cache busting”. 9 | RUN apt-get update && apt-get install -y --no-install-recommends \ 10 | build-essential \ 11 | cmake \ 12 | git \ 13 | curl 14 | 15 | # add non-root user 16 | RUN useradd --create-home --shell /bin/bash containeruser 17 | USER containeruser 18 | WORKDIR /home/containeruser 19 | 20 | # install miniconda and python 21 | RUN curl -o ~/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh && \ 22 | chmod +x ~/miniconda.sh && \ 23 | ~/miniconda.sh -b -p /home/containeruser/conda && \ 24 | rm ~/miniconda.sh && \ 25 | /home/containeruser/conda/bin/conda clean -ya && \ 26 | /home/containeruser/conda/bin/conda install -y python=$PYTHON_VERSION 27 | 28 | # add conda to path 29 | ENV PATH /home/containeruser/conda/bin:$PATH 30 | 31 | # Now install this repo 32 | # We need only the master branch not all branches 33 | 34 | COPY requirements.txt requirements.txt 35 | COPY requirements-extra.txt requirements-extra.txt 36 | RUN pip install -r requirements.txt && \ 37 | pip install -r requirements-extra.txt && 38 | 39 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | # Adopted from PyTorch Lightning Bolts 2 | 3 | # Include the README and MD Files 4 | include *.md 5 | 6 | # Include the license file 7 | include LICENSE 8 | exclude *.sh 9 | exclude *.toml 10 | exclude *.svg 11 | 12 | # Include the Requirements 13 | include requirements.txt 14 | recursive-include requirements *.txt 15 | 16 | # exclude tests from package 17 | recursive-exclude tests * 18 | recursive-exclude site * 19 | exclude tests 20 | recursive-exclude * __pycache_ 21 | 22 | # Exclude the documentation files 23 | recursive-exclude docs * 24 | exclude docs 25 | 26 | # Exclude build configs 27 | exclude *.yml 28 | 29 | prune .git 30 | prune .github 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Quickvision 2 | 3 | - Faster Computer Vision. 4 | 5 |
6 | 7 | [![GitHub issues](https://img.shields.io/github/issues/Quick-AI/quickvision)](https://github.com/Quick-AI/quickvision/issues) 8 | [![GitHub forks](https://img.shields.io/github/forks/Quick-AI/quickvision)](https://github.com/Quick-AI/quickvision/network) 9 | [![GitHub stars](https://img.shields.io/github/stars/Quick-AI/quickvision)](https://github.com/Quick-AI/quickvision/stargazers) 10 | [![GitHub license](https://img.shields.io/github/license/Quick-AI/quickvision)](https://github.com/Quick-AI/quickvision/blob/master/LICENSE) 11 | [![codecov](https://codecov.io/gh/Quick-AI/quickvision/branch/master/graph/badge.svg?token=VAFPQTQK1I)](https://codecov.io/gh/Quick-AI/quickvision) 12 | 13 | ![PEP8](https://github.com/Quick-AI/quickvision/workflows/Check%20Code%20formatting/badge.svg) 14 | ![CI Tests](https://github.com/Quick-AI/quickvision/workflows/CI%20Tests/badge.svg) 15 | ![Docs](https://github.com/Quick-AI/quickvision/workflows/Deploy%20mkdocs/badge.svg) 16 | ![PyPi Release](https://github.com/Quick-AI/quickvision/workflows/PyPi%20Release/badge.svg) 17 | 18 | [![Slack](https://img.shields.io/badge/slack-chat-green.svg?logo=slack)](https://join.slack.com/t/quickai/shared_invite/zt-iz7tqk3r-IQa4SoxJGIK5WS8VdZhzeQ) 19 | [![Downloads](https://pepy.tech/badge/quickvision)](https://pepy.tech/project/quickvision) 20 | [![Downloads](https://pepy.tech/badge/quickvision/month)](https://pepy.tech/project/quickvision) 21 | [![Downloads](https://pepy.tech/badge/quickvision/week)](https://pepy.tech/project/quickvision) 22 | 23 |
24 | 25 | ![demo](/assets/demo.png) 26 | 27 | ### Install Quickvision 28 | 29 | - Install from PyPi. 30 | - Current stable `release 0.1.1` needs `PyTorch 1.7.1` and `torchvision 0.8.2`. 31 | 32 | ``` 33 | pip install quickvision 34 | ``` 35 | 36 | ## What is Quickvision? 37 | 38 | - Quickvision makes Computer Vision tasks much faster and easier with PyTorch. 39 | 40 | It provides: - 41 | 42 | 1. Easy to use PyTorch native API, for `fit()`, `train_step()`, `val_step()` of models. 43 | 2. Easily customizable and configurable models with various backbones. 44 | 3. A complete PyTorch native interface. All models are `nn.Module`, all the training APIs are optional and not binded to models. 45 | 4. A lightning API which helps to accelerate training over multiple GPUs, TPUs. 46 | 5. A datasets API to convert common data formats very easily and quickly to PyTorch formats. 47 | 6. A minimal package, with very low dependencies. 48 | 49 | - Train your models faster. Quickvision has already implemented the long learning in PyTorch. 50 | 51 | ## Quickvision is just PyTorch!! 52 | 53 | - Quickvision does not make you learn a new library. If you know PyTorch, you are good to go!!! 54 | - Quickvision does not abstract any code from PyTorch, nor implements any custom classes over it. 55 | - It keeps the data format in `Tensor` so that you don't need to convert it. 56 | 57 | ### Do you want just a model with some backbone configuration? 58 | 59 | - Use model made by us. It's just a `nn.Module` which has Tensors only Input and Output format. 60 | - Quickvision provides reference scripts too for training it! 61 | 62 | ### Do you want to train your model but not write lengthy loops? 63 | 64 | - Just use our training methods such as `fit()`, `train_step()`, `val_step()`. 65 | 66 | ### Do you want multi GPU training but worried about model configuration? 67 | 68 | - Just subclass the PyTorch Lightning model! 69 | - Implement the `train_step()`, `val_step()`. 70 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Support Policy 2 | 3 | ## Supported Versions 4 | 5 | - Quickvision tries to stay up to date with Latest PyTorch and torchvision. 6 | - Also, we maintain only the latest version after release. 7 | 8 | | Quickvision Version | Supported PyTorch Version | Supported Torchvision Version | 9 | | ------------------- | ------------------------- | ----------------------------- | 10 | | 0.1.0 | 1.7.0 | 0.8.1 | 11 | | 0.1.1 | 1.7.1 | 0.8.2 | 12 | 13 | ## Reporting a Bug 14 | 15 | - Please file an issue, we would be happy to help. 16 | -------------------------------------------------------------------------------- /assets/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/assets/demo.png -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/_static/styles.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/docs/_static/styles.css -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. quickvision documentation master file, created by 2 | sphinx-quickstart on Mon Nov 30 18:43:31 2020. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Quickvision API 7 | =============== 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | :caption: Contents: 12 | 13 | Indices and tables 14 | ================== 15 | 16 | * :ref:`genindex` 17 | * :ref:`modindex` 18 | * :ref:`search` 19 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx>=2.0, <3.0 2 | sphinx-togglebutton 3 | sphinx-copybutton 4 | sphinx-autodoc-typehints 5 | sphinx_rtd_theme 6 | recommonmark 7 | sphinxcontrib-fulltoc 8 | sphinx-paramlinks 9 | docutils 10 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | 14 | import builtins 15 | import glob 16 | import inspect 17 | import re 18 | import shutil 19 | 20 | import os 21 | import sys 22 | import sphinx_rtd_theme 23 | 24 | PATH_HERE = os.path.abspath(os.path.dirname(__file__)) 25 | PATH_ROOT = os.path.join(PATH_HERE, '..', '..') 26 | sys.path.insert(0, os.path.abspath(PATH_ROOT)) 27 | 28 | 29 | # -- Project information ----------------------------------------------------- 30 | 31 | project = 'quickvision' 32 | copyright = '2020, Quick-AI' 33 | author = 'Quick-AI' 34 | 35 | # The full version, including alpha/beta/rc tags 36 | release = '0.1.0' 37 | 38 | 39 | # -- General configuration --------------------------------------------------- 40 | 41 | # Add any Sphinx extension module names here, as strings. They can be 42 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 43 | # ones. 44 | extensions = ["sphinx_rtd_theme", "sphinx.ext.autodoc", "sphinx_autodoc_typehints", "recommonmark", 45 | "sphinx_copybutton", "sphinx_togglebutton", "sphinx_paramlinks", "sphinx_autodoc_typehints"] 46 | 47 | # Add any paths that contain templates here, relative to this directory. 48 | templates_path = ['_templates'] 49 | 50 | # List of patterns, relative to source directory, that match files and 51 | # directories to ignore when looking for source files. 52 | # This pattern also affects html_static_path and html_extra_path. 53 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 54 | 55 | nbsphinx_execute = 'never' 56 | nbsphinx_allow_errors = True 57 | nbsphinx_requirejs_path = '' 58 | 59 | source_suffix = {'.rst': 'restructuredtext', '.txt': 'markdown', '.md': 'markdown', '.ipynb': 'nbsphinx',} 60 | 61 | # The master toctree document. 62 | master_doc = 'index' 63 | 64 | # The language for content autogenerated by Sphinx. Refer to documentation 65 | # for a list of supported languages. 66 | # 67 | # This is also used if you do content translation via gettext catalogs. 68 | # Usually you set "language" from the command line for these cases. 69 | language = None 70 | 71 | # The name of the Pygments (syntax highlighting) style to use. 72 | pygments_style = None 73 | 74 | # -- Options for HTML output ------------------------------------------------- 75 | 76 | # The theme to use for HTML and HTML Help pages. See the documentation for 77 | # a list of builtin themes. 78 | # 79 | html_theme = 'sphinx_rtd_theme' 80 | 81 | # Add any paths that contain custom static files (such as style sheets) here, 82 | # relative to this directory. They are copied after the builtin static files, 83 | # so a file named "default.css" will overwrite the builtin "default.css". 84 | html_static_path = ['_static'] 85 | 86 | # Example configuration for intersphinx: refer to the Python standard library. 87 | intersphinx_mapping = { 88 | 'pytorch_lightning': ('https://pytorch-lightning.readthedocs.io/en/stable/', None), 89 | 'python': ('https://docs.python.org/3', None), 90 | 'torch': ('https://pytorch.org/docs/stable/', None), 91 | 'numpy': ('https://docs.scipy.org/doc/numpy/', None), 92 | 'PIL': ('https://pillow.readthedocs.io/en/stable/', None), 93 | } 94 | 95 | # -- Options for todo extension ---------------------------------------------- 96 | 97 | # If true, `todo` and `todoList` produce output, else they produce nothing. 98 | todo_include_todos = True 99 | 100 | # define mapping from PyPI names to python imports 101 | PACKAGE_MAPPING = { 102 | 'pytorch-lightning': 'pytorch_lightning', 103 | 'scikit-learn': 'sklearn', 104 | 'Pillow': 'PIL', 105 | 'opencv-python': 'cv2', 106 | } 107 | -------------------------------------------------------------------------------- /docs/source/modules.rst: -------------------------------------------------------------------------------- 1 | quickvision 2 | =========== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | quickvision 8 | -------------------------------------------------------------------------------- /docs/source/quickvision.datasets.rst: -------------------------------------------------------------------------------- 1 | quickvision.datasets package 2 | ============================ 3 | 4 | Submodules 5 | ---------- 6 | 7 | quickvision.datasets.classification module 8 | ------------------------------------------ 9 | 10 | .. automodule:: quickvision.datasets.classification 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | quickvision.datasets.detection module 16 | ------------------------------------- 17 | 18 | .. automodule:: quickvision.datasets.detection 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | Module contents 24 | --------------- 25 | 26 | .. automodule:: quickvision.datasets 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | -------------------------------------------------------------------------------- /docs/source/quickvision.layers.rst: -------------------------------------------------------------------------------- 1 | quickvision.layers package 2 | ========================== 3 | 4 | Module contents 5 | --------------- 6 | 7 | .. automodule:: quickvision.layers 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /docs/source/quickvision.losses.rst: -------------------------------------------------------------------------------- 1 | quickvision.losses package 2 | ========================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | quickvision.losses.detr\_loss module 8 | ------------------------------------ 9 | 10 | .. automodule:: quickvision.losses.detr_loss 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | quickvision.losses.segmentation module 16 | -------------------------------------- 17 | 18 | .. automodule:: quickvision.losses.segmentation 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | Module contents 24 | --------------- 25 | 26 | .. automodule:: quickvision.losses 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | -------------------------------------------------------------------------------- /docs/source/quickvision.metrics.rst: -------------------------------------------------------------------------------- 1 | quickvision.metrics package 2 | =========================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | quickvision.metrics.classification module 8 | ----------------------------------------- 9 | 10 | .. automodule:: quickvision.metrics.classification 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | Module contents 16 | --------------- 17 | 18 | .. automodule:: quickvision.metrics 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | -------------------------------------------------------------------------------- /docs/source/quickvision.models.classification.cnn.rst: -------------------------------------------------------------------------------- 1 | quickvision.models.classification.cnn package 2 | ============================================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | quickvision.models.classification.cnn.engine module 8 | --------------------------------------------------- 9 | 10 | .. automodule:: quickvision.models.classification.cnn.engine 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | quickvision.models.classification.cnn.lightning\_trainer module 16 | --------------------------------------------------------------- 17 | 18 | .. automodule:: quickvision.models.classification.cnn.lightning_trainer 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | quickvision.models.classification.cnn.model\_factory module 24 | ----------------------------------------------------------- 25 | 26 | .. automodule:: quickvision.models.classification.cnn.model_factory 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | quickvision.models.classification.cnn.utils module 32 | -------------------------------------------------- 33 | 34 | .. automodule:: quickvision.models.classification.cnn.utils 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | Module contents 40 | --------------- 41 | 42 | .. automodule:: quickvision.models.classification.cnn 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | -------------------------------------------------------------------------------- /docs/source/quickvision.models.classification.rst: -------------------------------------------------------------------------------- 1 | quickvision.models.classification package 2 | ========================================= 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | quickvision.models.classification.cnn 11 | 12 | Module contents 13 | --------------- 14 | 15 | .. automodule:: quickvision.models.classification 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | -------------------------------------------------------------------------------- /docs/source/quickvision.models.components.rst: -------------------------------------------------------------------------------- 1 | quickvision.models.components package 2 | ===================================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | quickvision.models.components.torchvision\_backbones module 8 | ----------------------------------------------------------- 9 | 10 | .. automodule:: quickvision.models.components.torchvision_backbones 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | Module contents 16 | --------------- 17 | 18 | .. automodule:: quickvision.models.components 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | -------------------------------------------------------------------------------- /docs/source/quickvision.models.detection.detr.rst: -------------------------------------------------------------------------------- 1 | quickvision.models.detection.detr package 2 | ========================================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | quickvision.models.detection.detr.engine module 8 | ----------------------------------------------- 9 | 10 | .. automodule:: quickvision.models.detection.detr.engine 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | quickvision.models.detection.detr.lightning\_trainer module 16 | ----------------------------------------------------------- 17 | 18 | .. automodule:: quickvision.models.detection.detr.lightning_trainer 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | quickvision.models.detection.detr.model\_factory module 24 | ------------------------------------------------------- 25 | 26 | .. automodule:: quickvision.models.detection.detr.model_factory 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | quickvision.models.detection.detr.utils module 32 | ---------------------------------------------- 33 | 34 | .. automodule:: quickvision.models.detection.detr.utils 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | Module contents 40 | --------------- 41 | 42 | .. automodule:: quickvision.models.detection.detr 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | -------------------------------------------------------------------------------- /docs/source/quickvision.models.detection.faster_rcnn.rst: -------------------------------------------------------------------------------- 1 | quickvision.models.detection.faster\_rcnn package 2 | ================================================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | quickvision.models.detection.faster\_rcnn.engine module 8 | ------------------------------------------------------- 9 | 10 | .. automodule:: quickvision.models.detection.faster_rcnn.engine 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | quickvision.models.detection.faster\_rcnn.lightning\_trainer module 16 | ------------------------------------------------------------------- 17 | 18 | .. automodule:: quickvision.models.detection.faster_rcnn.lightning_trainer 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | quickvision.models.detection.faster\_rcnn.model\_factory module 24 | --------------------------------------------------------------- 25 | 26 | .. automodule:: quickvision.models.detection.faster_rcnn.model_factory 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | Module contents 32 | --------------- 33 | 34 | .. automodule:: quickvision.models.detection.faster_rcnn 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | -------------------------------------------------------------------------------- /docs/source/quickvision.models.detection.retinanet.rst: -------------------------------------------------------------------------------- 1 | quickvision.models.detection.retinanet package 2 | ============================================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | quickvision.models.detection.retinanet.engine module 8 | ---------------------------------------------------- 9 | 10 | .. automodule:: quickvision.models.detection.retinanet.engine 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | quickvision.models.detection.retinanet.lightning\_trainer module 16 | ---------------------------------------------------------------- 17 | 18 | .. automodule:: quickvision.models.detection.retinanet.lightning_trainer 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | quickvision.models.detection.retinanet.model\_factory module 24 | ------------------------------------------------------------ 25 | 26 | .. automodule:: quickvision.models.detection.retinanet.model_factory 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | Module contents 32 | --------------- 33 | 34 | .. automodule:: quickvision.models.detection.retinanet 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | -------------------------------------------------------------------------------- /docs/source/quickvision.models.detection.rst: -------------------------------------------------------------------------------- 1 | quickvision.models.detection package 2 | ==================================== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | quickvision.models.detection.detr 11 | quickvision.models.detection.faster_rcnn 12 | quickvision.models.detection.retinanet 13 | 14 | Submodules 15 | ---------- 16 | 17 | quickvision.models.detection.utils module 18 | ----------------------------------------- 19 | 20 | .. automodule:: quickvision.models.detection.utils 21 | :members: 22 | :undoc-members: 23 | :show-inheritance: 24 | 25 | Module contents 26 | --------------- 27 | 28 | .. automodule:: quickvision.models.detection 29 | :members: 30 | :undoc-members: 31 | :show-inheritance: 32 | -------------------------------------------------------------------------------- /docs/source/quickvision.models.rst: -------------------------------------------------------------------------------- 1 | quickvision.models package 2 | ========================== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | quickvision.models.classification 11 | quickvision.models.components 12 | quickvision.models.detection 13 | quickvision.models.segmentation 14 | 15 | Module contents 16 | --------------- 17 | 18 | .. automodule:: quickvision.models 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | -------------------------------------------------------------------------------- /docs/source/quickvision.models.segmentation.deeplab.rst: -------------------------------------------------------------------------------- 1 | quickvision.models.segmentation.deeplab package 2 | =============================================== 3 | 4 | Module contents 5 | --------------- 6 | 7 | .. automodule:: quickvision.models.segmentation.deeplab 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /docs/source/quickvision.models.segmentation.fcn.rst: -------------------------------------------------------------------------------- 1 | quickvision.models.segmentation.fcn package 2 | =========================================== 3 | 4 | Module contents 5 | --------------- 6 | 7 | .. automodule:: quickvision.models.segmentation.fcn 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /docs/source/quickvision.models.segmentation.rst: -------------------------------------------------------------------------------- 1 | quickvision.models.segmentation package 2 | ======================================= 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | quickvision.models.segmentation.deeplab 11 | quickvision.models.segmentation.fcn 12 | 13 | Module contents 14 | --------------- 15 | 16 | .. automodule:: quickvision.models.segmentation 17 | :members: 18 | :undoc-members: 19 | :show-inheritance: 20 | -------------------------------------------------------------------------------- /docs/source/quickvision.optimizers.rst: -------------------------------------------------------------------------------- 1 | quickvision.optimizers package 2 | ============================== 3 | 4 | Module contents 5 | --------------- 6 | 7 | .. automodule:: quickvision.optimizers 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /docs/source/quickvision.pretrained.rst: -------------------------------------------------------------------------------- 1 | quickvision.pretrained package 2 | ============================== 3 | 4 | Module contents 5 | --------------- 6 | 7 | .. automodule:: quickvision.pretrained 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /docs/source/quickvision.rst: -------------------------------------------------------------------------------- 1 | quickvision package 2 | =================== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | quickvision.datasets 11 | quickvision.layers 12 | quickvision.losses 13 | quickvision.metrics 14 | quickvision.models 15 | quickvision.optimizers 16 | quickvision.pretrained 17 | quickvision.utils 18 | 19 | Module contents 20 | --------------- 21 | 22 | .. automodule:: quickvision 23 | :members: 24 | :undoc-members: 25 | :show-inheritance: 26 | -------------------------------------------------------------------------------- /docs/source/quickvision.utils.rst: -------------------------------------------------------------------------------- 1 | quickvision.utils package 2 | ========================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | quickvision.utils.averaging\_utils module 8 | ----------------------------------------- 9 | 10 | .. automodule:: quickvision.utils.averaging_utils 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | quickvision.utils.early\_stopping\_utlis module 16 | ----------------------------------------------- 17 | 18 | .. automodule:: quickvision.utils.early_stopping_utlis 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | quickvision.utils.nested\_tensor\_utils module 24 | ---------------------------------------------- 25 | 26 | .. automodule:: quickvision.utils.nested_tensor_utils 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | quickvision.utils.progress\_utils module 32 | ---------------------------------------- 33 | 34 | .. automodule:: quickvision.utils.progress_utils 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | quickvision.utils.torch\_utils module 40 | ------------------------------------- 41 | 42 | .. automodule:: quickvision.utils.torch_utils 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | Module contents 48 | --------------- 49 | 50 | .. automodule:: quickvision.utils 51 | :members: 52 | :undoc-members: 53 | :show-inheritance: 54 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | ## Quickvision Notebook Examples ! 2 | 3 | - Play Around with Quickvision in these simple Colab Notebooks. 4 | 5 | - [QuickStart](https://colab.research.google.com/drive/1meZSlorxXern_-U0uZxcQSACJW6-VW8x?usp=sharing) 6 | - [Transfer Learning CNNs with PyTorch Lightning](https://colab.research.google.com/drive/1LJuP991cVGZOUCzFHOWSZn0vCOPTg9sc?usp=sharing) 7 | - [Training SOTA CNNs with PyTorch Image Models](https://colab.research.google.com/drive/1pZpTuG2VEFOY6oqWeHusXj7O2CiVjez4?usp=sharing) 8 | - [Quantization Aware Training](https://colab.research.google.com/drive/1GK_GwD3h_b3qyfYESYGaJMHdiuNcGVrM?usp=sharing) 9 | - [Object Detection with PyTorch and Quickvision](https://colab.research.google.com/drive/15iGgoGEMZP6xc4d9uwChvShTEbq-N0PM?usp=sharing) 10 | - [Object Detection with PyTorch Lightning and Quickvision](https://colab.research.google.com/drive/1fTmnTV5ryaug1E-fGWS90kygITELiP8Q?usp=sharing) 11 | - [Using Wandb with Quickvision](https://colab.research.google.com/drive/1uUzoVIvHaEx4Z7P0QLBDeSVIGykhRA4b?usp=sharing) 12 | -------------------------------------------------------------------------------- /examples/classification/intro_to_quickvision_with_w&b.py: -------------------------------------------------------------------------------- 1 | # Author: [@SauravMaheshkar](https://github.com/SauravMaheshkar) 2 | 3 | # 👁 Introducing Quickvision 4 | 5 | # **Quickvision** is a Computer Vision Library built on Top of Torchvision, PyTorch and Lightning 6 | 7 | # It provides: - 8 | 9 | # 1. Easy to use PyTorch native API, for `fit()`, `train_step()`, `val_step()` of models. 10 | # 2. Easily customizable and configurable models with various backbones. 11 | # 3. A complete PyTorch native interface. 12 | # All models are `nn.Module`, all the training APIs are optional and not binded to models. 13 | # 4. A lightning API which helps to accelerate training over multiple GPUs, TPUs. 14 | # 5. A datasets API to convert common data formats very easily and quickly to PyTorch formats. 15 | # 6. A minimal package, with very low dependencies. 16 | 17 | # Quickvision is just PyTorch!! 18 | 19 | # - Quickvision does not make you learn a new library. If you know PyTorch, you are good to go!!! 20 | # - Quickvision does not abstract any code from PyTorch, nor implements any custom classes over it. 21 | # - It keeps the data format in `Tensor` so that you don't need to convert it. 22 | 23 | # Do you want just a model with some backbone configuration? 24 | # - Use model made by us. It's just a `nn.Module` which has Tensors only Input and Output format. 25 | # - Quickvision provides reference scripts too for training it! 26 | 27 | # Do you want to train your model but not write lengthy loops? 28 | # - Just use our training methods such as `fit()`, `train_step()`, `val_step()`. 29 | 30 | # Do you want multi GPU training but worried about model configuration? 31 | # --- 32 | # - Just subclass the PyTorch Lightning model! 33 | # - Implement the `train_step()`, `val_step()`. 34 | 35 | # We'll also use Weights and Biases for Logging. 36 | 37 | # Weights & Biases is a machine learning experiment tracking, dataset versioning, and project collaboration tool. 38 | 39 | # We'll show you how:- 40 | 41 | # 1. Quickvision allows you to bring your own `Dataset`, `Model` or `Code` Recipe 42 | 43 | # 2. You may use models, training functions or Dataset loading utilites from quickvision. 44 | 45 | # 3. Seamless API to connect with Lightning as well. 46 | 47 | # 4. Faster Experimentation with same control with PyTorch or Lightning. 48 | 49 | # 5. Using the `wandb.log` API to log metrics. 50 | 51 | 52 | # 🚀 Install, Import and Login 53 | 54 | # For this tutorial, we'll use the [WideResnet](https://arxiv.org/abs/1605.07146) model 55 | # You can download the stable version of the library using 56 | 57 | # pip install quickvision 58 | 59 | # The current stable release `0.1` requires `PyTorch 1.7` and `torchvision 0.8.1` 60 | 61 | import random 62 | import torch 63 | import numpy as np 64 | import os 65 | 66 | import torch.nn as nn 67 | import torch.optim as optim 68 | import torchvision 69 | import torchvision.transforms as T 70 | from tqdm import tqdm 71 | import pytorch_lightning as pl 72 | import wandb 73 | 74 | from quickvision.models.classification import cnn 75 | 76 | 77 | # Our hero!! 78 | import quickvision 79 | 80 | if __name__ == "__main__": 81 | # Ensure deterministic behavior 82 | torch.backends.cudnn.deterministic = True 83 | random.seed(hash("setting random seeds") % 2**32 - 1) 84 | np.random.seed(hash("improves reproducibility") % 2**32 - 1) 85 | torch.manual_seed(hash("by removing stochasticity") % 2**32 - 1) 86 | torch.cuda.manual_seed_all(hash("so runs are repeatable") % 2**32 - 1) 87 | 88 | device = torch.device("cuda" if torch.cuda.is_available() else "cpu") 89 | 90 | """In order to log data to wandb, you'll need to log in. 91 | 92 | If this is your first time using W&B, you'll need to sign up for a free account at the link that appears. 93 | """ 94 | 95 | wandb.login() 96 | 97 | """# 👷🏻‍♂️ Define Data Loading and the Model 98 | 99 | Quickivision is not binded to any library for transforms. 100 | Let's keep it simple and use torchvision transforms for now. 101 | """ 102 | 103 | # Train and validation Transforms which you would like 104 | train_transforms = T.Compose([T.ToTensor(), T.Normalize((0.5,), (0.5,))]) 105 | valid_transforms = T.Compose([T.ToTensor(), T.Normalize((0.5,), (0.5,))]) 106 | 107 | """We'll use the `cifar10` dataset for this tutorial using the `torchvision.datasets.CIFAR10()` """ 108 | 109 | train_set = torchvision.datasets.CIFAR10("./data", download=True, train=True, transform=train_transforms) 110 | valid_set = torchvision.datasets.CIFAR10("./data", download=True, train=False, transform=valid_transforms) 111 | 112 | # Create data loaders 113 | train_loader = torch.utils.data.DataLoader(train_set, batch_size=32, shuffle=True) 114 | valid_loader = torch.utils.data.DataLoader(valid_set, batch_size=32, shuffle=False) 115 | 116 | """Quickvision Provides simple functions to create models with pretrained weights.""" 117 | 118 | # To create model with imagenet pretrained weights 119 | model = cnn.create_vision_cnn("wide_resnet50_2", num_classes=10, pretrained="imagenet") 120 | 121 | # Alternatively if you don't need pretrained weights 122 | # model_bare = cnn.create_vision_cnn("resnet50", num_classes=10, pretrained=None) 123 | 124 | # It also supports other weights, do check a list which are supported ! 125 | # model_ssl = cnn.create_vision_cnn("resnet50", num_classes=10, pretrained="ssl") 126 | 127 | """Just like in torch we define the criterion and optimizer""" 128 | 129 | criterion = nn.CrossEntropyLoss() 130 | optimizer = optim.Adam(model.parameters(), lr=1e-3) 131 | 132 | """# 💪🏻 Training the Model 133 | 134 | Instead of doing something like 135 | 136 | ``` 137 | model = model.to(device) 138 | for epoch in range(2): 139 | for batch_idx, (inputs, target) in enumerate(train_loader): 140 | optimizer.zero_grad() 141 | inputs = inputs.to(device) 142 | target = target.to(device) 143 | out = model(inputs) 144 | loss = criterion(out, target) 145 | loss.backward() 146 | optimizer.step() 147 | ``` 148 | 149 | Quickvision already implements these boring procedures for you to speed up training ! 150 | 151 | You can use the `.fit()` method as shown to train the model using a single line of code !! 152 | 153 | ``` 154 | history = cnn.fit(model=model, epochs=2, train_loader=train_loader, 155 | val_loader=valid_loader, criterion=criterion, device=device, optimizer=optimizer) 156 | ``` 157 | 158 | If you prefer more granular control you can use our `train_step()` and `val_step()` methods. 159 | We calculate commonly used metrics such as accuracy here for you. 160 | """ 161 | 162 | wandb.init(project="intro-to-quickvision") 163 | 164 | for epoch in tqdm(range(5)): 165 | print() 166 | print(f"Training Epoch = {epoch}") 167 | train_metrics = cnn.train_step(model, train_loader, criterion, device, optimizer) 168 | print() 169 | wandb.log({"Training Top1 acc": train_metrics["top1"], 170 | "Training Top5 acc": train_metrics["top5"], "Training loss": train_metrics["loss"]}) 171 | 172 | print(f"Validating Epoch = {epoch}") 173 | valid_metrics = cnn.val_step(model, valid_loader, criterion, device) 174 | print() 175 | wandb.log({"Validation Top1 acc": valid_metrics["top1"], 176 | "Validation Top5 acc": valid_metrics["top5"], "Validation loss": valid_metrics["loss"]}) 177 | 178 | """You can also train with Lightning! 179 | 180 | * We have the same logics implemented for PyTorch Lightning as well. 181 | * This directly allows you to use all Lighning features such as Multi-GPU training, TPU Training, logging etc. 182 | 183 | Quickly Prototype with Torch, transfer it to Lightning ! 184 | 185 | ``` 186 | model_imagenet = cnn.lit_cnn("resnet18", num_classes=10, pretrained="imagenet") 187 | 188 | gpus = 1 if torch.cuda.is_available() else 0 189 | 190 | # Again use all possible Trainer Params from Lightning here !! 191 | trainer = pl.Trainer(gpus=gpus, max_epochs=2) 192 | trainer.fit(model_imagenet, train_loader, valid_loader) 193 | ``` 194 | 195 | # 🔖 Save the Model 196 | """ 197 | 198 | torch.save(model.state_dict(), os.path.join(wandb.run.dir, 'model.pt')) 199 | torch.onnx.export(model, "model.onnx") 200 | wandb.save("model.onnx") 201 | -------------------------------------------------------------------------------- /examples/classification/quantization_aware_training.py: -------------------------------------------------------------------------------- 1 | """ 2 | Quantization Aware Training with Quickvision 3 | """ 4 | 5 | import torch 6 | import torchvision 7 | import torch.nn as nn 8 | import torch.optim as optim 9 | import torch.nn.functional as F 10 | from tqdm import tqdm 11 | import torchvision.transforms as T 12 | 13 | import torch.quantization 14 | from torch.quantization import convert 15 | from torch.quantization import QuantStub, DeQuantStub 16 | from quickvision.models.classification import cnn 17 | 18 | # Install Quickvision 19 | # pip install -q git+https://github.com/Quick-AI/quickvision.git 20 | 21 | 22 | if __name__ == "__main__": 23 | 24 | train_transforms = T.Compose([T.ToTensor(), T.Normalize((0.5,), (0.5,))]) 25 | valid_transforms = T.Compose([T.ToTensor(), T.Normalize((0.5,), (0.5,))]) 26 | 27 | # Create CIFAR10 Dataset and DataLoaders" 28 | train_dataset = torchvision.datasets.CIFAR10("./data", download=True, train=True, transform=train_transforms) 29 | valid_dataset = torchvision.datasets.CIFAR10("./data", download=True, train=False, transform=valid_transforms) 30 | 31 | TRAIN_BATCH_SIZE = 512 # Training Batch Size 32 | VALID_BATCH_SIZE = 512 # Validation Batch Size 33 | 34 | train_loader = torch.utils.data.DataLoader(train_dataset, TRAIN_BATCH_SIZE, shuffle=True) 35 | valid_loader = torch.utils.data.DataLoader(valid_dataset, VALID_BATCH_SIZE, shuffle=False) 36 | 37 | # Create Quantization Aware Model 38 | 39 | qat_model = cnn.create_vision_cnn("mobilenet_v2", pretrained="imagenet", num_classes=10) 40 | 41 | criterion = nn.CrossEntropyLoss() 42 | optimizer = optim.Adam(qat_model.parameters(), lr=1e-3) 43 | 44 | # Set Quantization Configurations 45 | 46 | qat_model.config = torch.quantization.get_default_qat_qconfig("fbgemm") 47 | _ = torch.quantization.prepare_qat(qat_model, inplace=True) 48 | 49 | # We can fine-tune / train the qat_models on GPU too. 50 | 51 | for param in qat_model.parameters(): 52 | param.requires_grad = True 53 | 54 | device = torch.device("cuda" if torch.cuda.is_available() else "cpu") 55 | _ = qat_model.to(device) 56 | 57 | NUM_TRAIN_BATCHES = 5 # You can pass these too in train step if you want small subset to train 58 | NUM_VAL_BATCHES = 5 # You can pass these too in train step if you want small subset to validate 59 | 60 | # Train with Quickvision ! 61 | 62 | history = cnn.fit(epochs=3, model=qat_model, train_loader=train_loader, 63 | val_loader=valid_loader, criterion=criterion, device=device, 64 | optimizer=optimizer) 65 | 66 | qat_model.cpu() # We need to move to cpu for conversion. 67 | 68 | qat_model_trained = torch.quantization.convert(qat_model, inplace=False) 69 | print("Converted the Quantization aware training model.") 70 | # torch.save(model_quantized_and_trained.state_dict(), config.QAT_SAVE_PATH) 71 | -------------------------------------------------------------------------------- /examples/classification/torchvision_train.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torchvision 3 | import torch.nn as nn 4 | import torch.optim as optim 5 | import torch.nn.functional as F 6 | from tqdm import tqdm 7 | import torchvision.transforms as T 8 | from quickvision.models.classification import cnn 9 | import config 10 | from quickvision import utils 11 | 12 | 13 | def create_cifar10_dataset(train_transforms, valid_transforms): 14 | """Creates CIFAR10 train dataset and a test dataset. 15 | Args: 16 | train_transforms: Transforms to be applied to train dataset. 17 | test_transforms: Transforms to be applied to test dataset. 18 | """ 19 | # This code can be re-used for other torchvision Image Dataset too. 20 | train_set = torchvision.datasets.CIFAR10("./data", download=True, train=True, transform=train_transforms) 21 | valid_set = torchvision.datasets.CIFAR10("./data", download=True, train=False, transform=valid_transforms) 22 | 23 | return train_set, valid_set 24 | 25 | 26 | def create_loaders(train_dataset, valid_dataset, 27 | train_batch_size=32, valid_batch_size=32, num_workers=4, **kwargs): 28 | 29 | """ 30 | Creates train loader and test loader from train and test datasets 31 | Args: 32 | train_dataset: Torchvision train dataset. 33 | valid_dataset: Torchvision valid dataset. 34 | train_batch_size (int) : Default 32, Training Batch size 35 | valid_batch_size (int) : Default 32, Validation Batch size 36 | num_workers (int) : Defualt 1, Number of workers for training and validation. 37 | """ 38 | 39 | train_loader = torch.utils.data.DataLoader(train_dataset, train_batch_size, shuffle=True, 40 | num_workers=num_workers) 41 | 42 | valid_loader = torch.utils.data.DataLoader(valid_dataset, valid_batch_size, shuffle=False, 43 | num_workers=num_workers) 44 | return train_loader, valid_loader 45 | 46 | 47 | device = torch.device("cuda" if torch.cuda.is_available() else "cpu") 48 | 49 | if __name__ == "__main__": 50 | print(f"Setting Seed for the run, seed = {config.SEED}") 51 | utils.seed_everything(config.SEED) 52 | 53 | print("Creating Train and Validation Dataset") 54 | train_set, valid_set = create_cifar10_dataset(config.train_transforms, config.valid_transforms) 55 | print("Train and Validation Datasets Created") 56 | 57 | print("Creating DataLoaders") 58 | train_loader, valid_loader = create_loaders(train_set, train_set, train_batch_size=32, valid_batch_size=32,) 59 | 60 | print("Train and Validation Dataloaders Created") 61 | print("Creating Model") 62 | 63 | # model = model_factory.create_timm_model(config.MODEL_NAME, num_classes=config.NUM_ClASSES, 64 | # in_channels=config.IN_CHANNELS, pretrained=config.PRETRAINED,) 65 | 66 | model = cnn.create_vision_cnn("resnet50", num_classes=10, pretrained="imagenet",) 67 | 68 | if torch.cuda.is_available(): 69 | print("Model Created. Moving it to CUDA") 70 | device = "cuda" 71 | else: 72 | print("Model Created. Training on CPU only") 73 | device = "cpu" 74 | 75 | optimizer = optim.Adam(model.parameters(), lr=config.LEARNING_RATE) 76 | 77 | # Optionially a schedulear 78 | # scheduler = optim.lr_scheduler.CyclicLR(optimizer=optimizer, base_lr=1e-4, max_lr=1e-3, mode="min") 79 | 80 | criterion = nn.CrossEntropyLoss() # All classification problems we need Cross entropy loss 81 | 82 | early_stopper = utils.EarlyStopping(patience=7, verbose=True, path=config.SAVE_PATH) 83 | 84 | history = cnn.fit(model=model, epochs=10, train_loader=train_loader, 85 | valid_loader=valid_loader, criterion=criterion, device=device, 86 | optimizer=optimizer, early_stopper=early_stopper,) 87 | 88 | print("Done !!") 89 | -------------------------------------------------------------------------------- /examples/classification/train_step_example.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torchvision 4 | import torch.optim as optim 5 | import torch.nn.functional as F 6 | from tqdm import tqdm 7 | import torchvision.transforms as T 8 | from quickvision.models.classification import cnn 9 | import config 10 | from quickvision import utils 11 | 12 | 13 | def create_cifar10_dataset(train_transforms, valid_transforms): 14 | """Creates CIFAR10 train dataset and a test dataset. 15 | Args: 16 | train_transforms: Transforms to be applied to train dataset. 17 | test_transforms: Transforms to be applied to test dataset. 18 | """ 19 | # This code can be re-used for other torchvision Image Dataset too. 20 | train_set = torchvision.datasets.CIFAR10("./data", download=True, train=True, transform=train_transforms) 21 | valid_set = torchvision.datasets.CIFAR10("./data", download=True, train=False, transform=valid_transforms) 22 | 23 | return train_set, valid_set 24 | 25 | 26 | def create_loaders(train_dataset, valid_dataset, 27 | train_batch_size=32, valid_batch_size=32, num_workers=4, **kwargs): 28 | 29 | """ 30 | Creates train loader and test loader from train and test datasets 31 | Args: 32 | train_dataset: Torchvision train dataset. 33 | valid_dataset: Torchvision valid dataset. 34 | train_batch_size (int) : Default 32, Training Batch size 35 | valid_batch_size (int) : Default 32, Validation Batch size 36 | num_workers (int) : Defualt 1, Number of workers for training and validation. 37 | """ 38 | 39 | train_loader = torch.utils.data.DataLoader(train_dataset, train_batch_size, shuffle=True, 40 | num_workers=num_workers) 41 | 42 | valid_loader = torch.utils.data.DataLoader(valid_dataset, valid_batch_size, shuffle=False, 43 | num_workers=num_workers) 44 | return train_loader, valid_loader 45 | 46 | 47 | device = torch.device("cuda" if torch.cuda.is_available() else "cpu") 48 | 49 | if __name__ == "__main__": 50 | 51 | MODEL_NAME = "resnet18" 52 | NUM_ClASSES = 10 53 | IN_CHANNELS = 3 54 | PRETRAINED = "imagenet" # If True -> Fine Tuning else Scratch Training 55 | EPOCHS = 5 56 | EARLY_STOPPING = True # If you need early stoppoing for validation loss 57 | SAVE_PATH = f"{MODEL_NAME}.pt" 58 | SEED = 42 59 | 60 | # Train and validation Transforms which you would like 61 | train_transforms = T.Compose([T.ToTensor(), T.Normalize((0.5,), (0.5,))]) 62 | valid_transforms = T.Compose([T.ToTensor(), T.Normalize((0.5,), (0.5,))]) 63 | 64 | utils.seed_everything(SEED) 65 | print(f"Setting Seed for the run, seed = {config.SEED}") 66 | 67 | print("Creating Train and Validation Dataset") 68 | train_set, valid_set = create_cifar10_dataset(train_transforms, valid_transforms) 69 | print("Train and Validation Datasets Created") 70 | 71 | print("Creating DataLoaders") 72 | train_loader, valid_loader = create_loaders(train_set, train_set) 73 | print("Train and Validation Dataloaders Created") 74 | 75 | print("Creating Model") 76 | model = cnn.create_vision_cnn(MODEL_NAME, num_classes=NUM_ClASSES, pretrained="imagenet") 77 | 78 | if torch.cuda.is_available(): 79 | print("Model Created. Moving it to CUDA") 80 | device = "cuda" 81 | else: 82 | print("Model Created. Training on CPU only") 83 | device = "cpu" 84 | 85 | optimizer = optim.Adam(model.parameters(), lr=1e-3) 86 | 87 | criterion = (nn.CrossEntropyLoss()) # All classification problems we need Cross entropy loss 88 | early_stopper = utils.EarlyStopping(patience=7, verbose=True, path=SAVE_PATH) 89 | 90 | for epoch in tqdm(range(EPOCHS)): 91 | print() 92 | print(f"Training Epoch = {epoch}") 93 | train_metrics = cnn.train_step(model, train_loader, criterion, device, optimizer) 94 | print() 95 | 96 | print(f"Validating Epoch = {epoch}") 97 | valid_metrics = cnn.val_step(model, valid_loader, criterion, device) 98 | 99 | validation_loss = valid_metrics["loss"] 100 | early_stopper(validation_loss, model=model) 101 | 102 | if early_stopper.early_stop: 103 | print("Saving Model and Early Stopping") 104 | print("Early Stopping. Ran out of Patience for validation loss") 105 | break 106 | 107 | print("Done Training, Model Saved to Disk") 108 | -------------------------------------------------------------------------------- /examples/classification/training_sota_cnns.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Training SOTA CNNs 3 | 4 | # Quickvision + PyTorch Image Models CNNs 5 | 6 | # - A Huge home to SOTA CNNs is 7 | # [PyTorch Image Models](https://github.com/rwightman/pytorch-image-models) by Ross Wightman. 8 | 9 | # - It has high quality models with ImageNet weights. 10 | 11 | # - Let's train these for Trasnfer Learning Tasks ! 12 | 13 | 14 | # Install PyTorch Image Models 15 | # ! pip install -q timm 16 | # ! pip install -q git+https://github.com/Quick-AI/quickvision.git 17 | 18 | import torch 19 | import torch.optim as optim 20 | from torch.nn import functional as F 21 | from tqdm import tqdm 22 | from torch import nn 23 | from torch.utils.data import DataLoader 24 | import torchvision 25 | import torchvision.transforms as T 26 | 27 | # - A List of PreTrained Models Provided By Timm 28 | 29 | import timm 30 | from pprint import pprint 31 | model_names = timm.list_models(pretrained=True) 32 | pprint(model_names) 33 | 34 | # - Let's stick to EfficientNet and Train it ! 35 | 36 | # CIFAR10 Dataset and Data Loader 37 | 38 | # - We use CIFAR10 Dataset to train on. 39 | # - It is directly available in torchvision 40 | 41 | 42 | if __name__ == "__main__": 43 | 44 | TRAIN_BATCH_SIZE = 512 # Training Batch Size 45 | VALID_BATCH_SIZE = 512 # Validation Batch Size 46 | 47 | train_transforms = T.Compose([T.ToTensor(), T.Normalize((0.5,), (0.5,))]) 48 | valid_transforms = T.Compose([T.ToTensor(), T.Normalize((0.5,), (0.5,))]) 49 | 50 | train_dataset = torchvision.datasets.CIFAR10("./data", download=True, train=True, transform=train_transforms) 51 | valid_dataset = torchvision.datasets.CIFAR10("./data", download=True, train=False, transform=valid_transforms) 52 | 53 | train_loader = DataLoader(train_dataset, TRAIN_BATCH_SIZE, shuffle=True) 54 | valid_loader = DataLoader(valid_dataset, VALID_BATCH_SIZE, shuffle=False) 55 | VALID_BATCH_SIZE = 512 # Validation Batch Size 56 | 57 | # Train EfficientNet from PyTorch Image Models 58 | 59 | model = timm.create_model("efficientnet_b0", pretrained=True, num_classes=10, in_chans=3) 60 | 61 | optimizer = optim.Adam(model.parameters(), lr=1e-3) 62 | criterion = nn.CrossEntropyLoss() 63 | 64 | # Here is where you can use Quickvision's Training Recipe to train these models 65 | 66 | from quickvision.models.classification import cnn 67 | 68 | if torch.cuda.is_available(): 69 | device = "cuda" 70 | else: 71 | device = "cpu" 72 | 73 | history = cnn.fit(model=model, epochs=2, train_loader=train_loader, 74 | val_loader=valid_loader, criterion=criterion, device=device, 75 | optimizer=optimizer) 76 | 77 | # - In return you get a Keras Like History Dictionary. 78 | # - It keeps a track of Metrics 79 | 80 | pprint(history) 81 | 82 | # - You can also get a granular control over these using `train_step`, `val_step` methods. 83 | 84 | EPOCHS = 2 85 | 86 | for epoch in tqdm(range(EPOCHS)): 87 | print() 88 | print(f"Training Epoch = {epoch}") 89 | train_metrics = cnn.train_step(model, train_loader, criterion, device, optimizer) 90 | print() 91 | 92 | print(f"Validating Epoch = {epoch}") 93 | valid_metrics = cnn.val_step(model, valid_loader, criterion, device) 94 | 95 | # - Again, quickvision computes metrics for you ! 96 | # - You can print them to have look ! 97 | 98 | pprint(train_metrics) 99 | 100 | pprint(valid_metrics) 101 | -------------------------------------------------------------------------------- /examples/classification/transfer_learning_with_pytorch_lightning.py: -------------------------------------------------------------------------------- 1 | import pytorch_lightning as pl 2 | import torch 3 | import torch.optim as optim 4 | from torch.nn import functional as F 5 | from torch.utils.data import DataLoader 6 | import torchvision 7 | import torchvision.transforms as T 8 | from quickvision.models.classification.cnn import lit_cnn 9 | 10 | TRAIN_BATCH_SIZE = 512 # Training Batch Size 11 | VALID_BATCH_SIZE = 512 # Validation Batch Size 12 | 13 | 14 | if __name__ == "__main__": 15 | train_transforms = T.Compose([T.ToTensor(), T.Normalize((0.5,), (0.5,))]) 16 | valid_transforms = T.Compose([T.ToTensor(), T.Normalize((0.5,), (0.5,))]) 17 | 18 | train_dataset = torchvision.datasets.CIFAR10("./data", download=True, train=True, transform=train_transforms) 19 | valid_dataset = torchvision.datasets.CIFAR10("./data", download=True, train=False, transform=valid_transforms) 20 | 21 | train_loader = DataLoader(train_dataset, TRAIN_BATCH_SIZE, shuffle=True) 22 | valid_loader = DataLoader(valid_dataset, VALID_BATCH_SIZE, shuffle=False) 23 | 24 | # Create a model with pretrained imagenet weights 25 | 26 | model_imagenet = lit_cnn("resnet18", num_classes=10, pretrained="imagenet") 27 | 28 | # You can pass all the possible Trainer Arguments. 29 | 30 | trainer = pl.Trainer(max_epochs=2, gpus=1) 31 | trainer.fit(model_imagenet, train_loader, valid_loader) 32 | 33 | # Training without any pretrained weights. 34 | 35 | model_ssl = lit_cnn("resnet18", num_classes=10, pretrained=None) 36 | 37 | trainer = pl.Trainer(max_epochs=2, gpus=1) 38 | trainer.fit(model_ssl, train_loader, valid_loader) 39 | 40 | # Custom Training with Lightning ! 41 | 42 | # To write your own Training logic, metrics, logging. Subclass the `lit_cnn` and write your own logic ! 43 | 44 | class CustomTraining(lit_cnn): 45 | def training_step(self, batch, batch_idx): 46 | images, targets = batch 47 | outputs = self.forward(images) 48 | train_loss = F.cross_entropy(outputs, targets, reduction='sum') 49 | # Possible we can compute top-1 and top-5 accuracy here. 50 | return {"loss": train_loss} 51 | 52 | def validation_step(self, batch, batch_idx): 53 | images, targets = batch 54 | outputs = self.forward(images) 55 | val_loss = F.cross_entropy(outputs, targets, reduction='sum') 56 | # Possible we can compute top-1 and top-5 accuracy here. 57 | return {"loss": val_loss} 58 | 59 | def configure_optimizers(self): 60 | return torch.optim.Adam(self.parameters(), lr=self.learning_rate) 61 | 62 | # Create Model provided by Quickvision ! 63 | 64 | model_imagenet = CustomTraining("resnet18", num_classes=10, pretrained="imagenet") 65 | 66 | # Train with PL Trainer 67 | 68 | trainer = pl.Trainer(max_epochs=2, gpus=1) 69 | trainer.fit(model_imagenet, train_loader, valid_loader) 70 | -------------------------------------------------------------------------------- /examples/detection/detr_coco/coco.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved 2 | """ 3 | COCO dataset which returns image_id for evaluation. 4 | 5 | Mostly copy-paste from https://github.com/pytorch/vision/blob/13b35ff/references/detection/coco_utils.py 6 | 7 | https://github.com/facebookresearch/detr 8 | 9 | path/to/coco/ 10 | annotations/ # annotation json files 11 | train2017/ # train images 12 | val2017/ # val images 13 | """ 14 | from pathlib import Path 15 | 16 | import torch 17 | import torch.utils.data 18 | import torchvision 19 | from pycocotools import mask as coco_mask 20 | 21 | import detr_transforms as T 22 | 23 | 24 | class CocoDetection(torchvision.datasets.CocoDetection): 25 | def __init__(self, img_folder, ann_file, transforms, return_masks): 26 | super(CocoDetection, self).__init__(img_folder, ann_file) 27 | self._transforms = transforms 28 | self.prepare = ConvertCocoPolysToMask(return_masks) 29 | 30 | def __getitem__(self, idx): 31 | img, target = super(CocoDetection, self).__getitem__(idx) 32 | image_id = self.ids[idx] 33 | target = {'image_id': image_id, 'annotations': target} 34 | img, target = self.prepare(img, target) 35 | if self._transforms is not None: 36 | img, target = self._transforms(img, target) 37 | return img, target 38 | 39 | 40 | def convert_coco_poly_to_mask(segmentations, height, width): 41 | masks = [] 42 | for polygons in segmentations: 43 | rles = coco_mask.frPyObjects(polygons, height, width) 44 | mask = coco_mask.decode(rles) 45 | if len(mask.shape) < 3: 46 | mask = mask[..., None] 47 | mask = torch.as_tensor(mask, dtype=torch.uint8) 48 | mask = mask.any(dim=2) 49 | masks.append(mask) 50 | if masks: 51 | masks = torch.stack(masks, dim=0) 52 | else: 53 | masks = torch.zeros((0, height, width), dtype=torch.uint8) 54 | return masks 55 | 56 | 57 | class ConvertCocoPolysToMask(object): 58 | def __init__(self, return_masks=False): 59 | self.return_masks = return_masks 60 | 61 | def __call__(self, image, target): 62 | w, h = image.size 63 | 64 | image_id = target["image_id"] 65 | image_id = torch.tensor([image_id]) 66 | 67 | anno = target["annotations"] 68 | 69 | anno = [obj for obj in anno if 'iscrowd' not in obj or obj['iscrowd'] == 0] 70 | 71 | boxes = [obj["bbox"] for obj in anno] 72 | # guard against no boxes via resizing 73 | boxes = torch.as_tensor(boxes, dtype=torch.float32).reshape(-1, 4) 74 | boxes[:, 2:] += boxes[:, :2] 75 | boxes[:, 0::2].clamp_(min=0, max=w) 76 | boxes[:, 1::2].clamp_(min=0, max=h) 77 | 78 | classes = [obj["category_id"] for obj in anno] 79 | classes = torch.tensor(classes, dtype=torch.int64) 80 | 81 | if self.return_masks: 82 | segmentations = [obj["segmentation"] for obj in anno] 83 | masks = convert_coco_poly_to_mask(segmentations, h, w) 84 | 85 | keypoints = None 86 | if anno and "keypoints" in anno[0]: 87 | keypoints = [obj["keypoints"] for obj in anno] 88 | keypoints = torch.as_tensor(keypoints, dtype=torch.float32) 89 | num_keypoints = keypoints.shape[0] 90 | if num_keypoints: 91 | keypoints = keypoints.view(num_keypoints, -1, 3) 92 | 93 | keep = (boxes[:, 3] > boxes[:, 1]) & (boxes[:, 2] > boxes[:, 0]) 94 | boxes = boxes[keep] 95 | classes = classes[keep] 96 | if self.return_masks: 97 | masks = masks[keep] 98 | if keypoints is not None: 99 | keypoints = keypoints[keep] 100 | 101 | target = {} 102 | target["boxes"] = boxes 103 | target["labels"] = classes 104 | if self.return_masks: 105 | target["masks"] = masks 106 | target["image_id"] = image_id 107 | if keypoints is not None: 108 | target["keypoints"] = keypoints 109 | 110 | # for conversion to coco api 111 | area = torch.tensor([obj["area"] for obj in anno]) 112 | iscrowd = torch.tensor([obj["iscrowd"] if "iscrowd" in obj else 0 for obj in anno]) 113 | target["area"] = area[keep] 114 | target["iscrowd"] = iscrowd[keep] 115 | 116 | target["orig_size"] = torch.as_tensor([int(h), int(w)]) 117 | target["size"] = torch.as_tensor([int(h), int(w)]) 118 | 119 | return image, target 120 | 121 | 122 | def make_coco_transforms(image_set): 123 | 124 | normalize = T.Compose([ 125 | T.ToTensor(), 126 | T.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) 127 | ]) 128 | 129 | scales = [480, 512, 544, 576, 608, 640, 672, 704, 736, 768, 800] 130 | 131 | if image_set == 'train': 132 | return T.Compose([ 133 | T.RandomHorizontalFlip(), 134 | T.RandomSelect( 135 | T.RandomResize(scales, max_size=1333), 136 | T.Compose([ 137 | T.RandomResize([400, 500, 600]), 138 | T.RandomSizeCrop(384, 600), 139 | T.RandomResize(scales, max_size=1333), 140 | ]) 141 | ), 142 | normalize, 143 | ]) 144 | 145 | if image_set == 'val': 146 | return T.Compose([ 147 | T.RandomResize([800], max_size=1333), 148 | normalize, 149 | ]) 150 | 151 | raise ValueError(f'unknown {image_set}') 152 | 153 | 154 | def build(image_set, args): 155 | root = Path(args.coco_path) 156 | assert root.exists(), f'provided COCO path {root} does not exist' 157 | mode = 'instances' 158 | PATHS = { 159 | "train": (root / "train2017", root / "annotations" / f'{mode}_train2017.json'), 160 | "val": (root / "val2017", root / "annotations" / f'{mode}_val2017.json'), 161 | } 162 | 163 | img_folder, ann_file = PATHS[image_set] 164 | dataset = CocoDetection(img_folder, ann_file, transforms=make_coco_transforms(image_set), return_masks=args.masks) 165 | return dataset 166 | -------------------------------------------------------------------------------- /examples/detection/detr_coco/detr_train.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | from argparse import ArgumentParser 4 | import torch 5 | import torch.utils.data 6 | from pprint import pprint 7 | from tqdm import tqdm 8 | import torchvision.datasets as dset 9 | import torchvision.transforms as T 10 | from PIL import Image 11 | import pytorch_lightning as pl 12 | from quickvision.models.detection.detr import lit_detr 13 | from coco import build as build_dataset 14 | from pytorch_lightning.loggers import WandbLogger 15 | 16 | 17 | def get_args(): 18 | parser = ArgumentParser() 19 | parser.add_argument("--coco_path", type=str) 20 | parser.add_argument("--masks", action="store_true", help="Train segmentation head if the flag is provided") 21 | args = parser.parse_args() 22 | args.coco_path = os.path.expandvars(args.coco_path) 23 | return args 24 | 25 | 26 | def collate_fn(batch): 27 | return tuple(zip(*batch)) 28 | 29 | 30 | if __name__ == "__main__": 31 | args = get_args() 32 | dataset = build_dataset("train", args) 33 | dataset_test = build_dataset("val", args) 34 | num_workers = 4 35 | # define training and validation data loaders 36 | train_loader = torch.utils.data.DataLoader(dataset, batch_size=2, shuffle=True, 37 | num_workers=num_workers, collate_fn=collate_fn) 38 | 39 | test_loader = torch.utils.data.DataLoader(dataset_test, batch_size=1, shuffle=False, 40 | num_workers=num_workers, collate_fn=collate_fn) 41 | 42 | num_classes = 91 43 | model = lit_detr(learning_rate=1e-3, num_classes=num_classes, backbone="resnet50", pretrained="coco",) 44 | 45 | wandb_logger = WandbLogger(project="coco", tags=["quickvision", "detr", "imagenet"]) 46 | trainer = pl.Trainer(max_epochs=1000, gpus=1, logger=wandb_logger) 47 | trainer.fit(model, train_loader, test_loader) 48 | -------------------------------------------------------------------------------- /examples/detection/detr_coco/detr_transforms.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved 2 | """ 3 | Transforms and data augmentation for both image + bbox. 4 | """ 5 | import random 6 | 7 | import PIL 8 | import torch 9 | import torchvision.transforms as T 10 | import torchvision.transforms.functional as F 11 | from torchvision.ops import box_convert 12 | 13 | from torchvision.ops.misc import interpolate 14 | 15 | 16 | def crop(image, target, region): 17 | cropped_image = F.crop(image, *region) 18 | 19 | target = target.copy() 20 | i, j, h, w = region 21 | 22 | # should we do something wrt the original size? 23 | target["size"] = torch.tensor([h, w]) 24 | 25 | fields = ["labels", "area", "iscrowd"] 26 | 27 | if "boxes" in target: 28 | boxes = target["boxes"] 29 | max_size = torch.as_tensor([w, h], dtype=torch.float32) 30 | cropped_boxes = boxes - torch.as_tensor([j, i, j, i]) 31 | cropped_boxes = torch.min(cropped_boxes.reshape(-1, 2, 2), max_size) 32 | cropped_boxes = cropped_boxes.clamp(min=0) 33 | area = (cropped_boxes[:, 1, :] - cropped_boxes[:, 0, :]).prod(dim=1) 34 | target["boxes"] = cropped_boxes.reshape(-1, 4) 35 | target["area"] = area 36 | fields.append("boxes") 37 | 38 | if "masks" in target: 39 | # FIXME should we update the area here if there are no boxes? 40 | target['masks'] = target['masks'][:, i:i + h, j:j + w] 41 | fields.append("masks") 42 | 43 | # remove elements for which the boxes or masks that have zero area 44 | if "boxes" in target or "masks" in target: 45 | # favor boxes selection when defining which elements to keep 46 | # this is compatible with previous implementation 47 | if "boxes" in target: 48 | cropped_boxes = target['boxes'].reshape(-1, 2, 2) 49 | keep = torch.all(cropped_boxes[:, 1, :] > cropped_boxes[:, 0, :], dim=1) 50 | else: 51 | keep = target['masks'].flatten(1).any(1) 52 | 53 | for field in fields: 54 | target[field] = target[field][keep] 55 | 56 | return cropped_image, target 57 | 58 | 59 | def hflip(image, target): 60 | flipped_image = F.hflip(image) 61 | 62 | w, h = image.size 63 | 64 | target = target.copy() 65 | if "boxes" in target: 66 | boxes = target["boxes"] 67 | boxes = boxes[:, [2, 1, 0, 3]] * torch.as_tensor([-1, 1, -1, 1]) + torch.as_tensor([w, 0, w, 0]) 68 | target["boxes"] = boxes 69 | 70 | if "masks" in target: 71 | target['masks'] = target['masks'].flip(-1) 72 | 73 | return flipped_image, target 74 | 75 | 76 | def resize(image, target, size, max_size=None): 77 | # size can be min_size (scalar) or (w, h) tuple 78 | 79 | def get_size_with_aspect_ratio(image_size, size, max_size=None): 80 | w, h = image_size 81 | if max_size is not None: 82 | min_original_size = float(min((w, h))) 83 | max_original_size = float(max((w, h))) 84 | if max_original_size / min_original_size * size > max_size: 85 | size = int(round(max_size * min_original_size / max_original_size)) 86 | 87 | if (w <= h and w == size) or (h <= w and h == size): 88 | return (h, w) 89 | 90 | if w < h: 91 | ow = size 92 | oh = int(size * h / w) 93 | else: 94 | oh = size 95 | ow = int(size * w / h) 96 | 97 | return (oh, ow) 98 | 99 | def get_size(image_size, size, max_size=None): 100 | if isinstance(size, (list, tuple)): 101 | return size[::-1] 102 | else: 103 | return get_size_with_aspect_ratio(image_size, size, max_size) 104 | 105 | size = get_size(image.size, size, max_size) 106 | rescaled_image = F.resize(image, size) 107 | 108 | if target is None: 109 | return rescaled_image, None 110 | 111 | ratios = tuple(float(s) / float(s_orig) for s, s_orig in zip(rescaled_image.size, image.size)) 112 | ratio_width, ratio_height = ratios 113 | 114 | target = target.copy() 115 | if "boxes" in target: 116 | boxes = target["boxes"] 117 | scaled_boxes = boxes * torch.as_tensor([ratio_width, ratio_height, ratio_width, ratio_height]) 118 | target["boxes"] = scaled_boxes 119 | 120 | if "area" in target: 121 | area = target["area"] 122 | scaled_area = area * (ratio_width * ratio_height) 123 | target["area"] = scaled_area 124 | 125 | h, w = size 126 | target["size"] = torch.tensor([h, w]) 127 | 128 | if "masks" in target: 129 | target['masks'] = interpolate( 130 | target['masks'][:, None].float(), size, mode="nearest")[:, 0] > 0.5 131 | 132 | return rescaled_image, target 133 | 134 | 135 | def pad(image, target, padding): 136 | # assumes that we only pad on the bottom right corners 137 | padded_image = F.pad(image, (0, 0, padding[0], padding[1])) 138 | if target is None: 139 | return padded_image, None 140 | target = target.copy() 141 | # should we do something wrt the original size? 142 | target["size"] = torch.tensor(padded_image.size[::-1]) 143 | if "masks" in target: 144 | target['masks'] = torch.nn.functional.pad(target['masks'], (0, padding[0], 0, padding[1])) 145 | return padded_image, target 146 | 147 | 148 | class RandomCrop(object): 149 | def __init__(self, size): 150 | self.size = size 151 | 152 | def __call__(self, img, target): 153 | region = T.RandomCrop.get_params(img, self.size) 154 | return crop(img, target, region) 155 | 156 | 157 | class RandomSizeCrop(object): 158 | def __init__(self, min_size: int, max_size: int): 159 | self.min_size = min_size 160 | self.max_size = max_size 161 | 162 | def __call__(self, img: PIL.Image.Image, target: dict): 163 | w = random.randint(self.min_size, min(img.width, self.max_size)) 164 | h = random.randint(self.min_size, min(img.height, self.max_size)) 165 | region = T.RandomCrop.get_params(img, [h, w]) 166 | return crop(img, target, region) 167 | 168 | 169 | class CenterCrop(object): 170 | def __init__(self, size): 171 | self.size = size 172 | 173 | def __call__(self, img, target): 174 | image_width, image_height = img.size 175 | crop_height, crop_width = self.size 176 | crop_top = int(round((image_height - crop_height) / 2.)) 177 | crop_left = int(round((image_width - crop_width) / 2.)) 178 | return crop(img, target, (crop_top, crop_left, crop_height, crop_width)) 179 | 180 | 181 | class RandomHorizontalFlip(object): 182 | def __init__(self, p=0.5): 183 | self.p = p 184 | 185 | def __call__(self, img, target): 186 | if random.random() < self.p: 187 | return hflip(img, target) 188 | return img, target 189 | 190 | 191 | class RandomResize(object): 192 | def __init__(self, sizes, max_size=None): 193 | assert isinstance(sizes, (list, tuple)) 194 | self.sizes = sizes 195 | self.max_size = max_size 196 | 197 | def __call__(self, img, target=None): 198 | size = random.choice(self.sizes) 199 | return resize(img, target, size, self.max_size) 200 | 201 | 202 | class RandomPad(object): 203 | def __init__(self, max_pad): 204 | self.max_pad = max_pad 205 | 206 | def __call__(self, img, target): 207 | pad_x = random.randint(0, self.max_pad) 208 | pad_y = random.randint(0, self.max_pad) 209 | return pad(img, target, (pad_x, pad_y)) 210 | 211 | 212 | class RandomSelect(object): 213 | """ 214 | Randomly selects between transforms1 and transforms2, 215 | with probability p for transforms1 and (1 - p) for transforms2 216 | """ 217 | def __init__(self, transforms1, transforms2, p=0.5): 218 | self.transforms1 = transforms1 219 | self.transforms2 = transforms2 220 | self.p = p 221 | 222 | def __call__(self, img, target): 223 | if random.random() < self.p: 224 | return self.transforms1(img, target) 225 | return self.transforms2(img, target) 226 | 227 | 228 | class ToTensor(object): 229 | def __call__(self, img, target): 230 | return F.to_tensor(img), target 231 | 232 | 233 | class RandomErasing(object): 234 | 235 | def __init__(self, *args, **kwargs): 236 | self.eraser = T.RandomErasing(*args, **kwargs) 237 | 238 | def __call__(self, img, target): 239 | return self.eraser(img), target 240 | 241 | 242 | class Normalize(object): 243 | def __init__(self, mean, std): 244 | self.mean = mean 245 | self.std = std 246 | 247 | def __call__(self, image, target=None): 248 | image = F.normalize(image, mean=self.mean, std=self.std) 249 | if target is None: 250 | return image, None 251 | target = target.copy() 252 | h, w = image.shape[-2:] 253 | if "boxes" in target: 254 | boxes = target["boxes"] 255 | boxes = box_convert(boxes, in_fmt="xyxy", out_fmt="cxcywh") 256 | boxes = boxes / torch.tensor([w, h, w, h], dtype=torch.float32) 257 | target["boxes"] = boxes 258 | return image, target 259 | 260 | 261 | class Compose(object): 262 | def __init__(self, transforms): 263 | self.transforms = transforms 264 | 265 | def __call__(self, image, target): 266 | for t in self.transforms: 267 | image, target = t(image, target) 268 | return image, target 269 | 270 | def __repr__(self): 271 | format_string = self.__class__.__name__ + "(" 272 | for t in self.transforms: 273 | format_string += "\n" 274 | format_string += " {0}".format(t) 275 | format_string += "\n)" 276 | return format_string 277 | -------------------------------------------------------------------------------- /examples/detection/quickvision_object_detection_tutorial.py: -------------------------------------------------------------------------------- 1 | 2 | # Training Object Detection Models with Quickvision ! 3 | 4 | # Defining the Dataset 5 | 6 | # The dataset should inherit from the standard 7 | # `torch.utils.data.Dataset` class, and implement `__len__` and `__getitem__`. 8 | 9 | # The only specificity that we require is that the dataset `__getitem__` should return: 10 | 11 | # * image: a PIL Image of size (H, W) 12 | # * target: a dict containing the following fields 13 | # * `boxes` (`FloatTensor[N, 4]`): the coordinates of the 14 | # `N` bounding boxes in `[x0, y0, x1, y1]` format, ranging from `0` to `W` and `0` to `H` 15 | # * `labels` (`Int64Tensor[N]`): the label for each bounding box 16 | 17 | # Writing a custom dataset for Penn-Fudan 18 | 19 | # Let's write a dataset for the Penn-Fudan dataset. 20 | 21 | # First, let's download and extract the data, present in 22 | # a zip file at https://www.cis.upenn.edu/~jshi/ped_html/PennFudanPed.zip 23 | 24 | # download the Penn-Fudan dataset 25 | # $ wget https://www.cis.upenn.edu/~jshi/ped_html/PennFudanPed.zip . 26 | # $ extract it in the current folder 27 | # $ unzip PennFudanPed.zip 28 | 29 | # Let's have a look at the dataset and how it is layed down. 30 | 31 | # The data is structured as follows 32 | 33 | # PennFudanPed/ 34 | # PedMasks/ 35 | # FudanPed00001_mask.png 36 | # FudanPed00002_mask.png 37 | # FudanPed00003_mask.png 38 | # FudanPed00004_mask.png 39 | # ... 40 | # PNGImages/ 41 | # FudanPed00001.png 42 | # FudanPed00002.png 43 | # FudanPed00003.png 44 | # FudanPed00004.png 45 | 46 | import os 47 | import numpy as np 48 | import torch 49 | import torch.utils.data 50 | from pprint import pprint 51 | from tqdm import tqdm 52 | import torchvision.transforms as T 53 | from PIL import Image 54 | from quickvision.models.detection import faster_rcnn 55 | from quickvision.models.detection import retinanet 56 | from torch.cuda import amp 57 | 58 | 59 | if __name__ == "__main__": 60 | 61 | # Here is one example of an image in the dataset, with its corresponding instance segmentation mask 62 | Image.open('PennFudanPed/PNGImages/FudanPed00001.png') 63 | 64 | # So each image has a corresponding segmentation mask, where 65 | # each color correspond to a different instance. 66 | # Let's write a `torch.utils.data.Dataset` class for this dataset. 67 | 68 | class PennFudanDataset(torch.utils.data.Dataset): 69 | def __init__(self, root): 70 | self.root = root 71 | # load all image files, sorting them to 72 | # ensure that they are aligned 73 | self.imgs = list(sorted(os.listdir(os.path.join(root, "PNGImages")))) 74 | self.masks = list(sorted(os.listdir(os.path.join(root, "PedMasks")))) 75 | 76 | def __getitem__(self, idx): 77 | # load images ad masks 78 | img_path = os.path.join(self.root, "PNGImages", self.imgs[idx]) 79 | mask_path = os.path.join(self.root, "PedMasks", self.masks[idx]) 80 | img = Image.open(img_path).convert("RGB") 81 | # note that we haven't converted the mask to RGB, 82 | # because each color corresponds to a different instance 83 | # with 0 being background 84 | mask = Image.open(mask_path) 85 | 86 | mask = np.array(mask) 87 | # instances are encoded as different colors 88 | obj_ids = np.unique(mask) 89 | # first id is the background, so remove it 90 | obj_ids = obj_ids[1:] 91 | 92 | # split the color-encoded mask into a set 93 | # of binary masks 94 | masks = mask == obj_ids[:, None, None] 95 | 96 | # get bounding box coordinates for each mask 97 | num_objs = len(obj_ids) 98 | boxes = [] 99 | for i in range(num_objs): 100 | pos = np.where(masks[i]) 101 | xmin = np.min(pos[1]) 102 | xmax = np.max(pos[1]) 103 | ymin = np.min(pos[0]) 104 | ymax = np.max(pos[0]) 105 | boxes.append([xmin, ymin, xmax, ymax]) 106 | 107 | boxes = torch.as_tensor(boxes, dtype=torch.float32) 108 | # there is only one class 109 | labels = torch.ones((num_objs,), dtype=torch.int64) 110 | masks = torch.as_tensor(masks, dtype=torch.uint8) 111 | 112 | image_id = torch.tensor([idx]) 113 | area = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0]) 114 | # suppose all instances are not crowd 115 | iscrowd = torch.zeros((num_objs,), dtype=torch.int64) 116 | 117 | target = {} 118 | target["boxes"] = boxes 119 | target["labels"] = labels 120 | # target["masks"] = masks 121 | # target["image_id"] = image_id 122 | target["area"] = area 123 | # target["iscrowd"] = iscrowd 124 | 125 | img = T.ToTensor()(img) 126 | 127 | return img, target 128 | 129 | def __len__(self): 130 | return len(self.imgs) 131 | 132 | # That's all for the dataset. Let's see how the outputs are structured for this dataset 133 | 134 | # So we can see that by default, the dataset returns a `PIL.Image` and a dictionary 135 | # containing several fields, including `boxes`, `labels`. 136 | 137 | # Putting everything together 138 | 139 | # We now have the dataset class, the models and the data transforms. Let's instantiate them 140 | 141 | def collate_fn(batch): 142 | return tuple(zip(*batch)) 143 | 144 | # use our dataset and defined transformations 145 | dataset = PennFudanDataset('PennFudanPed') 146 | dataset_test = PennFudanDataset('PennFudanPed') 147 | 148 | # split the dataset in train and test set 149 | torch.manual_seed(1) 150 | indices = torch.randperm(len(dataset)).tolist() 151 | dataset = torch.utils.data.Subset(dataset, indices[:-50]) 152 | dataset_test = torch.utils.data.Subset(dataset_test, indices[-50:]) 153 | 154 | # define training and validation data loaders 155 | train_loader = torch.utils.data.DataLoader(dataset, batch_size=2, shuffle=True, 156 | num_workers=4, collate_fn=collate_fn) 157 | 158 | test_loader = torch.utils.data.DataLoader(dataset_test, batch_size=1, shuffle=False, 159 | num_workers=4, collate_fn=collate_fn) 160 | 161 | # Now let's instantiate the model and the optimizer 162 | 163 | # And now let's train the model for 10 epochs, evaluating at the end of every epoch. 164 | 165 | # Quickvision to Train Detection Models ! 166 | 167 | # Training Faster RCNN 168 | 169 | backbone = faster_rcnn.create_fastercnn_backbone("resnet101", fpn=True, pretrained="imagenet", 170 | trainable_backbone_layers=3) 171 | 172 | # our dataset has two classes only - background and person 173 | num_classes = 2 174 | model = faster_rcnn.create_vision_fastercnn(num_classes=num_classes, backbone=backbone) 175 | 176 | # - Quickvision supports Mixed Precision training as well ! 177 | 178 | # Let's use Mixed Precision training 179 | 180 | scaler = amp.GradScaler() 181 | params = [p for p in model.parameters() if p.requires_grad] 182 | optimizer = torch.optim.SGD(params, lr=0.005, momentum=0.9, weight_decay=0.0005) 183 | 184 | # and a learning rate scheduler which decreases the learning rate by 10x every 3 epochs 185 | lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=3, gamma=0.1) 186 | 187 | # let's train it for 10 epochs 188 | num_epochs = 3 189 | 190 | for epoch in tqdm(range(num_epochs)): 191 | # train for one epoch, printing every 10 iterations 192 | train_metrics = faster_rcnn.train_step(model, train_loader, 193 | device="cuda", optimizer=optimizer, 194 | scheduler=lr_scheduler, log_interval=100, scaler=scaler) 195 | val_metrics = faster_rcnn.val_step(model, test_loader, device="cuda") 196 | print("Training Metrics: ") 197 | pprint(train_metrics) 198 | print("Validation metrics") 199 | pprint(val_metrics) 200 | 201 | # Training Retina Net 202 | 203 | backbone = retinanet.create_retinanet_backbone("resnet50", fpn=True, 204 | pretrained="imagenet", trainable_backbone_layers=3) 205 | 206 | # our dataset has two classes only - background and person 207 | num_classes = 2 208 | model = retinanet.create_vision_retinanet(num_classes=num_classes, backbone=backbone) 209 | 210 | # Let's use Mixed Precision training 211 | 212 | scaler = amp.GradScaler() 213 | params = [p for p in model.parameters() if p.requires_grad] 214 | optimizer = torch.optim.SGD(params, lr=0.005, momentum=0.9, weight_decay=0.0005) 215 | 216 | # and a learning rate scheduler which decreases the learning rate by 10x every 3 epochs 217 | lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=3, gamma=0.1) 218 | 219 | # let's train it for 10 epochs 220 | num_epochs = 3 221 | 222 | for epoch in tqdm(range(num_epochs)): 223 | # train for one epoch, printing every 10 iterations 224 | train_metrics = retinanet.train_step(model, train_loader, device="cuda", 225 | optimizer=optimizer, scheduler=lr_scheduler, 226 | log_interval=100, scaler=scaler) 227 | 228 | val_metrics = retinanet.val_step(model, test_loader, device="cuda") 229 | print("Training Metrics: ") 230 | pprint(train_metrics) 231 | print("Validation metrics") 232 | pprint(val_metrics) 233 | -------------------------------------------------------------------------------- /examples/detection/training_detection_models_with_pytorch_lightning.py: -------------------------------------------------------------------------------- 1 | # Training Detection Models with Quickvision and PyTorch Lightning 2 | 3 | # This tutorial follows the dataset creation from torchvision tutorial as well as from Quickvision PyTorch tutorial. 4 | 5 | # Feel free to skip that part. 6 | 7 | # Writing a custom dataset for Penn-Fudan 8 | 9 | # Let's write a dataset for the Penn-Fudan dataset. 10 | 11 | # First, let's download and extract the data, present in a 12 | # zip file at https://www.cis.upenn.edu/~jshi/ped_html/PennFudanPed.zip 13 | 14 | # download the Penn-Fudan dataset 15 | # wget https://www.cis.upenn.edu/~jshi/ped_html/PennFudanPed.zip . 16 | # extract it in the current folder 17 | # unzip PennFudanPed.zip 18 | 19 | # Let's have a look at the dataset and how it is layed down. 20 | 21 | # The data is structured as follows 22 | 23 | # PennFudanPed/ 24 | # PedMasks/ 25 | # FudanPed00001_mask.png 26 | # FudanPed00002_mask.png 27 | # FudanPed00003_mask.png 28 | # FudanPed00004_mask.png 29 | # ... 30 | # PNGImages/ 31 | # FudanPed00001.png 32 | # FudanPed00002.png 33 | # FudanPed00003.png 34 | # FudanPed00004.png 35 | 36 | 37 | # Here is one example of an image in the dataset, with its corresponding instance segmentation mask 38 | 39 | # from PIL import Image 40 | # Image.open('PennFudanPed/PNGImages/FudanPed00001.png') 41 | 42 | import os 43 | import numpy as np 44 | import torch 45 | import torch.utils.data 46 | from pprint import pprint 47 | from tqdm import tqdm 48 | import torchvision.transforms as T 49 | from PIL import Image 50 | import pytorch_lightning as pl 51 | from quickvision.models.detection.faster_rcnn import lit_frcnn 52 | from quickvision.models.detection.retinanet import lit_retinanet 53 | 54 | 55 | if __name__ == "__main__": 56 | 57 | class PennFudanDataset(torch.utils.data.Dataset): 58 | def __init__(self, root): 59 | self.root = root 60 | # load all image files, sorting them to 61 | # ensure that they are aligned 62 | self.imgs = list(sorted(os.listdir(os.path.join(root, "PNGImages")))) 63 | self.masks = list(sorted(os.listdir(os.path.join(root, "PedMasks")))) 64 | 65 | def __getitem__(self, idx): 66 | # load images ad masks 67 | img_path = os.path.join(self.root, "PNGImages", self.imgs[idx]) 68 | mask_path = os.path.join(self.root, "PedMasks", self.masks[idx]) 69 | img = Image.open(img_path).convert("RGB") 70 | # note that we haven't converted the mask to RGB, 71 | # because each color corresponds to a different instance 72 | # with 0 being background 73 | mask = Image.open(mask_path) 74 | 75 | mask = np.array(mask) 76 | # instances are encoded as different colors 77 | obj_ids = np.unique(mask) 78 | # first id is the background, so remove it 79 | obj_ids = obj_ids[1:] 80 | 81 | # split the color-encoded mask into a set 82 | # of binary masks 83 | masks = mask == obj_ids[:, None, None] 84 | 85 | # get bounding box coordinates for each mask 86 | num_objs = len(obj_ids) 87 | boxes = [] 88 | for i in range(num_objs): 89 | pos = np.where(masks[i]) 90 | xmin = np.min(pos[1]) 91 | xmax = np.max(pos[1]) 92 | ymin = np.min(pos[0]) 93 | ymax = np.max(pos[0]) 94 | boxes.append([xmin, ymin, xmax, ymax]) 95 | 96 | boxes = torch.as_tensor(boxes, dtype=torch.float32) 97 | # there is only one class 98 | labels = torch.ones((num_objs,), dtype=torch.int64) 99 | masks = torch.as_tensor(masks, dtype=torch.uint8) 100 | 101 | image_id = torch.tensor([idx]) 102 | area = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0]) 103 | # suppose all instances are not crowd 104 | iscrowd = torch.zeros((num_objs,), dtype=torch.int64) 105 | 106 | target = {} 107 | target["boxes"] = boxes 108 | target["labels"] = labels 109 | # target["masks"] = masks 110 | # target["image_id"] = image_id 111 | target["area"] = area 112 | # target["iscrowd"] = iscrowd 113 | 114 | img = T.ToTensor()(img) 115 | 116 | return img, target 117 | 118 | def __len__(self): 119 | return len(self.imgs) 120 | 121 | # That's all for the dataset. Let's see how the outputs are structured for this dataset 122 | 123 | # So we can see that by default, the dataset returns a `PIL.Image` and a dictionary 124 | # containing several fields, including `boxes`, `labels`. 125 | 126 | # Putting everything together 127 | 128 | # We now have the dataset class, the models and the data transforms. Let's instantiate them 129 | 130 | def collate_fn(batch): 131 | return tuple(zip(*batch)) 132 | 133 | # use our dataset and defined transformations 134 | dataset = PennFudanDataset('PennFudanPed') 135 | dataset_test = PennFudanDataset('PennFudanPed') 136 | 137 | # split the dataset in train and test set 138 | torch.manual_seed(1) 139 | indices = torch.randperm(len(dataset)).tolist() 140 | dataset = torch.utils.data.Subset(dataset, indices[:-50]) 141 | dataset_test = torch.utils.data.Subset(dataset_test, indices[-50:]) 142 | 143 | # define training and validation data loaders 144 | train_loader = torch.utils.data.DataLoader(dataset, batch_size=2, shuffle=True, 145 | num_workers=4, collate_fn=collate_fn) 146 | 147 | test_loader = torch.utils.data.DataLoader(dataset_test, batch_size=1, 148 | shuffle=False, num_workers=4, collate_fn=collate_fn) 149 | 150 | # Training using PyTorch Lightning ! 151 | 152 | # !pip install -q quickvision 153 | 154 | model = lit_frcnn(learning_rate=1e-3, num_classes=2, backbone="resnet50", 155 | fpn=True, pretrained_backbone="imagenet") 156 | 157 | trainer = pl.Trainer(max_epochs=2, gpus=1) 158 | trainer.fit(model, train_loader, test_loader) 159 | 160 | # Custom Training with Lightning ! 161 | 162 | # - To write your own Training logic, metrics, logging. Subclass the `lit_frcnn` and write your own logic ! 163 | 164 | class CustomTraining(lit_frcnn): 165 | def training_step(self, batch, batch_idx): 166 | images, targets = batch 167 | targets = [{k: v for k, v in t.items()} for t in targets] 168 | 169 | # fasterrcnn takes both images and targets for training, returns 170 | loss_dict = self.model(images, targets) 171 | loss = sum(loss for loss in loss_dict.values()) 172 | return {"loss": loss, "log": loss_dict} 173 | 174 | def configure_optimizers(self): 175 | return torch.optim.Adam(self.model.parameters(), lr=self.learning_rate) 176 | 177 | custom_model = CustomTraining(learning_rate=1e-3, num_classes=2, 178 | backbone="resnet50", fpn=True, pretrained_backbone="imagenet") 179 | 180 | trainer = pl.Trainer(max_epochs=2, gpus=1) 181 | trainer.fit(custom_model, train_loader, test_loader) 182 | 183 | # Training RetinaNet with Lightning 184 | 185 | model = lit_retinanet(learning_rate=1e-3, num_classes=2, 186 | backbone="resnet50", fpn=True, pretrained_backbone="imagenet") 187 | 188 | trainer = pl.Trainer(max_epochs=2, gpus=1) 189 | trainer.fit(model, train_loader, test_loader) 190 | 191 | # Custom Training with Lightning 192 | 193 | class CustomTraining(lit_retinanet): 194 | def training_step(self, batch, batch_idx): 195 | images, targets = batch 196 | targets = [{k: v for k, v in t.items()} for t in targets] 197 | 198 | # fasterrcnn takes both images and targets for training, returns 199 | loss_dict = self.model(images, targets) 200 | loss = sum(loss for loss in loss_dict.values()) 201 | return {"loss": loss, "log": loss_dict} 202 | 203 | def configure_optimizers(self): 204 | return torch.optim.Adam(self.model.parameters(), lr=self.learning_rate) 205 | 206 | custom_retina_model = CustomTraining(learning_rate=1e-3, num_classes=2, 207 | backbone="resnet50", fpn=True, pretrained_backbone="imagenet") 208 | 209 | trainer = pl.Trainer(max_epochs=2, gpus=1) 210 | trainer.fit(custom_retina_model, train_loader, test_loader) 211 | -------------------------------------------------------------------------------- /examples/notebooks/Quantization_Aware_Training.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "name": "Quantization_Aware_Training.ipynb", 7 | "provenance": [] 8 | }, 9 | "kernelspec": { 10 | "name": "python3", 11 | "display_name": "Python 3" 12 | }, 13 | "accelerator": "GPU" 14 | }, 15 | "cells": [ 16 | { 17 | "cell_type": "markdown", 18 | "metadata": { 19 | "id": "X_Szb8iNNW1T" 20 | }, 21 | "source": [ 22 | "# Quantization Aware Training with Quickvision " 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "metadata": { 28 | "id": "284CKdbmNmwY" 29 | }, 30 | "source": [ 31 | "import torch\n", 32 | "import torchvision\n", 33 | "import torch.nn as nn\n", 34 | "import torch.optim as optim\n", 35 | "import torch.nn.functional as F\n", 36 | "from tqdm import tqdm\n", 37 | "import torchvision.transforms as T\n", 38 | "\n", 39 | "import torch.quantization\n", 40 | "from torch.quantization import convert\n", 41 | "from torch.quantization import QuantStub, DeQuantStub" 42 | ], 43 | "execution_count": 1, 44 | "outputs": [] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "metadata": { 49 | "id": "zA1vGYrROFf6" 50 | }, 51 | "source": [ 52 | "## Install Quickvision" 53 | ] 54 | }, 55 | { 56 | "cell_type": "code", 57 | "metadata": { 58 | "id": "u47wjNirOHbd" 59 | }, 60 | "source": [ 61 | "! pip install -q git+https://github.com/Quick-AI/quickvision.git" 62 | ], 63 | "execution_count": null, 64 | "outputs": [] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "metadata": { 69 | "id": "gSqljABcOFJY" 70 | }, 71 | "source": [ 72 | "from quickvision.models.classification import cnn" 73 | ], 74 | "execution_count": 3, 75 | "outputs": [] 76 | }, 77 | { 78 | "cell_type": "markdown", 79 | "metadata": { 80 | "id": "TlWOay9VOfpC" 81 | }, 82 | "source": [ 83 | "## Create CIFAR10 Dataset and DataLoaders" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "metadata": { 89 | "id": "K3vOZRG1J7_t" 90 | }, 91 | "source": [ 92 | "train_transforms = T.Compose([T.ToTensor(), T.Normalize((0.5,), (0.5,))])\n", 93 | "valid_transforms = T.Compose([T.ToTensor(), T.Normalize((0.5,), (0.5,))])" 94 | ], 95 | "execution_count": 5, 96 | "outputs": [] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "metadata": { 101 | "id": "wfLY-Ow6NWh4", 102 | "colab": { 103 | "base_uri": "https://localhost:8080/" 104 | }, 105 | "outputId": "d1eda126-a4b3-443a-8fec-78876f4b68f6" 106 | }, 107 | "source": [ 108 | "train_dataset = torchvision.datasets.CIFAR10(\"./data\", download=True, train=True, transform=train_transforms)\n", 109 | "valid_dataset = torchvision.datasets.CIFAR10(\"./data\", download=True, train=False, transform=valid_transforms)" 110 | ], 111 | "execution_count": 8, 112 | "outputs": [ 113 | { 114 | "output_type": "stream", 115 | "text": [ 116 | "Files already downloaded and verified\n", 117 | "Files already downloaded and verified\n" 118 | ], 119 | "name": "stdout" 120 | } 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "metadata": { 126 | "id": "qgA7bqckLJ4S" 127 | }, 128 | "source": [ 129 | "TRAIN_BATCH_SIZE = 512 # Training Batch Size\n", 130 | "VALID_BATCH_SIZE = 512 # Validation Batch Size" 131 | ], 132 | "execution_count": 11, 133 | "outputs": [] 134 | }, 135 | { 136 | "cell_type": "code", 137 | "metadata": { 138 | "id": "MDrXW2hRMe6o" 139 | }, 140 | "source": [ 141 | "train_loader = torch.utils.data.DataLoader(train_dataset, TRAIN_BATCH_SIZE, shuffle=True)\n", 142 | "valid_loader = torch.utils.data.DataLoader(valid_dataset, VALID_BATCH_SIZE, shuffle=False)" 143 | ], 144 | "execution_count": 12, 145 | "outputs": [] 146 | }, 147 | { 148 | "cell_type": "markdown", 149 | "metadata": { 150 | "id": "Y2-sjNpCOn9y" 151 | }, 152 | "source": [ 153 | "## Create Quantization Aware Model" 154 | ] 155 | }, 156 | { 157 | "cell_type": "code", 158 | "metadata": { 159 | "id": "PFnjBOK7Je2w" 160 | }, 161 | "source": [ 162 | "qat_model = cnn.create_vision_cnn(\"mobilenet_v2\", pretrained=\"imagenet\", num_classes=10)" 163 | ], 164 | "execution_count": 18, 165 | "outputs": [] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "metadata": { 170 | "id": "QdmBKH5QNpbz" 171 | }, 172 | "source": [ 173 | "criterion = nn.CrossEntropyLoss()\n", 174 | "optimizer = optim.Adam(qat_model.parameters(), lr=1e-3)" 175 | ], 176 | "execution_count": 19, 177 | "outputs": [] 178 | }, 179 | { 180 | "cell_type": "markdown", 181 | "metadata": { 182 | "id": "JJBvbLz0PWNI" 183 | }, 184 | "source": [ 185 | "## Set Quantization Configurations" 186 | ] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "metadata": { 191 | "id": "qb_q01e_NrK4" 192 | }, 193 | "source": [ 194 | " qat_model.config = torch.quantization.get_default_qat_qconfig(\"fbgemm\")\n", 195 | "_ = torch.quantization.prepare_qat(qat_model, inplace=True)\n", 196 | "\n", 197 | "# We can fine-tune / train the qat_models on GPU too.\n", 198 | "\n", 199 | "for param in qat_model.parameters():\n", 200 | " param.requires_grad = True\n", 201 | "\n", 202 | "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", 203 | "_ = qat_model.to(device)\n", 204 | "\n", 205 | "NUM_TRAIN_BATCHES = 5 # You can pass these too in train step if you want small subset to train\n", 206 | "NUM_VAL_BATCHES = 5 # You can pass these too in train step if you want small subset to validate" 207 | ], 208 | "execution_count": null, 209 | "outputs": [] 210 | }, 211 | { 212 | "cell_type": "markdown", 213 | "metadata": { 214 | "id": "WYpwpNxgPaPo" 215 | }, 216 | "source": [ 217 | "## Train with Quickvision !" 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "metadata": { 223 | "id": "dRAHFNgMPZ36" 224 | }, 225 | "source": [ 226 | "history = cnn.fit(epochs=3, model=qat_model, train_loader=train_loader,\n", 227 | " val_loader=valid_loader, criterion=criterion, device=device,\n", 228 | " optimizer=optimizer)\n", 229 | "\n", 230 | "qat_model.cpu() # We need to move to cpu for conversion.\n", 231 | "\n", 232 | "qat_model_trained = torch.quantization.convert(qat_model, inplace=False)\n", 233 | "print(\"Converted the Quantization aware training model.\")\n", 234 | "# torch.save(model_quantized_and_trained.state_dict(), config.QAT_SAVE_PATH)" 235 | ], 236 | "execution_count": null, 237 | "outputs": [] 238 | }, 239 | { 240 | "cell_type": "code", 241 | "metadata": { 242 | "id": "z0kYoDC9LjFc" 243 | }, 244 | "source": [ 245 | "" 246 | ], 247 | "execution_count": null, 248 | "outputs": [] 249 | } 250 | ] 251 | } -------------------------------------------------------------------------------- /quickvision/README.md: -------------------------------------------------------------------------------- 1 | # Quickvision ! 2 | 3 | - This is how it is structured: - 4 | 5 | ## Datasets (Unstable): - 6 | 7 | - Provides `torchvision` datasets for common use cases and scenarios. 8 | 9 | ## Losses (Unstable): - 10 | 11 | - Losses which are not currently supported by torch, but are popularly used. 12 | 13 | ## Models: - 14 | 15 | - Implementation of Computer Vision Models for training as well as transfer learning. 16 | - Here we leverage Torchvision models and models from PyTorch Hub as well. 17 | - These are implemented with a PyTorch training API as well as consistent Lightning API. 18 | - All have APIs such as `train_step`, `val_step`, `fit` and sanity APIs. 19 | - All support Mixed Precision Training. 20 | 21 | ## Optimizers (Unstable): - 22 | 23 | - Commonly used Optimizers pertaining to computer vision. 24 | 25 | ## Pretrained: - 26 | 27 | - A list of pretrained models and weights which we use. 28 | - You can contribute your models trained on different datasets here ! 29 | 30 | ## Utils (Unstable): - 31 | 32 | - Commonly used utilities. 33 | -------------------------------------------------------------------------------- /quickvision/__init__.py: -------------------------------------------------------------------------------- 1 | from quickvision import pretrained 2 | from quickvision import layers 3 | from quickvision import losses 4 | from quickvision import models 5 | from quickvision import optimizers 6 | from quickvision import datasets 7 | from quickvision import metrics 8 | -------------------------------------------------------------------------------- /quickvision/datasets/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/quickvision/datasets/README.md -------------------------------------------------------------------------------- /quickvision/datasets/__init__.py: -------------------------------------------------------------------------------- 1 | from quickvision.datasets.classification import ( 2 | create_folder_dataset, 3 | CSVSingleLabelDataset, 4 | ) 5 | -------------------------------------------------------------------------------- /quickvision/datasets/classification.py: -------------------------------------------------------------------------------- 1 | # Add code for Mapping using dataframe containaing id and target. 2 | # Port from pytorch_cnn_trainer 3 | # https://github.com/oke-aditya/pytorch_cnn_trainer 4 | 5 | import torchvision 6 | from torchvision import datasets 7 | from torch.utils.data import Dataset 8 | import os 9 | import torch 10 | from PIL import Image 11 | 12 | __all__ = ["create_folder_dataset", "CSVSingleLabelDataset"] 13 | 14 | 15 | def create_folder_dataset(root_dir, transforms, split: float = 0.8, **kwargs): 16 | """ 17 | Creates Train and Validation Dataset from a Root folder 18 | Arrange dataset as follows: - 19 | root/class_a/image01.png 20 | root/class_b/image01.png 21 | 22 | Creates train and validation dataset from this root dir. 23 | This applies same transforms to both train and validation 24 | 25 | Args: 26 | root_dir : Root directory of the dataset to read from 27 | transforms: Transforms to be applied to train and validation datasets. 28 | split: Float number denoting percentage of train items 29 | 30 | """ 31 | complete_dataset = datasets.ImageFolder(root_dir, transform=transforms) 32 | train_split = len(complete_dataset) * split 33 | valid_split = len(complete_dataset) * (1 - split) 34 | 35 | train_set, valid_set = torch.utils.data.random_split(complete_dataset, [train_split, valid_split]) 36 | return train_set, valid_set 37 | 38 | 39 | class CSVSingleLabelDataset(Dataset): 40 | """ 41 | Creates Torchvision Dataset From CSV File. 42 | Args: 43 | df: DataFrame with 2 columns ``image_id`` and ``target``. 44 | data_dir: Directory from where data is to be read. 45 | image_id: Column name which has IDs of the images. 46 | target: target column name. 47 | transform: Trasforms to apply while creating Dataset. 48 | img_type: Type of the image like `png` or `jpg` etc. 49 | """ 50 | 51 | def __init__(self, df, data_dir, image_id, target, transform, img_type): 52 | super().__init__() 53 | self.df = df 54 | self.data_dir = data_dir 55 | self.image_id = image_id 56 | self.target = target 57 | self.transform = transform 58 | self.img_type = img_type 59 | 60 | def __len__(self): 61 | return len(self.df) 62 | 63 | def __getitem__(self, idx): 64 | img_name = self.df[self.image_id][idx] 65 | label = self.df[self.target][idx] 66 | 67 | img_path = os.path.join(self.data_dir, str(img_name) + f'.{self.img_type}') 68 | image = Image.open(img_path) 69 | image = self.transform(image) 70 | 71 | return image, label 72 | -------------------------------------------------------------------------------- /quickvision/datasets/detection.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch.utils.data import DataLoader, Dataset 3 | import os 4 | from PIL import Image 5 | 6 | __all__ = ["frcnn_dataset"] 7 | 8 | 9 | class frcnn_dataset(Dataset): 10 | """ 11 | df: DataFrame having columns "image_id, 12 | """ 13 | def __init__(self, df, image_dir, target, transforms=None, train=True): 14 | super().__init__() 15 | self.image_ids = df["image_id"].unique() 16 | self.image_dir = image_dir 17 | self.transforms = transforms 18 | self.df = df 19 | self.train = train 20 | self.target = target 21 | 22 | def __len__(self): 23 | return self.image_ids.shape[0] 24 | 25 | def __getitem__(self, idx): 26 | image_id = self.image_ids[idx] 27 | image_src = os.path.join(self.image_dir, str(image_id)) 28 | -------------------------------------------------------------------------------- /quickvision/layers/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/quickvision/layers/README.md -------------------------------------------------------------------------------- /quickvision/layers/__init__.py: -------------------------------------------------------------------------------- 1 | from quickvision.layers import functional 2 | from quickvision.layers.act_mish import Mish 3 | from quickvision.layers.block_mlp import MLP 4 | -------------------------------------------------------------------------------- /quickvision/layers/act_mish.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.nn.functional as F 4 | from quickvision.layers.functional import mish 5 | 6 | __all__ = ["Mish"] 7 | 8 | 9 | class Mish(nn.Module): 10 | """ 11 | Applies the mish function element-wise: 12 | mish(x) = x * tanh(softplus(x)) = x * tanh(ln(1 + exp(x))) 13 | Shape: 14 | - Input: (N, *) where * means, any number of additional 15 | dimensions 16 | - Output: (N, *), same shape as the input 17 | Examples: 18 | >>> m = Mish() 19 | >>> input = torch.randn(2) 20 | >>> output = m(input) 21 | """ 22 | 23 | def __init__(self, inplace: bool = False): 24 | super().__init__() 25 | self.inplace = inplace 26 | 27 | def forward(self, x): 28 | return mish(x, inplace=self.inplace) 29 | -------------------------------------------------------------------------------- /quickvision/layers/block_mlp.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.nn.functional as F 4 | 5 | 6 | __all__ = ["MLP"] 7 | 8 | 9 | class MLP(nn.Module): 10 | """ 11 | A very simple Multi Layered Perceptron classifier. 12 | Args: 13 | in_features (int): Input features of Network 14 | hidden_features (int): Intermediate Features of network 15 | out_features (int): Output layers of network. 16 | """ 17 | 18 | def __init__(self, in_features: int, hidden_features: int, proj_features: int): 19 | super().__init__() 20 | self.in_features = in_features 21 | self.proj_features = proj_features 22 | self.hidden_features = hidden_features 23 | self.l1 = nn.Linear(self.in_features, self.hidden_features) 24 | self.l2 = nn.Linear(self.hidden_features, self.proj_features) 25 | 26 | def forward(self, x): 27 | x = self.l1(x) 28 | x = F.relu(x) 29 | x = self.l2(x) 30 | return (x) 31 | -------------------------------------------------------------------------------- /quickvision/layers/functional/__init__.py: -------------------------------------------------------------------------------- 1 | from quickvision.layers.functional.act_mish import mish 2 | -------------------------------------------------------------------------------- /quickvision/layers/functional/act_mish.py: -------------------------------------------------------------------------------- 1 | # Taken from Mish author himself https://github.com/digantamisra98/Mish/ 2 | 3 | import torch 4 | import torch.nn as nn 5 | import torch.nn.functional as F 6 | 7 | __all__ = ["mish"] 8 | 9 | 10 | @torch.jit.script 11 | def mish(x, inplace: bool = False): 12 | """ 13 | Applies the mish function element-wise: 14 | Mish: A Self Regularized Non-Monotonic Neural Activation Function - https://arxiv.org/abs/1908.08681 15 | mish(x) = x * tanh(softplus(x)) = x * tanh(ln(1 + exp(x))) 16 | """ 17 | if inplace: 18 | return x.mul_(F.tanh(F.softplus(x))) 19 | else: 20 | return x.mul(F.tanh(F.softplus(x))) 21 | -------------------------------------------------------------------------------- /quickvision/losses/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/quickvision/losses/README.md -------------------------------------------------------------------------------- /quickvision/losses/__init__.py: -------------------------------------------------------------------------------- 1 | from quickvision.losses.detr_loss import HungarianMatcher, build_matcher, SetCriterion 2 | from quickvision.losses.dice_loss import DiceLoss 3 | -------------------------------------------------------------------------------- /quickvision/losses/dice_loss.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | from quickvision.losses.functional import dice_loss 4 | from typing import Optional 5 | 6 | __all__ = ["DiceLoss"] 7 | 8 | 9 | class DiceLoss(nn.Module): 10 | """ 11 | Computes the DICE loss, similar to generalized IOU for masks 12 | Args: 13 | inputs: A float tensor of arbitrary shape. 14 | The predictions for each example. 15 | targets: A float tensor with the same shape as inputs. Stores the binary 16 | classification label for each element in inputs 17 | (0 for the negative class and 1 for the positive class). 18 | weights: A float tensor of weights for each sample. 19 | reduction: A string specifying whether loss should be a sum or average. 20 | """ 21 | 22 | def __init__(self, weights: Optional[torch.Tensor] = None, reduction: str = "mean"): 23 | super().__init__() 24 | 25 | self.weights = weights 26 | self.reduction = reduction 27 | 28 | def forward(self, inputs, targets): 29 | return dice_loss(inputs, targets, self.weights, self.reduction) 30 | -------------------------------------------------------------------------------- /quickvision/losses/functional/__init__.py: -------------------------------------------------------------------------------- 1 | from quickvision.losses.functional.dice_loss import dice_loss 2 | -------------------------------------------------------------------------------- /quickvision/losses/functional/dice_loss.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from typing import Optional 3 | 4 | __all__ = ["dice_loss"] 5 | 6 | 7 | def dice_loss( 8 | inputs, targets, weights: Optional[torch.Tensor] = None, reduction: str = "sum" 9 | ): 10 | """ 11 | Computes the DICE loss, similar to generalized IOU for masks 12 | Args: 13 | inputs: A float tensor of arbitrary shape. 14 | The predictions for each example. 15 | targets: A float tensor with the same shape as inputs. Stores the binary 16 | classification label for each element in inputs 17 | (0 for the negative class and 1 for the positive class). 18 | weights: A float tensor of weights for each sample. 19 | reduction: A string specifying whether loss should be a sum or average. 20 | """ 21 | inputs = inputs.sigmoid() 22 | inputs = inputs.flatten(1) 23 | numerator = 2 * (inputs * targets).sum(1) 24 | denominator = inputs.sum(-1) + targets.sum(-1) 25 | loss = 1 - (numerator + 1) / (denominator + 1) 26 | 27 | if weights is not None: 28 | loss *= weights 29 | 30 | if reduction == "mean": 31 | return loss.mean() 32 | else: 33 | return loss.sum() 34 | -------------------------------------------------------------------------------- /quickvision/metrics/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/quickvision/metrics/README.md -------------------------------------------------------------------------------- /quickvision/metrics/__init__.py: -------------------------------------------------------------------------------- 1 | from quickvision.metrics.classification import accuracy 2 | -------------------------------------------------------------------------------- /quickvision/metrics/classification.py: -------------------------------------------------------------------------------- 1 | import torch 2 | 3 | __all__ = ["accuracy"] 4 | 5 | 6 | @torch.no_grad() 7 | def accuracy(output, target, topk=(1,)): 8 | """Computes the accuracy over the k top predictions for the specified values of k""" 9 | maxk = max(topk) 10 | batch_size = target.size(0) 11 | _, pred = output.topk(maxk, 1, True, True) 12 | pred = pred.t() 13 | correct = pred.eq(target.reshape(1, -1).expand_as(pred)) 14 | return [correct[:k].reshape(-1).float().sum(0) * 100. / batch_size for k in topk] 15 | -------------------------------------------------------------------------------- /quickvision/models/README.md: -------------------------------------------------------------------------------- 1 | ## Quickvision Models 2 | 3 | - Currently Supported Models are listed below. 4 | 5 | ### Classification 6 | - From Torchvision CNNs 7 | 8 | - Resnet: - Resnet18, Resnet34, Resnet50, Resnet101, Resnet152, ResNext 9 | - VGG: - VGG13, VGG16, VGG19 10 | - Mobilenet_v2 11 | - MNASNet:- MnasNet 0_5, MnasNet 0_75, MnasNet 1_0, MnasNet 1_3 12 | 13 | ### Detection 14 | 15 | - Detr From FaceBook Detr, using `torch.hub` 16 | 17 | - RetinaNet From Torchvision 18 | 19 | - FasterRCNN From Torchvision. 20 | -------------------------------------------------------------------------------- /quickvision/models/__init__.py: -------------------------------------------------------------------------------- 1 | from quickvision.models import components 2 | from quickvision.models import classification 3 | from quickvision.models import detection 4 | from quickvision.models import segmentation 5 | -------------------------------------------------------------------------------- /quickvision/models/classification/README.md: -------------------------------------------------------------------------------- 1 | ### Pretrained CNN weights supported. 2 | 3 | -------------------------------------------------------------------------------- /quickvision/models/classification/__init__.py: -------------------------------------------------------------------------------- 1 | from quickvision.models.classification import cnn 2 | -------------------------------------------------------------------------------- /quickvision/models/classification/cnn/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/quickvision/models/classification/cnn/README.md -------------------------------------------------------------------------------- /quickvision/models/classification/cnn/__init__.py: -------------------------------------------------------------------------------- 1 | from quickvision.models.classification.cnn.engine import ( 2 | train_step, 3 | val_step, 4 | fit, 5 | train_sanity_fit, 6 | val_sanity_fit, 7 | sanity_fit, 8 | ) 9 | from quickvision.models.classification.cnn.model_factory import ( 10 | CNN, 11 | create_cnn, 12 | ) 13 | from quickvision.models.classification.cnn.lightning_trainer import LitCNN 14 | -------------------------------------------------------------------------------- /quickvision/models/classification/cnn/lightning_trainer.py: -------------------------------------------------------------------------------- 1 | # Port from pytorch_cnn_trainer 2 | # https://github.com/oke-aditya/pytorch_cnn_trainer 3 | 4 | import pytorch_lightning as pl 5 | import torch 6 | from quickvision.models.components import create_torchvision_backbone 7 | import torch.nn as nn 8 | from torch.nn import functional as F 9 | from pytorch_lightning.metrics.functional import accuracy 10 | 11 | __all__ = ["LitCNN"] 12 | 13 | 14 | class LitCNN(pl.LightningModule): 15 | """ 16 | Creates a CNN which can be fine-tuned. 17 | """ 18 | def __init__(self, pretrained_backbone: str, 19 | num_classes: int, learning_rate: float = 0.0001, 20 | pretrained: str = None, **kwargs,): 21 | super().__init__() 22 | 23 | self.num_classes = num_classes 24 | self.bottom, self.out_channels = create_torchvision_backbone(pretrained_backbone, pretrained) 25 | self.top = nn.Linear(self.out_channels, self.num_classes) 26 | self.learning_rate = learning_rate 27 | 28 | def forward(self, x): 29 | x = self.bottom(x) 30 | x = self.top(x.view(-1, self.out_channels)) 31 | return x 32 | 33 | def training_step(self, batch, batch_idx): 34 | images, targets = batch 35 | outputs = self.forward(images) 36 | train_loss = F.cross_entropy(outputs, targets, reduction='sum') 37 | # Possible we can compute top-1 and top-5 accuracy here. 38 | return {"loss": train_loss} 39 | 40 | def validation_step(self, batch, batch_idx): 41 | images, targets = batch 42 | outputs = self.forward(images) 43 | val_loss = F.cross_entropy(outputs, targets, reduction='sum') 44 | # Possible we can compute top-1 and top-5 accuracy here. 45 | return {"loss": val_loss} 46 | 47 | def configure_optimizers(self): 48 | return torch.optim.SGD(self.parameters(), lr=self.learning_rate) 49 | -------------------------------------------------------------------------------- /quickvision/models/classification/cnn/model_factory.py: -------------------------------------------------------------------------------- 1 | # Model should have AdaptiveAvgPool2d layer and then Linear Layer 2 | # We will remove the Linear Layer and create a new Linear Layer with num_classes 3 | # Port from pytorch_cnn_trainer https://github.com/oke-aditya/pytorch_cnn_trainer 4 | 5 | from quickvision.models.components import create_torchvision_backbone 6 | import torch.nn as nn 7 | 8 | __all__ = ["CNN", "create_cnn"] 9 | 10 | 11 | class CNN(nn.Module): 12 | def __init__(self, model_name: str, num_classes: int, pretrained: str = None): 13 | super().__init__() 14 | self.num_classes = num_classes 15 | self.bottom, self.out_channels = create_torchvision_backbone(model_name, pretrained) 16 | self.top = nn.Linear(self.out_channels, self.num_classes) 17 | 18 | def forward(self, x): 19 | x = self.bottom(x) 20 | x = self.top(x.view(-1, self.out_channels)) 21 | return x 22 | 23 | 24 | def create_cnn(model_name: str, num_classes: int, 25 | pretrained: str = None,): 26 | 27 | # We do not pass in_channels here since torchvision models is not supported with it. 28 | 29 | """ 30 | Creates CNN model from Torchvision. It replaces the top classification layer with num_classes you need. 31 | Args: 32 | model_name (str) : Name of the model. E.g. resnet18 33 | num_classes (int) : Number of classes for classification. 34 | pretrained (str) : Pretrained weights dataset "imagenet", etc 35 | """ 36 | model = CNN(model_name, num_classes, pretrained) 37 | return model 38 | -------------------------------------------------------------------------------- /quickvision/models/classification/cnn/utils.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/quickvision/models/classification/cnn/utils.py -------------------------------------------------------------------------------- /quickvision/models/components/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/quickvision/models/components/README.md -------------------------------------------------------------------------------- /quickvision/models/components/__init__.py: -------------------------------------------------------------------------------- 1 | from quickvision.models.components.torchvision_backbones import create_torchvision_backbone 2 | -------------------------------------------------------------------------------- /quickvision/models/components/torchvision_backbones.py: -------------------------------------------------------------------------------- 1 | 2 | import torch.nn as nn 3 | from quickvision.pretrained._model_zoo import TORCHVISION_MODEL_ZOO 4 | from quickvision.pretrained._pretrained_weights import _load_pretrained_weights 5 | from quickvision.pretrained._pretrained_cnns import WEIGHTS_DICT 6 | 7 | __all__ = ["create_torchvision_backbone"] 8 | 9 | RESNET_SMALL_MODELS = ["resnet18", "resnet34"] 10 | RESNET_LARGE_MODELS = ["resnet50", "resnet101", "resnet152", "resnext50_32x4d", "resnext101_32x8d", 11 | "wide_resnet50_2", "wide_resnet101_2"] 12 | VGG_MODELS = ["vgg11", "vgg13", "vgg16", "vgg19", ] 13 | MNASNET_MODELS = ["mnasnet0_5", "mnasnet0_75", "mnasnet1_0", "mnasnet1_3"] 14 | MOBILENET_MODELS = ["mobilenet_v2", "mobilenet_v3_large"] 15 | 16 | 17 | def _create_backbone_generic(model: nn.Module, out_channels: int): 18 | """ 19 | Generic Backbone creater. It removes the last linear layer. 20 | Args: 21 | model: torch.nn model 22 | out_channels: Number of out_channels in last layer. 23 | """ 24 | modules_total = list(model.children()) 25 | modules = modules_total[:-1] 26 | ft_backbone = nn.Sequential(*modules) 27 | ft_backbone.out_channels = out_channels 28 | return ft_backbone 29 | 30 | 31 | # Use this when you have Adaptive Pooling layer in End. 32 | # When Model.features is not applicable. 33 | def _create_backbone_adaptive(model: nn.Module, out_channels: int = None): 34 | """ 35 | Creates backbone by removing linear after Adaptive Pooling layer. 36 | Args: 37 | model: torch.nn model with adaptive pooling layer. 38 | out_channels (Optional) : Number of out_channels in last layer. 39 | """ 40 | if out_channels is None: 41 | modules_total = list(model.children()) 42 | out_channels = modules_total[-1].in_features 43 | return _create_backbone_generic(model, out_channels=out_channels) 44 | 45 | 46 | def _create_backbone_features(model: nn.Module, out_channels: int): 47 | """ 48 | Creates backbone from feature sequential block. 49 | Args: 50 | model: torch.nn model with features as sequential block. 51 | out_channels: Number of out_channels in last layer. 52 | """ 53 | ft_backbone = model.features 54 | ft_backbone.out_channels = out_channels 55 | return ft_backbone 56 | 57 | 58 | def create_torchvision_backbone(model_name: str, pretrained: str = None): 59 | """ 60 | Creates CNN backbone from Torchvision. 61 | Args: 62 | model_name (str) : Name of the model. E.g. resnet18 63 | pretrained (str) : Pretrained weights dataset "imagenet", etc 64 | """ 65 | 66 | model_selected = TORCHVISION_MODEL_ZOO[model_name] 67 | net = model_selected(pretrained=False) 68 | 69 | if pretrained is not None: 70 | state_dict = _load_pretrained_weights(WEIGHTS_DICT, model_name, pretrained=pretrained) 71 | net.load_state_dict(state_dict) 72 | 73 | if model_name in MOBILENET_MODELS: 74 | out_channels = 1280 75 | ft_backbone = _create_backbone_features(net, out_channels) 76 | return ft_backbone, out_channels 77 | 78 | elif model_name in VGG_MODELS: 79 | out_channels = 512 80 | ft_backbone = _create_backbone_features(net, out_channels) 81 | return ft_backbone, out_channels 82 | 83 | elif model_name in RESNET_SMALL_MODELS: 84 | out_channels = 512 85 | ft_backbone = _create_backbone_adaptive(net, out_channels) 86 | return ft_backbone, out_channels 87 | 88 | elif model_name in RESNET_LARGE_MODELS: 89 | out_channels = 2048 90 | ft_backbone = _create_backbone_adaptive(net, 2048) 91 | return ft_backbone, out_channels 92 | 93 | elif model_name in MNASNET_MODELS: 94 | out_channels = 1280 95 | ft_backbone = _create_backbone_adaptive(net, 1280) 96 | return ft_backbone, out_channels 97 | 98 | else: 99 | raise ValueError(f"Unsupported model: '{model_name}'") 100 | -------------------------------------------------------------------------------- /quickvision/models/detection/README.md: -------------------------------------------------------------------------------- 1 | ### Comparision of Detr, FasterRCNN and RetinaNet over MS COCO 2 | 3 | 4 | ### Detr Supported Models and Backbones 5 | 6 | ### Faster RCNN Supported Models and Backbones 7 | 8 | 9 | ### RetinNet Supported Models and Backbones. 10 | 11 | 12 | 13 | ### References to learn : - 14 | -------------------------------------------------------------------------------- /quickvision/models/detection/__init__.py: -------------------------------------------------------------------------------- 1 | from quickvision.models.detection import faster_rcnn 2 | from quickvision.models.detection import detr 3 | from quickvision.models.detection import retinanet 4 | -------------------------------------------------------------------------------- /quickvision/models/detection/detr/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/quickvision/models/detection/detr/README.md -------------------------------------------------------------------------------- /quickvision/models/detection/detr/__init__.py: -------------------------------------------------------------------------------- 1 | from quickvision.models.detection.detr.model_factory import ( 2 | create_detr_backbone, 3 | create_detr 4 | ) 5 | 6 | from quickvision.models.detection.detr.engine import ( 7 | train_step, 8 | val_step, 9 | fit, 10 | train_sanity_fit, 11 | val_sanity_fit, 12 | sanity_fit, 13 | ) 14 | 15 | from quickvision.models.detection.detr.utils import ( 16 | PostProcess 17 | ) 18 | 19 | from quickvision.models.detection.detr.lightning_trainer import ( 20 | LitDETR 21 | ) 22 | -------------------------------------------------------------------------------- /quickvision/models/detection/detr/lightning_trainer.py: -------------------------------------------------------------------------------- 1 | import pytorch_lightning as pl 2 | import torch 3 | import torch.nn as nn 4 | from quickvision.losses import detr_loss 5 | from quickvision.models.detection.detr import create_detr_backbone 6 | 7 | __all__ = ["LitDETR"] 8 | 9 | 10 | class LitDETR(pl.LightningModule): 11 | def __init__(self, learning_rate: float = 1e-3, num_classes: int = 91, 12 | num_queries: int = 5, pretrained: str = None, 13 | backbone: str = "resnet50", **kwargs, ): 14 | 15 | """ 16 | PyTorch Lightning implementation of `Detr: End-to-End Object Detection with Transformers 17 | During training, the model expects both the input tensors, as well as targets (list of dictionary), 18 | containing: 19 | - boxes (`FloatTensor[N, 4]`): the ground truth boxes in `[x1, y1, x2, y2]` format. 20 | - labels (`Int64Tensor[N]`): the class label for each ground truh box 21 | Args: 22 | learning_rate: the learning rate 23 | num_classes: number of detection classes (including background) 24 | num_queries: number of queries to the transformer module. 25 | pretrained: if "coco", returns a model pre-trained on COCO train2017 26 | backbone: Supported Detection backbones are "resnet50", "resnet101", "resnet50_dc5", "resnet101_dc5". 27 | 28 | It returns a dict with the following elements: 29 | - "pred_logits": the classification logits (including no-object) for all queries. 30 | Shape= [batch_size x num_queries x (num_classes + 1)] 31 | - "pred_boxes": The normalized boxes coordinates for all queries, represented as 32 | (center_x, center_y, height, width). These values are normalized in [0, 1], 33 | relative to the size of each individual image (disregarding possible padding). 34 | See PostProcess for information on how to retrieve the unnormalized bounding box. 35 | """ 36 | super().__init__() 37 | 38 | self.model = create_detr_backbone(backbone, pretrained) 39 | in_features = self.model.class_embed.in_features 40 | self.model.class_embed = nn.Linear(in_features=in_features, out_features=num_classes) 41 | self.model.num_queries = num_queries 42 | self.learning_rate = learning_rate 43 | 44 | matcher = detr_loss.HungarianMatcher() 45 | weight_dict = {"loss_ce": 1, "loss_bbox": 1, "loss_giou": 1} 46 | losses = ['labels', 'boxes', 'cardinality'] 47 | self.criterion = detr_loss.SetCriterion(num_classes - 1, matcher, weight_dict, eos_coef=0.5, losses=losses) 48 | 49 | def forward(self, x): 50 | return self.model(x) 51 | 52 | def training_step(self, batch, batch_idx): 53 | images, targets = batch 54 | images = list(image for image in images) 55 | targets = [{k: v for k, v in t.items()} for t in targets] 56 | outputs = self.model(images) 57 | 58 | self.criterion.train() 59 | loss_dict = self.criterion(outputs, targets) 60 | weight_dict = self.criterion.weight_dict 61 | loss = sum(loss_dict[k] * weight_dict[k] for k in loss_dict.keys() if k in weight_dict) 62 | return {"loss": loss, "log": loss_dict} 63 | 64 | def validation_step(self, batch, batch_idx): 65 | images, targets = batch 66 | images = list(image for image in images) 67 | targets = [{k: v for k, v in t.items()} for t in targets] 68 | outputs = self.model(images) 69 | 70 | self.criterion.eval() 71 | loss_dict = self.criterion(outputs, targets) 72 | weight_dict = self.criterion.weight_dict 73 | loss = sum(loss_dict[k] * weight_dict[k] for k in loss_dict.keys() if k in weight_dict) 74 | return {"loss": loss, "log": loss_dict} 75 | 76 | def configure_optimizers(self): 77 | return torch.optim.SGD(self.model.parameters(), lr=self.learning_rate, 78 | momentum=0.9, weight_decay=0.005,) 79 | -------------------------------------------------------------------------------- /quickvision/models/detection/detr/model_factory.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | from quickvision.pretrained._pretrained_weights import _load_pretrained_weights 4 | from quickvision.pretrained._pretrained_detection import detr_weights_dict 5 | 6 | __all__ = ["DETR", "create_detr", "create_detr_backbone",] 7 | 8 | 9 | class DETR(nn.Module): 10 | """ 11 | Creates Detr Model for Object Detection. 12 | Args: 13 | num_classes: Number of classes to detect. 14 | num_queries: Number of queries for transformer in Detr. 15 | backbone: Backbone created from create_detr_backbone. 16 | """ 17 | def __init__(self, num_classes: int, num_queries: int, backbone: str): 18 | super().__init__() 19 | self.num_classes = num_classes 20 | self.num_queries = num_queries 21 | 22 | self.model = backbone 23 | self.in_features = self.model.class_embed.in_features 24 | 25 | self.model.class_embed = nn.Linear(in_features=self.in_features, out_features=self.num_classes) 26 | self.model.num_queries = self.num_queries 27 | 28 | def forward(self, images): 29 | return self.model(images) 30 | 31 | 32 | def create_detr(num_classes: int, num_queries: int, backbone: str): 33 | """ 34 | Creates Detr Model for Object Detection 35 | Args: 36 | num_classes: Number of classes to detect. 37 | num_queries: Number of queries for transformer in Detr. 38 | backbone: Backbone created from create_detr_backbone. 39 | """ 40 | 41 | model = DETR(num_classes, num_queries, backbone) 42 | return model 43 | 44 | 45 | def create_detr_backbone(model_name: str, pretrained: str = None,): 46 | """ 47 | Creates Detr Backbone for Detection. 48 | Args: 49 | model_name: Name of supported bacbone. Supported Backbones are 50 | "resnet50", "resnet101", "resnet50_dc5", "resnet101_dc5" 51 | pretrained: (str) If "coco", returns Detr pretrained on COCO Dataset. 52 | """ 53 | if(model_name == "resnet50"): 54 | backbone = torch.hub.load('facebookresearch/detr', 'detr_resnet50', pretrained=False) 55 | 56 | elif(model_name == "resnet101"): 57 | backbone = torch.hub.load('facebookresearch/detr', 'detr_resnet101', pretrained=False) 58 | 59 | elif(model_name == "resnet50_dc5"): 60 | backbone = torch.hub.load('facebookresearch/detr', 'detr_resnet50_dc5', pretrained=False) 61 | 62 | elif(model_name == "resnet101_dc5"): 63 | backbone = torch.hub.load('facebookresearch/detr', 'detr_resnet101_dc5', pretrained=False) 64 | 65 | else: 66 | raise ValueError("Unuspported backbone") 67 | 68 | if pretrained is not None: 69 | checkpoint = _load_pretrained_weights(detr_weights_dict, model_name, pretrained=pretrained,) 70 | backbone.load_state_dict(checkpoint["model"]) 71 | return backbone 72 | 73 | return backbone 74 | -------------------------------------------------------------------------------- /quickvision/models/detection/detr/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved 2 | # Taken From FB Detr Repo 3 | 4 | import torch 5 | import torch.nn as nn 6 | import torch.functional as F 7 | from torchvision.ops import box_convert 8 | 9 | __all__ = ["PostProcess"] 10 | 11 | 12 | class PostProcess(nn.Module): 13 | """ 14 | This module converts the model's output into the format expected by the coco api 15 | """ 16 | @torch.no_grad() 17 | def forward(self, outputs, target_sizes): 18 | """ 19 | Perform the computation 20 | Parameters: 21 | outputs: raw outputs of the model 22 | target_sizes: tensor of dimension [batch_size x 2] containing the size of each images of the batch 23 | For evaluation, this must be the original image size (before any data augmentation) 24 | For visualization, this should be the image size after data augment, but before padding 25 | """ 26 | out_logits, out_bbox = outputs['pred_logits'], outputs['pred_boxes'] 27 | 28 | assert len(out_logits) == len(target_sizes) 29 | assert target_sizes.shape[1] == 2 30 | 31 | prob = F.softmax(out_logits, -1) 32 | scores, labels = prob[..., :-1].max(-1) 33 | 34 | # convert to [x0, y0, x1, y1] format 35 | # boxes = box_ops.box_cxcywh_to_xyxy(out_bbox) 36 | boxes = box_convert(out_bbox, in_fmt="cxcywh", out_fmt="xyxy") 37 | # and from relative [0, 1] to absolute [0, height] coordinates 38 | img_h, img_w = target_sizes.unbind(1) 39 | scale_fct = torch.stack([img_w, img_h, img_w, img_h], dim=1) 40 | boxes = boxes * scale_fct[:, None, :] 41 | 42 | results = [{'scores': s, 'labels': l, 'boxes': b} for s, l, b in zip(scores, labels, boxes)] 43 | 44 | return results 45 | -------------------------------------------------------------------------------- /quickvision/models/detection/faster_rcnn/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/quickvision/models/detection/faster_rcnn/README.md -------------------------------------------------------------------------------- /quickvision/models/detection/faster_rcnn/__init__.py: -------------------------------------------------------------------------------- 1 | from quickvision.models.detection.faster_rcnn.model_factory import ( 2 | create_fastercnn, 3 | create_fastercnn_backbone, 4 | ) 5 | 6 | from quickvision.models.detection.faster_rcnn.engine import ( 7 | train_step, 8 | val_step, 9 | fit, 10 | train_sanity_fit, 11 | val_sanity_fit, 12 | sanity_fit, 13 | ) 14 | 15 | from quickvision.models.detection.faster_rcnn.lightning_trainer import LitFRCNN 16 | -------------------------------------------------------------------------------- /quickvision/models/detection/faster_rcnn/lightning_trainer.py: -------------------------------------------------------------------------------- 1 | import pytorch_lightning as pl 2 | import torch 3 | import torch.nn as nn 4 | from quickvision.models.components import create_torchvision_backbone 5 | from quickvision.models.detection.faster_rcnn import create_fastercnn_backbone 6 | from quickvision.models.detection.utils import _evaluate_iou, _evaluate_giou 7 | from torchvision.models.detection.faster_rcnn import (fasterrcnn_resnet50_fpn, FasterRCNN, FastRCNNPredictor,) 8 | 9 | __all__ = ["LitFRCNN"] 10 | 11 | 12 | class LitFRCNN(pl.LightningModule): 13 | """ 14 | Creates a Faster CNN which can be fine-tuned. 15 | """ 16 | 17 | def __init__(self, learning_rate: float = 0.0001, num_classes: int = 91, 18 | backbone: str = None, fpn: bool = True, 19 | pretrained_backbone: str = None, trainable_backbone_layers: int = 3, **kwargs,): 20 | 21 | """ 22 | Args: 23 | learning_rate: the learning rate 24 | num_classes: number of detection classes (including background) 25 | pretrained: if true, returns a model pre-trained on COCO train2017 26 | pretrained_backbone (str): if "imagenet", returns a model with backbone pre-trained on Imagenet 27 | trainable_backbone_layers: number of trainable resnet layers starting from final block 28 | """ 29 | super().__init__() 30 | self.learning_rate = learning_rate 31 | self.num_classes = num_classes 32 | self.backbone = backbone 33 | if backbone is None: 34 | self.model = fasterrcnn_resnet50_fpn(pretrained=True, 35 | trainable_backbone_layers=trainable_backbone_layers,) 36 | 37 | in_features = self.model.roi_heads.box_predictor.cls_score.in_features 38 | self.model.roi_heads.box_predictor = FastRCNNPredictor(in_features, self.num_classes) 39 | 40 | else: 41 | backbone_model = create_fastercnn_backbone(self.backbone, fpn, pretrained_backbone, 42 | trainable_backbone_layers, **kwargs,) 43 | self.model = FasterRCNN(backbone_model, num_classes=num_classes, **kwargs) 44 | 45 | def forward(self, x): 46 | self.model.eval() 47 | return self.model(x) 48 | 49 | def training_step(self, batch, batch_idx): 50 | images, targets = batch 51 | targets = [{k: v for k, v in t.items()} for t in targets] 52 | 53 | # fasterrcnn takes both images and targets for training, returns 54 | loss_dict = self.model(images, targets) 55 | loss = sum(loss for loss in loss_dict.values()) 56 | return {"loss": loss, "log": loss_dict} 57 | 58 | def validation_step(self, batch, batch_idx): 59 | images, targets = batch 60 | # fasterrcnn takes only images for eval() mode 61 | outs = self.model(images) 62 | iou = torch.stack([_evaluate_iou(t, o) for t, o in zip(targets, outs)]).mean() 63 | giou = torch.stack([_evaluate_giou(t, o) for t, o in zip(targets, outs)]).mean() 64 | return {"val_iou": iou, "val_giou": giou} 65 | 66 | def validation_epoch_end(self, outs): 67 | avg_iou = torch.stack([o["val_iou"] for o in outs]).mean() 68 | avg_giou = torch.stack([o["val_giou"] for o in outs]).mean() 69 | logs = {"val_iou": avg_iou, "val_giou": avg_giou} 70 | return {"avg_val_iou": avg_iou, "avg_val_giou": avg_giou, "log": logs} 71 | 72 | def configure_optimizers(self): 73 | return torch.optim.SGD(self.model.parameters(), lr=self.learning_rate, 74 | momentum=0.9, weight_decay=0.005,) 75 | -------------------------------------------------------------------------------- /quickvision/models/detection/faster_rcnn/model_factory.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | from torchvision.models.detection.faster_rcnn import (fasterrcnn_resnet50_fpn, FasterRCNN, FastRCNNPredictor,) 3 | from torchvision.models.detection.backbone_utils import resnet_fpn_backbone, mobilenet_backbone 4 | from quickvision.models.components import create_torchvision_backbone 5 | 6 | __all__ = ["create_fastercnn", "create_fastercnn_backbone"] 7 | 8 | 9 | def create_fastercnn(num_classes: int = 91, backbone: nn.Module = None, **kwargs,): 10 | """ 11 | Creates Faster RCNN implementation based on torchvision library. 12 | Args: 13 | num_classes (int) : number of classes. 14 | Do not have class_id "0" it is reserved as background. 15 | num_classes = number of classes to label + 1 for background. 16 | """ 17 | 18 | if backbone is None: 19 | # Creates the default fasterrcnn as given in pytorch. Trained on COCO dataset 20 | model = fasterrcnn_resnet50_fpn(pretrained=True, **kwargs,) 21 | in_features = model.roi_heads.box_predictor.cls_score.in_features 22 | model.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes) 23 | 24 | else: 25 | model = FasterRCNN(backbone, num_classes=num_classes, **kwargs) 26 | 27 | return model 28 | 29 | 30 | def create_fastercnn_backbone(backbone: str, fpn: bool = True, pretrained: str = None, 31 | trainable_backbone_layers: int = 3, **kwargs,) -> nn.Module: 32 | 33 | """ 34 | Args: 35 | backbone (str): 36 | Supported backones are: "resnet18", "resnet34","resnet50", "resnet101", "resnet152", 37 | "resnext50_32x4d", "resnext101_32x8d", "wide_resnet50_2", "wide_resnet101_2", 38 | as resnets with fpn backbones. 39 | Without fpn backbones supported are: "resnet18", "resnet34", "resnet50","resnet101", 40 | "resnet152", "resnext101_32x8d", "mobilenet_v2", "vgg11", "vgg13", "vgg16", "vgg19", 41 | fpn (bool): If True then constructs fpn as well. 42 | pretrained (str): If None creates imagenet weights backbone. 43 | """ 44 | 45 | if fpn: 46 | # Creates a torchvision resnet model with fpn added. 47 | print("Resnet FPN Backbones works only for imagenet weights") 48 | backbone = resnet_fpn_backbone(backbone, pretrained=True, 49 | trainable_layers=trainable_backbone_layers, **kwargs,) 50 | else: 51 | # This does not create fpn backbone, it is supported for all models 52 | print("FPN is not supported for Non Resnet Backbones") 53 | backbone, _ = create_torchvision_backbone(backbone, pretrained) 54 | return backbone 55 | -------------------------------------------------------------------------------- /quickvision/models/detection/retinanet/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/quickvision/models/detection/retinanet/README.md -------------------------------------------------------------------------------- /quickvision/models/detection/retinanet/__init__.py: -------------------------------------------------------------------------------- 1 | from quickvision.models.detection.retinanet.model_factory import ( 2 | create_retinanet_backbone, 3 | create_retinanet 4 | ) 5 | 6 | from quickvision.models.detection.retinanet.engine import ( 7 | train_step, 8 | val_step, 9 | fit, 10 | train_sanity_fit, 11 | val_sanity_fit, 12 | sanity_fit, 13 | ) 14 | 15 | from quickvision.models.detection.retinanet.lightning_trainer import LitRetinanet 16 | -------------------------------------------------------------------------------- /quickvision/models/detection/retinanet/lightning_trainer.py: -------------------------------------------------------------------------------- 1 | import pytorch_lightning as pl 2 | import torch 3 | import torch.nn as nn 4 | from quickvision.models.components import create_torchvision_backbone 5 | from quickvision.models.detection.retinanet import create_retinanet_backbone 6 | from quickvision.models.detection.utils import _evaluate_iou, _evaluate_giou 7 | from torchvision.models.detection.retinanet import retinanet_resnet50_fpn, RetinaNet, RetinaNetHead 8 | 9 | __all__ = ["LitRetinanet"] 10 | 11 | 12 | class LitRetinanet(pl.LightningModule): 13 | """ 14 | Creates a ReinaNet which can be fine-tuned. 15 | """ 16 | 17 | def __init__(self, learning_rate: float = 0.0001, num_classes: int = 91, 18 | backbone: str = None, fpn: bool = True, 19 | pretrained_backbone: str = None, trainable_backbone_layers: int = 3, 20 | **kwargs, ): 21 | """ 22 | Args: 23 | learning_rate: the learning rate 24 | num_classes: number of detection classes (including background) 25 | pretrained: if true, returns a model pre-trained on COCO train2017 26 | pretrained_backbone (str): if "imagenet", returns a model with backbone pre-trained on Imagenet 27 | trainable_backbone_layers: number of trainable resnet layers starting from final block 28 | """ 29 | super().__init__() 30 | self.learning_rate = learning_rate 31 | self.num_classes = num_classes 32 | self.backbone = backbone 33 | if backbone is None: 34 | self.model = retinanet_resnet50_fpn(pretrained=True, **kwargs) 35 | 36 | self.model.head = RetinaNetHead(in_channels=self.model.backbone.out_channels, 37 | num_anchors=self.model.head.classification_head.num_anchors, 38 | num_classes=num_classes, **kwargs) 39 | 40 | else: 41 | backbone_model = create_retinanet_backbone(self.backbone, fpn, pretrained_backbone, 42 | trainable_backbone_layers, **kwargs) 43 | self.model = RetinaNet(backbone_model, num_classes=num_classes, **kwargs) 44 | 45 | def forward(self, x): 46 | self.model.eval() 47 | return self.model(x) 48 | 49 | def training_step(self, batch, batch_idx): 50 | images, targets = batch 51 | targets = [{k: v for k, v in t.items()} for t in targets] 52 | 53 | # RetinaNet takes both images and targets for training, returns 54 | loss_dict = self.model(images, targets) 55 | loss = sum(loss for loss in loss_dict.values()) 56 | return {"loss": loss, "log": loss_dict} 57 | 58 | def validation_step(self, batch, batch_idx): 59 | images, targets = batch 60 | # Retinanet takes only images for eval() mode 61 | outs = self.model(images) 62 | iou = torch.stack([_evaluate_iou(t, o) for t, o in zip(targets, outs)]).mean() 63 | giou = torch.stack([_evaluate_giou(t, o) for t, o in zip(targets, outs)]).mean() 64 | return {"val_iou": iou, "val_giou": giou} 65 | 66 | def validation_epoch_end(self, outs): 67 | avg_iou = torch.stack([o["val_iou"] for o in outs]).mean() 68 | avg_giou = torch.stack([o["val_giou"] for o in outs]).mean() 69 | logs = {"val_iou": avg_iou, "val_giou": avg_giou} 70 | return {"avg_val_iou": avg_iou, "avg_val_giou": avg_giou, "log": logs} 71 | 72 | def configure_optimizers(self): 73 | return torch.optim.SGD(self.model.parameters(), lr=self.learning_rate, momentum=0.9, weight_decay=0.005,) 74 | -------------------------------------------------------------------------------- /quickvision/models/detection/retinanet/model_factory.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | from torchvision.models.detection.retinanet import RetinaNet, retinanet_resnet50_fpn, RetinaNetHead 3 | from torchvision.models.detection.backbone_utils import resnet_fpn_backbone, mobilenet_backbone 4 | from quickvision.models.components import create_torchvision_backbone 5 | 6 | __all__ = ["create_retinanet", "create_retinanet_backbone"] 7 | 8 | 9 | def create_retinanet(num_classes: int = 91, backbone: nn.Module = None, **kwargs,): 10 | """ 11 | Creates RetinaNet implementation based on torchvision library. 12 | Args: 13 | num_classes (int) : number of classes. 14 | Do not have class_id "0" it is reserved as background. 15 | num_classes = number of classes to label + 1 for background. 16 | """ 17 | if backbone is None: 18 | model = retinanet_resnet50_fpn(pretrained=True, num_classes=91, **kwargs,) 19 | model.head = RetinaNetHead( 20 | in_channels=model.backbone.out_channels, 21 | num_anchors=model.head.classification_head.num_anchors, 22 | num_classes=num_classes,) 23 | else: 24 | model = RetinaNet(backbone, num_classes=num_classes, **kwargs) 25 | 26 | return model 27 | 28 | 29 | def create_retinanet_backbone(backbone: str, fpn: bool = True, pretrained: str = None, 30 | trainable_backbone_layers: int = 3, **kwargs) -> nn.Module: 31 | """ 32 | Args: 33 | backbone (str): 34 | Supported backones are: "resnet18", "resnet34","resnet50", "resnet101", "resnet152", 35 | "resnext50_32x4d", "resnext101_32x8d", "wide_resnet50_2", "wide_resnet101_2", 36 | as resnets with fpn backbones. 37 | Without fpn backbones supported are: "resnet18", "resnet34", "resnet50","resnet101", 38 | "resnet152", "resnext101_32x8d", "mobilenet_v2", "vgg11", "vgg13", "vgg16", "vgg19", 39 | fpn (bool): If True then constructs fpn as well. 40 | pretrained (bool): Creates a pretrained backbone with imagenet weights. 41 | """ 42 | if fpn: 43 | # Creates a torchvision resnet model with fpn added. 44 | print("Resnet FPN Backbones works only for imagenet weights") 45 | backbone = resnet_fpn_backbone(backbone, pretrained=True, 46 | trainable_layers=trainable_backbone_layers, **kwargs) 47 | else: 48 | # This does not create fpn backbone, it is supported for all models 49 | print("FPN is not supported for Non Resnet Backbones") 50 | backbone, _ = create_torchvision_backbone(backbone, pretrained) 51 | return backbone 52 | -------------------------------------------------------------------------------- /quickvision/models/detection/utils.py: -------------------------------------------------------------------------------- 1 | from torchvision.ops import box_iou, generalized_box_iou 2 | import torch 3 | 4 | _all__ = ["_evaluate_iou", "_evaluate_giou"] 5 | 6 | 7 | def _evaluate_iou(target, pred): 8 | """ 9 | Evaluate intersection over union (IOU) for target from dataset and output prediction 10 | from model. 11 | """ 12 | # Taken from pl-bolts 13 | if pred["boxes"].shape[0] == 0: 14 | # no box detected, 0 IOU 15 | return torch.tensor(0.0, device=pred["boxes"].device) 16 | return box_iou(target["boxes"], pred["boxes"]).diag().mean() 17 | 18 | 19 | def _evaluate_giou(target, pred): 20 | """ 21 | Evaluate generalized intersection over union (gIOU) for target from dataset and output prediction 22 | from model. 23 | """ 24 | 25 | if pred["boxes"].shape[0] == 0: 26 | # no box detected, 0 IOU 27 | return torch.tensor(0.0, device=pred["boxes"].device) 28 | return generalized_box_iou(target["boxes"], pred["boxes"]).diag().mean() 29 | -------------------------------------------------------------------------------- /quickvision/models/segmentation/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/quickvision/models/segmentation/README.md -------------------------------------------------------------------------------- /quickvision/models/segmentation/__init__.py: -------------------------------------------------------------------------------- 1 | from quickvision.models.segmentation import fcn 2 | from quickvision.models.segmentation import deeplab 3 | -------------------------------------------------------------------------------- /quickvision/models/segmentation/deeplab/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/quickvision/models/segmentation/deeplab/README.md -------------------------------------------------------------------------------- /quickvision/models/segmentation/deeplab/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/quickvision/models/segmentation/deeplab/__init__.py -------------------------------------------------------------------------------- /quickvision/models/segmentation/fcn/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/quickvision/models/segmentation/fcn/README.md -------------------------------------------------------------------------------- /quickvision/models/segmentation/fcn/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/quickvision/models/segmentation/fcn/__init__.py -------------------------------------------------------------------------------- /quickvision/optimizers/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/quickvision/optimizers/README.md -------------------------------------------------------------------------------- /quickvision/optimizers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/quickvision/optimizers/__init__.py -------------------------------------------------------------------------------- /quickvision/pretrained/README.md: -------------------------------------------------------------------------------- 1 | ### Here we store pretrained weights for models. 2 | 3 | - To add your pretrained weights raise a PR and we will help you ! 4 | 5 | -------------------------------------------------------------------------------- /quickvision/pretrained/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/quickvision/pretrained/__init__.py -------------------------------------------------------------------------------- /quickvision/pretrained/_model_zoo.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 3 | import torchvision 4 | 5 | TORCHVISION_MODEL_ZOO = { 6 | "vgg11": torchvision.models.vgg11, 7 | "vgg13": torchvision.models.vgg13, 8 | "vgg16": torchvision.models.vgg16, 9 | "vgg19": torchvision.models.vgg19, 10 | "resnet18": torchvision.models.resnet18, 11 | "resnet34": torchvision.models.resnet34, 12 | "resnet50": torchvision.models.resnet50, 13 | "resnet101": torchvision.models.resnet101, 14 | "resnet152": torchvision.models.resnet152, 15 | "resnext50_32x4d": torchvision.models.resnext50_32x4d, 16 | "resnext101_32x8d": torchvision.models.resnext101_32x8d, 17 | "mnasnet0_5": torchvision.models.mnasnet0_5, 18 | "mnasnet0_75": torchvision.models.mnasnet0_75, 19 | "mnasnet1_0": torchvision.models.mnasnet1_0, 20 | "mnasnet1_3": torchvision.models.mnasnet1_3, 21 | "mobilenet_v2": torchvision.models.mobilenet_v2, 22 | "mobilenet_v3_large": torchvision.models.mobilenet_v3_large, 23 | "wide_resnet50_2": torchvision.models.wide_resnet50_2, 24 | "wide_resnet101_2": torchvision.models.wide_resnet101_2, 25 | } 26 | -------------------------------------------------------------------------------- /quickvision/pretrained/_pretrained_cnns.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 3 | WEIGHTS_DICT = { 4 | "mobilenet_v2": { 5 | "imagenet": "https://download.pytorch.org/models/mobilenet_v2-b0353104.pth" 6 | }, 7 | "mobilenet_v3_small": { 8 | "imagenet": "https://download.pytorch.org/models/mobilenet_v3_small-047dcff4.pth" 9 | }, 10 | "mobilenet_v3_large": { 11 | "imagenet": "https://download.pytorch.org/models/mobilenet_v3_large-8738ca79.pth" 12 | }, 13 | "mnasnet0_5": { 14 | "imagenet": "https://download.pytorch.org/models/mnasnet0.5_top1_67.823-3ffadce67e.pth", 15 | }, 16 | "mnasnet0_75": { 17 | "imagenet": None, 18 | }, 19 | "mnasnet1_0": { 20 | "imagenet": "https://download.pytorch.org/models/mnasnet1.0_top1_73.512-f206786ef8.pth", 21 | }, 22 | "mnasnet1_3": { 23 | "imagenet": None, 24 | }, 25 | "vgg11": { 26 | "imagenet": "https://download.pytorch.org/models/vgg11-bbd30ac9.pth", 27 | }, 28 | "vgg13": { 29 | "imagenet": "https://download.pytorch.org/models/vgg13-c768596a.pth", 30 | }, 31 | "vgg16": { 32 | "imagenet": "https://download.pytorch.org/models/vgg16-397923af.pth", 33 | }, 34 | "vgg19": { 35 | "imagenet": "https://download.pytorch.org/models/vgg19-dcbb9e9d.pth", 36 | }, 37 | "resnet18": { 38 | "imagenet": "https://download.pytorch.org/models/resnet18-5c106cde.pth", 39 | "ssl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_supervised_resnet18-d92f0530.pth", 40 | "swsl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_weakly_supervised_resnet18-118f1556.pth", 41 | }, 42 | "resnet34": { 43 | "imagenet": "https://download.pytorch.org/models/resnet34-333f7ec4.pth", 44 | }, 45 | "resnet50": { 46 | "imagenet": "https://download.pytorch.org/models/resnet50-19c8e357.pth", 47 | "ssl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_supervised_resnet50-08389792.pth", 48 | "swsl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_weakly_supervised_resnet50-16a12f1b.pth", 49 | "svaw-800": "https://dl.fbaipublicfiles.com/deepcluster/swav_800ep_pretrain.pth.tar", 50 | "deepcluster-v2-800": "https://dl.fbaipublicfiles.com/deepcluster/deepclusterv2_800ep_pretrain.pth.tar", 51 | "sela-v2-400": "https://dl.fbaipublicfiles.com/deepcluster/selav2_400ep_pretrain.pth.tar", 52 | }, 53 | "resnet101": { 54 | "imagenet": "https://download.pytorch.org/models/resnet101-5d3b4d8f.pth", 55 | }, 56 | "resnet152": { 57 | "imagenet": "https://download.pytorch.org/models/resnet152-b121ed2d.pth", 58 | }, 59 | "resnext50_32x4d": { 60 | "imagenet": "https://download.pytorch.org/models/resnext50_32x4d-7cdf4587.pth", 61 | "ssl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_supervised_resnext50_32x4-ddb3e555.pth", 62 | "swsl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_weakly_supervised_resnext50_32x4-72679e44.pth", 63 | }, 64 | "resnext101_32x4d": { 65 | "imagenet": "https://download.pytorch.org/models/resnext50_32x4d-7cdf4587.pth", 66 | "ssl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_supervised_resnext101_32x4-dc43570a.pth", 67 | "swsl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_weakly_supervised_resnext101_32x4-3f87e46b.pth", 68 | }, 69 | "resnext101_32x8d": { 70 | "imagenet": "https://download.pytorch.org/models/resnext101_32x8d-8ba56ff5.pth", 71 | "instagram": "https://download.pytorch.org/models/ig_resnext101_32x8-c38310e5.pth", 72 | "ssl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_supervised_resnext101_32x8-2cfe2f8b.pth", 73 | "swsl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_weakly_supervised_resnext101_32x8-b4712904.pth", 74 | }, 75 | "resnext101_32x16d": { 76 | "instagram": "https://download.pytorch.org/models/ig_resnext101_32x16-c6f796b0.pth", 77 | "ssl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_supervised_resnext101_32x16-15fffa57.pth", 78 | "swsl": "https://dl.fbaipublicfiles.com/semiweaksupervision/model_files/semi_weakly_supervised_resnext101_32x16-f3559a9c.pth", 79 | }, 80 | "resnext101_32x32d": { 81 | "instagram": "https://download.pytorch.org/models/ig_resnext101_32x32-e4b90b00.pth", 82 | }, 83 | "resnext101_32x48d": { 84 | "instagram": "https://download.pytorch.org/models/ig_resnext101_32x48-3e41cc8a.pth", 85 | }, 86 | "wide_resnet50_2": { 87 | "imagenet": "https://download.pytorch.org/models/wide_resnet50_2-95faca4d.pth", 88 | }, 89 | "wide_resnet101_2": { 90 | "imagenet": "https://download.pytorch.org/models/wide_resnet101_2-32ee1156.pth", 91 | }, 92 | } 93 | -------------------------------------------------------------------------------- /quickvision/pretrained/_pretrained_detection.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 3 | frcnn_weights_dict = { 4 | "resnet50": { 5 | "coco": "https://download.pytorch.org/models/fasterrcnn_resnet50_fpn_coco-258fb6c6.pth" 6 | }, 7 | "mobilenet_v3_large_320": { 8 | "coco": "https://download.pytorch.org/models/fasterrcnn_mobilenet_v3_large_320_fpn-907ea3f9.pth" 9 | }, 10 | "mobilenet_v3_large": { 11 | "coco": "https://download.pytorch.org/models/fasterrcnn_mobilenet_v3_large_fpn-fb6a3cc7.pth" 12 | }, 13 | } 14 | 15 | retina_weights_dict = { 16 | "resnet50": { 17 | "coco": "https://download.pytorch.org/models/retinanet_resnet50_fpn_coco-eeacb38b.pth" 18 | }, 19 | } 20 | 21 | detr_weights_dict = { 22 | "resnet50": { 23 | "coco": "https://dl.fbaipublicfiles.com/detr/detr-r50-e632da11.pth" 24 | }, 25 | "resnet101": { 26 | "coco": "https://dl.fbaipublicfiles.com/detr/detr-r101-2c7b67e5.pth" 27 | }, 28 | "resnet50_dc5": { 29 | "coco": "ttps://dl.fbaipublicfiles.com/detr/detr-r50-dc5-f0fb7ef5.pth" 30 | }, 31 | "resnet101_dc5": { 32 | "coco": "https://dl.fbaipublicfiles.com/detr/detr-r101-dc5-a2e86def.pth" 33 | }, 34 | } 35 | -------------------------------------------------------------------------------- /quickvision/pretrained/_pretrained_weights.py: -------------------------------------------------------------------------------- 1 | import torch 2 | 3 | __all__ = ["_load_pretrained_weights"] 4 | 5 | 6 | def _load_pretrained_weights(weights_dict, model_name: str, pretrained: str): 7 | state_dict = torch.hub.load_state_dict_from_url(weights_dict[model_name][pretrained], map_location="cpu") 8 | return state_dict 9 | -------------------------------------------------------------------------------- /quickvision/utils/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/quickvision/utils/README.md -------------------------------------------------------------------------------- /quickvision/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from quickvision.utils.averaging_utils import AverageMeter 2 | from quickvision.utils.early_stopping_utlis import EarlyStopping 3 | from quickvision.utils.nested_tensor_utils import NestedTensor, nested_tensor_from_tensor_list 4 | from quickvision.utils.progress_utils import ProgressMeter 5 | from quickvision.utils.torch_utils import seed_everything, set_debug_apis, print_size_of_model 6 | -------------------------------------------------------------------------------- /quickvision/utils/averaging_utils.py: -------------------------------------------------------------------------------- 1 | 2 | __all__ = ["AverageMeter"] 3 | 4 | 5 | class AverageMeter(object): 6 | """Computes and stores the average and current value""" 7 | 8 | def __init__(self): 9 | self.reset() 10 | 11 | def reset(self): 12 | self.val = 0 13 | self.avg = 0 14 | self.sum = 0 15 | self.count = 0 16 | 17 | def update(self, val, n=1): 18 | self.val = val 19 | self.sum += val * n 20 | self.count += n 21 | self.avg = self.sum / self.count 22 | -------------------------------------------------------------------------------- /quickvision/utils/early_stopping_utlis.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import numpy as np 3 | 4 | __all__ = ["EarlyStopping"] 5 | 6 | 7 | class EarlyStopping: 8 | """ 9 | Early stops the training if validation loss 10 | doesn't improve after a given patience. 11 | """ 12 | 13 | def __init__(self, patience=7, verbose=False, 14 | delta=0.0001, path="checkpoint.pt"): 15 | 16 | """ 17 | Args: 18 | patience (int): How long to wait after 19 | last time validation loss improved. 20 | Default: 7 21 | verbose (bool): If True, prints a message 22 | for each validation loss improvement. 23 | Default: False 24 | delta (float): Minimum change in the 25 | monitored quantity to qualify as an improvement. 26 | Default: 0 27 | path (str): Path for the checkpoint to be saved to. 28 | Default: 'checkpoint.pt' 29 | """ 30 | self.patience = patience 31 | self.verbose = verbose 32 | self.counter = 0 33 | self.best_score = None 34 | self.early_stop = False 35 | self.val_loss_min = np.Inf 36 | self.delta = delta 37 | self.path = path 38 | 39 | def __call__(self, val_loss, model): 40 | 41 | score = -val_loss 42 | 43 | if self.best_score is None: 44 | self.best_score = score 45 | self.save_checkpoint(val_loss, model) 46 | elif score < self.best_score + self.delta: 47 | self.counter += 1 48 | print(f"EarlyStopping counter: {self.counter} out of {self.patience}") 49 | if self.counter >= self.patience: 50 | self.early_stop = True 51 | else: 52 | self.best_score = score 53 | self.save_checkpoint(val_loss, model) 54 | self.counter = 0 55 | 56 | def save_checkpoint(self, val_loss, model): 57 | """Saves model when validation loss decrease.""" 58 | if self.verbose: 59 | print(f"Validation loss decreased ({self.val_loss_min:.6f} --> {val_loss:.6f}). Saving model ...") 60 | torch.save(model.state_dict(), self.path) 61 | self.val_loss_min = val_loss 62 | -------------------------------------------------------------------------------- /quickvision/utils/nested_tensor_utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import torch 3 | import numpy as np 4 | import random 5 | import torchvision 6 | from typing import Optional, List 7 | from torch import Tensor 8 | 9 | 10 | __all__ = ["NestedTensor", "nested_tensor_from_tensor_list"] 11 | 12 | 13 | class NestedTensor(object): 14 | def __init__(self, tensors, mask: Optional[Tensor]): 15 | self.tensors = tensors 16 | self.mask = mask 17 | 18 | def to(self, device: torch.device) -> "NestedTensor": 19 | cast_tensor = self.tensors.to(device) 20 | mask = self.mask 21 | if mask is not None: 22 | assert mask is not None 23 | cast_mask = mask.to(device) 24 | else: 25 | cast_mask = None 26 | return NestedTensor(cast_tensor, cast_mask) 27 | 28 | def decompose(self): 29 | return self.tensors, self.mask 30 | 31 | def __repr__(self): 32 | return str(self.tensors) 33 | 34 | 35 | def _max_by_axis(the_list): 36 | # type: (List[List[int]]) -> List[int] 37 | maxes = the_list[0] 38 | for sublist in the_list[1:]: 39 | for index, item in enumerate(sublist): 40 | maxes[index] = max(maxes[index], item) 41 | return maxes 42 | 43 | 44 | def nested_tensor_from_tensor_list(tensor_list: List[Tensor]): 45 | # TODO make this more general 46 | if tensor_list[0].ndim == 3: 47 | if torchvision._is_tracing(): 48 | # nested_tensor_from_tensor_list() does not export well to ONNX 49 | # call _onnx_nested_tensor_from_tensor_list() instead 50 | return _onnx_nested_tensor_from_tensor_list(tensor_list) 51 | 52 | # TODO make it support different-sized images 53 | max_size = _max_by_axis([list(img.shape) for img in tensor_list]) 54 | # min_size = tuple(min(s) for s in zip(*[img.shape for img in tensor_list])) 55 | batch_shape = [len(tensor_list)] + max_size 56 | b, c, h, w = batch_shape 57 | dtype = tensor_list[0].dtype 58 | device = tensor_list[0].device 59 | tensor = torch.zeros(batch_shape, dtype=dtype, device=device) 60 | mask = torch.ones((b, h, w), dtype=torch.bool, device=device) 61 | for img, pad_img, m in zip(tensor_list, tensor, mask): 62 | pad_img[: img.shape[0], : img.shape[1], : img.shape[2]].copy_(img) 63 | m[: img.shape[1], :img.shape[2]] = False 64 | else: 65 | raise ValueError('not supported') 66 | return NestedTensor(tensor, mask) 67 | 68 | 69 | # _onnx_nested_tensor_from_tensor_list() is an implementation of 70 | # nested_tensor_from_tensor_list() that is supported by ONNX tracing. 71 | @torch.jit.unused 72 | def _onnx_nested_tensor_from_tensor_list(tensor_list: List[Tensor]) -> NestedTensor: 73 | max_size = [] 74 | for i in range(tensor_list[0].dim()): 75 | max_size_i = torch.max(torch.stack([img.shape[i] for img in tensor_list]).to(torch.float32)).to(torch.int64) 76 | max_size.append(max_size_i) 77 | max_size = tuple(max_size) 78 | 79 | # work around for 80 | # pad_img[: img.shape[0], : img.shape[1], : img.shape[2]].copy_(img) 81 | # m[: img.shape[1], :img.shape[2]] = False 82 | # which is not yet supported in onnx 83 | padded_imgs = [] 84 | padded_masks = [] 85 | for img in tensor_list: 86 | padding = [(s1 - s2) for s1, s2 in zip(max_size, tuple(img.shape))] 87 | padded_img = torch.nn.functional.pad(img, (0, padding[2], 0, padding[1], 0, padding[0])) 88 | padded_imgs.append(padded_img) 89 | 90 | m = torch.zeros_like(img[0], dtype=torch.int, device=img.device) 91 | padded_mask = torch.nn.functional.pad(m, (0, padding[2], 0, padding[1]), "constant", 1) 92 | padded_masks.append(padded_mask.to(torch.bool)) 93 | 94 | tensor = torch.stack(padded_imgs) 95 | mask = torch.stack(padded_masks) 96 | 97 | return NestedTensor(tensor, mask=mask) 98 | -------------------------------------------------------------------------------- /quickvision/utils/progress_utils.py: -------------------------------------------------------------------------------- 1 | __all__ = ["ProgressMeter"] 2 | 3 | 4 | class ProgressMeter(object): 5 | def __init__(self, num_batches, meters, prefix=""): 6 | self.batch_fmtstr = self._get_batch_fmtstr(num_batches) 7 | self.meters = meters 8 | self.prefix = prefix 9 | 10 | def display(self, batch): 11 | entries = [self.prefix + self.batch_fmtstr.format(batch)] 12 | entries += [str(meter) for meter in self.meters] 13 | print("\t".join(entries)) 14 | 15 | def _get_batch_fmtstr(self, num_batches): 16 | num_digits = len(str(num_batches // 1)) 17 | fmt = "{:" + str(num_digits) + "d}" 18 | return "[" + fmt + "/" + fmt.format(num_batches) + "]" 19 | -------------------------------------------------------------------------------- /quickvision/utils/torch_utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import torch 3 | import numpy as np 4 | import random 5 | import torchvision 6 | from typing import Optional, List 7 | from torch import Tensor 8 | 9 | __all__ = ["set_debug_apis", "seed_everything", "print_size_of_model"] 10 | 11 | 12 | def set_debug_apis(state: bool = False): 13 | torch.autograd.profiler.profile(enabled=state) 14 | torch.autograd.profiler.emit_nvtx(enabled=state) 15 | torch.autograd.set_detect_anomaly(mode=state) 16 | 17 | 18 | def seed_everything(seed): 19 | """ 20 | Makes code deterministic using a given seed. 21 | Internally sets all seeds of torch, numpy and random. 22 | """ 23 | random.seed(seed) 24 | os.environ["PYTHONHASHSEED"] = str(seed) 25 | np.random.seed(seed) 26 | torch.manual_seed(seed) 27 | torch.cuda.manual_seed(seed) 28 | torch.backends.cudnn.deterministic = True 29 | torch.backends.cudnn.benchmark = True 30 | 31 | 32 | def print_size_of_model(model): 33 | torch.save(model.state_dict(), "temp.p") 34 | print("Size (MB):", os.path.getsize("temp.p") / 1e6) 35 | os.remove("temp.p") 36 | -------------------------------------------------------------------------------- /requirements-test.txt: -------------------------------------------------------------------------------- 1 | --find-links https://download.pytorch.org/whl/torch_stable.html 2 | torch==1.8.1+cpu 3 | torchvision==0.9.1+cpu 4 | pytorch_lightning==1.2.6 5 | scipy==1.5.4 6 | timm 7 | pandas 8 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | torch==1.8.1 2 | torchvision==0.9.1 3 | pytorch_lightning==1.2.6 4 | scipy==1.5.4 5 | 6 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | # Taken from PyTorch Lightning Bolts. 2 | 3 | [tool:pytest] 4 | norecursedirs = 5 | .git 6 | dist 7 | build 8 | addopts = 9 | --strict 10 | --doctest-modules 11 | --durations=0 12 | 13 | [coverage:report] 14 | exclude_lines = 15 | pragma: no-cover 16 | pass 17 | if __name__ == .__main__.: 18 | add_model_specific_args 19 | 20 | [coverage:run] 21 | # TODO, remove this ignores in future 22 | 23 | [flake8] 24 | max-line-length = 120 25 | exclude = .tox,*.egg,build,temp 26 | select = E,W,F 27 | doctests = True 28 | verbose = 2 29 | # https://pep8.readthedocs.io/en/latest/intro.html#error-codes 30 | format = pylint 31 | # see: https://www.flake8rules.com/ 32 | ignore = 33 | E731 # Do not assign a lambda expression, use a def 34 | E231 # Ignore missing space after comma 35 | W504 # Line break occurred after a binary operator 36 | F401 # Module imported but unused 37 | F841 # Local variable name is assigned to but never used 38 | W605 # Invalid escape sequence 'x' 39 | 40 | # setup.cfg or tox.ini 41 | [check-manifest] 42 | ignore = 43 | *.yml 44 | .github 45 | .github/* 46 | .circleci 47 | 48 | [metadata] 49 | license_file = LICENSE 50 | description-file = README.md 51 | # long_description = file:README.md 52 | # long_description_content_type = text/markdown 53 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Adopted from PyTorch Lightning Bolts. 2 | # !/usr/bin/env python 3 | 4 | import os 5 | 6 | # Always prefer setuptools over distutils 7 | from setuptools import setup, find_packages 8 | 9 | try: 10 | import builtins 11 | except ImportError: 12 | import __builtin__ as builtins 13 | 14 | # https://packaging.python.org/guides/single-sourcing-package-version/ 15 | # http://blog.ionelmc.ro/2014/05/25/python-packaging/ 16 | 17 | PATH_ROOT = os.path.dirname(__file__) 18 | 19 | 20 | def load_requirements(path_dir=PATH_ROOT, file_name='requirements.txt', comment_char='#'): 21 | with open(os.path.join(path_dir, file_name), 'r', encoding="utf-8", errors="ignore") as file: 22 | lines = [ln.strip() for ln in file.readlines()] 23 | reqs = [] 24 | for ln in lines: 25 | if comment_char in ln: # filer all comments 26 | ln = ln[:ln.index(comment_char)].strip() 27 | if ln.startswith('http'): # skip directly installed dependencies 28 | continue 29 | if ln: # if requirement is not empty 30 | reqs.append(ln) 31 | return reqs 32 | 33 | 34 | def load_long_description(): 35 | # url = os.path.join("https://github.com/Quick-AI/quickvision", 'raw', , 'docs') 36 | text = open('README.md', encoding='utf-8', errors="ignore").read() 37 | # replace relative repository path to absolute link to the release 38 | # text = text.replace('](docs', f']({url}') 39 | # SVG images are not readable on PyPI, so replace them with PNG 40 | text = text.replace('.svg', '.png') 41 | return text 42 | 43 | 44 | # https://packaging.python.org/discussions/install-requires-vs-requirements / 45 | setup( 46 | name='quickvision', 47 | version="0.2.1", 48 | description="Computer Vision models and training", 49 | author="Aditya Oke", 50 | author_email="okeaditya315@gmail.com", 51 | # url=pl_bolts.__homepage__, 52 | download_url="https://github.com/Quick-AI/quickvision", 53 | license="apache2", 54 | packages=find_packages(exclude=['tests', 'docs']), 55 | 56 | long_description=load_long_description(), 57 | long_description_content_type='text/markdown', 58 | include_package_data=True, 59 | zip_safe=False, 60 | 61 | keywords=['Deep Learning', 'PyTorch'], 62 | python_requires='>=3.6', 63 | setup_requires=[], 64 | install_requires=load_requirements(), 65 | 66 | project_urls={ 67 | "Bug Tracker": "https://github.com/Quick-AI/quickvision/issues", 68 | "Documentation": "https://quick-ai.github.io/quickvision/", 69 | "Source Code": "https://github.com/Quick-AI/quickvision", 70 | }, 71 | 72 | classifiers=[ 73 | 'Environment :: Console', 74 | 'Natural Language :: English', 75 | # How mature is this project? Common values are 76 | # 3 - Alpha, 4 - Beta, 5 - Production/Stable 77 | 'Development Status :: 3 - Alpha', 78 | # Indicate who your project is intended for 79 | 'Intended Audience :: Developers', 80 | 'Topic :: Scientific/Engineering :: Artificial Intelligence', 81 | 'Topic :: Scientific/Engineering :: Image Recognition', 82 | 'Topic :: Scientific/Engineering :: Information Analysis', 83 | # Pick your license as you wish 84 | 'License :: OSI Approved :: Apache Software License', 85 | 'Operating System :: OS Independent', 86 | # Specify the Python versions you support here. In particular, ensure 87 | # that you indicate whether you support Python 2, Python 3 or both. 88 | 'Programming Language :: Python :: 3', 89 | 'Programming Language :: Python :: 3.6', 90 | 'Programming Language :: Python :: 3.7', 91 | 'Programming Language :: Python :: 3.8', 92 | ], 93 | ) 94 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # Tests 2 | 3 | - These tests are automatically triggered by the CI 4 | - Write your tests in this folder using [unittest](https://docs.python.org/3/library/unittest.html). 5 | -------------------------------------------------------------------------------- /tests/assets/10001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/tests/assets/10001.png -------------------------------------------------------------------------------- /tests/assets/10002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/tests/assets/10002.png -------------------------------------------------------------------------------- /tests/assets/csv_dataset.csv: -------------------------------------------------------------------------------- 1 | Image,Label 2 | 10001,0 3 | 10002,1 -------------------------------------------------------------------------------- /tests/assets/grace_hopper_517x606.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oke-aditya/quickvision/dc3c083356f3afa12c8992254249d3a1a3ea0d7d/tests/assets/grace_hopper_517x606.jpg -------------------------------------------------------------------------------- /tests/dataset_utils.py: -------------------------------------------------------------------------------- 1 | import torchvision 2 | import torch 3 | import torchvision.transforms as T 4 | from torchvision import ops 5 | from torchvision import datasets 6 | from torch.utils.data import Dataset, DataLoader 7 | from PIL import Image 8 | 9 | __all__ = ["create_cifar10_dataset", "create_loaders", "DummyDetectionDataset"] 10 | 11 | 12 | class DummyDetectionDataset(Dataset): 13 | """ 14 | Generate a dummy dataset for detection 15 | Example:: 16 | >>> ds = DummyDetectionDataset() 17 | >>> dl = DataLoader(ds, batch_size=7) 18 | """ 19 | def __init__(self, img_shape: tuple = (3, 256, 256), num_boxes: int = 1, 20 | num_classes: int = 2, class_start: int = 0, 21 | num_samples: int = 10000, box_fmt: str = "xyxy", 22 | normalize: bool = False): 23 | """ 24 | Args: 25 | *shapes: list of shapes 26 | img_shape: Tuple of (channels, height, width) 27 | num_boxes: Number of boxes per images 28 | num_classes: Number of classes for image. 29 | num_samples: how many samples to use in this dataset. 30 | box_fmt: Format of Bounding boxes, supported : "xyxy", "xywh", "cxcywh" 31 | """ 32 | super().__init__() 33 | self.img_shape = img_shape 34 | self.num_samples = num_samples 35 | self.num_boxes = num_boxes 36 | self.num_classes = num_classes 37 | self.box_fmt = box_fmt 38 | self.class_start = class_start 39 | self.class_end = self.class_start + self.num_classes 40 | self.normalize = normalize 41 | 42 | def __len__(self): 43 | return self.num_samples 44 | 45 | def _random_bbox(self): 46 | c, h, w = self.img_shape 47 | xs = torch.randint(w, (2,), dtype=torch.float32) 48 | ys = torch.randint(h, (2,), dtype=torch.float32) 49 | if self.normalize: 50 | xs /= self.img_shape[0] # divide by the width 51 | ys /= self.img_shape[1] # divide by the height 52 | # A small hacky fix to avoid degenerate boxes. 53 | return [min(xs), min(ys), max(xs) + 0.1, max(ys) + 0.1] 54 | 55 | def __getitem__(self, idx: int): 56 | img = torch.rand(self.img_shape) 57 | boxes = torch.tensor([self._random_bbox() for _ in range(self.num_boxes)], dtype=torch.float32) 58 | boxes = ops.clip_boxes_to_image(boxes, (self.img_shape[1], self.img_shape[2])) 59 | # No problems if we pass same in_fmt and out_fmt, it is covered by box_convert 60 | converted_boxes = ops.box_convert(boxes, in_fmt="xyxy", out_fmt=self.box_fmt) 61 | labels = torch.randint(self.class_start, self.class_end, (self.num_boxes,), dtype=torch.long) 62 | return img, {"boxes": converted_boxes, "labels": labels} 63 | 64 | 65 | def create_cifar10_dataset(): 66 | """ 67 | Creates CIFAR10 train dataset and a test dataset. 68 | """ 69 | 70 | train_transforms = T.Compose([T.ToTensor(), T.Normalize((0.5,), (0.5,))]) 71 | valid_transforms = T.Compose([T.ToTensor(), T.Normalize((0.5,), (0.5,))]) 72 | 73 | train_set = datasets.CIFAR10("./data", download=True, train=True, transform=train_transforms) 74 | valid_set = datasets.CIFAR10("./data", download=True, train=False, transform=valid_transforms) 75 | 76 | return train_set, valid_set 77 | 78 | 79 | def create_loaders(train_dataset, valid_dataset, train_batch_size=32, valid_batch_size=32, num_workers=4): 80 | 81 | """ 82 | Creates train loader and test loader from train and test datasets 83 | Args: 84 | train_dataset: Torchvision train dataset. 85 | valid_dataset: Torchvision valid dataset. 86 | train_batch_size (int) : Default 32, Training Batch size 87 | valid_batch_size (int) : Default 32, Validation Batch size 88 | num_workers (int) : Defualt 1, Number of workers for training and validation. 89 | """ 90 | 91 | train_loader = DataLoader(train_dataset, train_batch_size, shuffle=True, 92 | num_workers=num_workers) 93 | 94 | valid_loader = DataLoader(valid_dataset, valid_batch_size, shuffle=False, 95 | num_workers=num_workers) 96 | 97 | return train_loader, valid_loader 98 | -------------------------------------------------------------------------------- /tests/py_utils.py: -------------------------------------------------------------------------------- 1 | 2 | __all__ = ["is_iterable", "cycle_over"] 3 | 4 | 5 | def is_iterable(obj): 6 | try: 7 | iter(obj) 8 | return True 9 | except TypeError: 10 | return False 11 | 12 | 13 | def cycle_over(objs): 14 | for idx, obj in enumerate(objs): 15 | yield obj, objs[:idx] + objs[idx + 1:] 16 | -------------------------------------------------------------------------------- /tests/test_datasets.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import torch 3 | import torch.nn as nn 4 | import pandas as pd 5 | import torchvision.transforms as T 6 | from quickvision.datasets.classification import CSVSingleLabelDataset 7 | from quickvision.models.classification import cnn 8 | 9 | df = pd.read_csv("tests/assets/csv_dataset.csv") 10 | data_dir = "tests/assets/" 11 | tfms = T.Compose([T.ToTensor(),]) 12 | 13 | 14 | class CSVSingleLabelDatasetTester(unittest.TestCase): 15 | def test_csv_single_label_dataset(self): 16 | complete_dataset = CSVSingleLabelDataset( 17 | df, data_dir, "Image", "Label", tfms, "png" 18 | ) 19 | self.assertTrue(complete_dataset[0]) 20 | 21 | train_loader = torch.utils.data.DataLoader(complete_dataset, num_workers=1) 22 | model = cnn.create_cnn("resnet18", 2, pretrained=None) 23 | opt = torch.optim.Adam(model.parameters(), lr=1e-3) 24 | loss = nn.CrossEntropyLoss() 25 | res = cnn.train_sanity_fit(model, train_loader, loss, "cpu", num_batches=1) 26 | self.assertTrue(res) 27 | 28 | 29 | if __name__ == "__main__": 30 | unittest.main() 31 | -------------------------------------------------------------------------------- /tests/test_layers.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import unittest 3 | import torch.nn as nn 4 | from quickvision import layers 5 | 6 | 7 | class MishTester(unittest.TestCase): 8 | def test_mish(self): 9 | pass 10 | 11 | 12 | class MLPTester(unittest.TestCase): 13 | def test_mlp(self): 14 | pass 15 | 16 | 17 | if __name__ == "__main__": 18 | unittest.main() 19 | -------------------------------------------------------------------------------- /tests/test_losses.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import unittest 4 | from quickvision import losses 5 | from quickvision.losses import functional as fl 6 | 7 | 8 | class DiceLossTester(unittest.TestCase): 9 | def test_dice_loss_functional(self): 10 | # Same thing what you do with below. 11 | # inputs = torch.tensor([[0.4, 0.2, 0.3], [0.6, 0.2, 0.3]], dtype=torch.float32) 12 | # targets = torch.tensor([[0], [1]], dtype=torch.float32) 13 | # loss = fl.dice_loss(inputs, targets) 14 | # Do a backward 15 | # loss.backward() 16 | # And now compare this loss with known valueQ 17 | # self.assertTrue() 18 | pass 19 | 20 | def test_dice_loss(self): 21 | # loss_fn = losses.DiceLoss() 22 | # inputs = torch.tensor([[0.4, 0.2, 0.3], [0.6, 0.2, 0.3]], dtype=torch.float32) 23 | # targets = torch.tensor([[0], [1]], dtype=torch.float32) 24 | # loss = loss_fn(inputs, targets) 25 | # See what expected loss should be 26 | # expected_loss = [] 27 | # Compare those two with epsilon 28 | # loss.backward() 29 | # Assert those with epsilon case 30 | # self.assertTrue() 31 | pass 32 | 33 | 34 | if __name__ == "__main__": 35 | unittest.main() 36 | -------------------------------------------------------------------------------- /tests/test_smoke.py: -------------------------------------------------------------------------------- 1 | import quickvision 2 | import torch 3 | import torchvision 4 | import pytorch_lightning as pl 5 | import quickvision.pretrained 6 | import quickvision.layers 7 | import quickvision.models 8 | import quickvision.optimizers 9 | import quickvision.utils 10 | import unittest 11 | 12 | 13 | class HelloTester(unittest.TestCase): 14 | def test_torch(self,): 15 | print(torch.__version__) 16 | return True 17 | 18 | def test_torchvision(self,): 19 | print(torchvision.__version__) 20 | 21 | def test_pl(self,): 22 | print(pl.__version__) 23 | 24 | 25 | if __name__ == "__main__": 26 | unittest.main() 27 | -------------------------------------------------------------------------------- /tests/test_torchvision_backbones.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import torch.nn as nn 3 | from quickvision.models.components.torchvision_backbones import create_torchvision_backbone 4 | 5 | supported_tv_models = ["vgg11", "vgg13", "vgg16", "vgg19", "resnet18", 6 | "resnet34", "resnet50", "resnet101", "resnet152", 7 | "resnext50_32x4d", "resnext101_32x8d", 8 | "mnasnet0_5", "mnasnet1_0", "mobilenet_v2", 9 | "wide_resnet50_2", "wide_resnet101_2", 10 | "mobilenet_v3_large"] 11 | 12 | error_model = "erronous_name" 13 | 14 | 15 | class BackboneTester(unittest.TestCase): 16 | def test_torchvision_backbones(self): 17 | for model_name in supported_tv_models: 18 | ft_backbone, out_channels = create_torchvision_backbone(model_name, pretrained=None) 19 | self.assertTrue(isinstance(ft_backbone, nn.Module)) 20 | self.assertTrue(isinstance(out_channels, int)) 21 | 22 | def test_invalid_model(self): 23 | self.assertRaises(KeyError, create_torchvision_backbone, error_model) 24 | 25 | # def test_torchvision_imagenet_backbones(self): 26 | # for model_name in supported_tv_models: 27 | # ft_backbone, out_channels = create_torchvision_backbone(model_name, pretrained="imagenet") 28 | # self.assertTrue(isinstance(ft_backbone, nn.Module)) 29 | # self.assertTrue(isinstance(out_channels, int)) 30 | 31 | 32 | if __name__ == "__main__": 33 | unittest.main() 34 | -------------------------------------------------------------------------------- /tests/test_utils.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import torch 3 | from quickvision.utils import ( 4 | nested_tensor_from_tensor_list, 5 | NestedTensor, 6 | seed_everything, 7 | set_debug_apis, 8 | AverageMeter, 9 | ProgressMeter, 10 | EarlyStopping, 11 | print_size_of_model, 12 | ) 13 | 14 | 15 | @unittest.skipIf(not torch.cuda.is_available(), "CUDA unavailable") 16 | class test_torch_utils(): 17 | def test_set_debug_apis(self): 18 | set_debug_apis(False) 19 | return True 20 | 21 | def test_seed_everything(): 22 | seed_everything(seed=42) 23 | return True 24 | 25 | 26 | if __name__ == '__main__': 27 | unittest.main() 28 | -------------------------------------------------------------------------------- /tests/torch_utils.py: -------------------------------------------------------------------------------- 1 | import torchvision 2 | import torch 3 | import torchvision.transforms as T 4 | from PIL import Image 5 | 6 | __all__ = ["im2tensor", "to_np", "requires_gradient"] 7 | 8 | 9 | def im2tensor(image): 10 | aug = T.Compose([ 11 | T.ToTensor(), 12 | T.Normalize((0.5,), (0.5,))]) 13 | tensor = aug(image) 14 | tensor = torch.unsqueeze(tensor, 0) 15 | return tensor 16 | 17 | 18 | def to_np(t): 19 | return t.detach().cpu().numpy() 20 | 21 | 22 | def requires_gradient(model, layer): 23 | return list(model.parameters())[layer].requires_grad 24 | --------------------------------------------------------------------------------