From 67abe10ca5dbca833c3777a42e920ac2a95a141a Mon Sep 17 00:00:00 2001 From: Steffen Schneider Date: Fri, 3 Apr 2020 14:16:23 +0200 Subject: [PATCH] Code Pre-Release Co-authored-by: George Pachitariu Co-authored-by: Evgenia Rusak --- .gitignore | 129 +++ LICENSE | 201 ++++ NOTICE | 247 ++++ README.md | 134 +++ _config.yml | 1 + assets/header.png | Bin 0 -> 92590 bytes assets/header.svg | 20 + build.sh | 4 + demo.py | 73 ++ examples/README.md | 1 + examples/batchnorm/.dockerignore | 1 + examples/batchnorm/.gitignore | 23 + examples/batchnorm/Dockerfile | 11 + examples/batchnorm/Makefile | 76 ++ examples/batchnorm/README.md | 43 + examples/batchnorm/bin/adapt_full.py | 213 ++++ examples/batchnorm/bin/plot.py | 49 + examples/batchnorm/config | 5 + .../batchnorm/scripts/paper/table1.sbatch | 18 + examples/batchnorm/scripts/paper/table1.sh | 157 +++ examples/batchnorm/src/config.py | 190 ++++ examples/batchnorm/src/evaluate.py | 403 +++++++ examples/batchnorm/src/meters.py | 59 + .../1/n01440764/ILSVRC2012_val_00000293.jpeg | Bin 0 -> 6277 bytes examples/batchnorm/tests/run_test.py | 80 ++ examples/imagenet_d/README.md | 14 + examples/imagenet_d/imagenet_dict.py | 1000 ++++++++++++++++ examples/imagenet_d/main.py | 382 +++++++ examples/imagenet_d/map_files.py | 393 +++++++ examples/imagenet_d/run.sh | 36 + examples/imagenet_d/show_mappings.ipynb | 1005 +++++++++++++++++ examples/imagenet_d/train_torchvision.sh | 19 + examples/robustness_eval/README.md | 27 + examples/robustness_eval/main.py | 437 +++++++ examples/selflearning/README.md | 0 examples/selflearning/gce.py | 119 ++ robusta/__init__.py | 26 + robusta/batchnorm/__init__.py | 31 + robusta/batchnorm/bn.py | 235 ++++ robusta/batchnorm/stages.py | 50 + robusta/datasets/__init__.py | 26 + robusta/datasets/base.py | 63 ++ robusta/datasets/imagenet200.py | 92 ++ robusta/datasets/imageneta.py | 71 ++ robusta/datasets/imagenetc.py | 37 + robusta/datasets/imagenetr.py | 26 + robusta/models/BiT_models.py | 300 +++++ robusta/models/__init__.py | 0 robusta/models/fixup.py | 275 +++++ robusta/models/imagenet_model.py | 40 + robusta/models/resnet_gn.py | 147 +++ robusta/selflearning/__init__.py | 38 + robusta/selflearning/functional.py | 39 + robusta/selflearning/nn.py | 46 + setup.py | 26 + test.sh | 3 + .../1/n01440764/ILSVRC2012_val_00000293.jpeg | Bin 0 -> 6277 bytes .../n01440764/ILSVRC2012_val_00000293.JPEG | Bin 0 -> 223122 bytes test/test_batchnorm.py | 32 + test/test_datasets.py | 45 + test/test_imports.py | 8 + test/test_models.py | 6 + test/test_selflearning.py | 0 63 files changed, 7232 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 NOTICE create mode 100644 README.md create mode 100644 _config.yml create mode 100644 assets/header.png create mode 100644 assets/header.svg create mode 100755 build.sh create mode 100644 demo.py create mode 100644 examples/README.md create mode 100644 examples/batchnorm/.dockerignore create mode 100644 examples/batchnorm/.gitignore create mode 100644 examples/batchnorm/Dockerfile create mode 100644 examples/batchnorm/Makefile create mode 100644 examples/batchnorm/README.md create mode 100644 examples/batchnorm/bin/adapt_full.py create mode 100644 examples/batchnorm/bin/plot.py create mode 100644 examples/batchnorm/config create mode 100644 examples/batchnorm/scripts/paper/table1.sbatch create mode 100755 examples/batchnorm/scripts/paper/table1.sh create mode 100644 examples/batchnorm/src/config.py create mode 100644 examples/batchnorm/src/evaluate.py create mode 100644 examples/batchnorm/src/meters.py create mode 100644 examples/batchnorm/tests/imagenet_c/1/n01440764/ILSVRC2012_val_00000293.jpeg create mode 100644 examples/batchnorm/tests/run_test.py create mode 100644 examples/imagenet_d/README.md create mode 100644 examples/imagenet_d/imagenet_dict.py create mode 100644 examples/imagenet_d/main.py create mode 100644 examples/imagenet_d/map_files.py create mode 100644 examples/imagenet_d/run.sh create mode 100644 examples/imagenet_d/show_mappings.ipynb create mode 100755 examples/imagenet_d/train_torchvision.sh create mode 100644 examples/robustness_eval/README.md create mode 100644 examples/robustness_eval/main.py create mode 100644 examples/selflearning/README.md create mode 100644 examples/selflearning/gce.py create mode 100644 robusta/__init__.py create mode 100644 robusta/batchnorm/__init__.py create mode 100644 robusta/batchnorm/bn.py create mode 100644 robusta/batchnorm/stages.py create mode 100644 robusta/datasets/__init__.py create mode 100644 robusta/datasets/base.py create mode 100644 robusta/datasets/imagenet200.py create mode 100644 robusta/datasets/imageneta.py create mode 100644 robusta/datasets/imagenetc.py create mode 100644 robusta/datasets/imagenetr.py create mode 100644 robusta/models/BiT_models.py create mode 100644 robusta/models/__init__.py create mode 100644 robusta/models/fixup.py create mode 100644 robusta/models/imagenet_model.py create mode 100644 robusta/models/resnet_gn.py create mode 100644 robusta/selflearning/__init__.py create mode 100644 robusta/selflearning/functional.py create mode 100644 robusta/selflearning/nn.py create mode 100644 setup.py create mode 100755 test.sh create mode 100644 test/dummy_datasets/ImageNet-C/gaussian_blur/1/n01440764/ILSVRC2012_val_00000293.jpeg create mode 100644 test/dummy_datasets/ImageNet2012/val/n01440764/ILSVRC2012_val_00000293.JPEG create mode 100644 test/test_batchnorm.py create mode 100644 test/test_datasets.py create mode 100644 test/test_imports.py create mode 100644 test/test_models.py create mode 100644 test/test_selflearning.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b6e4761 --- /dev/null +++ b/.gitignore @@ -0,0 +1,129 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..93d6465 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2020-2021 Evgenia Rusak, Steffen Schneider, George Pachitariu + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..65f7c43 --- /dev/null +++ b/NOTICE @@ -0,0 +1,247 @@ +# Example scripts + +## Pytorch Examples: https://github.com/pytorch/examples +License for the Fixup and ImageNet models: +https://github.com/hongyi-zhang/Fixup/blob/master/LICENSE +https://github.com/pytorch/vision/blob/master/LICENSE + + +BSD 3-Clause License + +Copyright (c) 2017, +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +# LICENSE FOR THE BiT Models and the GroupNorm models: +https://github.com/google-research/big_transfer/blob/master/LICENSE +https://github.com/ppwwyyxx/GroupNorm-reproduce/blob/master/LICENSE + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [2020] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..1d5c7ff --- /dev/null +++ b/README.md @@ -0,0 +1,134 @@ +![](assets/header.png) + +# Robustness evaluation and Adaptation of ImageNet models + +This repo contains a growing collection of helper functions, tools and methods for robustness evaluation and adaptation of ImageNet scale models. +The focus is on simple methods that work at scale. + +We currently have the following features available: +- [`examples/batchnorm`](/examples/batchnorm): A reference implementation of batch norm adaptation used by Schneider, Rusak *et al.* (NeurIPS 2020) +- [`examples/selflearning`](/examples/selflearning): A reference implementation of self learning with robust pseudo labeling used by Rusak, Schneider *et al.* (arxiv 2021) +- [`examples/imagenet_d`](/examples/imagenet_d): Example runs on the ImageNet-D dataset used by Rusak, Schneider *et al.* (arxiv 2021) + +Planned features for future releases are (please open an issue if you can think of additional interesting parts to add): +- Helper functions for robustness datasets like ImageNet-A, ImageNet-R and ImageNet-C +- [`examples/clip`](/examples/clip): Robustness evaluation for [CLIP](https://github.com/openai/CLIP.git), Radford *et al.* (2021) +- [`examples/dino`](/examples/dino): Robustness evaluation for [DINO](https://github.com/facebookresearch/dino), Caron *et al.* (2021) + - Blog Post with robustness results: https://stes.io/dino/ + +## News + +- May '21: We will present our work on self-learing as a contributed talk at the [WeaSuL 2021](https://weasul.github.io/) workshop at ICLR. +- April '21: The pre-print for "Adapting ImageNet-scale models to complex distribution shifts with self-learning" is now available on arXiv: arxiv.org/abs/2104.12928 +- September 2020: The BatchNorm adaptation paper was accepted for poster presentation at NeurIPS 2020. +- July '20: A shorter workshop version of the paper was accepted for oral presentation at the Uncertainty & Robustness in Deep Learning Workshop at ICML 2020. +- June '20: The pre-print for "Improving robustness against common corruptions by covariate shift adaptation" is available on arXiv: arxiv.org/abs/2006.16971.pdf + +## ☕ The `robusta` toolbox for **Robust**ness and **A**daptation + +### Motivation + +Besides reference implementations, this repo is mainly intended to provide a quick and easy way to adapt your own code. +In particular, when developing new methods for improving robustness on deep learning models, we find it interesting to report results after *adapting* your model to the test datasets. This paints a more holistic image of model robustness: Some people might be interested in ad-hoc model performance, other might be interested in the performance obtained in a transductive inference setting. + +Note that the package is not intended for general purpose domain adaptation. Instead, we focus on providing simple methods that prove to be effective for ImageNet scale model adaptation at test time. The package provides helper functions that are "minimally invasive" and can easily be added to existing source code for model evaluation. + + +### Quick Start + +We will release the first stable version of the package on PyPI. Until then, you can install directly from the main repo: + +```bash +pip install git+git://github.com/bethgelab/robustness.git +``` + +Here is an example for how to use `robusta` for batchnorm adaptation & robust pseudo-labeling. + +``` python + model = torchvision.models.resnet50(pretrained=True) + + # We provide implementations for ImageNet-val, ImageNetC, ImageNetR, + # ImageNetA and ImageNetD: + val_dataset = robusta.datasets.imagenetc.ImageNetC( + root=dataset_folder, corruption="gaussian_blur", severity=1, + transform=transforms.Compose([transforms.ToTensor()]) + ) + val_loader = torch.utils.data.DataLoader( + val_dataset, batch_size=batch_size, shuffle=True) + + # We offer different options for batch norm adaptation; + # alternatives are "ema", "batch_wise_prior", ... + robusta.batchnorm.adapt(model, adapt_type="batch_wise") + + # The accuracy metric can be specific to the dataset: + # For example, ImageNet-R requires remapping into 200 classes. + accuracy_metric = val_dataset.accuracy + + # You can also easily use self-learning in your model. + # Self-learning adaptation can be combined with batch norm adaptation, example: + parameters = robusta.selflearning.adapt(model, adapt_type="affine") + optimizer = torch.optim.SGD(parameters, lr=1e-3) + + # You can choose from a set of adaptation losses (GCE, Entropy, ...) + rpl_loss = robusta.selflearning.GeneralizedCrossEntropy(q=0.8) + + acc1_sum, acc5_sum, num_samples = 0., 0., 0. + for epoch in range(num_epochs): + predictions = [] + for images, targets in val_loader: + + logits = model(images) + predictions = logits.argmax(dim=1) + + # Predictions are optional. If you do not specify them, + # they will be computed within the loss function. + loss = rpl_loss(logits, predictions) + + # When using self-learning, you need to add an additional optimizer + # step in your evaluation loop. + optimizer.zero_grad() + loss.backward() + optimizer.step() + + acc1_sum, acc5_sum += accuracy_metric(predictions, targets, topk=(1,5)) + num_samples += len(targets) + print(f"Top-1: {acc1_sum/num_samples}, Top-5: {acc5_sum/num_samples}") +``` + + +## Example Implementations + +### Batch Norm Adaptation + +[[Paper](https://arxiv.org/abs/2006.16971.pdf)] [[Web](https://domainadaptation.org/batchnorm)] [[README](examples/batchnorm/README.md)] [[Implementation](batchnorm/src/evaluate.py)] + +We propose to go beyond the assumption of a single sample from the target domain when evaluating robustness. Re-computing BatchNorm statistics is a simple baseline algorithm for improving the corruption error up to 14% points over a wide range of models, when access to more than a single sample is possible. + +### Self-Learning + +[[Paper](https://arxiv.org/abs/2104.12928)] [[Web](https://domainadaptation.org/selflearing)] [[README](examples/selflearning/README.md)] [[Implementation](selflearning/gce.py)] + +Test-time adaptation with self-learning improves robustness of large-scale computer vision models on ImageNet-C, -R, and -A. + +### Robustness evaluation of DINO models + +[[Blog Post](https://stes.io/dino)] [[*Implementation coming soon*](examples/dino/README.md)] + +## License + +Unless noted otherwise, code in this repo is released under an [Apache 2.0](/LICENSE) license. Some parts of the implementation use third party code. We typically indicate this in the file header or in the methods directly, and include the original license in the [NOTICE](/NOTICE) file. + +This repo does not contain the full code-base used in [Rusak, Schneider et al. (2021)](https://domainadaptation.org/selflearing) and is instead currently limited to a reference re-implementation for robust-pseudo labeling and entropy minimization. A full version of the codebase might be independently released in the future. + +If you want to use part of this code commercially, please carefully check the involved parts. Part of the third-party implementations might be released under licences with a non-commercial use clause such as CC-NC. If in doubt, please reach out. + +## Contact + +Please reach out for feature requests. Contributions welcome! + +- [Evgenia Rusak](https://github.com/EvgeniaAR) +- [George Pachitariu](https://github.com/georgepachitariu) +- [Steffen Schneider](https://stes.io) + + +*Note: The current version of this code base is a work in progress. We still decided to do this pre-release since the core methods are conceptually easy to use in your own code (batch norm adaptation, self-learning, ... and the current state might already be a useful place to start.* diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..2f7efbe --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-minimal \ No newline at end of file diff --git a/assets/header.png b/assets/header.png new file mode 100644 index 0000000000000000000000000000000000000000..90b5f6250ac6270d5866348254945492b4d975fc GIT binary patch literal 92590 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%0AxYkgn&b@y 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 literal 0 HcmV?d00001 diff --git a/assets/header.svg b/assets/header.svg new file mode 100644 index 0000000..9e5aea8 --- /dev/null +++ b/assets/header.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..b8fcb6a --- /dev/null +++ b/build.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# Build the python package + +python setup.py bdist_wheel --universal diff --git a/demo.py b/demo.py new file mode 100644 index 0000000..09bf15f --- /dev/null +++ b/demo.py @@ -0,0 +1,73 @@ +""" An example script for using the robusta package. +""" +import robusta +import torch +import torchvision +from torchvision import transforms + + +# To make this demo run with 0 dependencies and run on any device +# (to show the library features) the demo doesn't use Cuda and the dataset has +# only 1 image. +# After you run it once, feel free to enable Cuda and change the dataset folder + + +def main(): + model = torchvision.models.resnet50(pretrained=True) + + # Dummy-ImageNetC dataset has only 1 image. + # Change this to evaluate on a full dataset. + batch_size = 1 + dataset_folder = 'test/dummy_datasets/ImageNet-C' + num_epochs = 1 + + # We provide implementations for ImageNet-val, ImageNetC, ImageNetR, + # ImageNetA and ImageNetD: + val_dataset = robusta.datasets.imagenetc.ImageNetC( + root=dataset_folder, corruption="gaussian_blur", severity=1, + transform=transforms.Compose([transforms.ToTensor()]) + ) + val_loader = torch.utils.data.DataLoader( + val_dataset, batch_size=batch_size, shuffle=True) + + # We offer different options for batch norm adaptation; + # alternatives are "ema", "batch_wise_prior", ... + robusta.batchnorm.adapt(model, adapt_type="batch_wise") + + # The accuracy metric can be specific to the dataset: + # For example, ImageNet-R requires remapping into 200 classes. + # accuracy_metric = val_dataset.accuracy + + # You can also easily use self-learning in your model. + # Self-learning adaptation can be combined with batch norm adaptation, example: + parameters = robusta.selflearning.adapt(model, adapt_type="affine") + optimizer = torch.optim.SGD(parameters, lr=1e-3) + + # You can choose from a set of adaptation losses (GCE, Entropy, ...) + rpl_loss = robusta.selflearning.GeneralizedCrossEntropy(q=0.8) + + acc1_sum, acc5_sum, num_samples = 0., 0., 0. + for epoch in range(num_epochs): + predictions = [] + for images, targets in val_loader: + + logits = model(images) + predictions = logits.argmax(dim=1) + + # Predictions are optional. If you do not specify them, + # they will be computed within the loss function. + loss = rpl_loss(logits, predictions) + + # When using self-learning, you need to add an additional optimizer + # step in your evaluation loop. + optimizer.zero_grad() + loss.backward() + optimizer.step() + + # acc1_sum, acc5_sum += accuracy_metric(predictions, targets, topk=(1,5)) + num_samples += len(targets) + print(f"Top-1: {acc1_sum/num_samples}, Top-5: {acc5_sum/num_samples}") + + +if __name__ == '__main__': + main() diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..aa5431b --- /dev/null +++ b/examples/README.md @@ -0,0 +1 @@ +# Reference implementations and examples \ No newline at end of file diff --git a/examples/batchnorm/.dockerignore b/examples/batchnorm/.dockerignore new file mode 100644 index 0000000..fa29cdf --- /dev/null +++ b/examples/batchnorm/.dockerignore @@ -0,0 +1 @@ +** \ No newline at end of file diff --git a/examples/batchnorm/.gitignore b/examples/batchnorm/.gitignore new file mode 100644 index 0000000..64a9b4d --- /dev/null +++ b/examples/batchnorm/.gitignore @@ -0,0 +1,23 @@ +logs/ +*.pdf +statistics* +__pycache__/ +*.csv +.torch/ +*.pt +.**.swp +.*.*.swp +*.ipynb +*.ipynb_checkpoints/ +plot_styles.py +scripts/run_experiment-evi.sh +scripts/run_experiment-evi.sh +scripts/run_adapt.sh +scripts/run_experiment-evaluate* +EfficientNet +exp/ +*.npy +rolands_klasse.py +tsne-embeddings/ +tsne-results/ +make_fig_1/ diff --git a/examples/batchnorm/Dockerfile b/examples/batchnorm/Dockerfile new file mode 100644 index 0000000..4b70028 --- /dev/null +++ b/examples/batchnorm/Dockerfile @@ -0,0 +1,11 @@ +FROM pytorch/pytorch + +RUN pip install --no-cache-dir --upgrade pip + +RUN pip --no-cache-dir install \ + matplotlib \ + scipy \ + scikit-learn \ + efficientnet_pytorch + +CMD [ "bash" ] diff --git a/examples/batchnorm/Makefile b/examples/batchnorm/Makefile new file mode 100644 index 0000000..5a8d3a8 --- /dev/null +++ b/examples/batchnorm/Makefile @@ -0,0 +1,76 @@ + include config + +test: docker + sudo docker run \ + -v /home/sschneider/shared:/home/ubuntu/shared \ + -v /home/sschneider/local:/home/ubuntu/local \ + -v /home/sschneider/phd:/home/ubuntu/phd \ + -v $(shell pwd):/home/ubuntu/run \ + -w /home/ubuntu/run \ + --ipc host -i \ + --entrypoint ptw stes/dj . -- --failed-first --exitfirst + +simg: + singularity build docker://bethgelab/deeplearning:cuda10.0-cudnn7-flake8 + +docker: + # Build docker container + sudo docker build \ + --build-arg userid=$(shell id -u) \ + --build-arg username=$(shell id -un) \ + -t ${DOCKER_NAME} . + +run_%: + # Start training in interactive mode + # see https://github.com/NVIDIA/nvidia-docker/issues/1026 + mkdir -p $(shell pwd)/.torch + mkdir -p /home/sschneider/local/cache + mkdir -p /home/sschneider/local/torch + sudo docker run \ + --gpus '"device=$(GPU)"' \ + -v /home/sschneider/shared:/home/ubuntu/shared \ + -v /home/sschneider/local:/home/ubuntu/local \ + -v /home/sschneider/phd:/home/ubuntu/phd \ + -v /mnt/qb/bethge/erusak/Better_baselines_for_ImageNet-C/models:/mnt/qb/bethge/erusak/Better_baselines_for_ImageNet-C/models \ + -v /home/sschneider/local/cache:/home/ubuntu/.cache \ + -v /home/sschneider/local/torch:/home/ubuntu/.torch \ + -v $(BASEDIR):$(BASEDIR) \ + --tmpfs /data \ + -v $(shell pwd):/home/ubuntu/run \ + -w /home/ubuntu/run -u $(shell id -u):$(shell echo `id -G | awk '{print $$2}'`) \ + --ipc host -d \ + --entrypoint bash stes/dj ./scripts/$@.sh $(BASEDIR) + +adapt: + # Start training in interactive mode + mkdir -p $(shell pwd)/.torch + sudo docker run \ + --gpus device=5 \ + -v /home/sschneider/shared:/home/ubuntu/shared \ + -v /home/sschneider/local:/home/ubuntu/local \ + -v /home/sschneider/phd:/home/ubuntu/phd \ + -v $(shell pwd)/.torch:/home/ubuntu/.cache/torch \ + -v /mnt/nvme/erusak:/data \ + -v $(shell pwd):/home/ubuntu/run \ + -w /home/ubuntu/run -u $(shell id -u):$(shell echo `id -G | awk '{print $$2}'`) \ + --ipc host -it \ + --entrypoint python stes/dj adapt.py \ + --batch-size 256 \ + --dataset /data/ImageNet-C/brightness/5 \ + --workers 20 \ + --output test_model_2.pt \ + --verbose + + +format: docker + sudo docker run \ + -v /home/sschneider/shared:/home/ubuntu/shared \ + -v /home/sschneider/local:/home/ubuntu/local \ + -v /home/sschneider/phd:/home/ubuntu/phd \ + -v $(shell pwd):/home/ubuntu/run \ + -w /home/ubuntu/run \ + --ipc host -i \ + --entrypoint black stes/dj *.py + +stop_docker: + sudo docker ps | grep stes/dj | awk '{print $$1}' | xargs sudo docker stop diff --git a/examples/batchnorm/README.md b/examples/batchnorm/README.md new file mode 100644 index 0000000..8b0352b --- /dev/null +++ b/examples/batchnorm/README.md @@ -0,0 +1,43 @@ +# Improving robustness against common corruptions by covariate shift adaptation + +Steffen Schneider*, Evgenia Rusak*, Luisa Eck, Oliver Bringmann, Wieland Brendel, Matthias Bethge + +Website: [domainadaptation.org/batchnorm](https://domainadaptation.org/batchnorm) + +This repository contains evaluation code for the paper *Improving robustness against common corruptions by covariate shift adaptation*. +We will release the code in the upcoming weeks. To get notified, watch and/or star this repository to get notified of updates! + +Today's state-of-the-art machine vision models are vulnerable to image corruptions like blurring or compression artefacts, limiting their performance in many real-world applications. We here argue that popular benchmarks to measure model robustness against common corruptions (like ImageNet-C) underestimate model robustness in many (but not all) application scenarios. The key insight is that in many scenarios, multiple unlabeled examples of the corruptions are available and can be used for unsupervised online adaptation. Replacing the activation statistics estimated by batch normalization on the training set with the statistics of the corrupted images consistently improves the robustness across 25 different popular computer vision models. Using the corrected statistics, ResNet-50 reaches 62.2% mCE on ImageNet-C compared to 76.7% without adaptation. With the more robust AugMix model, we improve the state of the art from 56.5% mCE to 51.0% mCE. Even adapting to a single sample improves robustness for the ResNet-50 and AugMix models, and 32 samples are sufficient to improve the current state of the art for a ResNet-50 architecture. We argue that results with adapted statistics should be included whenever reporting scores in corruption benchmarks and other out-of-distribution generalization settings + +## Main results + +### Results for vanilla trained and robust models on ImageNet-C + +With a simple recalculation of batch normalization statistics, we improve the mean Corruption Error (mCE) of all commonly tested robust models. +| Model | mCE, w/o adapt [%] ↘ | mCE, partial adapt [%] ↘ | mCE, full adapt [%] ↘ | +|-|-|-|-| +| Vanilla ResNet50 | 76.7 | 65.0 | 62.2 | +| [SIN](https://github.com/rgeirhos/texture-vs-shape) | 69.3 | 61.5 | 59.5| +| [ANT](https://github.com/bethgelab/game-of-noise) | 63.4 | 56.1 |53.6 | +| [ANT+SIN](https://github.com/bethgelab/game-of-noise) | 60.7 |55.3 |53.6| +| [AugMix](https://github.com/google-research/augmix) | 65.3 | 55.4 | 51.0 | +| [AssembleNet](https://github.com/clovaai/assembled-cnn) | 52.3 | -- | 50.1 | +| [DeepAugment](https://github.com/hendrycks/imagenet-r) | 60.4 | 52.3 |49.4 | +| [DeepAugment+AugMix](https://github.com/hendrycks/imagenet-r) | 53.6 | 48.4 |45.4| +| [DeepAug+AM+RNXt101](https://github.com/hendrycks/imagenet-r) | **44.5** |**40.7** | **38.0** | + +### Results for models trained with [Fixup](https://github.com/hongyi-zhang/Fixup) and [GroupNorm](https://github.com/ppwwyyxx/GroupNorm-reproduce) on ImageNet-C + +Fixup and GN trained models perform better than non-adapted BN models but worse than adapted BN models. + +| Model | [Fixup](https://github.com/hongyi-zhang/Fixup), mCE [%] ↘ | [GroupNorm](https://github.com/ppwwyyxx/GroupNorm-reproduce), mCE [%] ↘ | BatchNorm, mCE [%] ↘ | BatchNorm+adapt, mCE [%] ↘ | +|-|-|-|-|-| +|ResNet-50 | 72.0 |72.4 |76.7 |**62.2**| +|ResNet-101 |68.2 |67.6 |69.0 |**59.1**| +|ResNet-152 |67.6 |65.4 |69.3 |**58.0**| + + +## News + +- The paper was accepted for poster presentation at NeurIPS 2020. +- A shorter workshop version of our paper was accepted for oral presentation at the [Uncertainty & Robustness in Deep Learning](https://sites.google.com/view/udlworkshop2020) Workshop at ICML '20. diff --git a/examples/batchnorm/bin/adapt_full.py b/examples/batchnorm/bin/adapt_full.py new file mode 100644 index 0000000..c37da92 --- /dev/null +++ b/examples/batchnorm/bin/adapt_full.py @@ -0,0 +1,213 @@ +""" Adapt torchvision models on the full test dataset. + +This scripts estimates the true test statistics on the full test dataset +by sequentially updating the statistics within the model. Tested on ResNet50 +models of torchvision, but should be applicable to other models as well. +""" + +from torchvision.datasets import ImageFolder +from torchvision.transforms import Normalize, ToTensor, CenterCrop, Compose +import numpy as np +import glob +import os +import tqdm + +import torch +from torch import nn +from torchvision.models import resnet +from torchvision import datasets +from torchvision.models import resnet + + +def get_stages(model): + input_stage = nn.Sequential( + model.conv1, + model.bn1, + model.relu, + model.maxpool, + ) + stages = ( + model.layer1, + model.layer2, + model.layer3, + model.layer4, + ) + flattened_stages = [] + for stage in stages: + if isinstance(stage, nn.Sequential): + for layer in stage: + flattened_stages.append(layer) + else: + flattened_stages.append(stage) + + stages = [] + current_module = [] + for stage in flattened_stages: + current_module.append(stage) + if count_bn_layers(stage) > 0: + stages.append(nn.Sequential(*current_module)) + current_module = [] + + stages = [ + input_stage, + ] + stages + return stages + + +def count_bn_layers(stage): + return len(list(filter(lambda m: isinstance(m, nn.BatchNorm2d), stage.modules()))) + + +def reset_stats(module): + if isinstance(module, nn.BatchNorm2d): + module.reset_running_stats() + # Use exponential moving average + module.momentum = None + for p in module.parameters(): + p.requires_grad_(False) + + +def get_args(): + + import argparse + + parser = argparse.ArgumentParser( + description = "Full model adaptation to the test set." + ) + + parser.add_argument("--batch-size", type=int, default=128) + parser.add_argument("--dataset", type=str, required=True) + parser.add_argument("--workers", "-j", type=int, default=0) + parser.add_argument("--output", "-o", type=str, required=True) + parser.add_argument("--dry-run", action="store_true") + parser.add_argument("--verbose", "-v", type=str, default="false") + parser.add_argument( + "--resume", + default="", + type=str, + metavar="PATH", + help="path to latest checkpoint (default: none)", + ) + + return parser.parse_args() + + +def get_dataset(args): + mean = [0.485, 0.456, 0.406] + std = [0.229, 0.224, 0.225] + + T = Compose([ToTensor(), Normalize(mean, std)]) + + fnames = glob.glob(os.path.join(args.dataset, "*")) + if len(fnames) == 1000: + print(f"| Use imagenet folder from {args.dataset}") + dataset = datasets.ImageFolder(args.dataset, Compose(T)) + else: + print(f"| Use subfolders in {args.dataset}") + print("|" + "\n|".join(fnames)) + dataset = torch.utils.data.ConcatDataset( + [datasets.ImageFolder(path, Compose(T)) for path in fnames] + ) + + return dataset + + +def get_loader(dataset, args): + loader = torch.utils.data.DataLoader( + dataset, + batch_size=args.batch_size, + shuffle=True, + num_workers=args.workers, + pin_memory=True, + ) + return loader + + +def iterate(loader, args): + if args.verbose == "tqdm": + return tqdm.tqdm(enumerate(loader), total=len(loader)) + else: + return enumerate(loader) + + +class ZipLoader: + def __init__(self, outputs, targets): + self.outputs = outputs + self.targets = targets + + def __getitem__(self, index): + if index >= len(self): + raise StopIteration + outputs = self.outputs[index] + targets = self.targets[index] + # self.outputs[index] = None + # self.targets[index] = None + return outputs, targets + + def __len__(self): + return len(self.outputs) + + +if __name__ == "__main__": + + args = get_args() + + def log(*largs, **kwargs): + if args.verbose != "false": + print(*largs, **kwargs) + + model = resnet.resnet50(pretrained=True) + + if args.resume: + print("load augmix model") + checkpoint = torch.load(args.resume) + state_dict = checkpoint["state_dict"] + # create new OrderedDict that does not contain `module.` + from collections import OrderedDict + + new_state_dict = OrderedDict() + for k, v in state_dict.items(): + name = k[7:] # remove `module.` + new_state_dict[name] = v + # load params + model.load_state_dict(new_state_dict) + + model.apply(reset_stats) + model.cuda().train() + + dataset = get_dataset(args) + + with torch.no_grad(): + loader = get_loader(dataset, args) + log(f"| Loaded dataset with {len(loader)} batches") + stages = get_stages(model) + for i, model_stage in enumerate(stages): + log( + f"| Compute outputs for model stage {i+1}/{len(stages)}:\n{model_stage}" + ) + num_bn = count_bn_layers(model_stage) + log(f"| Found {num_bn} adaptation layers. Looping {num_bn}x.") + assert num_bn >= 1 + for n in range(num_bn): + log(f"| Estimation {n+1}/{num_bn}") + outputs = [] + targets = [] + for batch_idx, (data, target) in enumerate(loader): + log(f"| Process {batch_idx}") + data = data.cuda() + output = model_stage(data) + del data + outputs.append(output.cpu()) + targets.append(target.cpu()) + if args.dry_run: + break + if batch_idx > 98: + break + del loader + + loader = ZipLoader(outputs, targets) + + log(f"| Saving model to {args.output}") + torch.save(model.cpu().state_dict(), args.output) + + log(f"| Done.") diff --git a/examples/batchnorm/bin/plot.py b/examples/batchnorm/bin/plot.py new file mode 100644 index 0000000..ea46d20 --- /dev/null +++ b/examples/batchnorm/bin/plot.py @@ -0,0 +1,49 @@ +import glob +import os.path as osp +import re +import numpy as np +from multiprocessing import Pool +import pandas as pd + +regex = re.compile(r'(\w*)_([\w_]*)-([0-9]*)_bsz([0-9]*)\.npz') + +def load_fname(fname): + + keys = [("model",str), ("corruption", str), ("severity", int), ("batchsize", int)] + (vals), = regex.findall(osp.basename(fname)) + args = {k:func(v) for (k,func),v in zip(keys,vals)} + + arr = np.load(fname, allow_pickle = True) + keys = ['top1', 'top5', 'loss'] + + return dict(**args, **{k:float(arr[k]) for k in keys}) + +fnames = glob.glob("/home/ubuntu/local/emissions/*.npz") +with Pool() as p: results = p.map(load_fname, fnames) +results = pd.DataFrame(results) + +import seaborn as sns +import matplotlib.pyplot as plt +%matplotlib inline + +for k in [1,5]: + + sns.set_context("poster") + plt.figure(figsize=(10,10)) + sns.lineplot(data = results.groupby(["corruption", "batchsize"]).mean().reset_index(), + x = "batchsize", y = f"top{k}", hue = "corruption") #, style = "severity") + plt.xscale("log") + plt.title(f"Top-{k} performance, averaged over severities") + plt.legend(loc=(1,0)) + sns.despine() + plt.show() + + sns.set_context("poster") + plt.figure(figsize=(10,10)) + sns.lineplot(data = results.groupby(["severity", "batchsize"]).mean().reset_index(), + x = "batchsize", y = f"top{k}", hue = "severity") #, style = "severity") + plt.xscale("log") + plt.title(f"Top-{k} performance, averaged over corruptions") + plt.legend(loc=(1,0)) + sns.despine() + plt.show() diff --git a/examples/batchnorm/config b/examples/batchnorm/config new file mode 100644 index 0000000..6131607 --- /dev/null +++ b/examples/batchnorm/config @@ -0,0 +1,5 @@ +DOCKER_DIRS="-v /home/sschneider/shared:/home/ubuntu/shared \ +-v /home/sschneider/local:/home/ubuntu/local \ +-v /home/sschneider/phd:/home/ubuntu/phd" +DOCKER_NAME="stes/dj" +GPU=1 diff --git a/examples/batchnorm/scripts/paper/table1.sbatch b/examples/batchnorm/scripts/paper/table1.sbatch new file mode 100644 index 0000000..83ec57f --- /dev/null +++ b/examples/batchnorm/scripts/paper/table1.sbatch @@ -0,0 +1,18 @@ +#!/bin/sh +#SBATCH --job-name=robustness +#SBATCH --output=logs/logs-%A_%a.out # Standard output and error log +#SBATCH --partition=gpu-2080ti-preemptable +#SBATCH --gres=gpu:1 + +scontrol show job "$SLURM_JOB_ID" + +# The image georgepachitariu/robustness was created using +# the Dockerfile from parent folder. +row="2" # This is the row in the table +singularity exec --nv -B /scratch_local \ + -B "$IMAGENET_C_PATH":/ImageNet-C:ro \ + -B "$CHECKPOINT_PATH":/checkpoints:ro \ + -B .:/batchnorm \ + -B ..:/deps \ + docker://georgepachitariu/robustness:latest \ + bash /batchnorm/scripts/paper/table1.sh $row 2>&1 diff --git a/examples/batchnorm/scripts/paper/table1.sh b/examples/batchnorm/scripts/paper/table1.sh new file mode 100755 index 0000000..6f3d278 --- /dev/null +++ b/examples/batchnorm/scripts/paper/table1.sh @@ -0,0 +1,157 @@ +#!/bin/bash + +IMANGENETC_PATH="/ImageNet-C" +CHECKPOINT_PATH="/checkpoints" +cd /batchnorm || exit + +export PYTHONPATH=/deps:/deps/robusta:$PYTHONPATH +# TODO for some reason this here does not work. Fix. +# pip install /deps || exit +python -c "print('Check import'); import robusta" || exit + +process_split() +{ + local model=$1 + local checkpoint=$2 + local batchsize=$3 + local extra_args=$4 + local split=$5 + + echo "Started for $model and split $split" + echo "Checkpoint: $checkpoint" + + # It's ok if we use "--resume" but give an empty $checkpoint. + # The adaptation module will ignore it. + python src/evaluate.py \ + --test-batch-size "$batchsize" \ + --imagenet-path "$IMANGENETC_PATH/$split" \ + --arch "$model" \ + --pretrained \ + --print-freq 1 \ + --workers 15 \ + --emission-path "$SCRATCH/run_experiment" \ + --resume "$checkpoint" \ + $extra_args + + echo "Successfully finished split $split" +} + +iterate_through_imagenet_c() +{ + local model=$1 + local checkpoint=$2 + local batchsize=$3 + local extra_args=$4 + + # This array doesn't contain the hold-out corruptions: + # gaussian_blur, saturate, spatter, speckle_noise + local all_splits=("brightness" "elastic_transform" "impulse_noise" + "pixelate" "snow" "zoom_blur" + "contrast" "fog" "gaussian_noise" + "jpeg_compression" "defocus_blur" "frost" + "glass_blur" "motion_blur" "shot_noise") + + # The dataset is split by corruption type into 15 splits. + # These splits can be run in 2 ways: + if [[ -n "$SLURM_ARRAY_TASK_ID" ]]; then + # 1. In a distributed environment (like Slurm), + # where each worker processes one split. + touch "logs/$SLURM_ARRAY_TASK_ID.active" + + split_index=$SLURM_ARRAY_TASK_ID + split=${all_splits[$split_index]} + + process_split "$model" "$checkpoint" "$batchsize" "$extra_args" "$split" + + rm "logs/$SLURM_ARRAY_TASK_ID.active" + else + # 2. Sequentially in a single process with a "for" loop; + for split in "${all_splits[@]}" + do + echo "Split $split started" + process_split "$model" "$checkpoint" "$batchsize" "$extra_args" "$split" + done + fi +} + + +# The row number is the same as the row number in Table 1 in the paper. +if [[ -n "$1" ]]; then + row=$1 +else + echo 'You need to give a parameter for which row of Table 1 to compute' +fi + + +if [[ "$row" = "1" ]]; then + model="resnet50" + checkpoint="" # no checkpoint + batchsize="512" + iterate_through_imagenet_c $model "$checkpoint" $batchsize +fi + +if [[ "$row" = "2" ]]; then + model="resnet50" + checkpoint="$CHECKPOINT_PATH/resnet50_train_45_epochs_combined_IN_SF-2a0d100e.pth.tar" + batchsize="512" + iterate_through_imagenet_c $model $checkpoint $batchsize +fi + +if [[ "$row" = "3" ]]; then + model="resnet50" + checkpoint="$CHECKPOINT_PATH/ANT3x3Model.pth" + batchsize="512" + iterate_through_imagenet_c $model $checkpoint $batchsize +fi + +if [[ "$row" = "4" ]]; then + model="resnet50" + checkpoint="$CHECKPOINT_PATH/ANT3x3_SIN_Model.pth" + batchsize="512" + iterate_through_imagenet_c $model $checkpoint $batchsize +fi + +if [[ "$row" = "5" ]]; then + model="resnet50" + checkpoint="$CHECKPOINT_PATH/augmix_checkpoint.pth.tar" + batchsize="512" + iterate_through_imagenet_c $model $checkpoint $batchsize +fi + +if [[ "$row" = "7" ]]; then + model="resnet50" + checkpoint="$CHECKPOINT_PATH/deepaugment.pth.tar" + batchsize="512" + iterate_through_imagenet_c $model $checkpoint $batchsize +fi + +if [[ "$row" = "8" ]]; then + model="resnet50" + checkpoint="$CHECKPOINT_PATH/deepaugment_and_augmix.pth.tar" + batchsize="512" + iterate_through_imagenet_c $model $checkpoint $batchsize +fi + +if [[ "$row" = "9" ]]; then + model="resnext101_32x8d" + checkpoint="$CHECKPOINT_PATH/resnext101_augmix_and_deepaugment.pth.tar" + batchsize="128" + iterate_through_imagenet_c $model $checkpoint $batchsize +fi + +### Appendix: not part of the table anymore, but it is run in the same way + +if [[ "$row" = "10" ]]; then + model="efficientnet-b0" + checkpoint="" # no checkpoint + batchsize="128" + iterate_through_imagenet_c $model "$checkpoint" $batchsize +fi + +if [[ "$row" = "11" ]]; then + model="resnet50" + checkpoint="$CHECKPOINT_PATH/resnext101_augmix_and_deepaugment.pth.tar" + batchsize="128" + extra_args="--ema-batchnorm" + iterate_through_imagenet_c $model $checkpoint $batchsize $extra_args +fi diff --git a/examples/batchnorm/src/config.py b/examples/batchnorm/src/config.py new file mode 100644 index 0000000..d42f18b --- /dev/null +++ b/examples/batchnorm/src/config.py @@ -0,0 +1,190 @@ +import argparse +import os + +import torchvision.models as models + + +def assert_exists(fname): + assert os.path.exists(fname) + return fname + + +def assert_not_exists(fname): + assert not os.path.exists(fname) + return fname + + +def parse_args(args): + model_names = sorted( + name + for name in models.__dict__ + if name.islower() + and not name.startswith("__") + and callable(models.__dict__[name]) + ) + model_names.extend( + [ + "resnext101_32x8d_wsl", + "resnext101_32x16d_wsl", + "resnext101_32x32d_wsl", + "resnext101_32x48d_wsl", + ] + ) + model_names.extend( + [ + "efficientnet-b0", + "efficientnet-b1", + "efficientnet-b2", + "efficientnet-b2", + "efficientnet-b4", + "efficientnet-b5", + "efficientnet-b6", + "efficientnet-b7", + ] + ) + model_names.extend( + [ + "fixup_resnet50", + "resnet50_gn", + "resnet101_gn", + "resnet152_gn", + "efficientnet-b0", + "efficientnet-b1", + "efficientnet-b2", + "efficientnet-b2", + "efficientnet-b4", + "efficientnet-b5", + "efficientnet-b6", + "efficientnet-b7", + ] + ) + + parser = argparse.ArgumentParser(description="PyTorch ImageNet Training") + parser.add_argument("--imagenet-path", type=assert_exists, required=True) + parser.add_argument("--resize-and-crop", action="store_true") + parser.add_argument("--emission-path", type=assert_not_exists, required=True) + parser.add_argument("--seed", type=int, default=None) + parser.add_argument( + "-a", + "--arch", + metavar="ARCH", + default="resnet50", + choices=model_names, + help="model architecture: " + " | ".join(model_names) + " (default: resnet18)", + ) + parser.add_argument( + "-j", + "--workers", + default=2, + type=int, + metavar="N", + help="number of data loading workers (default: 2)", + ) + parser.add_argument( + "--resizepar", + default=256, + type=int, + ) + parser.add_argument( + "--croppar", + default=224, + type=int, + ) + parser.add_argument( + "-tb", + "--test-batch-size", + default=200, + type=int, + metavar="N", + help="mini-batch size (default: 256), this is the total " + "batch size of all GPUs on the current node when " + "using Data Parallel", + ) + parser.add_argument( + "--resume", + default="", + type=str, + metavar="PATH", + help="path to latest checkpoint (default: none)", + ) + parser.add_argument("--no-shuffle", action="store_true") + parser.add_argument( + "-e", + "--evaluate", + dest="evaluate", + action="store_true", + help="evaluate model on validation set", + ) + parser.add_argument("--gpu", default=None, type=int, help="GPU id to use.") + parser.add_argument( + "--train-mode-during-eval", + action="store_true", + help="use train mode during evaluation.", + ) + parser.add_argument( + "--adapt-only-one-layer", + action="store_true", + help="use train mode during evaluation.", + ) + parser.add_argument("--layer-to-adapt", default=0, type=int) + parser.add_argument( + "--pretrained", action="store_true", help="use pre-trained model" + ) + parser.add_argument("--dry-run", action="store_true") + + # ############ Ablation arguments + parser.add_argument( + "--adapt-mean", action="store_true", + help="use test time statistics for mean" + ) + + parser.add_argument( + "--adapt-var", action="store_true", + help="use test time statistics for variance" + ) + + parser.add_argument( + "--adapt-stage", + type=int, + default=None, + help="use test time statistics in only this stage", + ) + parser.add_argument( + "--leave-stage", + type=int, + default=None, + help="use test time statistics everywhere except in this stage", + ) + parser.add_argument( + "--adapt-prior", + type=float, + default=None, + help="use train time statistics as a prior during evaluation. Specify the averaging factor directly", + ) + parser.add_argument( + "--adapt-prior-bsz", + type=int, + default=None, + help="use train time statistics as a prior during evaluation. Specify the training set size.", + ) + + # EMA + parser.add_argument( + "--ema-batchnorm", + action="store_true", + help="use test time ema statistics for all batch norm layers", + ) + parser.add_argument("--ema-warmup-samples", type=int, default=5000) + # ############ End ablation arguments + + parser.add_argument( + "-p", + "--print-freq", + default=1000, + type=int, + metavar="N", + help="print frequency (default: 10)", + ) + parser.add_argument("--tqdm", action="store_true") + + return parser.parse_args(args) diff --git a/examples/batchnorm/src/evaluate.py b/examples/batchnorm/src/evaluate.py new file mode 100644 index 0000000..5268279 --- /dev/null +++ b/examples/batchnorm/src/evaluate.py @@ -0,0 +1,403 @@ +import argparse +import glob +import numpy as np +import os +import PIL +import random +import shutil +import time +import types +import warnings + +import torch +import torch.nn as nn +import torch.nn.parallel +import torch.backends.cudnn as cudnn +import torch.optim +import torch.multiprocessing as mp +import torch.utils.data +import torchvision.transforms as transforms +import torchvision.datasets as datasets +import torchvision.models as models +from efficientnet_pytorch import EfficientNet + +from robusta.batchnorm import bn +from robusta.batchnorm import stages +from robusta.models.fixup import fixup_resnet50 +from robusta.models.resnet_gn import resnet50 as resnet_50_gn +from robusta.models.resnet_gn import resnet152 as resnet_152_gn +from robusta.models.resnet_gn import resnet101 as resnet_101_gn + +import config +from meters import AverageMeter, ProgressMeter, get_accuracy + +try: + import tqdm.tqdm as tqdm +except ImportError: + tqdm = None + + +def print_version(): + for name, val in globals().items(): + if isinstance(val, types.ModuleType): + try: + print(f"| {val.__name__} version: {imp.__version__}") + except Exception: + continue + + +def main(argv): + print_version() + + args = config.parse_args(argv) + print("| Parsed arguments:", args) + + if args.seed is not None: + random.seed(args.seed) + torch.manual_seed(args.seed) + cudnn.deterministic = True + warnings.warn( + "You have chosen to seed training. " + "This will turn on the CUDNN deterministic setting, " + "which can slow down your training considerably! " + "You may see unexpected behavior when restarting " + "from checkpoints." + ) + + if args.gpu is not None: + warnings.warn( + "You have chosen a specific GPU. This will completely " + "disable data parallelism." + ) + + ngpus_per_node = torch.cuda.device_count() + print(f"| Using {ngpus_per_node} gpus.") + return main_worker(args.gpu, ngpus_per_node, args) + + +def main_worker(gpu, ngpus_per_node, args): + args.gpu = gpu + + if args.gpu is not None: + print("| Use GPU: {} for training".format(args.gpu)) + + # create model + if args.arch.startswith("resnext") and args.arch.endswith("wsl"): + valid_options = [ + "resnext101_32x8d_wsl", + "resnext101_32x16d_wsl", + "resnext101_32x32d_wsl", + "resnext101_32x48d_wsl", + ] + assert args.arch in valid_options + model = torch.hub.load("facebookresearch/WSL-Images", args.arch) + else: + if args.pretrained: + print("| => using pre-trained model '{}'".format(args.arch)) + if "efficientnet" in args.arch: + model = EfficientNet.from_pretrained(args.arch) + elif "fixup_resnet50" in args.arch: + model = fixup_resnet50() + elif "resnet50_gn" in args.arch: + model = resnet_50_gn() + elif "resnet101_gn" in args.arch: + model = resnet_101_gn() + elif "resnet152_gn" in args.arch: + model = resnet_152_gn() + else: + model = models.__dict__[args.arch](pretrained=True) + else: + if "efficientnet" in args.arch: + raise NotImplementedError( + "Using a not pretrained Efficient Net is not supported." + ) + elif "resnet50_gn" in args.arch: + model = resnet_50_gn() + elif "resnet101_gn" in args.arch: + model = resnet_101_gn() + elif "resnet152_gn" in args.arch: + model = resnet_152_gn() + else: + print("| => creating model '{}'".format(args.arch)) + model = models.__dict__[args.arch]() + + if args.gpu is not None: + torch.cuda.set_device(args.gpu) + model = torch.nn.DataParallel(model).cuda() + + # DataParallel will divide and allocate batch_size to all available GPU + if args.arch.startswith("alexnet") or args.arch.startswith("vgg"): + model.features = torch.nn.DataParallel(model.features) + model.cuda() + + # define loss function (criterion) and optimizer + criterion = nn.CrossEntropyLoss() + + if args.gpu is not None: + criterion = criterion.cuda(args.gpu) + + # optionally evaluate previous model + if args.resume != "": + print('Loading model checkpoint') + checkpoint = torch.load(args.resume) + model.load_state_dict(checkpoint["state_dict"]) + + # cudnn.benchmark = True + + # Data loading code + normalize = transforms.Normalize( + mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225] + ) + T = [] + if args.resize_and_crop and "efficientnet" not in args.arch: + T.extend( + [ + transforms.Resize(args.resizepar), + transforms.CenterCrop(args.croppar), + ] + ) + if "efficientnet" in args.arch: + image_size = EfficientNet.get_image_size(args.arch) + T.extend( + [ + transforms.Resize(image_size, interpolation=PIL.Image.BICUBIC), + transforms.CenterCrop(image_size), + ] + ) + T.extend([transforms.ToTensor(), normalize]) + print(T) + + fnames = glob.glob(os.path.join(args.imagenet_path, "*")) + print(f"Dataset at {args.imagenet_path}, {len(fnames)}") + if len(fnames) == 1000: + print(f"| Use imagenet folder from {args.imagenet_path}") + dataset = datasets.ImageFolder( + args.imagenet_path, transforms.Compose(T)) + else: + print(f"| Use subfolders in {args.imagenet_path}") + print("|" + "\n|".join(fnames)) + dataset = torch.utils.data.ConcatDataset( + [datasets.ImageFolder(path, transforms.Compose(T)) + for path in fnames] + ) + + val_loader = torch.utils.data.DataLoader( + dataset, + batch_size=args.test_batch_size, + shuffle=not args.no_shuffle, + num_workers=args.workers, + pin_memory=True, + ) + print("| Shuffling: ", not args.no_shuffle) + + Evaluation.gpu = args.gpu + evaluate = Evaluation(model, criterion, args) + emissions = evaluate(val_loader) + + top1, top5, loss = \ + emissions.top1.avg, emissions.top5.avg, emissions.losses.avg + print(f"| Finished eval with: top-1 {top1}; top-5 {top5}; loss {loss}") + + return emissions + # emissions.finalize().save(args.emission_path, args=args) + + +class Emissions: + """ Experiment Outputs """ + + def __init__(self, num_batches): + self.num_batches = num_batches + self.init_metrics() + + def init_metrics(self): + self.targets = [] + self.predictions = [] + self.batch_time = AverageMeter("Time", ":6.3f") + self.losses = AverageMeter("Loss", ":.4e") + self.top1 = AverageMeter("Acc@1", ":6.2f") + self.top5 = AverageMeter("Acc@5", ":6.2f") + self.progress = ProgressMeter( + self.num_batches, + [self.batch_time, self.losses, self.top1, self.top5], + prefix="| Test: ", + ) + self.finalized = False + + def update_metrics(self, output, target, loss): + assert not self.finalized + acc1, acc5 = get_accuracy(output, target, topk=(1, 5)) + self.losses.update(loss.item(), output.size(0)) + self.top1.update(acc1[0].cpu(), output.size(0)) + self.top5.update(acc5[0].cpu(), output.size(0)) + + def append(self, output, targets, loss, time=0): + assert len(output) == len(targets) + assert not self.finalized + + self.batch_time.update(time) + self.update_metrics(output, targets, loss) + + self.predictions.append(output.detach().cpu().numpy()) + self.targets.append(targets.detach().cpu().numpy()) + + def finalize(self): + assert not self.finalized + # assert self.num_batches == len(self.targets) + # assert self.num_batches == len(self.predictions) + + self.predictions = np.concatenate(self.predictions, axis=0) + self.targets = np.concatenate(self.targets, axis=0) + self.finalized = True + return self + + def save(self, fname, **kwargs): + assert self.finalized + assert not os.path.exists(fname) + np.savez( + fname, + top1=self.top1.avg, + top5=self.top5.avg, + loss=self.losses.avg, + predictions=self.predictions, + targets=self.targets, + **kwargs, + ) + return self + + +class Evaluation: + """ Evaluation loop """ + + def __init__(self, model, criterion, args): + super(Evaluation, self).__init__() + + self.args = args + self.model = model + self.criterion = Evaluation.to_device(criterion) + + + gpu = None + def to_device(arg): + if Evaluation.gpu is not None: + return arg.cuda(Evaluation.gpu, non_blocking=True) + else: + return arg + + def iterate(self, loader): + if self.args.tqdm and tqdm is not None: + return tqdm(enumerate(loader)) + return enumerate(loader) + + def use_train_statistics(self, module): + if isinstance(module, nn.BatchNorm2d): + module.train() + + @property + def elapsed_time(self): + """ return elapsed time since last call """ + if not hasattr(self, "_end"): + self._end = time.time() + span = time.time() - self._end + self._end = time.time() + return span + + def select_ablations(self): + """ Check args for ablation settings """ + if self.args.ema_batchnorm: + print( + "| Collecting statistics during test time with exponential \ + moving averaging. Experimental version from 30-04" + ) + bn.adapt(self.model) + self.warmup_batches = ( + self.args.ema_warmup_samples // self.args.test_batch_size + ) + assert self.warmup_batches > 0 + return + + if self.args.adapt_mean or self.args.adapt_var: + print( + f"| Adapting mean[{self.args.adapt_mean}] and \ + var[{self.args.adapt_var}]" + ) + bn.adapt_parts(self.model, + self.args.adapt_mean, self.args.adapt_var) + return + + if self.args.adapt_stage is not None: + print(f"| Adapting only model stage {self.args.adapt_stage}") + stages.choose_one_adaptation(self.model, self.args.adapt_stage) + return + + if self.args.leave_stage is not None: + print(f"| Adapting all but model stage {self.args.adapt_stage}") + stages.leave_one_out_adaptation(self.model, self.args.leave_stage) + return + + if self.args.adapt_prior is not None or \ + self.args.adapt_prior_bsz is not None: + + assert self.args.adapt_prior is None or \ + self.args.adapt_prior_bsz is None + + if self.args.adapt_prior_bsz is not None: + n = self.args.test_batch_size + N = self.args.adapt_prior_bsz + setattr(self.args, "adapt_prior", float(N) / float(N + n)) + print( + f"| Using a prior on the statistics with \ + lambda = {self.args.adapt_prior}" + ) + bn.adapt_bayesian(self.model, self.args.adapt_prior, + Evaluation.to_device) + return + + def __call__(self, val_loader): + print("| Start evaluation") + + os.system("git log -n1 --oneline") + self.warmup_batches = 0 + self.model.eval() + if self.args.train_mode_during_eval: + print("| Using model in train() mode") + self.model.apply(self.use_train_statistics) + + assert self.args.ema_batchnorm is False, \ + "--ema-batchnorm mode does not make sense with \ + --train-mode-during-eval" + else: + print("| Using model in eval() mode") + self.model.eval() + self.select_ablations() + + emissions = Emissions(len(val_loader)) + with torch.no_grad(): + _ = self.elapsed_time + + if self.warmup_batches > 0: + print(f"| Starting warmup for a total of \ + {self.warmup_batches} batches") + for i, (images, target) in self.iterate(val_loader): + if i > self.warmup_batches: + break + images = Evaluation.to_device(images) + output = self.model(images) + + print("| Starting evaluation") + for i, (images, target) in self.iterate(val_loader): + images = Evaluation.to_device(images) + target = Evaluation.to_device(target) + output = self.model(images) + loss = self.criterion(output, target) + + emissions.append(output, target, loss, self.elapsed_time) + if i % self.args.print_freq == 0: + emissions.progress.display(i) + if self.args.dry_run: + break + + return emissions + +if __name__ == '__main__': + import sys + main(sys.argv[1:]) diff --git a/examples/batchnorm/src/meters.py b/examples/batchnorm/src/meters.py new file mode 100644 index 0000000..6305aee --- /dev/null +++ b/examples/batchnorm/src/meters.py @@ -0,0 +1,59 @@ +import torch + +class AverageMeter(object): + """Computes and stores the average and current value""" + + def __init__(self, name, fmt=":f"): + self.name = name + self.fmt = fmt + self.reset() + + def reset(self): + self.val = 0 + self.avg = 0 + self.sum = 0 + self.count = 0 + + def update(self, val, n=1): + self.val = val + self.sum += val * n + self.count += n + self.avg = self.sum / self.count + + def __str__(self): + fmtstr = "{name} {val" + self.fmt + "} ({avg" + self.fmt + "})" + return fmtstr.format(**self.__dict__) + + +class ProgressMeter(object): + def __init__(self, num_batches, meters, prefix=""): + self.batch_fmtstr = self._get_batch_fmtstr(num_batches) + self.meters = meters + self.prefix = prefix + + def display(self, batch): + entries = [self.prefix + self.batch_fmtstr.format(batch)] + entries += [str(meter) for meter in self.meters] + print("\t".join(entries)) + + def _get_batch_fmtstr(self, num_batches): + num_digits = len(str(num_batches // 1)) + fmt = "{:" + str(num_digits) + "d}" + return "[" + fmt + "/" + fmt.format(num_batches) + "]" + + +def get_accuracy(output, target, topk=(1,)): + """Computes the accuracy over the k top predictions for the specified values of k""" + with torch.no_grad(): + maxk = max(topk) + batch_size = target.size(0) + + _, pred = output.topk(maxk, 1, True, True) + pred = pred.t() + correct = pred.eq(target.view(1, -1).expand_as(pred)) + + res = [] + for k in topk: + correct_k = correct[:k].reshape(-1).float().sum(0, keepdim=True) + res.append(correct_k.mul_(100.0 / batch_size)) + return res diff --git a/examples/batchnorm/tests/imagenet_c/1/n01440764/ILSVRC2012_val_00000293.jpeg b/examples/batchnorm/tests/imagenet_c/1/n01440764/ILSVRC2012_val_00000293.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..1a5081b757933e0b6a259d0074bf4a22e6ea0a05 GIT binary patch literal 6277 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 0 + + +@pytest.mark.parametrize("extra_args", [ + ("--resize-and-crop", ), + ("--ema-batchnorm", ), + ("--adapt-mean", ), + ("--adapt-stage", "0"), + ("--leave-stage", "0"), + ("--adapt-prior", "0"), + ("--adapt-prior-bsz", "0"), + ("--train-mode-during-eval", ) + ]) +def test_adaptations(extra_args): + emissions = run.main([ + "--test-batch-size", "1", + "--imagenet-path", str(module_folder)+"/tests/imagenet_c", + "--arch", "resnet50", + "--pretrained", + "--emission-path", "/tmp/run_experiment", + *extra_args # expand values from tuple + ]) + + # This is a dry run test. We test that: + # 1. code compiles + # 2. applying an adaptation works + # 3. feed-forward works and we have a prediction for an input image. + assert emissions.predictions is not None and \ + emissions.predictions[0].max() > 0 + + +if __name__ == '__main__': + pytest.main() diff --git a/examples/imagenet_d/README.md b/examples/imagenet_d/README.md new file mode 100644 index 0000000..b016cd6 --- /dev/null +++ b/examples/imagenet_d/README.md @@ -0,0 +1,14 @@ +# ImageNet-D Reference Code + +drawing + +Run run.sh to reproduce the files in logs/ + +Example evaluation: + +```python +python3 main.py /gpfs01/bethge/data/visda-2019/clipart/ --pretrained -b 1024 --evaluate --workers 25 >logs/clipart_eval.log +``` + +- Supports ad hoc evaluation and BN adaptation. +- The VisDA-2019 folder needs to be specified. Then, VisDA-2019 classes are mapped to ImageNet classes and symlinks are created in the main directory for the mapped classes. The symlink folders now have 1000 sub-directories corresponding to the 1000 ImageNet classes with symlinks pointing to the corresponding VisDA-2019 images. We refer to these symlink folders as ImageNet-D because it is a subset of VisDA-2019, mapped to ImageNet classes. diff --git a/examples/imagenet_d/imagenet_dict.py b/examples/imagenet_d/imagenet_dict.py new file mode 100644 index 0000000..0e94dfd --- /dev/null +++ b/examples/imagenet_d/imagenet_dict.py @@ -0,0 +1,1000 @@ +map_dict = {0: 'tench, Tinca tinca', + 1: 'goldfish, Carassius auratus', + 2: 'great white shark, white shark, man-eater, man-eating shark, Carcharodon carcharias', + 3: 'tiger shark, Galeocerdo cuvieri', + 4: 'hammerhead, hammerhead shark', + 5: 'electric ray, crampfish, numbfish, torpedo', + 6: 'stingray', + 7: 'cock', + 8: 'hen', + 9: 'ostrich, Struthio camelus', + 10: 'brambling, Fringilla montifringilla', + 11: 'goldfinch, Carduelis carduelis', + 12: 'house finch, linnet, Carpodacus mexicanus', + 13: 'junco, snowbird', + 14: 'indigo bunting, indigo finch, indigo bird, Passerina cyanea', + 15: 'robin, American robin, Turdus migratorius', + 16: 'bulbul', + 17: 'jay', + 18: 'magpie', + 19: 'chickadee', + 20: 'water ouzel, dipper', + 21: 'kite', + 22: 'bald eagle, American eagle, Haliaeetus leucocephalus', + 23: 'vulture', + 24: 'great grey owl, great gray owl, Strix nebulosa', + 25: 'European fire salamander, Salamandra salamandra', + 26: 'common newt, Triturus vulgaris', + 27: 'eft', + 28: 'spotted salamander, Ambystoma maculatum', + 29: 'axolotl, mud puppy, Ambystoma mexicanum', + 30: 'bullfrog, Rana catesbeiana', + 31: 'tree frog, tree-frog', + 32: 'tailed frog, bell toad, ribbed toad, tailed toad, Ascaphus trui', + 33: 'loggerhead, loggerhead turtle, Caretta caretta', + 34: 'leatherback turtle, leatherback, leathery turtle, Dermochelys coriacea', + 35: 'mud turtle', + 36: 'terrapin', + 37: 'box turtle, box tortoise', + 38: 'banded gecko', + 39: 'common iguana, iguana, Iguana iguana', + 40: 'American chameleon, anole, Anolis carolinensis', + 41: 'whiptail, whiptail lizard', + 42: 'agama', + 43: 'frilled lizard, Chlamydosaurus kingi', + 44: 'alligator lizard', + 45: 'Gila monster, Heloderma suspectum', + 46: 'green lizard, Lacerta viridis', + 47: 'African chameleon, Chamaeleo chamaeleon', + 48: 'Komodo dragon, Komodo lizard, dragon lizard, giant lizard, Varanus komodoensis', + 49: 'African crocodile, Nile crocodile, Crocodylus niloticus', + 50: 'American alligator, Alligator mississipiensis', + 51: 'triceratops', + 52: 'thunder snake, worm snake, Carphophis amoenus', + 53: 'ringneck snake, ring-necked snake, ring snake', + 54: 'hognose snake, puff adder, sand viper', + 55: 'green snake, grass snake', + 56: 'king snake, kingsnake', + 57: 'garter snake, grass snake', + 58: 'water snake', + 59: 'vine snake', + 60: 'night snake, Hypsiglena torquata', + 61: 'boa constrictor, Constrictor constrictor', + 62: 'rock python, rock snake, Python sebae', + 63: 'Indian cobra, Naja naja', + 64: 'green mamba', + 65: 'sea snake', + 66: 'horned viper, cerastes, sand viper, horned asp, Cerastes cornutus', + 67: 'diamondback, diamondback rattlesnake, Crotalus adamanteus', + 68: 'sidewinder, horned rattlesnake, Crotalus cerastes', + 69: 'trilobite', + 70: 'harvestman, daddy longlegs, Phalangium opilio', + 71: 'scorpion', + 72: 'black and gold garden spider, Argiope aurantia', + 73: 'barn spider, Araneus cavaticus', + 74: 'garden spider, Aranea diademata', + 75: 'black widow, Latrodectus mactans', + 76: 'tarantula', + 77: 'wolf spider, hunting spider', + 78: 'tick', + 79: 'centipede', + 80: 'black grouse', + 81: 'ptarmigan', + 82: 'ruffed grouse, partridge, Bonasa umbellus', + 83: 'prairie chicken, prairie grouse, prairie fowl', + 84: 'peacock', + 85: 'quail', + 86: 'partridge', + 87: 'African grey, African gray, Psittacus erithacus', + 88: 'macaw', + 89: 'sulphur-crested cockatoo, Kakatoe galerita, Cacatua galerita', + 90: 'lorikeet', + 91: 'coucal', + 92: 'bee eater', + 93: 'hornbill', + 94: 'hummingbird', + 95: 'jacamar', + 96: 'toucan', + 97: 'drake', + 98: 'red-breasted merganser, Mergus serrator', + 99: 'goose', + 100: 'black swan, Cygnus atratus', + 101: 'tusker', + 102: 'echidna, spiny anteater, anteater', + 103: 'platypus, duckbill, duckbilled platypus, duck-billed platypus, Ornithorhynchus anatinus', + 104: 'wallaby, brush kangaroo', + 105: 'koala, koala bear, kangaroo bear, native bear, Phascolarctos cinereus', + 106: 'wombat', + 107: 'jellyfish', + 108: 'sea anemone, anemone', + 109: 'brain coral', + 110: 'flatworm, platyhelminth', + 111: 'nematode, nematode worm, roundworm', + 112: 'conch', + 113: 'snail', + 114: 'slug', + 115: 'sea slug, nudibranch', + 116: 'chiton, coat-of-mail shell, sea cradle, polyplacophore', + 117: 'chambered nautilus, pearly nautilus, nautilus', + 118: 'Dungeness crab, Cancer magister', + 119: 'rock crab, Cancer irroratus', + 120: 'fiddler crab', + 121: 'king crab, Alaska crab, Alaskan king crab, Alaska king crab, Paralithodes camtschatica', + 122: 'American lobster, Northern lobster, Maine lobster, Homarus americanus', + 123: 'spiny lobster, langouste, rock lobster, crawfish, crayfish, sea crawfish', + 124: 'crayfish, crawfish, crawdad, crawdaddy', + 125: 'hermit crab', + 126: 'isopod', + 127: 'white stork, Ciconia ciconia', + 128: 'black stork, Ciconia nigra', + 129: 'spoonbill', + 130: 'flamingo', + 131: 'little blue heron, Egretta caerulea', + 132: 'American egret, great white heron, Egretta albus', + 133: 'bittern', + 134: 'crane', + 135: 'limpkin, Aramus pictus', + 136: 'European gallinule, Porphyrio porphyrio', + 137: 'American coot, marsh hen, mud hen, water hen, Fulica americana', + 138: 'bustard', + 139: 'ruddy turnstone, Arenaria interpres', + 140: 'red-backed sandpiper, dunlin, Erolia alpina', + 141: 'redshank, Tringa totanus', + 142: 'dowitcher', + 143: 'oystercatcher, oyster catcher', + 144: 'pelican', + 145: 'king penguin, Aptenodytes patagonica', + 146: 'albatross, mollymawk', + 147: 'grey whale, gray whale, devilfish, Eschrichtius gibbosus, Eschrichtius robustus', + 148: 'killer whale, killer, orca, grampus, sea wolf, Orcinus orca', + 149: 'dugong, Dugong dugon', + 150: 'sea lion', + 151: 'Chihuahua', + 152: 'Japanese spaniel', + 153: 'Maltese dog, Maltese terrier, Maltese', + 154: 'Pekinese, Pekingese, Peke', + 155: 'Shih-Tzu', + 156: 'Blenheim spaniel', + 157: 'papillon', + 158: 'toy terrier', + 159: 'Rhodesian ridgeback', + 160: 'Afghan hound, Afghan', + 161: 'basset, basset hound', + 162: 'beagle', + 163: 'bloodhound, sleuthhound', + 164: 'bluetick', + 165: 'black-and-tan coonhound', + 166: 'Walker hound, Walker foxhound', + 167: 'English foxhound', + 168: 'redbone', + 169: 'borzoi, Russian wolfhound', + 170: 'Irish wolfhound', + 171: 'Italian greyhound', + 172: 'whippet', + 173: 'Ibizan hound, Ibizan Podenco', + 174: 'Norwegian elkhound, elkhound', + 175: 'otterhound, otter hound', + 176: 'Saluki, gazelle hound', + 177: 'Scottish deerhound, deerhound', + 178: 'Weimaraner', + 179: 'Staffordshire bullterrier, Staffordshire bull terrier', + 180: 'American Staffordshire terrier, Staffordshire terrier, American pit bull terrier, pit bull terrier', + 181: 'Bedlington terrier', + 182: 'Border terrier', + 183: 'Kerry blue terrier', + 184: 'Irish terrier', + 185: 'Norfolk terrier', + 186: 'Norwich terrier', + 187: 'Yorkshire terrier', + 188: 'wire-haired fox terrier', + 189: 'Lakeland terrier', + 190: 'Sealyham terrier, Sealyham', + 191: 'Airedale, Airedale terrier', + 192: 'cairn, cairn terrier', + 193: 'Australian terrier', + 194: 'Dandie Dinmont, Dandie Dinmont terrier', + 195: 'Boston bull, Boston terrier', + 196: 'miniature schnauzer', + 197: 'giant schnauzer', + 198: 'standard schnauzer', + 199: 'Scotch terrier, Scottish terrier, Scottie', + 200: 'Tibetan terrier, chrysanthemum dog', + 201: 'silky terrier, Sydney silky', + 202: 'soft-coated wheaten terrier', + 203: 'West Highland white terrier', + 204: 'Lhasa, Lhasa apso', + 205: 'flat-coated retriever', + 206: 'curly-coated retriever', + 207: 'golden retriever', + 208: 'Labrador retriever', + 209: 'Chesapeake Bay retriever', + 210: 'German short-haired pointer', + 211: 'vizsla, Hungarian pointer', + 212: 'English setter', + 213: 'Irish setter, red setter', + 214: 'Gordon setter', + 215: 'Brittany spaniel', + 216: 'clumber, clumber spaniel', + 217: 'English springer, English springer spaniel', + 218: 'Welsh springer spaniel', + 219: 'cocker spaniel, English cocker spaniel, cocker', + 220: 'Sussex spaniel', + 221: 'Irish water spaniel', + 222: 'kuvasz', + 223: 'schipperke', + 224: 'groenendael', + 225: 'malinois', + 226: 'briard', + 227: 'kelpie', + 228: 'komondor', + 229: 'Old English sheepdog, bobtail', + 230: 'Shetland sheepdog, Shetland sheep dog, Shetland', + 231: 'collie', + 232: 'Border collie', + 233: 'Bouvier des Flandres, Bouviers des Flandres', + 234: 'Rottweiler', + 235: 'German shepherd, German shepherd dog, German police dog, alsatian', + 236: 'Doberman, Doberman pinscher', + 237: 'miniature pinscher', + 238: 'Greater Swiss Mountain dog', + 239: 'Bernese mountain dog', + 240: 'Appenzeller', + 241: 'EntleBucher', + 242: 'boxer', + 243: 'bull mastiff', + 244: 'Tibetan mastiff', + 245: 'French bulldog', + 246: 'Great Dane', + 247: 'Saint Bernard, St Bernard', + 248: 'Eskimo dog, husky', + 249: 'malamute, malemute, Alaskan malamute', + 250: 'Siberian husky', + 251: 'dalmatian, coach dog, carriage dog', + 252: 'affenpinscher, monkey pinscher, monkey dog', + 253: 'basenji', + 254: 'pug, pug-dog', + 255: 'Leonberg', + 256: 'Newfoundland, Newfoundland dog', + 257: 'Great Pyrenees', + 258: 'Samoyed, Samoyede', + 259: 'Pomeranian', + 260: 'chow, chow chow', + 261: 'keeshond', + 262: 'Brabancon griffon', + 263: 'Pembroke, Pembroke Welsh corgi', + 264: 'Cardigan, Cardigan Welsh corgi', + 265: 'toy poodle', + 266: 'miniature poodle', + 267: 'standard poodle', + 268: 'Mexican hairless', + 269: 'timber wolf, grey wolf, gray wolf, Canis lupus', + 270: 'white wolf, Arctic wolf, Canis lupus tundrarum', + 271: 'red wolf, maned wolf, Canis rufus, Canis niger', + 272: 'coyote, prairie wolf, brush wolf, Canis latrans', + 273: 'dingo, warrigal, warragal, Canis dingo', + 274: 'dhole, Cuon alpinus', + 275: 'African hunting dog, hyena dog, Cape hunting dog, Lycaon pictus', + 276: 'hyena, hyaena', + 277: 'red fox, Vulpes vulpes', + 278: 'kit fox, Vulpes macrotis', + 279: 'Arctic fox, white fox, Alopex lagopus', + 280: 'grey fox, gray fox, Urocyon cinereoargenteus', + 281: 'tabby, tabby cat', + 282: 'tiger cat', + 283: 'Persian cat', + 284: 'Siamese cat, Siamese', + 285: 'Egyptian cat', + 286: 'cougar, puma, catamount, mountain lion, painter, panther, Felis concolor', + 287: 'lynx, catamount', + 288: 'leopard, Panthera pardus', + 289: 'snow leopard, ounce, Panthera uncia', + 290: 'jaguar, panther, Panthera onca, Felis onca', + 291: 'lion, king of beasts, Panthera leo', + 292: 'tiger, Panthera tigris', + 293: 'cheetah, chetah, Acinonyx jubatus', + 294: 'brown bear, bruin, Ursus arctos', + 295: 'American black bear, black bear, Ursus americanus, Euarctos americanus', + 296: 'ice bear, polar bear, Ursus Maritimus, Thalarctos maritimus', + 297: 'sloth bear, Melursus ursinus, Ursus ursinus', + 298: 'mongoose', + 299: 'meerkat, mierkat', + 300: 'tiger beetle', + 301: 'ladybug, ladybeetle, lady beetle, ladybird, ladybird beetle', + 302: 'ground beetle, carabid beetle', + 303: 'long-horned beetle, longicorn, longicorn beetle', + 304: 'leaf beetle, chrysomelid', + 305: 'dung beetle', + 306: 'rhinoceros beetle', + 307: 'weevil', + 308: 'fly', + 309: 'bee', + 310: 'ant, emmet, pismire', + 311: 'grasshopper, hopper', + 312: 'cricket', + 313: 'walking stick, walkingstick, stick insect', + 314: 'cockroach, roach', + 315: 'mantis, mantid', + 316: 'cicada, cicala', + 317: 'leafhopper', + 318: 'lacewing, lacewing fly', + 319: "dragonfly, darning needle, devil's darning needle, sewing needle, snake feeder, snake doctor, mosquito hawk, skeeter hawk", + 320: 'damselfly', + 321: 'admiral', + 322: 'ringlet, ringlet butterfly', + 323: 'monarch, monarch butterfly, milkweed butterfly, Danaus plexippus', + 324: 'cabbage butterfly', + 325: 'sulphur butterfly, sulfur butterfly', + 326: 'lycaenid, lycaenid butterfly', + 327: 'starfish, sea star', + 328: 'sea urchin', + 329: 'sea cucumber, holothurian', + 330: 'wood rabbit, cottontail, cottontail rabbit', + 331: 'hare', + 332: 'Angora, Angora rabbit', + 333: 'hamster', + 334: 'porcupine, hedgehog', + 335: 'fox squirrel, eastern fox squirrel, Sciurus niger', + 336: 'marmot', + 337: 'beaver', + 338: 'guinea pig, Cavia cobaya', + 339: 'sorrel', + 340: 'zebra', + 341: 'hog, pig, grunter, squealer, Sus scrofa', + 342: 'wild boar, boar, Sus scrofa', + 343: 'warthog', + 344: 'hippopotamus, hippo, river horse, Hippopotamus amphibius', + 345: 'ox', + 346: 'water buffalo, water ox, Asiatic buffalo, Bubalus bubalis', + 347: 'bison', + 348: 'ram, tup', + 349: 'bighorn, bighorn sheep, cimarron, Rocky Mountain bighorn, Rocky Mountain sheep, Ovis canadensis', + 350: 'ibex, Capra ibex', + 351: 'hartebeest', + 352: 'impala, Aepyceros melampus', + 353: 'gazelle', + 354: 'Arabian camel, dromedary, Camelus dromedarius', + 355: 'llama', + 356: 'weasel', + 357: 'mink', + 358: 'polecat, fitch, foulmart, foumart, Mustela putorius', + 359: 'black-footed ferret, ferret, Mustela nigripes', + 360: 'otter', + 361: 'skunk, polecat, wood pussy', + 362: 'badger', + 363: 'armadillo', + 364: 'three-toed sloth, ai, Bradypus tridactylus', + 365: 'orangutan, orang, orangutang, Pongo pygmaeus', + 366: 'gorilla, Gorilla gorilla', + 367: 'chimpanzee, chimp, Pan troglodytes', + 368: 'gibbon, Hylobates lar', + 369: 'siamang, Hylobates syndactylus, Symphalangus syndactylus', + 370: 'guenon, guenon monkey', + 371: 'patas, hussar monkey, Erythrocebus patas', + 372: 'baboon', + 373: 'macaque', + 374: 'langur', + 375: 'colobus, colobus monkey', + 376: 'proboscis monkey, Nasalis larvatus', + 377: 'marmoset', + 378: 'capuchin, ringtail, Cebus capucinus', + 379: 'howler monkey, howler', + 380: 'titi, titi monkey', + 381: 'spider monkey, Ateles geoffroyi', + 382: 'squirrel monkey, Saimiri sciureus', + 383: 'Madagascar cat, ring-tailed lemur, Lemur catta', + 384: 'indri, indris, Indri indri, Indri brevicaudatus', + 385: 'Indian elephant, Elephas maximus', + 386: 'African elephant, Loxodonta africana', + 387: 'lesser panda, red panda, panda, bear cat, cat bear, Ailurus fulgens', + 388: 'giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca', + 389: 'barracouta, snoek', + 390: 'eel', + 391: 'coho, cohoe, coho salmon, blue jack, silver salmon, Oncorhynchus kisutch', + 392: 'rock beauty, Holocanthus tricolor', + 393: 'anemone fish', + 394: 'sturgeon', + 395: 'gar, garfish, garpike, billfish, Lepisosteus osseus', + 396: 'lionfish', + 397: 'puffer, pufferfish, blowfish, globefish', + 398: 'abacus', + 399: 'abaya', + 400: "academic gown, academic robe, judge's robe", + 401: 'accordion, piano accordion, squeeze box', + 402: 'acoustic guitar', + 403: 'aircraft carrier, carrier, flattop, attack aircraft carrier', + 404: 'airliner', + 405: 'airship, dirigible', + 406: 'altar', + 407: 'ambulance', + 408: 'amphibian, amphibious vehicle', + 409: 'analog clock', + 410: 'apiary, bee house', + 411: 'apron', + 412: 'ashcan, trash can, garbage can, wastebin, ash bin, ash-bin, ashbin, dustbin, trash barrel, trash bin', + 413: 'assault rifle, assault gun', + 414: 'backpack, back pack, knapsack, packsack, rucksack, haversack', + 415: 'bakery, bakeshop, bakehouse', + 416: 'balance beam, beam', + 417: 'balloon', + 418: 'ballpoint, ballpoint pen, ballpen, Biro', + 419: 'Band Aid', + 420: 'banjo', + 421: 'bannister, banister, balustrade, balusters, handrail', + 422: 'barbell', + 423: 'barber chair', + 424: 'barbershop', + 425: 'barn', + 426: 'barometer', + 427: 'barrel, cask', + 428: 'barrow, garden cart, lawn cart, wheelbarrow', + 429: 'baseball', + 430: 'basketball', + 431: 'bassinet', + 432: 'bassoon', + 433: 'bathing cap, swimming cap', + 434: 'bath towel', + 435: 'bathtub, bathing tub, bath, tub', + 436: 'beach wagon, station wagon, wagon, estate car, beach waggon, station waggon, waggon', + 437: 'beacon, lighthouse, beacon light, pharos', + 438: 'beaker', + 439: 'bearskin, busby, shako', + 440: 'beer bottle', + 441: 'beer glass', + 442: 'bell cote, bell cot', + 443: 'bib', + 444: 'bicycle-built-for-two, tandem bicycle, tandem', + 445: 'bikini, two-piece', + 446: 'binder, ring-binder', + 447: 'binoculars, field glasses, opera glasses', + 448: 'birdhouse', + 449: 'boathouse', + 450: 'bobsled, bobsleigh, bob', + 451: 'bolo tie, bolo, bola tie, bola', + 452: 'bonnet, poke bonnet', + 453: 'bookcase', + 454: 'bookshop, bookstore, bookstall', + 455: 'bottlecap', + 456: 'bow', + 457: 'bow tie, bow-tie, bowtie', + 458: 'brass, memorial tablet, plaque', + 459: 'brassiere, bra, bandeau', + 460: 'breakwater, groin, groyne, mole, bulwark, seawall, jetty', + 461: 'breastplate, aegis, egis', + 462: 'broom', + 463: 'bucket, pail', + 464: 'buckle', + 465: 'bulletproof vest', + 466: 'bullet train, bullet', + 467: 'butcher shop, meat market', + 468: 'cab, hack, taxi, taxicab', + 469: 'caldron, cauldron', + 470: 'candle, taper, wax light', + 471: 'cannon', + 472: 'canoe', + 473: 'can opener, tin opener', + 474: 'cardigan', + 475: 'car mirror', + 476: 'carousel, carrousel, merry-go-round, roundabout, whirligig', + 477: "carpenter's kit, tool kit", + 478: 'carton', + 479: 'car wheel', + 480: 'cash machine, cash dispenser, automated teller machine, automatic teller machine, automated teller, automatic teller, ATM', + 481: 'cassette', + 482: 'cassette player', + 483: 'castle', + 484: 'catamaran', + 485: 'CD player', + 486: 'cello, violoncello', + 487: 'cellular telephone, cellular phone, cellphone, cell, mobile phone', + 488: 'chain', + 489: 'chainlink fence', + 490: 'chain mail, ring mail, mail, chain armor, chain armour, ring armor, ring armour', + 491: 'chain saw, chainsaw', + 492: 'chest', + 493: 'chiffonier, commode', + 494: 'chime, bell, gong', + 495: 'china cabinet, china closet', + 496: 'Christmas stocking', + 497: 'church, church building', + 498: 'cinema, movie theater, movie theatre, movie house, picture palace', + 499: 'cleaver, meat cleaver, chopper', + 500: 'cliff dwelling', + 501: 'cloak', + 502: 'clog, geta, patten, sabot', + 503: 'cocktail shaker', + 504: 'coffee mug', + 505: 'coffeepot', + 506: 'coil, spiral, volute, whorl, helix', + 507: 'combination lock', + 508: 'computer keyboard, keypad', + 509: 'confectionery, confectionary, candy store', + 510: 'container ship, containership, container vessel', + 511: 'convertible', + 512: 'corkscrew, bottle screw', + 513: 'cornet, horn, trumpet, trump', + 514: 'cowboy boot', + 515: 'cowboy hat, ten-gallon hat', + 516: 'cradle', + 517: 'crane', + 518: 'crash helmet', + 519: 'crate', + 520: 'crib, cot', + 521: 'Crock Pot', + 522: 'croquet ball', + 523: 'crutch', + 524: 'cuirass', + 525: 'dam, dike, dyke', + 526: 'desk', + 527: 'desktop computer', + 528: 'dial telephone, dial phone', + 529: 'diaper, nappy, napkin', + 530: 'digital clock', + 531: 'digital watch', + 532: 'dining table, board', + 533: 'dishrag, dishcloth', + 534: 'dishwasher, dish washer, dishwashing machine', + 535: 'disk brake, disc brake', + 536: 'dock, dockage, docking facility', + 537: 'dogsled, dog sled, dog sleigh', + 538: 'dome', + 539: 'doormat, welcome mat', + 540: 'drilling platform, offshore rig', + 541: 'drum, membranophone, tympan', + 542: 'drumstick', + 543: 'dumbbell', + 544: 'Dutch oven', + 545: 'electric fan, blower', + 546: 'electric guitar', + 547: 'electric locomotive', + 548: 'entertainment center', + 549: 'envelope', + 550: 'espresso maker', + 551: 'face powder', + 552: 'feather boa, boa', + 553: 'file, file cabinet, filing cabinet', + 554: 'fireboat', + 555: 'fire engine, fire truck', + 556: 'fire screen, fireguard', + 557: 'flagpole, flagstaff', + 558: 'flute, transverse flute', + 559: 'folding chair', + 560: 'football helmet', + 561: 'forklift', + 562: 'fountain', + 563: 'fountain pen', + 564: 'four-poster', + 565: 'freight car', + 566: 'French horn, horn', + 567: 'frying pan, frypan, skillet', + 568: 'fur coat', + 569: 'garbage truck, dustcart', + 570: 'gasmask, respirator, gas helmet', + 571: 'gas pump, gasoline pump, petrol pump, island dispenser', + 572: 'goblet', + 573: 'go-kart', + 574: 'golf ball', + 575: 'golfcart, golf cart', + 576: 'gondola', + 577: 'gong, tam-tam', + 578: 'gown', + 579: 'grand piano, grand', + 580: 'greenhouse, nursery, glasshouse', + 581: 'grille, radiator grille', + 582: 'grocery store, grocery, food market, market', + 583: 'guillotine', + 584: 'hair slide', + 585: 'hair spray', + 586: 'half track', + 587: 'hammer', + 588: 'hamper', + 589: 'hand blower, blow dryer, blow drier, hair dryer, hair drier', + 590: 'hand-held computer, hand-held microcomputer', + 591: 'handkerchief, hankie, hanky, hankey', + 592: 'hard disc, hard disk, fixed disk', + 593: 'harmonica, mouth organ, harp, mouth harp', + 594: 'harp', + 595: 'harvester, reaper', + 596: 'hatchet', + 597: 'holster', + 598: 'home theater, home theatre', + 599: 'honeycomb', + 600: 'hook, claw', + 601: 'hoopskirt, crinoline', + 602: 'horizontal bar, high bar', + 603: 'horse cart, horse-cart', + 604: 'hourglass', + 605: 'iPod', + 606: 'iron, smoothing iron', + 607: "jack-o'-lantern", + 608: 'jean, blue jean, denim', + 609: 'jeep, landrover', + 610: 'jersey, T-shirt, tee shirt', + 611: 'jigsaw puzzle', + 612: 'jinrikisha, ricksha, rickshaw', + 613: 'joystick', + 614: 'kimono', + 615: 'knee pad', + 616: 'knot', + 617: 'lab coat, laboratory coat', + 618: 'ladle', + 619: 'lampshade, lamp shade', + 620: 'laptop, laptop computer', + 621: 'lawn mower, mower', + 622: 'lens cap, lens cover', + 623: 'letter opener, paper knife, paperknife', + 624: 'library', + 625: 'lifeboat', + 626: 'lighter, light, igniter, ignitor', + 627: 'limousine, limo', + 628: 'liner, ocean liner', + 629: 'lipstick, lip rouge', + 630: 'Loafer', + 631: 'lotion', + 632: 'loudspeaker, speaker, speaker unit, loudspeaker system, speaker system', + 633: "loupe, jeweler's loupe", + 634: 'lumbermill, sawmill', + 635: 'magnetic compass', + 636: 'mailbag, postbag', + 637: 'mailbox, letter box', + 638: 'maillot', + 639: 'maillot, tank suit', + 640: 'manhole cover', + 641: 'maraca', + 642: 'marimba, xylophone', + 643: 'mask', + 644: 'matchstick', + 645: 'maypole', + 646: 'maze, labyrinth', + 647: 'measuring cup', + 648: 'medicine chest, medicine cabinet', + 649: 'megalith, megalithic structure', + 650: 'microphone, mike', + 651: 'microwave, microwave oven', + 652: 'military uniform', + 653: 'milk can', + 654: 'minibus', + 655: 'miniskirt, mini', + 656: 'minivan', + 657: 'missile', + 658: 'mitten', + 659: 'mixing bowl', + 660: 'mobile home, manufactured home', + 661: 'Model T', + 662: 'modem', + 663: 'monastery', + 664: 'monitor', + 665: 'moped', + 666: 'mortar', + 667: 'mortarboard', + 668: 'mosque', + 669: 'mosquito net', + 670: 'motor scooter, scooter', + 671: 'mountain bike, all-terrain bike, off-roader', + 672: 'mountain tent', + 673: 'mouse, computer mouse', + 674: 'mousetrap', + 675: 'moving van', + 676: 'muzzle', + 677: 'nail', + 678: 'neck brace', + 679: 'necklace', + 680: 'nipple', + 681: 'notebook, notebook computer', + 682: 'obelisk', + 683: 'oboe, hautboy, hautbois', + 684: 'ocarina, sweet potato', + 685: 'odometer, hodometer, mileometer, milometer', + 686: 'oil filter', + 687: 'organ, pipe organ', + 688: 'oscilloscope, scope, cathode-ray oscilloscope, CRO', + 689: 'overskirt', + 690: 'oxcart', + 691: 'oxygen mask', + 692: 'packet', + 693: 'paddle, boat paddle', + 694: 'paddlewheel, paddle wheel', + 695: 'padlock', + 696: 'paintbrush', + 697: "pajama, pyjama, pj's, jammies", + 698: 'palace', + 699: 'panpipe, pandean pipe, syrinx', + 700: 'paper towel', + 701: 'parachute, chute', + 702: 'parallel bars, bars', + 703: 'park bench', + 704: 'parking meter', + 705: 'passenger car, coach, carriage', + 706: 'patio, terrace', + 707: 'pay-phone, pay-station', + 708: 'pedestal, plinth, footstall', + 709: 'pencil box, pencil case', + 710: 'pencil sharpener', + 711: 'perfume, essence', + 712: 'Petri dish', + 713: 'photocopier', + 714: 'pick, plectrum, plectron', + 715: 'pickelhaube', + 716: 'picket fence, paling', + 717: 'pickup, pickup truck', + 718: 'pier', + 719: 'piggy bank, penny bank', + 720: 'pill bottle', + 721: 'pillow', + 722: 'ping-pong ball', + 723: 'pinwheel', + 724: 'pirate, pirate ship', + 725: 'pitcher, ewer', + 726: "plane, carpenter's plane, woodworking plane", + 727: 'planetarium', + 728: 'plastic bag', + 729: 'plate rack', + 730: 'plow, plough', + 731: "plunger, plumber's helper", + 732: 'Polaroid camera, Polaroid Land camera', + 733: 'pole', + 734: 'police van, police wagon, paddy wagon, patrol wagon, wagon, black Maria', + 735: 'poncho', + 736: 'pool table, billiard table, snooker table', + 737: 'pop bottle, soda bottle', + 738: 'pot, flowerpot', + 739: "potter's wheel", + 740: 'power drill', + 741: 'prayer rug, prayer mat', + 742: 'printer', + 743: 'prison, prison house', + 744: 'projectile, missile', + 745: 'projector', + 746: 'puck, hockey puck', + 747: 'punching bag, punch bag, punching ball, punchball', + 748: 'purse', + 749: 'quill, quill pen', + 750: 'quilt, comforter, comfort, puff', + 751: 'racer, race car, racing car', + 752: 'racket, racquet', + 753: 'radiator', + 754: 'radio, wireless', + 755: 'radio telescope, radio reflector', + 756: 'rain barrel', + 757: 'recreational vehicle, RV, R.V.', + 758: 'reel', + 759: 'reflex camera', + 760: 'refrigerator, icebox', + 761: 'remote control, remote', + 762: 'restaurant, eating house, eating place, eatery', + 763: 'revolver, six-gun, six-shooter', + 764: 'rifle', + 765: 'rocking chair, rocker', + 766: 'rotisserie', + 767: 'rubber eraser, rubber, pencil eraser', + 768: 'rugby ball', + 769: 'rule, ruler', + 770: 'running shoe', + 771: 'safe', + 772: 'safety pin', + 773: 'saltshaker, salt shaker', + 774: 'sandal', + 775: 'sarong', + 776: 'sax, saxophone', + 777: 'scabbard', + 778: 'scale, weighing machine', + 779: 'school bus', + 780: 'schooner', + 781: 'scoreboard', + 782: 'screen, CRT screen', + 783: 'screw', + 784: 'screwdriver', + 785: 'seat belt, seatbelt', + 786: 'sewing machine', + 787: 'shield, buckler', + 788: 'shoe shop, shoe-shop, shoe store', + 789: 'shoji', + 790: 'shopping basket', + 791: 'shopping cart', + 792: 'shovel', + 793: 'shower cap', + 794: 'shower curtain', + 795: 'ski', + 796: 'ski mask', + 797: 'sleeping bag', + 798: 'slide rule, slipstick', + 799: 'sliding door', + 800: 'slot, one-armed bandit', + 801: 'snorkel', + 802: 'snowmobile', + 803: 'snowplow, snowplough', + 804: 'soap dispenser', + 805: 'soccer ball', + 806: 'sock', + 807: 'solar dish, solar collector, solar furnace', + 808: 'sombrero', + 809: 'soup bowl', + 810: 'space bar', + 811: 'space heater', + 812: 'space shuttle', + 813: 'spatula', + 814: 'speedboat', + 815: "spider web, spider's web", + 816: 'spindle', + 817: 'sports car, sport car', + 818: 'spotlight, spot', + 819: 'stage', + 820: 'steam locomotive', + 821: 'steel arch bridge', + 822: 'steel drum', + 823: 'stethoscope', + 824: 'stole', + 825: 'stone wall', + 826: 'stopwatch, stop watch', + 827: 'stove', + 828: 'strainer', + 829: 'streetcar, tram, tramcar, trolley, trolley car', + 830: 'stretcher', + 831: 'studio couch, day bed', + 832: 'stupa, tope', + 833: 'submarine, pigboat, sub, U-boat', + 834: 'suit, suit of clothes', + 835: 'sundial', + 836: 'sunglass', + 837: 'sunglasses, dark glasses, shades', + 838: 'sunscreen, sunblock, sun blocker', + 839: 'suspension bridge', + 840: 'swab, swob, mop', + 841: 'sweatshirt', + 842: 'swimming trunks, bathing trunks', + 843: 'swing', + 844: 'switch, electric switch, electrical switch', + 845: 'syringe', + 846: 'table lamp', + 847: 'tank, army tank, armored combat vehicle, armoured combat vehicle', + 848: 'tape player', + 849: 'teapot', + 850: 'teddy, teddy bear', + 851: 'television, television system', + 852: 'tennis ball', + 853: 'thatch, thatched roof', + 854: 'theater curtain, theatre curtain', + 855: 'thimble', + 856: 'thresher, thrasher, threshing machine', + 857: 'throne', + 858: 'tile roof', + 859: 'toaster', + 860: 'tobacco shop, tobacconist shop, tobacconist', + 861: 'toilet seat', + 862: 'torch', + 863: 'totem pole', + 864: 'tow truck, tow car, wrecker', + 865: 'toyshop', + 866: 'tractor', + 867: 'trailer truck, tractor trailer, trucking rig, rig, articulated lorry, semi', + 868: 'tray', + 869: 'trench coat', + 870: 'tricycle, trike, velocipede', + 871: 'trimaran', + 872: 'tripod', + 873: 'triumphal arch', + 874: 'trolleybus, trolley coach, trackless trolley', + 875: 'trombone', + 876: 'tub, vat', + 877: 'turnstile', + 878: 'typewriter keyboard', + 879: 'umbrella', + 880: 'unicycle, monocycle', + 881: 'upright, upright piano', + 882: 'vacuum, vacuum cleaner', + 883: 'vase', + 884: 'vault', + 885: 'velvet', + 886: 'vending machine', + 887: 'vestment', + 888: 'viaduct', + 889: 'violin, fiddle', + 890: 'volleyball', + 891: 'waffle iron', + 892: 'wall clock', + 893: 'wallet, billfold, notecase, pocketbook', + 894: 'wardrobe, closet, press', + 895: 'warplane, military plane', + 896: 'washbasin, handbasin, washbowl, lavabo, wash-hand basin', + 897: 'washer, automatic washer, washing machine', + 898: 'water bottle', + 899: 'water jug', + 900: 'water tower', + 901: 'whiskey jug', + 902: 'whistle', + 903: 'wig', + 904: 'window screen', + 905: 'window shade', + 906: 'Windsor tie', + 907: 'wine bottle', + 908: 'wing', + 909: 'wok', + 910: 'wooden spoon', + 911: 'wool, woolen, woollen', + 912: 'worm fence, snake fence, snake-rail fence, Virginia fence', + 913: 'wreck', + 914: 'yawl', + 915: 'yurt', + 916: 'web site, website, internet site, site', + 917: 'comic book', + 918: 'crossword puzzle, crossword', + 919: 'street sign', + 920: 'traffic light, traffic signal, stoplight', + 921: 'book jacket, dust cover, dust jacket, dust wrapper', + 922: 'menu', + 923: 'plate', + 924: 'guacamole', + 925: 'consomme', + 926: 'hot pot, hotpot', + 927: 'trifle', + 928: 'ice cream, icecream', + 929: 'ice lolly, lolly, lollipop, popsicle', + 930: 'French loaf', + 931: 'bagel, beigel', + 932: 'pretzel', + 933: 'cheeseburger', + 934: 'hotdog, hot dog, red hot', + 935: 'mashed potato', + 936: 'head cabbage', + 937: 'broccoli', + 938: 'cauliflower', + 939: 'zucchini, courgette', + 940: 'spaghetti squash', + 941: 'acorn squash', + 942: 'butternut squash', + 943: 'cucumber, cuke', + 944: 'artichoke, globe artichoke', + 945: 'bell pepper', + 946: 'cardoon', + 947: 'mushroom', + 948: 'Granny Smith', + 949: 'strawberry', + 950: 'orange', + 951: 'lemon', + 952: 'fig', + 953: 'pineapple, ananas', + 954: 'banana', + 955: 'jackfruit, jak, jack', + 956: 'custard apple', + 957: 'pomegranate', + 958: 'hay', + 959: 'carbonara', + 960: 'chocolate sauce, chocolate syrup', + 961: 'dough', + 962: 'meat loaf, meatloaf', + 963: 'pizza, pizza pie', + 964: 'potpie', + 965: 'burrito', + 966: 'red wine', + 967: 'espresso', + 968: 'cup', + 969: 'eggnog', + 970: 'alp', + 971: 'bubble', + 972: 'cliff, drop, drop-off', + 973: 'coral reef', + 974: 'geyser', + 975: 'lakeside, lakeshore', + 976: 'promontory, headland, head, foreland', + 977: 'sandbar, sand bar', + 978: 'seashore, coast, seacoast, sea-coast', + 979: 'valley, vale', + 980: 'volcano', + 981: 'ballplayer, baseball player', + 982: 'groom, bridegroom', + 983: 'scuba diver', + 984: 'rapeseed', + 985: 'daisy', + 986: "yellow lady's slipper, yellow lady-slipper, Cypripedium calceolus, Cypripedium parviflorum", + 987: 'corn', + 988: 'acorn', + 989: 'hip, rose hip, rosehip', + 990: 'buckeye, horse chestnut, conker', + 991: 'coral fungus', + 992: 'agaric', + 993: 'gyromitra', + 994: 'stinkhorn, carrion fungus', + 995: 'earthstar', + 996: 'hen-of-the-woods, hen of the woods, Polyporus frondosus, Grifola frondosa', + 997: 'bolete', + 998: 'ear, spike, capitulum', + 999: 'toilet tissue, toilet paper, bathroom tissue'} \ No newline at end of file diff --git a/examples/imagenet_d/main.py b/examples/imagenet_d/main.py new file mode 100644 index 0000000..794c069 --- /dev/null +++ b/examples/imagenet_d/main.py @@ -0,0 +1,382 @@ +import argparse +import os +import random +import shutil +import time +import warnings + +import torch +import torch.nn as nn +import torch.nn.parallel +import torch.backends.cudnn as cudnn +import torch.distributed as dist +import torch.optim +import torch.multiprocessing as mp +import torch.utils.data +import torch.utils.data.distributed +import torchvision.transforms as transforms +import torchvision.datasets as datasets +import torchvision.models as models + +from map_files import * + +model_names = sorted(name for name in models.__dict__ + if name.islower() and not name.startswith("__") + and callable(models.__dict__[name])) + +parser = argparse.ArgumentParser(description='PyTorch ImageNet Training') +parser.add_argument('data', metavar='DIR', + help='path to dataset') +parser.add_argument('-a', '--arch', metavar='ARCH', default='resnet50', + choices=model_names, + help='model architecture: ' + + ' | '.join(model_names) + + ' (default: resnet18)') +parser.add_argument('-j', '--workers', default=4, type=int, metavar='N', + help='number of data loading workers (default: 4)') +parser.add_argument('--epochs', default=90, type=int, metavar='N', + help='number of total epochs to run') +parser.add_argument('--start-epoch', default=0, type=int, metavar='N', + help='manual epoch number (useful on restarts)') +parser.add_argument('-b', '--batch-size', default=256, type=int, + metavar='N', + help='mini-batch size (default: 256), this is the total ' + 'batch size of all GPUs on the current node when ' + 'using Data Parallel or Distributed Data Parallel') +parser.add_argument('--lr', '--learning-rate', default=0.1, type=float, + metavar='LR', help='initial learning rate', dest='lr') +parser.add_argument('--momentum', default=0.9, type=float, metavar='M', + help='momentum') +parser.add_argument('--wd', '--weight-decay', default=1e-4, type=float, + metavar='W', help='weight decay (default: 1e-4)', + dest='weight_decay') +parser.add_argument('-p', '--print-freq', default=10, type=int, + metavar='N', help='print frequency (default: 10)') +parser.add_argument('--resume', default='', type=str, metavar='PATH', + help='path to latest checkpoint (default: none)') +parser.add_argument('-e', '--evaluate', dest='evaluate', action='store_true', + help='evaluate model on validation set') +parser.add_argument('--pretrained', dest='pretrained', action='store_true', + help='use pre-trained model') +parser.add_argument('--world-size', default=-1, type=int, + help='number of nodes for distributed training') +parser.add_argument('--rank', default=-1, type=int, + help='node rank for distributed training') +parser.add_argument('--dist-url', default='tcp://224.66.41.62:23456', type=str, + help='url used to set up distributed training') +parser.add_argument('--dist-backend', default='nccl', type=str, + help='distributed backend') +parser.add_argument('--seed', default=None, type=int, + help='seed for initializing training. ') +parser.add_argument('--gpu', default=None, type=int, + help='GPU id to use.') +parser.add_argument('--multiprocessing-distributed', action='store_true', + help='Use multi-processing distributed training to launch ' + 'N processes per node, which has N GPUs. This is the ' + 'fastest way to use PyTorch for either single node or ' + 'multi node data parallel training') +#new +parser.add_argument('--use-train-statistics', action='store_true') + +def use_train_statistics(module): + if isinstance(module, nn.BatchNorm2d): + module.train() + +best_acc1 = 0 + + +def main(): + args = parser.parse_args() + print(args) + + if args.seed is not None: + random.seed(args.seed) + torch.manual_seed(args.seed) + cudnn.deterministic = True + warnings.warn('You have chosen to seed training. ' + 'This will turn on the CUDNN deterministic setting, ' + 'which can slow down your training considerably! ' + 'You may see unexpected behavior when restarting ' + 'from checkpoints.') + + if args.gpu is not None: + warnings.warn('You have chosen a specific GPU. This will completely ' + 'disable data parallelism.') + + if args.dist_url == "env://" and args.world_size == -1: + args.world_size = int(os.environ["WORLD_SIZE"]) + + args.distributed = args.world_size > 1 or args.multiprocessing_distributed + + ngpus_per_node = torch.cuda.device_count() + if args.multiprocessing_distributed: + # Since we have ngpus_per_node processes per node, the total world_size + # needs to be adjusted accordingly + args.world_size = ngpus_per_node * args.world_size + # Use torch.multiprocessing.spawn to launch distributed processes: the + # main_worker process function + mp.spawn(main_worker, nprocs=ngpus_per_node, args=(ngpus_per_node, args)) + else: + # Simply call main_worker function + main_worker(args.gpu, ngpus_per_node, args) + + +def main_worker(gpu, ngpus_per_node, args): + global best_acc1 + args.gpu = gpu + + if args.gpu is not None: + print("Use GPU: {} for training".format(args.gpu)) + + if args.distributed: + if args.dist_url == "env://" and args.rank == -1: + args.rank = int(os.environ["RANK"]) + if args.multiprocessing_distributed: + # For multiprocessing distributed training, rank needs to be the + # global rank among all the processes + args.rank = args.rank * ngpus_per_node + gpu + dist.init_process_group(backend=args.dist_backend, init_method=args.dist_url, + world_size=args.world_size, rank=args.rank) + # create model + if args.pretrained: + print("=> using pre-trained model '{}'".format(args.arch)) + model = models.__dict__[args.arch](pretrained=True) + else: + print("=> creating model '{}'".format(args.arch)) + model = models.__dict__[args.arch]() + + if not torch.cuda.is_available(): + print('using CPU, this will be slow') + elif args.distributed: + # For multiprocessing distributed, DistributedDataParallel constructor + # should always set the single device scope, otherwise, + # DistributedDataParallel will use all available devices. + if args.gpu is not None: + torch.cuda.set_device(args.gpu) + model.cuda(args.gpu) + # When using a single GPU per process and per + # DistributedDataParallel, we need to divide the batch size + # ourselves based on the total number of GPUs we have + args.batch_size = int(args.batch_size / ngpus_per_node) + args.workers = int((args.workers + ngpus_per_node - 1) / ngpus_per_node) + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + else: + model.cuda() + # DistributedDataParallel will divide and allocate batch_size to all + # available GPUs if device_ids are not set + model = torch.nn.parallel.DistributedDataParallel(model) + elif args.gpu is not None: + torch.cuda.set_device(args.gpu) + model = model.cuda(args.gpu) + else: + # DataParallel will divide and allocate batch_size to all available GPUs + if args.arch.startswith('alexnet') or args.arch.startswith('vgg'): + model.features = torch.nn.DataParallel(model.features) + model.cuda() + else: + model = torch.nn.DataParallel(model).cuda() + + # define loss function (criterion) and optimizer + criterion = nn.CrossEntropyLoss().cuda(args.gpu) + + optimizer = torch.optim.SGD(model.parameters(), args.lr, + momentum=args.momentum, + weight_decay=args.weight_decay) + + # optionally resume from a checkpoint + if args.resume: + if os.path.isfile(args.resume): + print("=> loading checkpoint '{}'".format(args.resume)) + if args.gpu is None: + checkpoint = torch.load(args.resume) + else: + # Map model to be loaded to specified single gpu. + loc = 'cuda:{}'.format(args.gpu) + checkpoint = torch.load(args.resume, map_location=loc) + args.start_epoch = checkpoint['epoch'] + best_acc1 = checkpoint['best_acc1'] + if args.gpu is not None: + # best_acc1 may be from a checkpoint from a different GPU + best_acc1 = best_acc1.to(args.gpu) + model.load_state_dict(checkpoint['state_dict']) + optimizer.load_state_dict(checkpoint['optimizer']) + print("=> loaded checkpoint '{}' (epoch {})" + .format(args.resume, checkpoint['epoch'])) + else: + print("=> no checkpoint found at '{}'".format(args.resume)) + + cudnn.benchmark = True + + # Data loading code + mapping_vector, _, _ = create_symlinks_and_get_imagenet_visda_mapping(args.data, map_dict) + + valdir = './visda_symlinks/' + args.data.split('/')[-2] + '/' + normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225]) + val_dataset = datasets.ImageFolder(valdir, transforms.Compose([ + transforms.Resize(256), + transforms.CenterCrop(224), + transforms.ToTensor(), + normalize, + ])) + val_loader = torch.utils.data.DataLoader(val_dataset, + batch_size=args.batch_size, shuffle=True, + num_workers=args.workers, pin_memory=True) + + print('Number of classes: ', len(val_dataset.classes)) + print('Number of images: ', len(val_loader.dataset)) + + if args.evaluate: + validate(val_loader, model, criterion, args, mapping_vector) + return + + for epoch in range(args.start_epoch, args.epochs): + + adjust_learning_rate(optimizer, epoch, args) + + # evaluate on validation set + acc1 = validate(val_loader, model, criterion, args) + + # remember best acc@1 and save checkpoint + is_best = acc1 > best_acc1 + best_acc1 = max(acc1, best_acc1) + + if not args.multiprocessing_distributed or (args.multiprocessing_distributed + and args.rank % ngpus_per_node == 0): + save_checkpoint({ + 'epoch': epoch + 1, + 'arch': args.arch, + 'state_dict': model.state_dict(), + 'best_acc1': best_acc1, + 'optimizer' : optimizer.state_dict(), + }, is_best) + + +def validate(val_loader, model, criterion, args, mapping_vector): + batch_time = AverageMeter('Time', ':6.3f') + top1 = AverageMeter('Acc@1', ':6.2f') + top5 = AverageMeter('Acc@5', ':6.2f') + progress = ProgressMeter( + len(val_loader), + [batch_time, top1, top5], + prefix='Test: ') + + # switch to evaluate mode + model.eval() + if args.use_train_statistics: + model.apply(use_train_statistics) + + nr_of_accepted_images = 0 + with torch.no_grad(): + end = time.time() + for i, (images, target) in enumerate(val_loader): + if args.gpu is not None: + images = images.cuda(args.gpu, non_blocking=True) + if torch.cuda.is_available(): + target = target.cuda(args.gpu, non_blocking=True) + + # compute output + output = model(images) + + # measure accuracy + # images are mapped in the accuracy function + acc = accuracy(output, target, topk=(1, 5), mapping_vector=mapping_vector) + top1.update(acc[0].item(), images.size(0)) + top5.update(acc[1].item(), images.size(0)) + + # measure elapsed time + batch_time.update(time.time() - end) + end = time.time() + + if i % args.print_freq == 0: + progress.display(i) + + # TODO: this should also be done with the ProgressMeter + print(' * Acc@1 {top1.avg:.3f} Acc@5 {top5.avg:.3f}' + .format(top1=top1, top5=top5)) + + return top1.avg + + +def save_checkpoint(state, is_best, filename='checkpoint.pth.tar'): + torch.save(state, filename) + if is_best: + shutil.copyfile(filename, 'model_best.pth.tar') + + +class AverageMeter(object): + """Computes and stores the average and current value""" + def __init__(self, name, fmt=':f'): + self.name = name + self.fmt = fmt + self.reset() + + def reset(self): + self.val = 0 + self.avg = 0 + self.sum = 0 + self.count = 0 + + def update(self, val, n=1): + self.val = val + self.sum += val * n + self.count += n + self.avg = self.sum / self.count + + def __str__(self): + fmtstr = '{name} {val' + self.fmt + '} ({avg' + self.fmt + '})' + return fmtstr.format(**self.__dict__) + + +class ProgressMeter(object): + def __init__(self, num_batches, meters, prefix=""): + self.batch_fmtstr = self._get_batch_fmtstr(num_batches) + self.meters = meters + self.prefix = prefix + + def display(self, batch): + entries = [self.prefix + self.batch_fmtstr.format(batch)] + entries += [str(meter) for meter in self.meters] + print('\t'.join(entries)) + + def _get_batch_fmtstr(self, num_batches): + num_digits = len(str(num_batches // 1)) + fmt = '{:' + str(num_digits) + 'd}' + return '[' + fmt + '/' + fmt.format(num_batches) + ']' + + +def adjust_learning_rate(optimizer, epoch, args): + """Sets the learning rate to the initial LR decayed by 10 every 30 epochs""" + lr = args.lr * (0.1 ** (epoch // 30)) + for param_group in optimizer.param_groups: + param_group['lr'] = lr + + +def accuracy(output, target, topk=(1,), mapping_vector=[]): + """Computes the accuracy over the k top predictions for the specified values of k""" + with torch.no_grad(): + maxk = max(topk) + batch_size = target.size(0) + + _, pred = output.topk(maxk, 1, True, True) + pred = pred.t() + + # map imagenet predictions for the top5 labels to visda classes + pred_label_visda = torch.zeros(pred.shape) + if torch.cuda.is_available(): + pred_label_visda = torch.zeros(pred.shape).cuda() + for k in range(maxk): + pred_label_visda[k] = map_imagenet_class_to_visda_class(pred[k], mapping_vector) + + correct = pred_label_visda.eq(target.view(1, -1).expand_as(pred_label_visda)) + + res = [] + for k in topk: + correct_k, _ = correct[:k].float().max(dim=0) + correct_k = correct_k.sum() + res.append(correct_k.mul_(100.0 / batch_size)) + return res + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/examples/imagenet_d/map_files.py b/examples/imagenet_d/map_files.py new file mode 100644 index 0000000..ff3b3ab --- /dev/null +++ b/examples/imagenet_d/map_files.py @@ -0,0 +1,393 @@ +import re +from imagenet_dict import map_dict +import torch +import os +import numpy as np +import glob + + +def get_imagenet_visda_mapping(visda_dir, map_dict): + + matching_names = dict() + matching_labels = dict() + map_dict_visda = dict() + count = 0 + + #if True: + label = 0 + visda = os.listdir(visda_dir) + for item in sorted(visda): + map_dict_visda[item] = label + item_split = item.split("_") + for ii in item_split: + for j in map_dict: + if re.search(r'\b' + ii + r'\b', map_dict[j]): + try: + matching_names[item].append([map_dict[j]]) + matching_labels[str(label)].append(j) + except: + matching_names[item] = list() + matching_names[item].append([map_dict[j]]) + + matching_labels[str(label)] = list() + matching_labels[str(label)].append(j) + label += 1 + + matching_names, matching_labels = clean_dataset(matching_names, matching_labels, map_dict_visda) + + return matching_names, matching_labels + + +def create_symlinks_and_get_imagenet_visda_mapping(visda_location, map_dict): + + # initial mapping and cleaning + matching_names, matching_labels = get_imagenet_visda_mapping(visda_location, map_dict) + + # some classes are ambiguous + ambiguous_matching = get_ambiguous_classes(matching_names) + + # create symlinks for all valid classes + if not os.path.exists('./visda_symlinks/'): + os.makedirs('./visda_symlinks/') + else: + print('Path ./visda_symlinks/ exists, skipping.') + target_folder = './visda_symlinks/' + visda_location.split('/')[-2] + '/' + if not os.path.exists(target_folder): + os.makedirs(target_folder) + else: + print('Path ', target_folder, ' exists, skipping.') + for folder in matching_names.keys(): + target_folder_class = os.path.join(target_folder, ambiguous_matching[folder]) + if not os.path.exists(target_folder_class): + os.makedirs(target_folder_class) + try: + allFiles_path_jpg = visda_location + folder + '/*.jpg' + allFiles_path_png = visda_location + folder + '/*.png' + allFiles_jpg = glob.glob(allFiles_path_jpg) + allFiles_png = glob.glob(allFiles_path_png) + allFiles = allFiles_jpg + allFiles_png + for file in allFiles: + newFile = target_folder_class + '/' + file.split('/')[-1] + os.symlink(file, newFile) + except FileExistsError: + pass + + # final mapping and cleaning with the symlinks + matching_names, matching_labels = get_imagenet_visda_mapping(target_folder, map_dict) + + mapping_vector = torch.zeros((1000)) + if torch.cuda.is_available(): + mapping_vector = torch.zeros((1000)).cuda() + mapping_vector -= 1 + mapping_vector_counts = dict() + for i in range(1000): + if i not in mapping_vector_counts.keys(): + mapping_vector_counts[i] = list() + for j in matching_labels: + if i in matching_labels[j]: + mapping_vector[i] = int(j) + mapping_vector_counts[i].append(j) + + # if classes are mapped to more than one class, we want to know about it: + for i in mapping_vector_counts.keys(): + if len(mapping_vector_counts[i]) > 1: + print(map_dict[i], i, 'is mapped to visda classes: ', mapping_vector_counts[i]) + + + return mapping_vector, matching_names, matching_labels + + +def clean_dataset(matching_names, matching_labels, map_dict_visda): + + # delete labels completely + del_list = ['mouse', 'fish', 'light_bulb', 'leaf', 'face', 'wine_glass', 'hockey_stick', 'star', 'see_saw', 'pencil', 'grass', 'fire_hydrant', 'brain', 'apple', 'river', 'rhinoceros', 'power_outlet', 'rain', 'pool', 'picture_frame', 'paper_clip', 'palm_tree', 'paint_can', 'mouth', 'The_Great_Wall_of_China', + 'garden', 'garden_hose', 'hand', 'house_plant', 'jacket', 'tree', 'sun', 'smiley_face', 'beach', 'diving_board', 'mountain'] + for item in del_list: + try: + del matching_names[item] + del matching_labels[str(map_dict_visda[item])] + except: + pass + # delete some imagenet labels + + del matching_names['cat'][5:] + del matching_labels[str(map_dict_visda['cat'])][5:] + + del matching_names['dog'][-1] + del matching_labels[str(map_dict_visda['dog'])][-1] + + del matching_names['pig'][0] + del matching_labels[str(map_dict_visda['pig'])][0] + + del matching_names['bear'][-1] + del matching_labels[str(map_dict_visda['bear'])][-1] + + del matching_names['horse'][0] + del matching_labels[str(map_dict_visda['horse'])][0] + + del matching_names['hot_air_balloon'][0:2] + del matching_labels[str(map_dict_visda['hot_air_balloon'])][0:2] + + del matching_names['hot_dog'][2:15] + del matching_labels[str(map_dict_visda['hot_dog'])][2:15] + + del matching_names['house'][0] + del matching_labels[str(map_dict_visda['house'])][0] + + del matching_names['ice_cream'][0] + del matching_labels[str(map_dict_visda['ice_cream'])][0] + + del matching_names['kangaroo'][1] + del matching_labels[str(map_dict_visda['kangaroo'])][1] + + del matching_names['washing_machine'][1:-1] + del matching_labels[str(map_dict_visda['washing_machine'])][1:-1] + + del matching_names['traffic_light'][1:-1] + del matching_labels[str(map_dict_visda['traffic_light'])][1:-1] + + del matching_names['table'][-1] + del matching_labels[str(map_dict_visda['table'])][-1] + + del matching_names['stop_sign'][0] + del matching_labels[str(map_dict_visda['stop_sign'])][0] + + del matching_names['spider'][-2] + del matching_labels[str(map_dict_visda['spider'])][-2] + + del matching_names['snake'][-2:] + del matching_labels[str(map_dict_visda['snake'])][-2:] + + del matching_names['sleeping_bag'][1] + del matching_labels[str(map_dict_visda['sleeping_bag'])][1] + + del matching_names['sleeping_bag'][1] # not a bug that this comes twice + del matching_labels[str(map_dict_visda['sleeping_bag'])][1] + + del matching_names['sheep'][0] + del matching_labels[str(map_dict_visda['sheep'])][0] + + del matching_names['sea_turtle'][:-4] + del matching_labels[str(map_dict_visda['sea_turtle'])][:-4] + + del matching_names['squirrel'][1] + del matching_labels[str(map_dict_visda['squirrel'])][1] + + del matching_names['lion'][0] + del matching_labels[str(map_dict_visda['lion'])][0] + + del matching_names['bee'][0] + del matching_labels[str(map_dict_visda['bee'])][0] + + del matching_names['bee'][-1] + del matching_labels[str(map_dict_visda['bee'])][-1] + + del matching_names['soccer_ball'][1:] + del matching_labels[str(map_dict_visda['soccer_ball'])][1:] + + del matching_names['tractor'][1] + del matching_labels[str(map_dict_visda['tractor'])][1] + + del matching_names['oven'][-1] + del matching_labels[str(map_dict_visda['oven'])][-1] + + del matching_names['piano'][0] + del matching_labels[str(map_dict_visda['piano'])][0] + + del matching_names['barn'][0] + del matching_labels[str(map_dict_visda['barn'])][0] + + del matching_names['tiger'][0:2] + del matching_labels[str(map_dict_visda['tiger'])][0:2] + + del matching_names['tiger'][-1] + del matching_labels[str(map_dict_visda['tiger'])][-1] + + del matching_names['monkey'][0] + del matching_labels[str(map_dict_visda['monkey'])][0] + + del matching_names['bear'][-2:] + del matching_labels[str(map_dict_visda['bear'])][-2:] + + del matching_names['car'][2] + del matching_labels[str(map_dict_visda['car'])][2] + + del matching_names['car'][-1] + del matching_labels[str(map_dict_visda['car'])][-1] + + # add items: + matching_names['airplane'] = [['warplane, military plane'], ['airliner'], ['airship, dirigible']] + matching_labels[str(map_dict_visda['airplane'])] = [895, 404, 405] + + matching_names['t-shirt'] = ['jersey, T-shirt, tee shirt'] + matching_labels[str(map_dict_visda['t-shirt'])] = [610] + + matching_names['teddy-bear'] = ['teddy, teddy bear'] + matching_labels[str(map_dict_visda['teddy-bear'])] = [850] + + matching_names['bicycle'].append(['mountain bike, all-terrain bike, off-roader']) + matching_labels[str(map_dict_visda['bicycle'])].extend([671]) + + matching_names['bus'].append(['trolleybus, trolley coach, trackless trolley']) + matching_labels[str(map_dict_visda['bus'])].extend([874]) + + matching_names['bus'].append(['minibus']) + matching_labels[str(map_dict_visda['bus'])].extend([654]) + + matching_names['frog'].append(['bullfrog, Rana catesbeiana']) + matching_labels[str(map_dict_visda['frog'])].extend([30]) + + matching_names['rabbit'].append(['hare']) + matching_labels[str(map_dict_visda['rabbit'])].extend([331]) + + matching_names['sea_turtle'].append(['terrapin']) + matching_labels[str(map_dict_visda['sea_turtle'])].extend([36]) + + matching_names['whale'].append(['dugong, Dugong dugon']) + matching_labels[str(map_dict_visda['whale'])].extend([149]) + + matching_names['pig'].append(['wild boar, boar, Sus scrofa']) + matching_labels[str(map_dict_visda['pig'])].extend([342]) + + matching_names['pig'].append(['warthog']) + matching_labels[str(map_dict_visda['pig'])].extend([343]) + + matching_names['pig'].append(['piggy bank, penny bank']) + matching_labels[str(map_dict_visda['pig'])].extend([719]) + + matching_names['car'].append(['police van, police wagon, paddy wagon, patrol wagon, wagon, black Maria']) + matching_labels[str(map_dict_visda['car'])].extend([734]) + + # add dogs to dog label: + matching_labels[str(map_dict_visda['dog'])].extend(list(np.arange(151, 269))) + for i in np.arange(151, 269): + if map_dict[i] not in matching_names['dog']: + matching_names['dog'].append([map_dict[i]]) + + # add more butterflies: + matching_labels[str(map_dict_visda['butterfly'])].extend(list(np.arange(320, 322))) + for i in np.arange(320, 322): + if map_dict[i] not in matching_names['butterfly']: + matching_names['butterfly'].append([map_dict[i]]) + + # add more mosquitos: + matching_labels[str(map_dict_visda['mosquito'])].extend(list(np.arange(318, 320))) + for i in np.arange(318, 320): + if map_dict[i] not in matching_names['mosquito']: + matching_names['mosquito'].append([map_dict[i]]) + + # add more monkeys: + matching_labels[str(map_dict_visda['monkey'])].extend(list(np.arange(365, 385))) + for i in np.arange(365, 385): + if map_dict[i] not in matching_names['monkey']: + matching_names['monkey'].append([map_dict[i]]) + + # add more snakes: + matching_labels[str(map_dict_visda['snake'])].extend(list(np.arange(52, 69))) + for i in np.arange(52, 69): + if map_dict[i] not in matching_names['snake']: + matching_names['snake'].append([map_dict[i]]) + + # add more spiders: + matching_labels[str(map_dict_visda['spider'])].extend(list(np.arange(72, 79))) + for i in np.arange(72, 79): + if map_dict[i] not in matching_names['spider']: + matching_names['spider'].append([map_dict[i]]) + + matching_names['spider'].append(['harvestman, daddy longlegs, Phalangium opilio']) + matching_labels[str(map_dict_visda['spider'])].extend([70]) + + # add more birds: + matching_labels[str(map_dict_visda['bird'])].extend(list(np.arange(80, 101))) + for i in np.arange(80, 101): + if map_dict[i] not in matching_names['bird']: + matching_names['bird'].append([map_dict[i]]) + + # add more birds: + matching_labels[str(map_dict_visda['bird'])].extend(list(np.arange(7, 24))) + for i in np.arange(7, 24): + if map_dict[i] not in matching_names['bird']: + matching_names['bird'].append([map_dict[i]]) + + # remove dublicates from labels: + for item in matching_labels: + tmp = set(matching_labels[item]) + matching_labels[item] = list(tmp) + + + return matching_names, matching_labels + + +def map_imagenet_class_to_visda_class(pred_label, mapping_vector): + + pred_label_visda_tensor = mapping_vector[pred_label].long() + + return pred_label_visda_tensor + + +def map_visda_class_to_imagenet_class(pred_label, mapping_vector): + + pred_label_visda_tensor = mapping_vector[pred_label].long() + + return pred_label_visda_tensor + + +def get_ambiguous_classes(matching_names): + + # these are the ambiguous classes + ambiguous_classes = [['alarm_clock', 'clock'], ['baseball', 'baseball_bat'], ['bed', 'couch'], ['car', 'police_car'], + ['coffee_cup', 'cup', 'mug'], ['computer', 'keyboard', 'laptop'], ['ice_cream', 'lollipop', 'popsicle'], + ['bus', 'school_bus'], ['truck', 'pickup_truck', 'firetruck', 'van'], ['bird', 'swan'], ['hot_tub', 'bathtub'], + ['telephone', 'cell_phone'], ['ceiling_fan', 'fan']] + + ambiguous_matching = {} + + ambiguous_matching['telephone'] = 'telephone' + ambiguous_matching['cell_phone'] = 'telephone' + + ambiguous_matching['fan'] = 'fan' + ambiguous_matching['ceiling_fan'] = 'fan' + + ambiguous_matching['clock'] = 'clock' + ambiguous_matching['alarm_clock'] = 'clock' + + ambiguous_matching['bathtub'] = 'bathtub' + ambiguous_matching['hot_tub'] = 'bathtub' + + ambiguous_matching['baseball'] = 'baseball' + ambiguous_matching['baseball_bat'] = 'baseball' + + ambiguous_matching['bed'] = 'bed' + ambiguous_matching['couch'] = 'bed' + + ambiguous_matching['car'] = 'car' + ambiguous_matching['police_car'] = 'car' + + ambiguous_matching['coffee_cup'] = 'cup' + ambiguous_matching['cup'] = 'cup' + ambiguous_matching['mug'] = 'cup' + + ambiguous_matching['computer'] = 'computer' + ambiguous_matching['keyboard'] = 'computer' + ambiguous_matching['laptop'] = 'computer' + + ambiguous_matching['ice_cream'] = 'ice_cream' + ambiguous_matching['lollipop'] = 'ice_cream' + ambiguous_matching['popsicle'] = 'ice_cream' + + ambiguous_matching['bus'] = 'bus' + ambiguous_matching['school_bus'] = 'bus' + + ambiguous_matching['truck'] = 'truck' + ambiguous_matching['pickup_truck'] = 'truck' + ambiguous_matching['van'] = 'truck' + ambiguous_matching['firetruck'] = 'truck' + + ambiguous_matching['bird'] = 'bird' + ambiguous_matching['swan'] = 'bird' + + for key in matching_names.keys(): + if key not in ambiguous_matching: + ambiguous_matching[key] = key + + return ambiguous_matching \ No newline at end of file diff --git a/examples/imagenet_d/run.sh b/examples/imagenet_d/run.sh new file mode 100644 index 0000000..9693fd3 --- /dev/null +++ b/examples/imagenet_d/run.sh @@ -0,0 +1,36 @@ +# Clipart + +python3 main.py /gpfs01/bethge/data/visda-2019/clipart/ --pretrained -b 1024 --evaluate --workers 25 --use-train-statistics >logs/clipart_train.log + +python3 main.py /gpfs01/bethge/data/visda-2019/clipart/ --pretrained -b 1024 --evaluate --workers 25 >logs/clipart_eval.log + + +# infograph + +python3 main.py /gpfs01/bethge/data/visda-2019/infograph/ --pretrained -b 1024 --evaluate --workers 25 --use-train-statistics >logs/infograph_train.log + +python3 main.py /gpfs01/bethge/data/visda-2019/infograph/ --pretrained -b 1024 --evaluate --workers 25 >logs/infograph_eval.log + +# painting + +python3 main.py /gpfs01/bethge/data/visda-2019/painting/ --pretrained -b 1024 --evaluate --workers 25 --use-train-statistics >logs/painting_train.log + +python3 main.py /gpfs01/bethge/data/visda-2019/painting/ --pretrained -b 1024 --evaluate --workers 25 >logs/painting_eval.log + +# quickdraw + +python3 main.py /gpfs01/bethge/data/visda-2019/quickdraw/ --pretrained -b 1024 --evaluate --workers 20 --use-train-statistics >logs/quickdraw_train.log + +python3 main.py /gpfs01/bethge/data/visda-2019/quickdraw/ --pretrained -b 1024 --evaluate --workers 20 >logs/quickdraw_eval.log + +# real + +python3 main.py /gpfs01/bethge/data/visda-2019/real/ --pretrained -b 1024 --evaluate --workers 20 --use-train-statistics >logs/real_train.log + +python3 main.py /gpfs01/bethge/data/visda-2019/real/ --pretrained -b 1024 --evaluate --workers 20 >logs/real_eval.log + +# sketch + +python3 main.py /gpfs01/bethge/data/visda-2019/sketch/ --pretrained -b 1024 --evaluate --workers 20 --use-train-statistics >logs/sketch_train.log + +python3 main.py /gpfs01/bethge/data/visda-2019/sketch/ --pretrained -b 1024 --evaluate --workers 20 >logs/sketch_eval.log \ No newline at end of file diff --git a/examples/imagenet_d/show_mappings.ipynb b/examples/imagenet_d/show_mappings.ipynb new file mode 100644 index 0000000..68bafb2 --- /dev/null +++ b/examples/imagenet_d/show_mappings.ipynb @@ -0,0 +1,1005 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from map_files import *" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Path ./visda_symlinks/ exists, skipping.\n", + "Path ./visda_symlinks/clipart/ exists, skipping.\n" + ] + } + ], + "source": [ + "mapping_vector, matching_names, matching_labels = create_symlinks_and_get_imagenet_visda_mapping('/gpfs01/bethge/data/visda-2019/clipart/', map_dict)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "aircraft_carrier : [['aircraft carrier, carrier, flattop, attack aircraft carrier'], ['aircraft carrier, carrier, flattop, attack aircraft carrier']]\n", + "ambulance : [['ambulance']]\n", + "ant : [['ant, emmet, pismire']]\n", + "backpack : [['backpack, back pack, knapsack, packsack, rucksack, haversack']]\n", + "banana : [['banana']]\n", + "barn : [['barn']]\n", + "baseball : [['baseball'], ['ballplayer, baseball player']]\n", + "basket : [['shopping basket']]\n", + "basketball : [['basketball']]\n", + "bathtub : [['bathtub, bathing tub, bath, tub']]\n", + "bear : [['koala, koala bear, kangaroo bear, native bear, Phascolarctos cinereus'], ['brown bear, bruin, Ursus arctos'], ['American black bear, black bear, Ursus americanus, Euarctos americanus'], ['ice bear, polar bear, Ursus Maritimus, Thalarctos maritimus'], ['sloth bear, Melursus ursinus, Ursus ursinus']]\n", + "bed : [['studio couch, day bed']]\n", + "bee : [['bee']]\n", + "belt : [['seat belt, seatbelt']]\n", + "bench : [['park bench']]\n", + "bicycle : [['bicycle-built-for-two, tandem bicycle, tandem'], ['mountain bike, all-terrain bike, off-roader']]\n", + "binoculars : [['binoculars, field glasses, opera glasses']]\n", + "bird : [['indigo bunting, indigo finch, indigo bird, Passerina cyanea'], ['black grouse'], ['ptarmigan'], ['ruffed grouse, partridge, Bonasa umbellus'], ['prairie chicken, prairie grouse, prairie fowl'], ['peacock'], ['quail'], ['partridge'], ['African grey, African gray, Psittacus erithacus'], ['macaw'], ['sulphur-crested cockatoo, Kakatoe galerita, Cacatua galerita'], ['lorikeet'], ['coucal'], ['bee eater'], ['hornbill'], ['hummingbird'], ['jacamar'], ['toucan'], ['drake'], ['red-breasted merganser, Mergus serrator'], ['goose'], ['black swan, Cygnus atratus'], ['cock'], ['hen'], ['ostrich, Struthio camelus'], ['brambling, Fringilla montifringilla'], ['goldfinch, Carduelis carduelis'], ['house finch, linnet, Carpodacus mexicanus'], ['junco, snowbird'], ['indigo bunting, indigo finch, indigo bird, Passerina cyanea'], ['robin, American robin, Turdus migratorius'], ['bulbul'], ['jay'], ['magpie'], ['chickadee'], ['water ouzel, dipper'], ['kite'], ['bald eagle, American eagle, Haliaeetus leucocephalus'], ['vulture']]\n", + "book : [['comic book'], ['book jacket, dust cover, dust jacket, dust wrapper']]\n", + "bottlecap : [['bottlecap']]\n", + "bowtie : [['bow tie, bow-tie, bowtie']]\n", + "bridge : [['steel arch bridge'], ['suspension bridge']]\n", + "broccoli : [['broccoli']]\n", + "broom : [['broom']]\n", + "bucket : [['bucket, pail']]\n", + "bus : [['school bus'], ['trolleybus, trolley coach, trackless trolley'], ['minibus']]\n", + "butterfly : [['ringlet, ringlet butterfly'], ['monarch, monarch butterfly, milkweed butterfly, Danaus plexippus'], ['cabbage butterfly'], ['sulphur butterfly, sulfur butterfly'], ['lycaenid, lycaenid butterfly'], ['damselfly'], ['admiral']]\n", + "camel : [['Arabian camel, dromedary, Camelus dromedarius']]\n", + "camera : [['Polaroid camera, Polaroid Land camera'], ['reflex camera']]\n", + "candle : [['candle, taper, wax light']]\n", + "cannon : [['cannon']]\n", + "canoe : [['canoe']]\n", + "car : [['beach wagon, station wagon, wagon, estate car, beach waggon, station waggon, waggon'], ['car mirror'], ['freight car'], ['passenger car, coach, carriage'], ['racer, race car, racing car'], ['sports car, sport car'], ['streetcar, tram, tramcar, trolley, trolley car'], ['police van, police wagon, paddy wagon, patrol wagon, wagon, black Maria']]\n", + "castle : [['castle']]\n", + "cat : [['tabby, tabby cat'], ['tiger cat'], ['Persian cat'], ['Siamese cat, Siamese'], ['Egyptian cat']]\n", + "cello : [['cello, violoncello']]\n", + "chair : [['barber chair'], ['folding chair'], ['rocking chair, rocker']]\n", + "church : [['church, church building']]\n", + "clock : [['analog clock'], ['digital clock'], ['wall clock']]\n", + "compass : [['magnetic compass']]\n", + "computer : [['computer keyboard, keypad'], ['desktop computer'], ['hand-held computer, hand-held microcomputer'], ['laptop, laptop computer'], ['mouse, computer mouse'], ['notebook, notebook computer']]\n", + "crab : [['Dungeness crab, Cancer magister'], ['rock crab, Cancer irroratus'], ['fiddler crab'], ['king crab, Alaska crab, Alaskan king crab, Alaska king crab, Paralithodes camtschatica'], ['hermit crab']]\n", + "crocodile : [['African crocodile, Nile crocodile, Crocodylus niloticus']]\n", + "cruise_ship : [['container ship, containership, container vessel'], ['pirate, pirate ship']]\n", + "cup : [['measuring cup'], ['cup']]\n", + "dishwasher : [['dishwasher, dish washer, dishwashing machine']]\n", + "dog : [['Maltese dog, Maltese terrier, Maltese'], ['Tibetan terrier, chrysanthemum dog'], ['Shetland sheepdog, Shetland sheep dog, Shetland'], ['German shepherd, German shepherd dog, German police dog, alsatian'], ['Greater Swiss Mountain dog'], ['Bernese mountain dog'], ['Eskimo dog, husky'], ['dalmatian, coach dog, carriage dog'], ['affenpinscher, monkey pinscher, monkey dog'], ['pug, pug-dog'], ['Newfoundland, Newfoundland dog'], ['African hunting dog, hyena dog, Cape hunting dog, Lycaon pictus'], ['dogsled, dog sled, dog sleigh'], ['Chihuahua'], ['Japanese spaniel'], ['Maltese dog, Maltese terrier, Maltese'], ['Pekinese, Pekingese, Peke'], ['Shih-Tzu'], ['Blenheim spaniel'], ['papillon'], ['toy terrier'], ['Rhodesian ridgeback'], ['Afghan hound, Afghan'], ['basset, basset hound'], ['beagle'], ['bloodhound, sleuthhound'], ['bluetick'], ['black-and-tan coonhound'], ['Walker hound, Walker foxhound'], ['English foxhound'], ['redbone'], ['borzoi, Russian wolfhound'], ['Irish wolfhound'], ['Italian greyhound'], ['whippet'], ['Ibizan hound, Ibizan Podenco'], ['Norwegian elkhound, elkhound'], ['otterhound, otter hound'], ['Saluki, gazelle hound'], ['Scottish deerhound, deerhound'], ['Weimaraner'], ['Staffordshire bullterrier, Staffordshire bull terrier'], ['American Staffordshire terrier, Staffordshire terrier, American pit bull terrier, pit bull terrier'], ['Bedlington terrier'], ['Border terrier'], ['Kerry blue terrier'], ['Irish terrier'], ['Norfolk terrier'], ['Norwich terrier'], ['Yorkshire terrier'], ['wire-haired fox terrier'], ['Lakeland terrier'], ['Sealyham terrier, Sealyham'], ['Airedale, Airedale terrier'], ['cairn, cairn terrier'], ['Australian terrier'], ['Dandie Dinmont, Dandie Dinmont terrier'], ['Boston bull, Boston terrier'], ['miniature schnauzer'], ['giant schnauzer'], ['standard schnauzer'], ['Scotch terrier, Scottish terrier, Scottie'], ['Tibetan terrier, chrysanthemum dog'], ['silky terrier, Sydney silky'], ['soft-coated wheaten terrier'], ['West Highland white terrier'], ['Lhasa, Lhasa apso'], ['flat-coated retriever'], ['curly-coated retriever'], ['golden retriever'], ['Labrador retriever'], ['Chesapeake Bay retriever'], ['German short-haired pointer'], ['vizsla, Hungarian pointer'], ['English setter'], ['Irish setter, red setter'], ['Gordon setter'], ['Brittany spaniel'], ['clumber, clumber spaniel'], ['English springer, English springer spaniel'], ['Welsh springer spaniel'], ['cocker spaniel, English cocker spaniel, cocker'], ['Sussex spaniel'], ['Irish water spaniel'], ['kuvasz'], ['schipperke'], ['groenendael'], ['malinois'], ['briard'], ['kelpie'], ['komondor'], ['Old English sheepdog, bobtail'], ['Shetland sheepdog, Shetland sheep dog, Shetland'], ['collie'], ['Border collie'], ['Bouvier des Flandres, Bouviers des Flandres'], ['Rottweiler'], ['German shepherd, German shepherd dog, German police dog, alsatian'], ['Doberman, Doberman pinscher'], ['miniature pinscher'], ['Greater Swiss Mountain dog'], ['Bernese mountain dog'], ['Appenzeller'], ['EntleBucher'], ['boxer'], ['bull mastiff'], ['Tibetan mastiff'], ['French bulldog'], ['Great Dane'], ['Saint Bernard, St Bernard'], ['Eskimo dog, husky'], ['malamute, malemute, Alaskan malamute'], ['Siberian husky'], ['dalmatian, coach dog, carriage dog'], ['affenpinscher, monkey pinscher, monkey dog'], ['basenji'], ['pug, pug-dog'], ['Leonberg'], ['Newfoundland, Newfoundland dog'], ['Great Pyrenees'], ['Samoyed, Samoyede'], ['Pomeranian'], ['chow, chow chow'], ['keeshond'], ['Brabancon griffon'], ['Pembroke, Pembroke Welsh corgi'], ['Cardigan, Cardigan Welsh corgi'], ['toy poodle'], ['miniature poodle'], ['standard poodle'], ['Mexican hairless']]\n", + "door : [['sliding door']]\n", + "dragon : [['Komodo dragon, Komodo lizard, dragon lizard, giant lizard, Varanus komodoensis']]\n", + "drill : [['power drill']]\n", + "duck : [['platypus, duckbill, duckbilled platypus, duck-billed platypus, Ornithorhynchus anatinus']]\n", + "dumbbell : [['dumbbell']]\n", + "ear : [['ear, spike, capitulum']]\n", + "elephant : [['Indian elephant, Elephas maximus'], ['African elephant, Loxodonta africana']]\n", + "envelope : [['envelope']]\n", + "eraser : [['rubber eraser, rubber, pencil eraser']]\n", + "fan : [['electric fan, blower']]\n", + "feather : [['feather boa, boa']]\n", + "fence : [['chainlink fence'], ['picket fence, paling'], ['worm fence, snake fence, snake-rail fence, Virginia fence']]\n", + "flamingo : [['flamingo']]\n", + "floor_lamp : [['lampshade, lamp shade'], ['table lamp']]\n", + "frog : [['tree frog, tree-frog'], ['tailed frog, bell toad, ribbed toad, tailed toad, Ascaphus trui'], ['bullfrog, Rana catesbeiana']]\n", + "frying_pan : [['frying pan, frypan, skillet'], ['frying pan, frypan, skillet']]\n", + "golf_club : [['golf ball'], ['golfcart, golf cart']]\n", + "guitar : [['acoustic guitar'], ['electric guitar']]\n", + "hammer : [['hammer']]\n", + "harp : [['harmonica, mouth organ, harp, mouth harp'], ['harp']]\n", + "hat : [['cowboy hat, ten-gallon hat']]\n", + "hedgehog : [['porcupine, hedgehog']]\n", + "helmet : [['crash helmet'], ['football helmet'], ['gasmask, respirator, gas helmet']]\n", + "hockey_puck : [['puck, hockey puck'], ['puck, hockey puck']]\n", + "horse : [['horse cart, horse-cart'], ['buckeye, horse chestnut, conker']]\n", + "hot_air_balloon : [['balloon']]\n", + "hot_dog : [['hot pot, hotpot'], ['hotdog, hot dog, red hot'], ['hotdog, hot dog, red hot']]\n", + "hourglass : [['hourglass']]\n", + "house : [['apiary, bee house'], ['cinema, movie theater, movie theatre, movie house, picture palace'], ['prison, prison house'], ['restaurant, eating house, eating place, eatery']]\n", + "ice_cream : [['ice cream, icecream'], ['ice lolly, lolly, lollipop, popsicle'], ['ice cream, icecream']]\n", + "kangaroo : [['wallaby, brush kangaroo']]\n", + "knee : [['knee pad']]\n", + "knife : [['letter opener, paper knife, paperknife']]\n", + "lantern : [[\"jack-o'-lantern\"]]\n", + "lighter : [['lighter, light, igniter, ignitor']]\n", + "lighthouse : [['beacon, lighthouse, beacon light, pharos']]\n", + "lion : [['cougar, puma, catamount, mountain lion, painter, panther, Felis concolor'], ['lion, king of beasts, Panthera leo']]\n", + "lipstick : [['lipstick, lip rouge']]\n", + "lobster : [['American lobster, Northern lobster, Maine lobster, Homarus americanus'], ['spiny lobster, langouste, rock lobster, crawfish, crayfish, sea crawfish']]\n", + "mailbox : [['mailbox, letter box']]\n", + "microphone : [['microphone, mike']]\n", + "microwave : [['microwave, microwave oven']]\n", + "monkey : [['guenon, guenon monkey'], ['patas, hussar monkey, Erythrocebus patas'], ['colobus, colobus monkey'], ['proboscis monkey, Nasalis larvatus'], ['howler monkey, howler'], ['titi, titi monkey'], ['spider monkey, Ateles geoffroyi'], ['squirrel monkey, Saimiri sciureus'], ['orangutan, orang, orangutang, Pongo pygmaeus'], ['gorilla, Gorilla gorilla'], ['chimpanzee, chimp, Pan troglodytes'], ['gibbon, Hylobates lar'], ['siamang, Hylobates syndactylus, Symphalangus syndactylus'], ['guenon, guenon monkey'], ['patas, hussar monkey, Erythrocebus patas'], ['baboon'], ['macaque'], ['langur'], ['colobus, colobus monkey'], ['proboscis monkey, Nasalis larvatus'], ['marmoset'], ['capuchin, ringtail, Cebus capucinus'], ['howler monkey, howler'], ['titi, titi monkey'], ['spider monkey, Ateles geoffroyi'], ['squirrel monkey, Saimiri sciureus'], ['Madagascar cat, ring-tailed lemur, Lemur catta'], ['indri, indris, Indri indri, Indri brevicaudatus']]\n", + "mosquito : [[\"dragonfly, darning needle, devil's darning needle, sewing needle, snake feeder, snake doctor, mosquito hawk, skeeter hawk\"], ['mosquito net'], ['lacewing, lacewing fly'], [\"dragonfly, darning needle, devil's darning needle, sewing needle, snake feeder, snake doctor, mosquito hawk, skeeter hawk\"]]\n", + "mushroom : [['mushroom']]\n", + "nail : [['nail']]\n", + "necklace : [['necklace']]\n", + "ocean : [['liner, ocean liner']]\n", + "oven : [['Dutch oven']]\n", + "owl : [['great grey owl, great gray owl, Strix nebulosa']]\n", + "paintbrush : [['paintbrush']]\n", + "panda : [['lesser panda, red panda, panda, bear cat, cat bear, Ailurus fulgens'], ['giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca']]\n", + "parachute : [['parachute, chute']]\n", + "penguin : [['king penguin, Aptenodytes patagonica']]\n", + "piano : [['grand piano, grand'], ['upright, upright piano']]\n", + "pig : [['hog, pig, grunter, squealer, Sus scrofa'], ['wild boar, boar, Sus scrofa'], ['warthog'], ['piggy bank, penny bank']]\n", + "pillow : [['pillow']]\n", + "pineapple : [['pineapple, ananas']]\n", + "pizza : [['pizza, pizza pie']]\n", + "potato : [['ocarina, sweet potato'], ['mashed potato']]\n", + "purse : [['purse']]\n", + "rabbit : [['wood rabbit, cottontail, cottontail rabbit'], ['Angora, Angora rabbit'], ['hare']]\n", + "radio : [['radio, wireless'], ['radio telescope, radio reflector']]\n", + "remote_control : [['remote control, remote'], ['remote control, remote']]\n", + "rifle : [['assault rifle, assault gun'], ['rifle']]\n", + "saw : [['chain saw, chainsaw']]\n", + "saxophone : [['sax, saxophone']]\n", + "scorpion : [['scorpion']]\n", + "screwdriver : [['screwdriver']]\n", + "sea_turtle : [['loggerhead, loggerhead turtle, Caretta caretta'], ['leatherback turtle, leatherback, leathery turtle, Dermochelys coriacea'], ['mud turtle'], ['box turtle, box tortoise'], ['terrapin']]\n", + "shark : [['great white shark, white shark, man-eater, man-eating shark, Carcharodon carcharias'], ['tiger shark, Galeocerdo cuvieri'], ['hammerhead, hammerhead shark']]\n", + "sheep : [['bighorn, bighorn sheep, cimarron, Rocky Mountain bighorn, Rocky Mountain sheep, Ovis canadensis']]\n", + "shoe : [['running shoe'], ['shoe shop, shoe-shop, shoe store']]\n", + "shovel : [['shovel']]\n", + "sleeping_bag : [['sleeping bag'], ['sleeping bag']]\n", + "snail : [['snail']]\n", + "snake : [['thunder snake, worm snake, Carphophis amoenus'], ['ringneck snake, ring-necked snake, ring snake'], ['hognose snake, puff adder, sand viper'], ['green snake, grass snake'], ['king snake, kingsnake'], ['garter snake, grass snake'], ['water snake'], ['vine snake'], ['night snake, Hypsiglena torquata'], ['rock python, rock snake, Python sebae'], ['sea snake'], ['thunder snake, worm snake, Carphophis amoenus'], ['ringneck snake, ring-necked snake, ring snake'], ['hognose snake, puff adder, sand viper'], ['green snake, grass snake'], ['king snake, kingsnake'], ['garter snake, grass snake'], ['water snake'], ['vine snake'], ['night snake, Hypsiglena torquata'], ['boa constrictor, Constrictor constrictor'], ['rock python, rock snake, Python sebae'], ['Indian cobra, Naja naja'], ['green mamba'], ['sea snake'], ['horned viper, cerastes, sand viper, horned asp, Cerastes cornutus'], ['diamondback, diamondback rattlesnake, Crotalus adamanteus'], ['sidewinder, horned rattlesnake, Crotalus cerastes']]\n", + "snorkel : [['snorkel']]\n", + "soccer_ball : [['soccer ball']]\n", + "sock : [['sock']]\n", + "speedboat : [['speedboat']]\n", + "spider : [['black and gold garden spider, Argiope aurantia'], ['barn spider, Araneus cavaticus'], ['garden spider, Aranea diademata'], ['wolf spider, hunting spider'], [\"spider web, spider's web\"], ['black and gold garden spider, Argiope aurantia'], ['barn spider, Araneus cavaticus'], ['garden spider, Aranea diademata'], ['black widow, Latrodectus mactans'], ['tarantula'], ['wolf spider, hunting spider'], ['tick'], ['harvestman, daddy longlegs, Phalangium opilio']]\n", + "spoon : [['wooden spoon']]\n", + "squirrel : [['fox squirrel, eastern fox squirrel, Sciurus niger']]\n", + "stethoscope : [['stethoscope']]\n", + "stop_sign : [['street sign']]\n", + "stove : [['stove']]\n", + "strawberry : [['strawberry']]\n", + "submarine : [['submarine, pigboat, sub, U-boat']]\n", + "swing_set : [['swing']]\n", + "syringe : [['syringe']]\n", + "table : [['dining table, board'], ['pool table, billiard table, snooker table']]\n", + "teapot : [['teapot']]\n", + "telephone : [['cellular telephone, cellular phone, cellphone, cell, mobile phone'], ['dial telephone, dial phone']]\n", + "television : [['television, television system']]\n", + "tennis_racquet : [['tennis ball'], ['racket, racquet']]\n", + "tent : [['mountain tent']]\n", + "tiger : [['tiger, Panthera tigris']]\n", + "toaster : [['toaster']]\n", + "toilet : [['toilet seat'], ['toilet tissue, toilet paper, bathroom tissue']]\n", + "tractor : [['tractor']]\n", + "traffic_light : [['traffic light, traffic signal, stoplight'], ['traffic light, traffic signal, stoplight']]\n", + "train : [['bullet train, bullet']]\n", + "trombone : [['trombone']]\n", + "truck : [['fire engine, fire truck'], ['garbage truck, dustcart'], ['pickup, pickup truck'], ['tow truck, tow car, wrecker'], ['trailer truck, tractor trailer, trucking rig, rig, articulated lorry, semi']]\n", + "trumpet : [['cornet, horn, trumpet, trump']]\n", + "umbrella : [['umbrella']]\n", + "vase : [['vase']]\n", + "violin : [['violin, fiddle']]\n", + "washing_machine : [['washer, automatic washer, washing machine'], ['washer, automatic washer, washing machine']]\n", + "whale : [['grey whale, gray whale, devilfish, Eschrichtius gibbosus, Eschrichtius robustus'], ['killer whale, killer, orca, grampus, sea wolf, Orcinus orca'], ['dugong, Dugong dugon']]\n", + "wheel : [['car wheel'], ['paddlewheel, paddle wheel'], [\"potter's wheel\"]]\n", + "wine_bottle : [['wine bottle'], ['red wine'], ['beer bottle'], ['corkscrew, bottle screw'], ['pill bottle'], ['pop bottle, soda bottle'], ['water bottle'], ['wine bottle']]\n", + "zebra : [['zebra']]\n", + "airplane : [['warplane, military plane'], ['airliner'], ['airship, dirigible']]\n", + "t-shirt : ['jersey, T-shirt, tee shirt']\n", + "teddy-bear : ['teddy, teddy bear']\n" + ] + } + ], + "source": [ + "# Structure: DICT:\n", + "# Visda class maps to list of ImageNet clases\n", + "\n", + "for key in matching_names:\n", + " print(key, ': ', matching_names[key])" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 The_Eiffel_Tower\n", + "1 The_Great_Wall_of_China\n", + "2 The_Mona_Lisa\n", + "7 angel\n", + "8 animal_migration\n", + "10 anvil\n", + "11 apple\n", + "12 arm\n", + "13 asparagus\n", + "14 axe\n", + "17 bandage\n", + "23 bat\n", + "25 beach\n", + "27 beard\n", + "35 birthday_cake\n", + "36 blackberry\n", + "37 blueberry\n", + "39 boomerang\n", + "42 bracelet\n", + "43 brain\n", + "44 bread\n", + "49 bulldozer\n", + "51 bush\n", + "53 cactus\n", + "54 cake\n", + "55 calculator\n", + "56 calendar\n", + "59 camouflage\n", + "60 campfire\n", + "65 carrot\n", + "68 ceiling_fan\n", + "69 cell_phone\n", + "72 chandelier\n", + "74 circle\n", + "75 clarinet\n", + "77 cloud\n", + "81 cookie\n", + "82 cooler\n", + "84 cow\n", + "86 crayon\n", + "88 crown\n", + "91 diamond\n", + "93 diving_board\n", + "95 dolphin\n", + "96 donut\n", + "99 dresser\n", + "101 drums\n", + "105 elbow\n", + "109 eye\n", + "110 eyeglasses\n", + "111 face\n", + "115 finger\n", + "116 fire_hydrant\n", + "117 fireplace\n", + "119 fish\n", + "121 flashlight\n", + "122 flip_flops\n", + "124 flower\n", + "125 flying_saucer\n", + "126 foot\n", + "127 fork\n", + "130 garden\n", + "131 garden_hose\n", + "132 giraffe\n", + "133 goatee\n", + "135 grapes\n", + "136 grass\n", + "138 hamburger\n", + "140 hand\n", + "143 headphones\n", + "145 helicopter\n", + "147 hexagon\n", + "149 hockey_stick\n", + "151 hospital\n", + "154 hot_tub\n", + "157 house_plant\n", + "158 hurricane\n", + "160 jacket\n", + "161 jail\n", + "163 key\n", + "167 ladder\n", + "170 leaf\n", + "171 leg\n", + "172 light_bulb\n", + "175 lightning\n", + "176 line\n", + "182 map\n", + "183 marker\n", + "184 matches\n", + "185 megaphone\n", + "186 mermaid\n", + "190 moon\n", + "192 motorbike\n", + "193 mountain\n", + "194 mouse\n", + "195 moustache\n", + "196 mouth\n", + "201 nose\n", + "203 octagon\n", + "204 octopus\n", + "205 onion\n", + "208 paint_can\n", + "210 palm_tree\n", + "212 pants\n", + "213 paper_clip\n", + "215 parrot\n", + "216 passport\n", + "217 peanut\n", + "218 pear\n", + "219 peas\n", + "220 pencil\n", + "224 picture_frame\n", + "229 pliers\n", + "231 pond\n", + "232 pool\n", + "234 postcard\n", + "236 power_outlet\n", + "239 raccoon\n", + "241 rain\n", + "242 rainbow\n", + "243 rake\n", + "245 rhinoceros\n", + "247 river\n", + "248 roller_coaster\n", + "249 rollerskates\n", + "250 sailboat\n", + "251 sandwich\n", + "255 scissors\n", + "259 see_saw\n", + "263 shorts\n", + "265 sink\n", + "266 skateboard\n", + "267 skull\n", + "268 skyscraper\n", + "270 smiley_face\n", + "274 snowflake\n", + "275 snowman\n", + "281 spreadsheet\n", + "282 square\n", + "283 squiggle\n", + "285 stairs\n", + "286 star\n", + "287 steak\n", + "288 stereo\n", + "290 stitches\n", + "294 streetlight\n", + "295 string_bean\n", + "297 suitcase\n", + "298 sun\n", + "300 sweater\n", + "302 sword\n", + "314 toe\n", + "316 tooth\n", + "317 toothbrush\n", + "318 toothpaste\n", + "319 tornado\n", + "323 tree\n", + "324 triangle\n", + "329 underwear\n", + "334 watermelon\n", + "335 waterslide\n", + "338 windmill\n", + "340 wine_glass\n", + "341 wristwatch\n", + "342 yoga\n", + "344 zigzag\n" + ] + } + ], + "source": [ + "# These Visda classes can not be mapped to any ImageNet class: # remove ambiguous classes as well, since they are covered\n", + "\n", + "ambiguous_classes = ['alarm_clock', 'baseball_bat', 'couch', 'police_car', 'coffee_cup', 'mug', 'keyboard', 'laptop', 'lollipop', 'popsicle', 'school_bus', \n", + " 'pickup_truck', 'firetruck', 'van', 'swan', 'bathtub', 'telephone', 'fan']\n", + "\n", + "\n", + "visda = os.listdir(\"/gpfs01/bethge/data/visda-2019/clipart/\")\n", + "for i, item in enumerate(sorted(visda)):\n", + " if item not in matching_names.keys():\n", + " if item not in ambiguous_classes:\n", + " print(i, item)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 tench, Tinca tinca\n", + "1 goldfish, Carassius auratus\n", + "5 electric ray, crampfish, numbfish, torpedo\n", + "6 stingray\n", + "25 European fire salamander, Salamandra salamandra\n", + "26 common newt, Triturus vulgaris\n", + "27 eft\n", + "28 spotted salamander, Ambystoma maculatum\n", + "29 axolotl, mud puppy, Ambystoma mexicanum\n", + "38 banded gecko\n", + "39 common iguana, iguana, Iguana iguana\n", + "40 American chameleon, anole, Anolis carolinensis\n", + "41 whiptail, whiptail lizard\n", + "42 agama\n", + "43 frilled lizard, Chlamydosaurus kingi\n", + "44 alligator lizard\n", + "45 Gila monster, Heloderma suspectum\n", + "46 green lizard, Lacerta viridis\n", + "47 African chameleon, Chamaeleo chamaeleon\n", + "50 American alligator, Alligator mississipiensis\n", + "51 triceratops\n", + "69 trilobite\n", + "79 centipede\n", + "101 tusker\n", + "102 echidna, spiny anteater, anteater\n", + "106 wombat\n", + "107 jellyfish\n", + "108 sea anemone, anemone\n", + "109 brain coral\n", + "110 flatworm, platyhelminth\n", + "111 nematode, nematode worm, roundworm\n", + "112 conch\n", + "114 slug\n", + "115 sea slug, nudibranch\n", + "116 chiton, coat-of-mail shell, sea cradle, polyplacophore\n", + "117 chambered nautilus, pearly nautilus, nautilus\n", + "124 crayfish, crawfish, crawdad, crawdaddy\n", + "126 isopod\n", + "127 white stork, Ciconia ciconia\n", + "128 black stork, Ciconia nigra\n", + "129 spoonbill\n", + "131 little blue heron, Egretta caerulea\n", + "132 American egret, great white heron, Egretta albus\n", + "133 bittern\n", + "134 crane\n", + "135 limpkin, Aramus pictus\n", + "136 European gallinule, Porphyrio porphyrio\n", + "137 American coot, marsh hen, mud hen, water hen, Fulica americana\n", + "138 bustard\n", + "139 ruddy turnstone, Arenaria interpres\n", + "140 red-backed sandpiper, dunlin, Erolia alpina\n", + "141 redshank, Tringa totanus\n", + "142 dowitcher\n", + "143 oystercatcher, oyster catcher\n", + "144 pelican\n", + "146 albatross, mollymawk\n", + "150 sea lion\n", + "269 timber wolf, grey wolf, gray wolf, Canis lupus\n", + "270 white wolf, Arctic wolf, Canis lupus tundrarum\n", + "271 red wolf, maned wolf, Canis rufus, Canis niger\n", + "272 coyote, prairie wolf, brush wolf, Canis latrans\n", + "273 dingo, warrigal, warragal, Canis dingo\n", + "274 dhole, Cuon alpinus\n", + "276 hyena, hyaena\n", + "277 red fox, Vulpes vulpes\n", + "278 kit fox, Vulpes macrotis\n", + "279 Arctic fox, white fox, Alopex lagopus\n", + "280 grey fox, gray fox, Urocyon cinereoargenteus\n", + "287 lynx, catamount\n", + "288 leopard, Panthera pardus\n", + "289 snow leopard, ounce, Panthera uncia\n", + "290 jaguar, panther, Panthera onca, Felis onca\n", + "293 cheetah, chetah, Acinonyx jubatus\n", + "298 mongoose\n", + "299 meerkat, mierkat\n", + "300 tiger beetle\n", + "301 ladybug, ladybeetle, lady beetle, ladybird, ladybird beetle\n", + "302 ground beetle, carabid beetle\n", + "303 long-horned beetle, longicorn, longicorn beetle\n", + "304 leaf beetle, chrysomelid\n", + "305 dung beetle\n", + "306 rhinoceros beetle\n", + "307 weevil\n", + "308 fly\n", + "311 grasshopper, hopper\n", + "312 cricket\n", + "313 walking stick, walkingstick, stick insect\n", + "314 cockroach, roach\n", + "315 mantis, mantid\n", + "316 cicada, cicala\n", + "317 leafhopper\n", + "327 starfish, sea star\n", + "328 sea urchin\n", + "329 sea cucumber, holothurian\n", + "333 hamster\n", + "336 marmot\n", + "337 beaver\n", + "338 guinea pig, Cavia cobaya\n", + "339 sorrel\n", + "344 hippopotamus, hippo, river horse, Hippopotamus amphibius\n", + "345 ox\n", + "346 water buffalo, water ox, Asiatic buffalo, Bubalus bubalis\n", + "347 bison\n", + "348 ram, tup\n", + "350 ibex, Capra ibex\n", + "351 hartebeest\n", + "352 impala, Aepyceros melampus\n", + "353 gazelle\n", + "355 llama\n", + "356 weasel\n", + "357 mink\n", + "358 polecat, fitch, foulmart, foumart, Mustela putorius\n", + "359 black-footed ferret, ferret, Mustela nigripes\n", + "360 otter\n", + "361 skunk, polecat, wood pussy\n", + "362 badger\n", + "363 armadillo\n", + "364 three-toed sloth, ai, Bradypus tridactylus\n", + "389 barracouta, snoek\n", + "390 eel\n", + "391 coho, cohoe, coho salmon, blue jack, silver salmon, Oncorhynchus kisutch\n", + "392 rock beauty, Holocanthus tricolor\n", + "393 anemone fish\n", + "394 sturgeon\n", + "395 gar, garfish, garpike, billfish, Lepisosteus osseus\n", + "396 lionfish\n", + "397 puffer, pufferfish, blowfish, globefish\n", + "398 abacus\n", + "399 abaya\n", + "400 academic gown, academic robe, judge's robe\n", + "401 accordion, piano accordion, squeeze box\n", + "406 altar\n", + "408 amphibian, amphibious vehicle\n", + "411 apron\n", + "412 ashcan, trash can, garbage can, wastebin, ash bin, ash-bin, ashbin, dustbin, trash barrel, trash bin\n", + "415 bakery, bakeshop, bakehouse\n", + "416 balance beam, beam\n", + "418 ballpoint, ballpoint pen, ballpen, Biro\n", + "419 Band Aid\n", + "420 banjo\n", + "421 bannister, banister, balustrade, balusters, handrail\n", + "422 barbell\n", + "424 barbershop\n", + "426 barometer\n", + "427 barrel, cask\n", + "428 barrow, garden cart, lawn cart, wheelbarrow\n", + "431 bassinet\n", + "432 bassoon\n", + "433 bathing cap, swimming cap\n", + "434 bath towel\n", + "438 beaker\n", + "439 bearskin, busby, shako\n", + "441 beer glass\n", + "442 bell cote, bell cot\n", + "443 bib\n", + "445 bikini, two-piece\n", + "446 binder, ring-binder\n", + "448 birdhouse\n", + "449 boathouse\n", + "450 bobsled, bobsleigh, bob\n", + "451 bolo tie, bolo, bola tie, bola\n", + "452 bonnet, poke bonnet\n", + "453 bookcase\n", + "454 bookshop, bookstore, bookstall\n", + "456 bow\n", + "458 brass, memorial tablet, plaque\n", + "459 brassiere, bra, bandeau\n", + "460 breakwater, groin, groyne, mole, bulwark, seawall, jetty\n", + "461 breastplate, aegis, egis\n", + "464 buckle\n", + "465 bulletproof vest\n", + "467 butcher shop, meat market\n", + "468 cab, hack, taxi, taxicab\n", + "469 caldron, cauldron\n", + "473 can opener, tin opener\n", + "474 cardigan\n", + "476 carousel, carrousel, merry-go-round, roundabout, whirligig\n", + "477 carpenter's kit, tool kit\n", + "478 carton\n", + "480 cash machine, cash dispenser, automated teller machine, automatic teller machine, automated teller, automatic teller, ATM\n", + "481 cassette\n", + "482 cassette player\n", + "484 catamaran\n", + "485 CD player\n", + "488 chain\n", + "490 chain mail, ring mail, mail, chain armor, chain armour, ring armor, ring armour\n", + "492 chest\n", + "493 chiffonier, commode\n", + "494 chime, bell, gong\n", + "495 china cabinet, china closet\n", + "496 Christmas stocking\n", + "499 cleaver, meat cleaver, chopper\n", + "500 cliff dwelling\n", + "501 cloak\n", + "502 clog, geta, patten, sabot\n", + "503 cocktail shaker\n", + "504 coffee mug\n", + "505 coffeepot\n", + "506 coil, spiral, volute, whorl, helix\n", + "507 combination lock\n", + "509 confectionery, confectionary, candy store\n", + "511 convertible\n", + "514 cowboy boot\n", + "516 cradle\n", + "517 crane\n", + "519 crate\n", + "520 crib, cot\n", + "521 Crock Pot\n", + "522 croquet ball\n", + "523 crutch\n", + "524 cuirass\n", + "525 dam, dike, dyke\n", + "526 desk\n", + "529 diaper, nappy, napkin\n", + "531 digital watch\n", + "533 dishrag, dishcloth\n", + "535 disk brake, disc brake\n", + "536 dock, dockage, docking facility\n", + "538 dome\n", + "539 doormat, welcome mat\n", + "540 drilling platform, offshore rig\n", + "541 drum, membranophone, tympan\n", + "542 drumstick\n", + "547 electric locomotive\n", + "548 entertainment center\n", + "550 espresso maker\n", + "551 face powder\n", + "553 file, file cabinet, filing cabinet\n", + "554 fireboat\n", + "556 fire screen, fireguard\n", + "557 flagpole, flagstaff\n", + "558 flute, transverse flute\n", + "561 forklift\n", + "562 fountain\n", + "563 fountain pen\n", + "564 four-poster\n", + "566 French horn, horn\n", + "568 fur coat\n", + "571 gas pump, gasoline pump, petrol pump, island dispenser\n", + "572 goblet\n", + "573 go-kart\n", + "576 gondola\n", + "577 gong, tam-tam\n", + "578 gown\n", + "580 greenhouse, nursery, glasshouse\n", + "581 grille, radiator grille\n", + "582 grocery store, grocery, food market, market\n", + "583 guillotine\n", + "584 hair slide\n", + "585 hair spray\n", + "586 half track\n", + "588 hamper\n", + "589 hand blower, blow dryer, blow drier, hair dryer, hair drier\n", + "591 handkerchief, hankie, hanky, hankey\n", + "592 hard disc, hard disk, fixed disk\n", + "595 harvester, reaper\n", + "596 hatchet\n", + "597 holster\n", + "598 home theater, home theatre\n", + "599 honeycomb\n", + "600 hook, claw\n", + "601 hoopskirt, crinoline\n", + "602 horizontal bar, high bar\n", + "605 iPod\n", + "606 iron, smoothing iron\n", + "608 jean, blue jean, denim\n", + "609 jeep, landrover\n", + "611 jigsaw puzzle\n", + "612 jinrikisha, ricksha, rickshaw\n", + "613 joystick\n", + "614 kimono\n", + "616 knot\n", + "617 lab coat, laboratory coat\n", + "618 ladle\n", + "621 lawn mower, mower\n", + "622 lens cap, lens cover\n", + "624 library\n", + "625 lifeboat\n", + "627 limousine, limo\n", + "630 Loafer\n", + "631 lotion\n", + "632 loudspeaker, speaker, speaker unit, loudspeaker system, speaker system\n", + "633 loupe, jeweler's loupe\n", + "634 lumbermill, sawmill\n", + "636 mailbag, postbag\n", + "638 maillot\n", + "639 maillot, tank suit\n", + "640 manhole cover\n", + "641 maraca\n", + "642 marimba, xylophone\n", + "643 mask\n", + "644 matchstick\n", + "645 maypole\n", + "646 maze, labyrinth\n", + "648 medicine chest, medicine cabinet\n", + "649 megalith, megalithic structure\n", + "652 military uniform\n", + "653 milk can\n", + "655 miniskirt, mini\n", + "656 minivan\n", + "657 missile\n", + "658 mitten\n", + "659 mixing bowl\n", + "660 mobile home, manufactured home\n", + "661 Model T\n", + "662 modem\n", + "663 monastery\n", + "664 monitor\n", + "665 moped\n", + "666 mortar\n", + "667 mortarboard\n", + "668 mosque\n", + "670 motor scooter, scooter\n", + "674 mousetrap\n", + "675 moving van\n", + "676 muzzle\n", + "678 neck brace\n", + "680 nipple\n", + "682 obelisk\n", + "683 oboe, hautboy, hautbois\n", + "685 odometer, hodometer, mileometer, milometer\n", + "686 oil filter\n", + "687 organ, pipe organ\n", + "688 oscilloscope, scope, cathode-ray oscilloscope, CRO\n", + "689 overskirt\n", + "690 oxcart\n", + "691 oxygen mask\n", + "692 packet\n", + "693 paddle, boat paddle\n", + "695 padlock\n", + "697 pajama, pyjama, pj's, jammies\n", + "698 palace\n", + "699 panpipe, pandean pipe, syrinx\n", + "700 paper towel\n", + "702 parallel bars, bars\n", + "704 parking meter\n", + "706 patio, terrace\n", + "707 pay-phone, pay-station\n", + "708 pedestal, plinth, footstall\n", + "709 pencil box, pencil case\n", + "710 pencil sharpener\n", + "711 perfume, essence\n", + "712 Petri dish\n", + "713 photocopier\n", + "714 pick, plectrum, plectron\n", + "715 pickelhaube\n", + "718 pier\n", + "722 ping-pong ball\n", + "723 pinwheel\n", + "725 pitcher, ewer\n", + "726 plane, carpenter's plane, woodworking plane\n", + "727 planetarium\n", + "728 plastic bag\n", + "729 plate rack\n", + "730 plow, plough\n", + "731 plunger, plumber's helper\n", + "733 pole\n", + "735 poncho\n", + "738 pot, flowerpot\n", + "741 prayer rug, prayer mat\n", + "742 printer\n", + "744 projectile, missile\n", + "745 projector\n", + "747 punching bag, punch bag, punching ball, punchball\n", + "749 quill, quill pen\n", + "750 quilt, comforter, comfort, puff\n", + "753 radiator\n", + "756 rain barrel\n", + "757 recreational vehicle, RV, R.V.\n", + "758 reel\n", + "760 refrigerator, icebox\n", + "763 revolver, six-gun, six-shooter\n", + "766 rotisserie\n", + "768 rugby ball\n", + "769 rule, ruler\n", + "771 safe\n", + "772 safety pin\n", + "773 saltshaker, salt shaker\n", + "774 sandal\n", + "775 sarong\n", + "777 scabbard\n", + "778 scale, weighing machine\n", + "780 schooner\n", + "781 scoreboard\n", + "782 screen, CRT screen\n", + "783 screw\n", + "786 sewing machine\n", + "787 shield, buckler\n", + "789 shoji\n", + "791 shopping cart\n", + "793 shower cap\n", + "794 shower curtain\n", + "795 ski\n", + "796 ski mask\n", + "798 slide rule, slipstick\n", + "800 slot, one-armed bandit\n", + "802 snowmobile\n", + "803 snowplow, snowplough\n", + "804 soap dispenser\n", + "807 solar dish, solar collector, solar furnace\n", + "808 sombrero\n", + "809 soup bowl\n", + "810 space bar\n", + "811 space heater\n", + "812 space shuttle\n", + "813 spatula\n", + "816 spindle\n", + "818 spotlight, spot\n", + "819 stage\n", + "820 steam locomotive\n", + "822 steel drum\n", + "824 stole\n", + "825 stone wall\n", + "826 stopwatch, stop watch\n", + "828 strainer\n", + "830 stretcher\n", + "832 stupa, tope\n", + "834 suit, suit of clothes\n", + "835 sundial\n", + "836 sunglass\n", + "837 sunglasses, dark glasses, shades\n", + "838 sunscreen, sunblock, sun blocker\n", + "840 swab, swob, mop\n", + "841 sweatshirt\n", + "842 swimming trunks, bathing trunks\n", + "844 switch, electric switch, electrical switch\n", + "847 tank, army tank, armored combat vehicle, armoured combat vehicle\n", + "848 tape player\n", + "853 thatch, thatched roof\n", + "854 theater curtain, theatre curtain\n", + "855 thimble\n", + "856 thresher, thrasher, threshing machine\n", + "857 throne\n", + "858 tile roof\n", + "860 tobacco shop, tobacconist shop, tobacconist\n", + "862 torch\n", + "863 totem pole\n", + "865 toyshop\n", + "868 tray\n", + "869 trench coat\n", + "870 tricycle, trike, velocipede\n", + "871 trimaran\n", + "872 tripod\n", + "873 triumphal arch\n", + "876 tub, vat\n", + "877 turnstile\n", + "878 typewriter keyboard\n", + "880 unicycle, monocycle\n", + "882 vacuum, vacuum cleaner\n", + "884 vault\n", + "885 velvet\n", + "886 vending machine\n", + "887 vestment\n", + "888 viaduct\n", + "890 volleyball\n", + "891 waffle iron\n", + "893 wallet, billfold, notecase, pocketbook\n", + "894 wardrobe, closet, press\n", + "896 washbasin, handbasin, washbowl, lavabo, wash-hand basin\n", + "899 water jug\n", + "900 water tower\n", + "901 whiskey jug\n", + "902 whistle\n", + "903 wig\n", + "904 window screen\n", + "905 window shade\n", + "906 Windsor tie\n", + "908 wing\n", + "909 wok\n", + "911 wool, woolen, woollen\n", + "913 wreck\n", + "914 yawl\n", + "915 yurt\n", + "916 web site, website, internet site, site\n", + "918 crossword puzzle, crossword\n", + "922 menu\n", + "923 plate\n", + "924 guacamole\n", + "925 consomme\n", + "927 trifle\n", + "930 French loaf\n", + "931 bagel, beigel\n", + "932 pretzel\n", + "933 cheeseburger\n", + "936 head cabbage\n", + "938 cauliflower\n", + "939 zucchini, courgette\n", + "940 spaghetti squash\n", + "941 acorn squash\n", + "942 butternut squash\n", + "943 cucumber, cuke\n", + "944 artichoke, globe artichoke\n", + "945 bell pepper\n", + "946 cardoon\n", + "948 Granny Smith\n", + "950 orange\n", + "951 lemon\n", + "952 fig\n", + "955 jackfruit, jak, jack\n", + "956 custard apple\n", + "957 pomegranate\n", + "958 hay\n", + "959 carbonara\n", + "960 chocolate sauce, chocolate syrup\n", + "961 dough\n", + "962 meat loaf, meatloaf\n", + "964 potpie\n", + "965 burrito\n", + "967 espresso\n", + "969 eggnog\n", + "970 alp\n", + "971 bubble\n", + "972 cliff, drop, drop-off\n", + "973 coral reef\n", + "974 geyser\n", + "975 lakeside, lakeshore\n", + "976 promontory, headland, head, foreland\n", + "977 sandbar, sand bar\n", + "978 seashore, coast, seacoast, sea-coast\n", + "979 valley, vale\n", + "980 volcano\n", + "982 groom, bridegroom\n", + "983 scuba diver\n", + "984 rapeseed\n", + "985 daisy\n", + "986 yellow lady's slipper, yellow lady-slipper, Cypripedium calceolus, Cypripedium parviflorum\n", + "987 corn\n", + "988 acorn\n", + "989 hip, rose hip, rosehip\n", + "991 coral fungus\n", + "992 agaric\n", + "993 gyromitra\n", + "994 stinkhorn, carrion fungus\n", + "995 earthstar\n", + "996 hen-of-the-woods, hen of the woods, Polyporus frondosus, Grifola frondosa\n", + "997 bolete\n" + ] + } + ], + "source": [ + "# These ImageNet classes could not be mapped to any Visda class:\n", + "for i, j in enumerate(mapping_vector):\n", + " if j == -1.:\n", + " print(i, map_dict[i])\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.8" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/examples/imagenet_d/train_torchvision.sh b/examples/imagenet_d/train_torchvision.sh new file mode 100755 index 0000000..de5af5b --- /dev/null +++ b/examples/imagenet_d/train_torchvision.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +sudo docker pull pytorch/pytorch + +cachedir=/tmp/.cache +localdir=/tmp/.local + +mkdir -p $cachedir +mkdir -p $localdir + +sudo docker run \ +--gpus 4 --ipc host \ +-v /run/user/7867/visda-2019:/data:ro \ +-v $(pwd)/gen-efficientnet-pytorch:/app \ +-v $cachedir:/.cache \ +-v $localdir:/.local \ +--user $(id -u) \ +--workdir /app \ +-it pytorch/pytorch ./run.sh diff --git a/examples/robustness_eval/README.md b/examples/robustness_eval/README.md new file mode 100644 index 0000000..d43b9b9 --- /dev/null +++ b/examples/robustness_eval/README.md @@ -0,0 +1,27 @@ +# Robustness evaluation on ImageNet-C, -R, -A and -D + +In this example we show how to use `robusta` for robustness evaluation on basis of the original [PyTorch ImageNet example](https://github.com/pytorch/examples/blob/master/imagenet/main.py). +You can check the difference to the original ImageNet train/evaluation script by running: + +```bash +diff main.py <(curl -s https://raw.githubusercontent.com/pytorch/examples/master/imagenet/main.py) +``` + +For standard robustness evaluation, you want to use + +``` python +import robusta.datasets + +robusta.datasets.ImageNet1k(datadir, split = "val") +robusta.datasets.ImageNet200(datadir) +robusta.datasets.ImageNetC(datadir, corruption = "gaussian_blur", severity = 3) +robusta.datasets.ImageNetR(datadir) +robusta.datasets.ImageNetA(datadir) +robusta.datasets.ImageNetD(datadir, domain = "sketch") +``` + +Each of the datasets is equipped with an accuracy metric that accepts ImageNet classes (1000 classes in PyTorch format). + +``` python +dataset.accuracy_metric() +``` \ No newline at end of file diff --git a/examples/robustness_eval/main.py b/examples/robustness_eval/main.py new file mode 100644 index 0000000..50ded37 --- /dev/null +++ b/examples/robustness_eval/main.py @@ -0,0 +1,437 @@ +import argparse +import os +import random +import shutil +import sys +import time +import warnings + +import torch +import torch.nn as nn +import torch.nn.parallel +import torch.backends.cudnn as cudnn +import torch.distributed as dist +import torch.optim +import torch.multiprocessing as mp +import torch.utils.data +import torch.utils.data.distributed +import torchvision.transforms as transforms +import torchvision.datasets as datasets +import torchvision.models as models + +import robusta + +model_names = sorted(name for name in models.__dict__ + if name.islower() and not name.startswith("__") + and callable(models.__dict__[name])) + +parser = argparse.ArgumentParser(description='PyTorch ImageNet Training') +parser.add_argument('data', metavar='DIR', + help='path to dataset') +parser.add_argument('-a', '--arch', metavar='ARCH', default='resnet18', + choices=model_names, + help='model architecture: ' + + ' | '.join(model_names) + + ' (default: resnet18)') +parser.add_argument('-j', '--workers', default=4, type=int, metavar='N', + help='number of data loading workers (default: 4)') +parser.add_argument('--epochs', default=90, type=int, metavar='N', + help='number of total epochs to run') +parser.add_argument('--start-epoch', default=0, type=int, metavar='N', + help='manual epoch number (useful on restarts)') +parser.add_argument('-b', '--batch-size', default=256, type=int, + metavar='N', + help='mini-batch size (default: 256), this is the total ' + 'batch size of all GPUs on the current node when ' + 'using Data Parallel or Distributed Data Parallel') +parser.add_argument('--lr', '--learning-rate', default=0.1, type=float, + metavar='LR', help='initial learning rate', dest='lr') +parser.add_argument('--momentum', default=0.9, type=float, metavar='M', + help='momentum') +parser.add_argument('--wd', '--weight-decay', default=1e-4, type=float, + metavar='W', help='weight decay (default: 1e-4)', + dest='weight_decay') +parser.add_argument('-p', '--print-freq', default=10, type=int, + metavar='N', help='print frequency (default: 10)') +parser.add_argument('--resume', default='', type=str, metavar='PATH', + help='path to latest checkpoint (default: none)') +parser.add_argument('-e', '--evaluate', dest='evaluate', action='store_true', + help='evaluate model on validation set') +parser.add_argument('--pretrained', dest='pretrained', action='store_true', + help='use pre-trained model') +parser.add_argument('--world-size', default=-1, type=int, + help='number of nodes for distributed training') +parser.add_argument('--rank', default=-1, type=int, + help='node rank for distributed training') +parser.add_argument('--dist-url', default='tcp://224.66.41.62:23456', type=str, + help='url used to set up distributed training') +parser.add_argument('--dist-backend', default='nccl', type=str, + help='distributed backend') +parser.add_argument('--seed', default=None, type=int, + help='seed for initializing training. ') +parser.add_argument('--gpu', default=None, type=int, + help='GPU id to use.') +parser.add_argument('--multiprocessing-distributed', action='store_true', + help='Use multi-processing distributed training to launch ' + 'N processes per node, which has N GPUs. This is the ' + 'fastest way to use PyTorch for either single node or ' + 'multi node data parallel training') + +best_acc1 = 0 + + +def main(): + args = parser.parse_args() + + if args.seed is not None: + random.seed(args.seed) + torch.manual_seed(args.seed) + cudnn.deterministic = True + warnings.warn('You have chosen to seed training. ' + 'This will turn on the CUDNN deterministic setting, ' + 'which can slow down your training considerably! ' + 'You may see unexpected behavior when restarting ' + 'from checkpoints.') + + if args.gpu is not None: + warnings.warn('You have chosen a specific GPU. This will completely ' + 'disable data parallelism.') + + if args.dist_url == "env://" and args.world_size == -1: + args.world_size = int(os.environ["WORLD_SIZE"]) + + args.distributed = args.world_size > 1 or args.multiprocessing_distributed + + ngpus_per_node = torch.cuda.device_count() + if args.multiprocessing_distributed: + # Since we have ngpus_per_node processes per node, the total world_size + # needs to be adjusted accordingly + args.world_size = ngpus_per_node * args.world_size + # Use torch.multiprocessing.spawn to launch distributed processes: the + # main_worker process function + mp.spawn(main_worker, nprocs=ngpus_per_node, args=(ngpus_per_node, args)) + else: + # Simply call main_worker function + main_worker(args.gpu, ngpus_per_node, args) + + +def main_worker(gpu, ngpus_per_node, args): + global best_acc1 + args.gpu = gpu + + if args.gpu is not None: + print("Use GPU: {} for training".format(args.gpu)) + + if args.distributed: + if args.dist_url == "env://" and args.rank == -1: + args.rank = int(os.environ["RANK"]) + if args.multiprocessing_distributed: + # For multiprocessing distributed training, rank needs to be the + # global rank among all the processes + args.rank = args.rank * ngpus_per_node + gpu + dist.init_process_group(backend=args.dist_backend, init_method=args.dist_url, + world_size=args.world_size, rank=args.rank) + # create model + if args.pretrained: + print("=> using pre-trained model '{}'".format(args.arch)) + model = models.__dict__[args.arch](pretrained=True) + else: + print("=> creating model '{}'".format(args.arch)) + model = models.__dict__[args.arch]() + + if not torch.cuda.is_available(): + print('using CPU, this will be slow') + elif args.distributed: + # For multiprocessing distributed, DistributedDataParallel constructor + # should always set the single device scope, otherwise, + # DistributedDataParallel will use all available devices. + if args.gpu is not None: + torch.cuda.set_device(args.gpu) + model.cuda(args.gpu) + # When using a single GPU per process and per + # DistributedDataParallel, we need to divide the batch size + # ourselves based on the total number of GPUs we have + args.batch_size = int(args.batch_size / ngpus_per_node) + args.workers = int((args.workers + ngpus_per_node - 1) / ngpus_per_node) + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + else: + model.cuda() + # DistributedDataParallel will divide and allocate batch_size to all + # available GPUs if device_ids are not set + model = torch.nn.parallel.DistributedDataParallel(model) + elif args.gpu is not None: + torch.cuda.set_device(args.gpu) + model = model.cuda(args.gpu) + else: + # DataParallel will divide and allocate batch_size to all available GPUs + if args.arch.startswith('alexnet') or args.arch.startswith('vgg'): + model.features = torch.nn.DataParallel(model.features) + model.cuda() + else: + model = torch.nn.DataParallel(model).cuda() + + # define loss function (criterion) and optimizer + criterion = nn.CrossEntropyLoss().cuda(args.gpu) + + optimizer = torch.optim.SGD(model.parameters(), args.lr, + momentum=args.momentum, + weight_decay=args.weight_decay) + + # optionally resume from a checkpoint + if args.resume: + if os.path.isfile(args.resume): + print("=> loading checkpoint '{}'".format(args.resume)) + if args.gpu is None: + checkpoint = torch.load(args.resume) + else: + # Map model to be loaded to specified single gpu. + loc = 'cuda:{}'.format(args.gpu) + checkpoint = torch.load(args.resume, map_location=loc) + args.start_epoch = checkpoint['epoch'] + best_acc1 = checkpoint['best_acc1'] + if args.gpu is not None: + # best_acc1 may be from a checkpoint from a different GPU + best_acc1 = best_acc1.to(args.gpu) + model.load_state_dict(checkpoint['state_dict']) + optimizer.load_state_dict(checkpoint['optimizer']) + print("=> loaded checkpoint '{}' (epoch {})" + .format(args.resume, checkpoint['epoch'])) + else: + print("=> no checkpoint found at '{}'".format(args.resume)) + + cudnn.benchmark = True + + # Data loading code + traindir = os.path.join(args.data, 'train') + valdir = args.data + normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225]) + + train_dataset = datasets.ImageFolder( + traindir, + transforms.Compose([ + transforms.RandomResizedCrop(224), + transforms.RandomHorizontalFlip(), + transforms.ToTensor(), + normalize, + ])) + + if args.distributed: + train_sampler = torch.utils.data.distributed.DistributedSampler(train_dataset) + else: + train_sampler = None + + train_loader = torch.utils.data.DataLoader( + train_dataset, batch_size=args.batch_size, shuffle=(train_sampler is None), + num_workers=args.workers, pin_memory=True, sampler=train_sampler) + + val_dataset = robusta.datasets.ImageNetC(root=valdir, + corruptions="zoom_blur", + severities=1, + transform=transforms.Compose([ + transforms.ToTensor(), + normalize, + ]) + ) + + val_loader = torch.utils.data.DataLoader( + val_dataset, + batch_size=args.batch_size, shuffle=False, + num_workers=args.workers, pin_memory=True) + + if args.evaluate: + validate(val_loader, model, criterion, args) + return + + for epoch in range(args.start_epoch, args.epochs): + if args.distributed: + train_sampler.set_epoch(epoch) + adjust_learning_rate(optimizer, epoch, args) + + # train for one epoch + train(train_loader, model, criterion, optimizer, epoch, args) + + # evaluate on validation set + acc1 = validate(val_loader, model, criterion, args) + + # remember best acc@1 and save checkpoint + is_best = acc1 > best_acc1 + best_acc1 = max(acc1, best_acc1) + + if not args.multiprocessing_distributed or (args.multiprocessing_distributed + and args.rank % ngpus_per_node == 0): + save_checkpoint({ + 'epoch': epoch + 1, + 'arch': args.arch, + 'state_dict': model.state_dict(), + 'best_acc1': best_acc1, + 'optimizer' : optimizer.state_dict(), + }, is_best) + + +def train(train_loader, model, criterion, optimizer, epoch, args): + batch_time = AverageMeter('Time', ':6.3f') + data_time = AverageMeter('Data', ':6.3f') + losses = AverageMeter('Loss', ':.4e') + top1 = AverageMeter('Acc@1', ':6.2f') + top5 = AverageMeter('Acc@5', ':6.2f') + progress = ProgressMeter( + len(train_loader), + [batch_time, data_time, losses, top1, top5], + prefix="Epoch: [{}]".format(epoch)) + + # switch to train mode + model.train() + + end = time.time() + for i, (images, target) in enumerate(train_loader): + # measure data loading time + data_time.update(time.time() - end) + + if args.gpu is not None: + images = images.cuda(args.gpu, non_blocking=True) + if torch.cuda.is_available(): + target = target.cuda(args.gpu, non_blocking=True) + + # compute output + output = model(images) + loss = criterion(output, target) + + # measure accuracy and record loss + acc1, acc5 = accuracy(output, target, topk=(1, 5)) + losses.update(loss.item(), images.size(0)) + top1.update(acc1[0], images.size(0)) + top5.update(acc5[0], images.size(0)) + + # compute gradient and do SGD step + optimizer.zero_grad() + loss.backward() + optimizer.step() + + # measure elapsed time + batch_time.update(time.time() - end) + end = time.time() + + if i % args.print_freq == 0: + progress.display(i) + + +def validate(val_loader, model, criterion, args): + batch_time = AverageMeter('Time', ':6.3f') + losses = AverageMeter('Loss', ':.4e') + top1 = AverageMeter('Acc@1', ':6.2f') + top5 = AverageMeter('Acc@5', ':6.2f') + progress = ProgressMeter( + len(val_loader), + [batch_time, losses, top1, top5], + prefix='Test: ') + + # switch to evaluate mode + model.eval() + + + with torch.no_grad(): + end = time.time() + for i, (images, target) in enumerate(val_loader): + if args.gpu is not None: + images = images.cuda(args.gpu, non_blocking=True) + if torch.cuda.is_available(): + target = target.cuda(args.gpu, non_blocking=True) + + # compute output + output = model(images) + loss = criterion(output, target) + + # measure accuracy and record loss + acc1, acc5 = val_accuracy(output, target, topk=(1, 5)) + losses.update(loss.item(), images.size(0)) + top1.update(acc1[0], images.size(0)) + top5.update(acc5[0], images.size(0)) + + # measure elapsed time + batch_time.update(time.time() - end) + end = time.time() + + if i % args.print_freq == 0: + progress.display(i) + + # TODO: this should also be done with the ProgressMeter + print(' * Acc@1 {top1.avg:.3f} Acc@5 {top5.avg:.3f}' + .format(top1=top1, top5=top5)) + + return top1.avg + + +def save_checkpoint(state, is_best, filename='checkpoint.pth.tar'): + torch.save(state, filename) + if is_best: + shutil.copyfile(filename, 'model_best.pth.tar') + + +class AverageMeter(object): + """Computes and stores the average and current value""" + def __init__(self, name, fmt=':f'): + self.name = name + self.fmt = fmt + self.reset() + + def reset(self): + self.val = 0 + self.avg = 0 + self.sum = 0 + self.count = 0 + + def update(self, val, n=1): + self.val = val + self.sum += val * n + self.count += n + self.avg = self.sum / self.count + + def __str__(self): + fmtstr = '{name} {val' + self.fmt + '} ({avg' + self.fmt + '})' + return fmtstr.format(**self.__dict__) + + +class ProgressMeter(object): + def __init__(self, num_batches, meters, prefix=""): + self.batch_fmtstr = self._get_batch_fmtstr(num_batches) + self.meters = meters + self.prefix = prefix + + def display(self, batch): + entries = [self.prefix + self.batch_fmtstr.format(batch)] + entries += [str(meter) for meter in self.meters] + print('\t'.join(entries)) + + def _get_batch_fmtstr(self, num_batches): + num_digits = len(str(num_batches // 1)) + fmt = '{:' + str(num_digits) + 'd}' + return '[' + fmt + '/' + fmt.format(num_batches) + ']' + + +def adjust_learning_rate(optimizer, epoch, args): + """Sets the learning rate to the initial LR decayed by 10 every 30 epochs""" + lr = args.lr * (0.1 ** (epoch // 30)) + for param_group in optimizer.param_groups: + param_group['lr'] = lr + + +def accuracy(output, target, topk=(1,)): + """Computes the accuracy over the k top predictions for the specified values of k""" + with torch.no_grad(): + maxk = max(topk) + batch_size = target.size(0) + + _, pred = output.topk(maxk, 1, True, True) + pred = pred.t() + correct = pred.eq(target.view(1, -1).expand_as(pred)) + + res = [] + for k in topk: + correct_k = correct[:k].reshape(-1).float().sum(0, keepdim=True) + res.append(correct_k.mul_(100.0 / batch_size)) + return res + + +if __name__ == '__main__': + main() diff --git a/examples/selflearning/README.md b/examples/selflearning/README.md new file mode 100644 index 0000000..e69de29 diff --git a/examples/selflearning/gce.py b/examples/selflearning/gce.py new file mode 100644 index 0000000..e3c0889 --- /dev/null +++ b/examples/selflearning/gce.py @@ -0,0 +1,119 @@ +""" Adapting ImageNet-scale models to complex distribution shifts with self-learning + +Run with: + + ❯ docker pull pytorch/pytorch + ❯ DATADIR="/path/to/imagenetc" + ❯ curl -s https://stes.io/gce.py > gce.py + ❯ docker run --gpus 1 -v ${DATADIR}:/data/imagenetc:ro \ + -v $(pwd):/app -w /app -u $(id -u) \ + --tmpfs /.cache --tmpfs /.local \ + -it pytorch/pytorch python gce.py + +Reference: + + Web: https://domainadaptation.org/selflearning/ + Paper: https://arxiv.org/abs/2104.12928 + +--- + +Copyright 2021 Evgenia Rusak and Steffen Schneider + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--- + +This software is not an Amazon product. + +""" + +import torch +from torchvision import models, datasets, transforms + +def get_dataset_loader(valdir, batch_size, shuffle): + val_dataset = datasets.ImageFolder(valdir, transforms.Compose([ + transforms.ToTensor(), + transforms.Normalize( + mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225] + ), + ])) + val_loader = torch.utils.data.DataLoader( + val_dataset, batch_size=batch_size, shuffle=shuffle + ) + return val_loader + +def gce(logits, target, q = 0.8): + """ Generalized cross entropy. + + Reference: https://arxiv.org/abs/1805.07836 + """ + probs = torch.nn.functional.softmax(logits, dim=1) + probs_with_correct_idx = probs.index_select(-1, target).diag() + loss = (1. - probs_with_correct_idx**q) / q + return loss.mean() + +def adapt_batchnorm(model): + model.eval() + parameters = [] + for module in model.modules(): + if isinstance(module, torch.nn.BatchNorm2d): + parameters.extend(module.parameters()) + module.train() + return parameters + + +# --- + +def evaluate( + datadir = '/data/imagenetc/gaussian_blur/3', + num_epochs = 5, + batch_size = 96, + learning_rate = 0.75e-3, + gce_q = 0.8 + ): + + model = models.resnet50(pretrained = True).cuda() + parameters = adapt_batchnorm(model) + val_loader = get_dataset_loader( + datadir, + batch_size = batch_size, + shuffle = True + ) + optimizer = torch.optim.SGD( + model.parameters(), lr = learning_rate + ) + + num_correct, num_samples = 0., 0. + for epoch in range(num_epochs): + predictions = [] + for images, targets in val_loader: + + logits = model(images.cuda()) + predictions = logits.argmax(dim = 1) + loss = gce(logits, predictions, q = gce_q) + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + num_correct += (predictions.detach().cpu() == targets).float().sum() + num_samples += len(targets) + print(f"Correct: {num_correct:#5.0f}/{num_samples:#5.0f} ({100 * num_correct / num_samples:.2f} %)") + +evaluate() diff --git a/robusta/__init__.py b/robusta/__init__.py new file mode 100644 index 0000000..903dbcf --- /dev/null +++ b/robusta/__init__.py @@ -0,0 +1,26 @@ +# Copyright 2020-2021 Evgenia Rusak, Steffen Schneider, George Pachitariu +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# --- +# This licence notice applies to all originally written code by the +# authors. Code taken from other open-source projects is indicated. +# See NOTICE for a list of all third-party licences used in the project. + +"""A package for robustness and adaptation on ImageNet scale.""" + +from robusta import batchnorm +from robusta import datasets +from robusta import models +from robusta import selflearning diff --git a/robusta/batchnorm/__init__.py b/robusta/batchnorm/__init__.py new file mode 100644 index 0000000..41f2621 --- /dev/null +++ b/robusta/batchnorm/__init__.py @@ -0,0 +1,31 @@ +# Copyright 2020-2021 Evgenia Rusak, Steffen Schneider, George Pachitariu +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# --- +# This licence notice applies to all originally written code by the +# authors. Code taken from other open-source projects is indicated. +# See NOTICE for a list of all third-party licences used in the project. + +import torch.nn + + +def adapt(model, adapt_type="batch_wise"): + if adapt_type not in ["batch_wise"]: + raise ValueError(adapt_type) + model.eval() + for module in model.modules(): + if isinstance(module, torch.nn.BatchNorm2d): + module.train() + return None diff --git a/robusta/batchnorm/bn.py b/robusta/batchnorm/bn.py new file mode 100644 index 0000000..52368cf --- /dev/null +++ b/robusta/batchnorm/bn.py @@ -0,0 +1,235 @@ +# Copyright 2020-2021 Evgenia Rusak, Steffen Schneider, George Pachitariu +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# --- +# This licence notice applies to all originally written code by the +# authors. Code taken from other open-source projects is indicated. +# See NOTICE for a list of all third-party licences used in the project. + +""" Batch norm variants +""" + +import torch +from torch import nn +from torch.nn import functional as F + + +def adapt_ema(model: nn.Module): + return EMABatchNorm.adapt_model(model) + + +def adapt_parts(model: nn.Module, adapt_mean: bool, adapt_var: bool): + return PartlyAdaptiveBN.adapt_model(model, adapt_mean, adapt_var) + + +def adapt_bayesian(model: nn.Module, prior: float): + return BayesianBatchNorm.adapt_model(model, prior=prior) + + +class PartlyAdaptiveBN(nn.Module): + @staticmethod + def find_bns(parent, estimate_mean, estimate_var): + replace_mods = [] + if parent is None: + return [] + for name, child in parent.named_children(): + child.requires_grad_(False) + if isinstance(child, nn.BatchNorm2d): + module = PartlyAdaptiveBN(child, estimate_mean, estimate_var) + replace_mods.append((parent, name, module)) + else: + replace_mods.extend( + PartlyAdaptiveBN.find_bns(child, estimate_mean, + estimate_var) + ) + + return replace_mods + + @staticmethod + def adapt_model(model, adapt_mean, adapt_var): + replace_mods = PartlyAdaptiveBN.find_bns(model, adapt_mean, adapt_var) + print(f"| Found {len(replace_mods)} modules to be replaced.") + for (parent, name, child) in replace_mods: + setattr(parent, name, child) + return model + + def __init__(self, layer, estimate_mean=True, estimate_var=True): + super().__init__() + self.layer = layer + + self.estimate_mean = estimate_mean + self.estimate_var = estimate_var + + self.register_buffer("source_mean", layer.running_mean.data) + self.register_buffer("source_var", layer.running_var.data) + + self.register_buffer( + "estimated_mean", + torch.zeros(layer.running_mean.size(), + device=layer.running_mean.device), + ) + self.register_buffer( + "estimated_var", + torch.ones(layer.running_var.size(), + device=layer.running_mean.device), + ) + + def reset(self): + self.estimated_mean.zero_() + self.estimated_var.fill_(1) + + @property + def running_mean(self): + if self.estimate_mean: + return self.estimated_mean + return self.source_mean + + @property + def running_var(self): + if self.estimate_var: + return self.estimated_var + return self.source_var + + def forward(self, input): + # Estimate training set statistics + self.reset() + F.batch_norm( + input, + self.estimated_mean, + self.estimated_var, + None, + None, + True, + 1.0, + self.layer.eps, + ) + + return F.batch_norm( + input, + self.running_mean, + self.running_var, + self.layer.weight, + self.layer.bias, + False, + 0.0, + self.layer.eps, + ) + + +class EMABatchNorm(nn.Module): + @staticmethod + def reset_stats(module): + module.reset_running_stats() + module.momentum = None + return module + + @staticmethod + def find_bns(parent): + replace_mods = [] + if parent is None: + return [] + for name, child in parent.named_children(): + child.requires_grad_(False) + if isinstance(child, nn.BatchNorm2d): + module = EMABatchNorm.reset_stats(child) + module = EMABatchNorm(module) + replace_mods.append((parent, name, module)) + else: + replace_mods.extend(EMABatchNorm.find_bns(child)) + + return replace_mods + + @staticmethod + def adapt_model(model): + replace_mods = EMABatchNorm.find_bns(model) + print(f"| Found {len(replace_mods)} modules to be replaced.") + for (parent, name, child) in replace_mods: + setattr(parent, name, child) + return model + + def __init__(self, layer): + super().__init__() + self.layer = layer + + def forward(self, x): + # store statistics, but discard result + self.layer.train() + self.layer(x) + # store statistics, use the stored stats + self.layer.eval() + return self.layer(x) + + +class BayesianBatchNorm(nn.Module): + """ Use the source statistics as a prior on the target statistics """ + + @staticmethod + def find_bns(parent, prior): + replace_mods = [] + if parent is None: + return [] + for name, child in parent.named_children(): + child.requires_grad_(False) + if isinstance(child, nn.BatchNorm2d): + module = BayesianBatchNorm(child, prior) + replace_mods.append((parent, name, module)) + else: + replace_mods.extend(BayesianBatchNorm.find_bns(child, prior)) + + return replace_mods + + @staticmethod + def adapt_model(model, prior): + replace_mods = BayesianBatchNorm.find_bns(model, prior) + print(f"| Found {len(replace_mods)} modules to be replaced.") + for (parent, name, child) in replace_mods: + setattr(parent, name, child) + return model + + def __init__(self, layer, prior): + assert prior >= 0 and prior <= 1 + + super().__init__() + self.layer = layer + self.layer.eval() + + self.norm = nn.BatchNorm2d( + self.layer.num_features, affine=False, momentum=1.0 + ) + + self.prior = prior + + def forward(self, input): + self.norm(input) + + running_mean = ( + self.prior * self.layer.running_mean + + (1 - self.prior) * self.norm.running_mean + ) + running_var = ( + self.prior * self.layer.running_var + + (1 - self.prior) * self.norm.running_var + ) + + return F.batch_norm( + input, + running_mean, + running_var, + self.layer.weight, + self.layer.bias, + False, + 0, + self.layer.eps, + ) diff --git a/robusta/batchnorm/stages.py b/robusta/batchnorm/stages.py new file mode 100644 index 0000000..fc2aa2c --- /dev/null +++ b/robusta/batchnorm/stages.py @@ -0,0 +1,50 @@ +# Copyright 2020-2021 Evgenia Rusak, Steffen Schneider, George Pachitariu +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# --- +# This licence notice applies to all originally written code by the +# authors. Code taken from other open-source projects is indicated. +# See NOTICE for a list of all third-party licences used in the project. + +""" Helper functions for stages ablations """ + +import torchvision +from torch import nn + +def split_model(model): + if not isinstance(model, torchvision.models.ResNet): + print("Only resnet models defined for this analysis so far") + return model.bn1, model.layer1, model.layer2, model.layer3, model.layer4 + + +def use_train_statistics(module): + if isinstance(module, nn.BatchNorm2d): + print(f"Setting {module} to adaptive") + module.train() + + +def choose_one_adaptation(model, stage): + """ select exactly on stage for adaptation """ + assert stage >= 0 and stage < 5 + model.eval() + split_model(model)[stage].apply(use_train_statistics) + + +def leave_one_out_adaptation(model, stage): + """ set all BN layers to train mode except for ones in the selected stage """ + assert stage >= 0 and stage < 5 + model.eval() + model.apply(use_train_statistics) + split_model(model)[stage].eval() diff --git a/robusta/datasets/__init__.py b/robusta/datasets/__init__.py new file mode 100644 index 0000000..df5d7bc --- /dev/null +++ b/robusta/datasets/__init__.py @@ -0,0 +1,26 @@ +# Copyright 2020-2021 Evgenia Rusak, Steffen Schneider, George Pachitariu +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# --- +# This licence notice applies to all originally written code by the +# authors. Code taken from other open-source projects is indicated. +# See NOTICE for a list of all third-party licences used in the project. + + +from robusta.datasets import base +from robusta.datasets import imagenet200 +from robusta.datasets import imageneta +from robusta.datasets import imagenetc +from robusta.datasets import imagenetr diff --git a/robusta/datasets/base.py b/robusta/datasets/base.py new file mode 100644 index 0000000..2ed6f2a --- /dev/null +++ b/robusta/datasets/base.py @@ -0,0 +1,63 @@ +# Copyright 2020-2021 Evgenia Rusak, Steffen Schneider, George Pachitariu +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# --- +# This licence notice applies to all originally written code by the +# authors. Code taken from other open-source projects is indicated. +# See NOTICE for a list of all third-party licences used in the project. + +import torchvision.datasets +import torchvision.transforms + +class TorchvisionTransform(torchvision.transforms.Compose): + """Standard torchvision transform for cropped and non-cropped datasets.""" + + def __init__(self, resize = False): + self.resize = resize + + self.mean = [0.485, 0.456, 0.406] + self.std = [0.229, 0.224, 0.225] + super().__init__([ + torchvision.transforms.Resize(256), + torchvision.transforms.CenterCrop(224), + torchvision.transforms.ToTensor(), + torchvision.transforms.Normalize( + self.mean, self.std + ) + ]) + +class ImageNetRobustnessDataset(torchvision.datasets.ImageFolder): + + def __init__(self, dataset_dir, transform = None, **kwargs): + if transform == "torchvision": + transform = TorchvisionTransform() + super().__init__(dataset_dir, transform = transform, **kwargs) + + def accuracy_metric(self, logits, targets): + raise NotImplementedError() + +class RemappedImageNet(): + + def __init__(self): + super().__init__() + + def map_logits(self, logits): + output = logits[:, imagenet_r_mask] + return output + + def accuracy_metric(self, logits, targets): + logits200 = self.map_logits(logits) + super().accuracy_metric(logits200, targets) + diff --git a/robusta/datasets/imagenet200.py b/robusta/datasets/imagenet200.py new file mode 100644 index 0000000..89165ee --- /dev/null +++ b/robusta/datasets/imagenet200.py @@ -0,0 +1,92 @@ +# Copyright 2020-2021 Evgenia Rusak, Steffen Schneider, George Pachitariu +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# --- +# This licence notice applies to all originally written code by the +# authors. Code taken from other open-source projects is indicated. +# See NOTICE for a list of all third-party licences used in the project. + +""" ImageNet-R + +Reference: https://github.com/hendrycks/imagenet-r + +Originally licensed under + +MIT License + +Copyright (c) 2020 Dan Hendrycks + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +from copy import copy +import os +import torch +import torch.nn as nn +import torch.nn.functional as F +import numpy as np +from tqdm import tqdm +import shutil + +from robusta.datasets.base import RemappedImageNet + + +class ImageNet200(RemappedImageNet): + """ Subset of ImageNet with 200 classes + + Reference: https://github.com/hendrycks/imagenet-r + """ + + def get_class_mask(self): + all_wnids = ['n01440764', 'n01443537', 'n01484850', 'n01491361', 'n01494475', 'n01496331', 'n01498041', 'n01514668', 'n01514859', 'n01518878', 'n01530575', 'n01531178', 'n01532829', 'n01534433', 'n01537544', 'n01558993', 'n01560419', 'n01580077', 'n01582220', 'n01592084', 'n01601694', 'n01608432', 'n01614925', 'n01616318', 'n01622779', 'n01629819', 'n01630670', 'n01631663', 'n01632458', 'n01632777', 'n01641577', 'n01644373', 'n01644900', 'n01664065', 'n01665541', 'n01667114', 'n01667778', 'n01669191', 'n01675722', 'n01677366', 'n01682714', 'n01685808', 'n01687978', 'n01688243', 'n01689811', 'n01692333', 'n01693334', 'n01694178', 'n01695060', 'n01697457', 'n01698640', 'n01704323', 'n01728572', 'n01728920', 'n01729322', 'n01729977', 'n01734418', 'n01735189', 'n01737021', 'n01739381', 'n01740131', 'n01742172', 'n01744401', 'n01748264', 'n01749939', 'n01751748', 'n01753488', 'n01755581', 'n01756291', 'n01768244', 'n01770081', 'n01770393', 'n01773157', 'n01773549', 'n01773797', 'n01774384', 'n01774750', 'n01775062', 'n01776313', 'n01784675', 'n01795545', 'n01796340', 'n01797886', 'n01798484', 'n01806143', 'n01806567', 'n01807496', 'n01817953', 'n01818515', 'n01819313', 'n01820546', 'n01824575', 'n01828970', 'n01829413', 'n01833805', 'n01843065', 'n01843383', 'n01847000', 'n01855032', 'n01855672', 'n01860187', 'n01871265', 'n01872401', 'n01873310', 'n01877812', 'n01882714', 'n01883070', 'n01910747', 'n01914609', 'n01917289', 'n01924916', 'n01930112', 'n01943899', 'n01944390', 'n01945685', 'n01950731', 'n01955084', 'n01968897', 'n01978287', 'n01978455', 'n01980166', 'n01981276', 'n01983481', 'n01984695', 'n01985128', 'n01986214', 'n01990800', 'n02002556', 'n02002724', 'n02006656', 'n02007558', 'n02009229', 'n02009912', 'n02011460', 'n02012849', 'n02013706', 'n02017213', 'n02018207', 'n02018795', 'n02025239', 'n02027492', 'n02028035', 'n02033041', 'n02037110', 'n02051845', 'n02056570', 'n02058221', 'n02066245', 'n02071294', 'n02074367', 'n02077923', 'n02085620', 'n02085782', 'n02085936', 'n02086079', 'n02086240', 'n02086646', 'n02086910', 'n02087046', 'n02087394', 'n02088094', 'n02088238', 'n02088364', 'n02088466', 'n02088632', 'n02089078', 'n02089867', 'n02089973', 'n02090379', 'n02090622', 'n02090721', 'n02091032', 'n02091134', 'n02091244', 'n02091467', 'n02091635', 'n02091831', 'n02092002', 'n02092339', 'n02093256', 'n02093428', 'n02093647', 'n02093754', 'n02093859', 'n02093991', 'n02094114', 'n02094258', 'n02094433', 'n02095314', 'n02095570', 'n02095889', 'n02096051', 'n02096177', 'n02096294', 'n02096437', 'n02096585', 'n02097047', 'n02097130', 'n02097209', 'n02097298', 'n02097474', 'n02097658', 'n02098105', 'n02098286', 'n02098413', 'n02099267', 'n02099429', 'n02099601', 'n02099712', 'n02099849', 'n02100236', 'n02100583', 'n02100735', 'n02100877', 'n02101006', 'n02101388', 'n02101556', 'n02102040', 'n02102177', 'n02102318', 'n02102480', 'n02102973', 'n02104029', 'n02104365', 'n02105056', 'n02105162', 'n02105251', 'n02105412', 'n02105505', 'n02105641', 'n02105855', 'n02106030', 'n02106166', 'n02106382', 'n02106550', 'n02106662', 'n02107142', 'n02107312', 'n02107574', 'n02107683', 'n02107908', 'n02108000', 'n02108089', 'n02108422', 'n02108551', 'n02108915', 'n02109047', 'n02109525', 'n02109961', 'n02110063', 'n02110185', 'n02110341', 'n02110627', 'n02110806', 'n02110958', 'n02111129', 'n02111277', 'n02111500', 'n02111889', 'n02112018', 'n02112137', 'n02112350', 'n02112706', 'n02113023', 'n02113186', 'n02113624', 'n02113712', 'n02113799', 'n02113978', 'n02114367', 'n02114548', 'n02114712', 'n02114855', 'n02115641', 'n02115913', 'n02116738', 'n02117135', 'n02119022', 'n02119789', 'n02120079', 'n02120505', 'n02123045', 'n02123159', 'n02123394', 'n02123597', 'n02124075', 'n02125311', 'n02127052', 'n02128385', 'n02128757', 'n02128925', 'n02129165', 'n02129604', 'n02130308', 'n02132136', 'n02133161', 'n02134084', 'n02134418', 'n02137549', 'n02138441', 'n02165105', 'n02165456', 'n02167151', 'n02168699', 'n02169497', 'n02172182', 'n02174001', 'n02177972', 'n02190166', 'n02206856', 'n02219486', 'n02226429', 'n02229544', 'n02231487', 'n02233338', 'n02236044', 'n02256656', 'n02259212', 'n02264363', 'n02268443', 'n02268853', 'n02276258', 'n02277742', 'n02279972', 'n02280649', 'n02281406', 'n02281787', 'n02317335', 'n02319095', 'n02321529', 'n02325366', 'n02326432', 'n02328150', 'n02342885', 'n02346627', 'n02356798', 'n02361337', 'n02363005', 'n02364673', 'n02389026', 'n02391049', 'n02395406', 'n02396427', 'n02397096', 'n02398521', 'n02403003', 'n02408429', 'n02410509', 'n02412080', 'n02415577', 'n02417914', 'n02422106', 'n02422699', 'n02423022', 'n02437312', 'n02437616', 'n02441942', 'n02442845', 'n02443114', 'n02443484', 'n02444819', 'n02445715', 'n02447366', 'n02454379', 'n02457408', 'n02480495', 'n02480855', 'n02481823', 'n02483362', 'n02483708', 'n02484975', 'n02486261', 'n02486410', 'n02487347', 'n02488291', 'n02488702', 'n02489166', 'n02490219', 'n02492035', 'n02492660', 'n02493509', 'n02493793', 'n02494079', 'n02497673', 'n02500267', 'n02504013', 'n02504458', 'n02509815', 'n02510455', 'n02514041', 'n02526121', 'n02536864', 'n02606052', 'n02607072', 'n02640242', 'n02641379', 'n02643566', 'n02655020', 'n02666196', 'n02667093', 'n02669723', 'n02672831', 'n02676566', 'n02687172', 'n02690373', 'n02692877', 'n02699494', 'n02701002', 'n02704792', 'n02708093', 'n02727426', 'n02730930', 'n02747177', 'n02749479', 'n02769748', 'n02776631', 'n02777292', 'n02782093', 'n02783161', 'n02786058', 'n02787622', 'n02788148', 'n02790996', 'n02791124', 'n02791270', 'n02793495', 'n02794156', 'n02795169', 'n02797295', 'n02799071', 'n02802426', 'n02804414', 'n02804610', 'n02807133', 'n02808304', 'n02808440', 'n02814533', 'n02814860', 'n02815834', 'n02817516', 'n02823428', 'n02823750', 'n02825657', 'n02834397', 'n02835271', 'n02837789', 'n02840245', 'n02841315', 'n02843684', 'n02859443', 'n02860847', 'n02865351', 'n02869837', 'n02870880', 'n02871525', 'n02877765', 'n02879718', 'n02883205', 'n02892201', 'n02892767', 'n02894605', 'n02895154', 'n02906734', 'n02909870', 'n02910353', 'n02916936', 'n02917067', 'n02927161', 'n02930766', 'n02939185', 'n02948072', 'n02950826', 'n02951358', 'n02951585', 'n02963159', 'n02965783', 'n02966193', 'n02966687', 'n02971356', 'n02974003', 'n02977058', 'n02978881', 'n02979186', 'n02980441', 'n02981792', 'n02988304', 'n02992211', 'n02992529', 'n02999410', 'n03000134', 'n03000247', 'n03000684', 'n03014705', 'n03016953', 'n03017168', 'n03018349', 'n03026506', 'n03028079', 'n03032252', 'n03041632', + 'n03042490', 'n03045698', 'n03047690', 'n03062245', 'n03063599', 'n03063689', 'n03065424', 'n03075370', 'n03085013', 'n03089624', 'n03095699', 'n03100240', 'n03109150', 'n03110669', 'n03124043', 'n03124170', 'n03125729', 'n03126707', 'n03127747', 'n03127925', 'n03131574', 'n03133878', 'n03134739', 'n03141823', 'n03146219', 'n03160309', 'n03179701', 'n03180011', 'n03187595', 'n03188531', 'n03196217', 'n03197337', 'n03201208', 'n03207743', 'n03207941', 'n03208938', 'n03216828', 'n03218198', 'n03220513', 'n03223299', 'n03240683', 'n03249569', 'n03250847', 'n03255030', 'n03259280', 'n03271574', 'n03272010', 'n03272562', 'n03290653', 'n03291819', 'n03297495', 'n03314780', 'n03325584', 'n03337140', 'n03344393', 'n03345487', 'n03347037', 'n03355925', 'n03372029', 'n03376595', 'n03379051', 'n03384352', 'n03388043', 'n03388183', 'n03388549', 'n03393912', 'n03394916', 'n03400231', 'n03404251', 'n03417042', 'n03424325', 'n03425413', 'n03443371', 'n03444034', 'n03445777', 'n03445924', 'n03447447', 'n03447721', 'n03450230', 'n03452741', 'n03457902', 'n03459775', 'n03461385', 'n03467068', 'n03476684', 'n03476991', 'n03478589', 'n03481172', 'n03482405', 'n03483316', 'n03485407', 'n03485794', 'n03492542', 'n03494278', 'n03495258', 'n03496892', 'n03498962', 'n03527444', 'n03529860', 'n03530642', 'n03532672', 'n03534580', 'n03535780', 'n03538406', 'n03544143', 'n03584254', 'n03584829', 'n03590841', 'n03594734', 'n03594945', 'n03595614', 'n03598930', 'n03599486', 'n03602883', 'n03617480', 'n03623198', 'n03627232', 'n03630383', 'n03633091', 'n03637318', 'n03642806', 'n03649909', 'n03657121', 'n03658185', 'n03661043', 'n03662601', 'n03666591', 'n03670208', 'n03673027', 'n03676483', 'n03680355', 'n03690938', 'n03691459', 'n03692522', 'n03697007', 'n03706229', 'n03709823', 'n03710193', 'n03710637', 'n03710721', 'n03717622', 'n03720891', 'n03721384', 'n03724870', 'n03729826', 'n03733131', 'n03733281', 'n03733805', 'n03742115', 'n03743016', 'n03759954', 'n03761084', 'n03763968', 'n03764736', 'n03769881', 'n03770439', 'n03770679', 'n03773504', 'n03775071', 'n03775546', 'n03776460', 'n03777568', 'n03777754', 'n03781244', 'n03782006', 'n03785016', 'n03786901', 'n03787032', 'n03788195', 'n03788365', 'n03791053', 'n03792782', 'n03792972', 'n03793489', 'n03794056', 'n03796401', 'n03803284', 'n03804744', 'n03814639', 'n03814906', 'n03825788', 'n03832673', 'n03837869', 'n03838899', 'n03840681', 'n03841143', 'n03843555', 'n03854065', 'n03857828', 'n03866082', 'n03868242', 'n03868863', 'n03871628', 'n03873416', 'n03874293', 'n03874599', 'n03876231', 'n03877472', 'n03877845', 'n03884397', 'n03887697', 'n03888257', 'n03888605', 'n03891251', 'n03891332', 'n03895866', 'n03899768', 'n03902125', 'n03903868', 'n03908618', 'n03908714', 'n03916031', 'n03920288', 'n03924679', 'n03929660', 'n03929855', 'n03930313', 'n03930630', 'n03933933', 'n03935335', 'n03937543', 'n03938244', 'n03942813', 'n03944341', 'n03947888', 'n03950228', 'n03954731', 'n03956157', 'n03958227', 'n03961711', 'n03967562', 'n03970156', 'n03976467', 'n03976657', 'n03977966', 'n03980874', 'n03982430', 'n03983396', 'n03991062', 'n03992509', 'n03995372', 'n03998194', 'n04004767', 'n04005630', 'n04008634', 'n04009552', 'n04019541', 'n04023962', 'n04026417', 'n04033901', 'n04033995', 'n04037443', 'n04039381', 'n04040759', 'n04041544', 'n04044716', 'n04049303', 'n04065272', 'n04067472', 'n04069434', 'n04070727', 'n04074963', 'n04081281', 'n04086273', 'n04090263', 'n04099969', 'n04111531', 'n04116512', 'n04118538', 'n04118776', 'n04120489', 'n04125021', 'n04127249', 'n04131690', 'n04133789', 'n04136333', 'n04141076', 'n04141327', 'n04141975', 'n04146614', 'n04147183', 'n04149813', 'n04152593', 'n04153751', 'n04154565', 'n04162706', 'n04179913', 'n04192698', 'n04200800', 'n04201297', 'n04204238', 'n04204347', 'n04208210', 'n04209133', 'n04209239', 'n04228054', 'n04229816', 'n04235860', 'n04238763', 'n04239074', 'n04243546', 'n04251144', 'n04252077', 'n04252225', 'n04254120', 'n04254680', 'n04254777', 'n04258138', 'n04259630', 'n04263257', 'n04264628', 'n04265275', 'n04266014', 'n04270147', 'n04273569', 'n04275548', 'n04277352', 'n04285008', 'n04286575', 'n04296562', 'n04310018', 'n04311004', 'n04311174', 'n04317175', 'n04325704', 'n04326547', 'n04328186', 'n04330267', 'n04332243', 'n04335435', 'n04336792', 'n04344873', 'n04346328', 'n04347754', 'n04350905', 'n04355338', 'n04355933', 'n04356056', 'n04357314', 'n04366367', 'n04367480', 'n04370456', 'n04371430', 'n04371774', 'n04372370', 'n04376876', 'n04380533', 'n04389033', 'n04392985', 'n04398044', 'n04399382', 'n04404412', 'n04409515', 'n04417672', 'n04418357', 'n04423845', 'n04428191', 'n04429376', 'n04435653', 'n04442312', 'n04443257', 'n04447861', 'n04456115', 'n04458633', 'n04461696', 'n04462240', 'n04465501', 'n04467665', 'n04476259', 'n04479046', 'n04482393', 'n04483307', 'n04485082', 'n04486054', 'n04487081', 'n04487394', 'n04493381', 'n04501370', 'n04505470', 'n04507155', 'n04509417', 'n04515003', 'n04517823', 'n04522168', 'n04523525', 'n04525038', 'n04525305', 'n04532106', 'n04532670', 'n04536866', 'n04540053', 'n04542943', 'n04548280', 'n04548362', 'n04550184', 'n04552348', 'n04553703', 'n04554684', 'n04557648', 'n04560804', 'n04562935', 'n04579145', 'n04579432', 'n04584207', 'n04589890', 'n04590129', 'n04591157', 'n04591713', 'n04592741', 'n04596742', 'n04597913', 'n04599235', 'n04604644', 'n04606251', 'n04612504', 'n04613696', 'n06359193', 'n06596364', 'n06785654', 'n06794110', 'n06874185', 'n07248320', 'n07565083', 'n07579787', 'n07583066', 'n07584110', 'n07590611', 'n07613480', 'n07614500', 'n07615774', 'n07684084', 'n07693725', 'n07695742', 'n07697313', 'n07697537', 'n07711569', 'n07714571', 'n07714990', 'n07715103', 'n07716358', 'n07716906', 'n07717410', 'n07717556', 'n07718472', 'n07718747', 'n07720875', 'n07730033', 'n07734744', 'n07742313', 'n07745940', 'n07747607', 'n07749582', 'n07753113', 'n07753275', 'n07753592', 'n07754684', 'n07760859', 'n07768694', 'n07802026', 'n07831146', 'n07836838', 'n07860988', 'n07871810', 'n07873807', 'n07875152', 'n07880968', 'n07892512', 'n07920052', 'n07930864', 'n07932039', 'n09193705', 'n09229709', 'n09246464', 'n09256479', 'n09288635', 'n09332890', 'n09399592', 'n09421951', 'n09428293', 'n09468604', 'n09472597', 'n09835506', 'n10148035', 'n10565667', 'n11879895', 'n11939491', 'n12057211', 'n12144580', 'n12267677', 'n12620546', 'n12768682', 'n12985857', 'n12998815', 'n13037406', 'n13040303', 'n13044778', 'n13052670', 'n13054560', 'n13133613', 'n15075141'] + imagenet_r_wnids = {'n01443537', 'n01484850', 'n01494475', 'n01498041', 'n01514859', 'n01518878', 'n01531178', 'n01534433', 'n01614925', 'n01616318', 'n01630670', 'n01632777', 'n01644373', 'n01677366', 'n01694178', 'n01748264', 'n01770393', 'n01774750', 'n01784675', 'n01806143', 'n01820546', 'n01833805', 'n01843383', 'n01847000', 'n01855672', 'n01860187', 'n01882714', 'n01910747', 'n01944390', 'n01983481', 'n01986214', 'n02007558', 'n02009912', 'n02051845', 'n02056570', 'n02066245', 'n02071294', 'n02077923', 'n02085620', 'n02086240', 'n02088094', 'n02088238', 'n02088364', 'n02088466', 'n02091032', 'n02091134', 'n02092339', 'n02094433', 'n02096585', 'n02097298', 'n02098286', 'n02099601', 'n02099712', 'n02102318', 'n02106030', 'n02106166', 'n02106550', 'n02106662', 'n02108089', 'n02108915', 'n02109525', 'n02110185', 'n02110341', 'n02110958', 'n02112018', 'n02112137', 'n02113023', 'n02113624', 'n02113799', 'n02114367', 'n02117135', 'n02119022', 'n02123045', 'n02128385', 'n02128757', 'n02129165', 'n02129604', 'n02130308', 'n02134084', 'n02138441', 'n02165456', 'n02190166', 'n02206856', 'n02219486', 'n02226429', 'n02233338', 'n02236044', 'n02268443', 'n02279972', 'n02317335', 'n02325366', 'n02346627', 'n02356798', 'n02363005', 'n02364673', 'n02391049', 'n02395406', 'n02398521', 'n02410509', + 'n02423022', 'n02437616', 'n02445715', 'n02447366', 'n02480495', 'n02480855', 'n02481823', 'n02483362', 'n02486410', 'n02510455', 'n02526121', 'n02607072', 'n02655020', 'n02672831', 'n02701002', 'n02749479', 'n02769748', 'n02793495', 'n02797295', 'n02802426', 'n02808440', 'n02814860', 'n02823750', 'n02841315', 'n02843684', 'n02883205', 'n02906734', 'n02909870', 'n02939185', 'n02948072', 'n02950826', 'n02951358', 'n02966193', 'n02980441', 'n02992529', 'n03124170', 'n03272010', 'n03345487', 'n03372029', 'n03424325', 'n03452741', 'n03467068', 'n03481172', 'n03494278', 'n03495258', 'n03498962', 'n03594945', 'n03602883', 'n03630383', 'n03649909', 'n03676483', 'n03710193', 'n03773504', 'n03775071', 'n03888257', 'n03930630', 'n03947888', 'n04086273', 'n04118538', 'n04133789', 'n04141076', 'n04146614', 'n04147183', 'n04192698', 'n04254680', 'n04266014', 'n04275548', 'n04310018', 'n04325704', 'n04347754', 'n04389033', 'n04409515', 'n04465501', 'n04487394', 'n04522168', 'n04536866', 'n04552348', 'n04591713', 'n07614500', 'n07693725', 'n07695742', 'n07697313', 'n07697537', 'n07714571', 'n07714990', 'n07718472', 'n07720875', 'n07734744', 'n07742313', 'n07745940', 'n07749582', 'n07753275', 'n07753592', 'n07768694', 'n07873807', 'n07880968', 'n07920052', 'n09472597', 'n09835506', 'n10565667', 'n12267677'} + return [wnid in imagenet_r_wnids for wnid in all_wnids] + + def create_symlinks_to_imagenet(self): + if not os.path.exists(self.imagenet_200_location): + os.makedirs(self.imagenet_200_location) + folders_of_interest = self.get_class_mask() + for folder in folders_of_interest: + os.symlink(self.imagenet_1k_location + folder, + self.imagenet_200_location+folder, target_is_directory=True) + else: + print('Folder containing IID validation images already exists') + + def __init__(self, imagenet_directory, imagenet_200_directory= "/tmp/in200", transform = None): + self.imagenet_1k_location = imagenet_directory + self.imagenet_200_location = imagenet_200_directory + self.create_symlinks_to_imagenet() + super().__init__( + self.imagenet_200_location, + transform= transform + ) \ No newline at end of file diff --git a/robusta/datasets/imageneta.py b/robusta/datasets/imageneta.py new file mode 100644 index 0000000..ff1d512 --- /dev/null +++ b/robusta/datasets/imageneta.py @@ -0,0 +1,71 @@ +# Copyright 2020-2021 Evgenia Rusak, Steffen Schneider, George Pachitariu +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# --- +# This licence notice applies to all originally written code by the +# authors. Code taken from other open-source projects is indicated. +# See NOTICE for a list of all third-party licences used in the project. + +""" The ImageNet-A dataset. + +Reference: https://github.com/hendrycks/natural-adv-examples + +MIT License + +Copyright (c) 2019 Dan Hendrycks + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +import torch +import torch.nn as nn +import torchvision.datasets as dset +import torchvision.transforms as trn +import torchvision.transforms.functional as trnF +import torchvision.models as models +import torch.utils.model_zoo as model_zoo +import torch.nn.functional as F +import numpy as np + +class ImageNetA: + + def __init__(self): + thousand_k_to_200 = {0: -1, 1: -1, 2: -1, 3: -1, 4: -1, 5: -1, 6: 0, 7: -1, 8: -1, 9: -1, 10: -1, 11: 1, 12: -1, 13: 2, 14: -1, 15: 3, 16: -1, 17: 4, 18: -1, 19: -1, 20: -1, 21: -1, 22: 5, 23: 6, 24: -1, 25: -1, 26: -1, 27: 7, 28: -1, 29: -1, 30: 8, 31: -1, 32: -1, 33: -1, 34: -1, 35: -1, 36: -1, 37: 9, 38: -1, 39: 10, 40: -1, 41: -1, 42: 11, 43: -1, 44: -1, 45: -1, 46: -1, 47: 12, 48: -1, 49: -1, 50: 13, 51: -1, 52: -1, 53: -1, 54: -1, 55: -1, 56: -1, 57: 14, 58: -1, 59: -1, 60: -1, 61: -1, 62: -1, 63: -1, 64: -1, 65: -1, 66: -1, 67: -1, 68: -1, 69: -1, 70: 15, 71: 16, 72: -1, 73: -1, 74: -1, 75: -1, 76: 17, 77: -1, 78: -1, 79: 18, 80: -1, 81: -1, 82: -1, 83: -1, 84: -1, 85: -1, 86: -1, 87: -1, 88: -1, 89: 19, 90: 20, 91: -1, 92: -1, 93: -1, 94: 21, 95: -1, 96: 22, 97: 23, 98: -1, 99: 24, 100: -1, 101: -1, 102: -1, 103: -1, 104: -1, 105: 25, 106: -1, 107: 26, 108: 27, 109: -1, 110: 28, 111: -1, 112: -1, 113: 29, 114: -1, 115: -1, 116: -1, 117: -1, 118: -1, 119: -1, 120: -1, 121: -1, 122: -1, 123: -1, 124: 30, 125: 31, 126: -1, 127: -1, 128: -1, 129: -1, 130: 32, 131: -1, 132: 33, 133: -1, 134: -1, 135: -1, 136: -1, 137: -1, 138: -1, 139: -1, 140: -1, 141: -1, 142: -1, 143: 34, 144: 35, 145: -1, 146: -1, 147: -1, 148: -1, 149: -1, 150: 36, 151: 37, 152: -1, 153: -1, 154: -1, 155: -1, 156: -1, 157: -1, 158: -1, 159: -1, 160: -1, 161: -1, 162: -1, 163: -1, 164: -1, 165: -1, 166: -1, 167: -1, 168: -1, 169: -1, 170: -1, 171: -1, 172: -1, 173: -1, 174: -1, 175: -1, 176: -1, 177: -1, 178: -1, 179: -1, 180: -1, 181: -1, 182: -1, 183: -1, 184: -1, 185: -1, 186: -1, 187: -1, 188: -1, 189: -1, 190: -1, 191: -1, 192: -1, 193: -1, 194: -1, 195: -1, 196: -1, 197: -1, 198: -1, 199: -1, 200: -1, 201: -1, 202: -1, 203: -1, 204: -1, 205: -1, 206: -1, 207: 38, 208: -1, 209: -1, 210: -1, 211: -1, 212: -1, 213: -1, 214: -1, 215: -1, 216: -1, 217: -1, 218: -1, 219: -1, 220: -1, 221: -1, 222: -1, 223: -1, 224: -1, 225: -1, 226: -1, 227: -1, 228: -1, 229: -1, 230: -1, 231: -1, 232: -1, 233: -1, 234: 39, 235: 40, 236: -1, 237: -1, 238: -1, 239: -1, 240: -1, 241: -1, 242: -1, 243: -1, 244: -1, 245: -1, 246: -1, 247: -1, 248: -1, 249: -1, 250: -1, 251: -1, 252: -1, 253: -1, 254: 41, 255: -1, 256: -1, 257: -1, 258: -1, 259: -1, 260: -1, 261: -1, 262: -1, 263: -1, 264: -1, 265: -1, 266: -1, 267: -1, 268: -1, 269: -1, 270: -1, 271: -1, 272: -1, 273: -1, 274: -1, 275: -1, 276: -1, 277: 42, 278: -1, 279: -1, 280: -1, 281: -1, 282: -1, 283: 43, 284: -1, 285: -1, 286: -1, 287: 44, 288: -1, 289: -1, 290: -1, 291: 45, 292: -1, 293: -1, 294: -1, 295: 46, 296: -1, 297: -1, 298: 47, 299: -1, 300: -1, 301: 48, 302: -1, 303: -1, 304: -1, 305: -1, 306: 49, 307: 50, 308: 51, 309: 52, 310: 53, 311: 54, 312: -1, 313: 55, 314: 56, 315: 57, 316: -1, 317: 58, 318: -1, 319: 59, 320: -1, 321: -1, 322: -1, 323: 60, 324: 61, 325: -1, 326: 62, 327: 63, 328: -1, 329: -1, 330: 64, 331: -1, 332: -1, 333: -1, 334: 65, 335: 66, 336: 67, 337: -1, 338: -1, 339: -1, 340: -1, 341: -1, 342: -1, 343: -1, 344: -1, 345: -1, 346: -1, 347: 68, 348: -1, 349: -1, 350: -1, 351: -1, 352: -1, 353: -1, 354: -1, 355: -1, 356: -1, 357: -1, 358: -1, 359: -1, 360: -1, 361: 69, 362: -1, 363: 70, 364: -1, 365: -1, 366: -1, 367: -1, 368: -1, 369: -1, 370: -1, 371: -1, 372: 71, 373: -1, 374: -1, 375: -1, 376: -1, 377: -1, 378: 72, 379: -1, 380: -1, 381: -1, 382: -1, 383: -1, 384: -1, 385: -1, 386: 73, 387: -1, 388: -1, 389: -1, 390: -1, 391: -1, 392: -1, 393: -1, 394: -1, 395: -1, 396: -1, 397: 74, 398: -1, 399: -1, 400: 75, 401: 76, 402: 77, 403: -1, 404: 78, 405: -1, 406: -1, 407: 79, 408: -1, 409: -1, 410: -1, 411: 80, 412: -1, 413: -1, 414: -1, 415: -1, 416: 81, 417: 82, 418: -1, 419: -1, 420: 83, 421: -1, 422: -1, 423: -1, 424: -1, 425: 84, 426: -1, 427: -1, 428: 85, 429: -1, 430: 86, 431: -1, 432: -1, 433: -1, 434: -1, 435: -1, 436: -1, 437: 87, 438: 88, 439: -1, 440: -1, 441: -1, 442: -1, 443: -1, 444: -1, 445: 89, 446: -1, 447: -1, 448: -1, 449: -1, 450: -1, 451: -1, 452: -1, 453: -1, 454: -1, 455: -1, 456: 90, 457: 91, 458: -1, 459: -1, 460: -1, 461: 92, 462: 93, 463: -1, 464: -1, 465: -1, 466: -1, 467: -1, 468: -1, 469: -1, 470: 94, 471: -1, 472: 95, 473: -1, 474: -1, 475: -1, 476: -1, 477: -1, 478: -1, 479: -1, 480: -1, 481: -1, 482: -1, 483: 96, 484: -1, 485: -1, 486: 97, 487: -1, 488: 98, 489: -1, 490: -1, 491: -1, 492: 99, 493: -1, 494: -1, 495: -1, 496: 100, 497: -1, 498: -1, 499: -1, 500: -1, 501: -1, 502: -1, 503: -1, 504: -1, 505: -1, 506: -1, 507: -1, 508: -1, 509: -1, 510: -1, 511: -1, 512: -1, 513: -1, 514: 101, 515: -1, 516: 102, 517: -1, 518: -1, 519: -1, 520: -1, 521: -1, 522: -1, 523: -1, 524: -1, 525: -1, 526: -1, 527: -1, 528: 103, 529: -1, 530: 104, 531: -1, 532: -1, 533: -1, 534: -1, 535: -1, 536: -1, 537: -1, 538: -1, 539: 105, 540: -1, 541: -1, 542: 106, 543: 107, 544: -1, 545: -1, 546: -1, 547: -1, 548: -1, 549: 108, 550: -1, 551: -1, 552: 109, 553: -1, 554: -1, 555: -1, 556: -1, 557: 110, 558: -1, 559: -1, 560: -1, 561: 111, 562: 112, 563: -1, 564: -1, 565: -1, 566: -1, 567: -1, 568: -1, 569: 113, 570: -1, 571: -1, 572: 114, 573: 115, 574: -1, 575: 116, 576: -1, 577: -1, 578: -1, 579: 117, 580: -1, 581: -1, 582: -1, 583: -1, 584: -1, 585: -1, 586: -1, 587: -1, 588: -1, 589: 118, 590: -1, 591: -1, 592: -1, 593: -1, 594: -1, 595: -1, 596: -1, 597: -1, 598: -1, 599: -1, 600: -1, 601: -1, 602: -1, 603: -1, 604: -1, 605: -1, 606: 119, 607: 120, 608: -1, 609: 121, 610: -1, 611: -1, 612: -1, 613: -1, 614: 122, 615: -1, 616: -1, 617: -1, 618: -1, 619: -1, 620: -1, 621: -1, 622: -1, 623: -1, 624: -1, 625: -1, 626: 123, 627: 124, 628: -1, 629: -1, 630: -1, 631: -1, 632: -1, 633: -1, 634: -1, 635: -1, 636: -1, 637: -1, 638: -1, 639: -1, 640: 125, 641: 126, 642: 127, 643: 128, 644: -1, 645: -1, 646: -1, 647: -1, 648: -1, 649: -1, 650: -1, 651: -1, 652: -1, 653: -1, 654: -1, 655: -1, 656: -1, 657: -1, 658: 129, 659: -1, 660: -1, 661: -1, 662: -1, 663: -1, 664: -1, 665: -1, 666: -1, 667: -1, 668: 130, 669: -1, 670: -1, 671: -1, 672: -1, 673: -1, 674: -1, 675: -1, 676: -1, 677: 131, 678: -1, 679: -1, 680: -1, 681: -1, 682: 132, 683: -1, 684: 133, 685: -1, 686: -1, 687: 134, 688: -1, 689: -1, 690: -1, 691: -1, 692: -1, 693: -1, 694: -1, 695: -1, 696: -1, 697: -1, 698: -1, 699: -1, 700: -1, 701: 135, 702: -1, 703: -1, 704: 136, 705: -1, 706: -1, 707: -1, 708: -1, 709: -1, 710: -1, 711: -1, 712: -1, 713: -1, 714: -1, 715: -1, 716: -1, 717: -1, 718: -1, 719: 137, 720: -1, 721: -1, 722: -1, 723: -1, 724: -1, 725: -1, 726: -1, 727: -1, 728: -1, 729: -1, 730: -1, 731: -1, 732: -1, 733: -1, 734: -1, 735: -1, 736: 138, 737: -1, 738: -1, 739: -1, 740: -1, 741: -1, 742: -1, 743: -1, 744: -1, 745: -1, 746: 139, 747: -1, 748: -1, 749: 140, 750: -1, 751: -1, 752: 141, 753: -1, 754: -1, 755: -1, 756: -1, 757: -1, 758: 142, 759: -1, 760: -1, 761: -1, 762: -1, 763: 143, 764: -1, 765: 144, 766: -1, 767: -1, 768: 145, 769: -1, 770: -1, 771: -1, 772: -1, 773: 146, 774: 147, 775: -1, 776: 148, 777: -1, 778: -1, 779: 149, 780: 150, 781: -1, 782: -1, 783: -1, 784: -1, 785: -1, 786: 151, 787: -1, 788: -1, 789: -1, 790: -1, 791: -1, 792: 152, 793: -1, 794: -1, 795: -1, 796: -1, 797: 153, 798: -1, 799: -1, 800: -1, 801: -1, 802: 154, 803: 155, 804: 156, 805: -1, 806: -1, 807: -1, 808: -1, 809: -1, 810: -1, 811: -1, 812: -1, 813: 157, 814: -1, 815: 158, 816: -1, 817: -1, 818: -1, 819: -1, 820: 159, 821: -1, 822: -1, 823: 160, 824: -1, 825: -1, 826: -1, 827: -1, 828: -1, 829: -1, 830: -1, 831: 161, 832: -1, 833: 162, 834: -1, 835: 163, 836: -1, 837: -1, 838: -1, 839: 164, 840: -1, 841: -1, 842: -1, 843: -1, 844: -1, 845: 165, 846: -1, 847: 166, 848: -1, 849: -1, 850: 167, 851: -1, 852: -1, 853: -1, 854: -1, 855: -1, 856: -1, 857: -1, 858: -1, 859: 168, 860: -1, 861: -1, 862: 169, 863: -1, 864: -1, 865: -1, 866: -1, 867: -1, 868: -1, 869: -1, 870: 170, 871: -1, 872: -1, 873: -1, 874: -1, 875: -1, 876: -1, 877: -1, 878: -1, 879: 171, 880: 172, 881: -1, 882: -1, 883: -1, 884: -1, 885: -1, 886: -1, 887: -1, 888: 173, 889: -1, 890: 174, 891: -1, 892: -1, 893: -1, 894: -1, 895: -1, 896: -1, 897: 175, 898: -1, 899: -1, 900: 176, 901: -1, 902: -1, 903: -1, 904: -1, 905: -1, 906: -1, 907: 177, 908: -1, 909: -1, 910: -1, 911: -1, 912: -1, 913: 178, 914: -1, 915: -1, 916: -1, 917: -1, 918: -1, 919: -1, 920: -1, 921: -1, 922: -1, 923: -1, 924: 179, 925: -1, 926: -1, 927: -1, 928: -1, 929: -1, 930: -1, 931: -1, 932: 180, 933: 181, 934: 182, 935: -1, 936: -1, 937: 183, 938: -1, 939: -1, 940: -1, 941: -1, 942: -1, 943: 184, 944: -1, 945: 185, 946: -1, 947: 186, 948: -1, 949: -1, 950: -1, 951: 187, 952: -1, 953: -1, 954: 188, 955: -1, 956: 189, 957: 190, 958: -1, 959: 191, 960: -1, 961: -1, 962: -1, 963: -1, 964: -1, 965: -1, 966: -1, 967: -1, 968: -1, 969: -1, 970: -1, 971: 192, 972: 193, 973: -1, 974: -1, 975: -1, 976: -1, 977: -1, 978: -1, 979: -1, 980: 194, 981: 195, 982: -1, 983: -1, 984: 196, 985: -1, 986: 197, 987: 198, 988: 199, 989: -1, 990: -1, 991: -1, 992: -1, 993: -1, 994: -1, 995: -1, 996: -1, 997: -1, 998: -1, 999: -1} + indices_in_1k = [k for k in thousand_k_to_200 if thousand_k_to_200[k] != -1] + + mean = [0.485, 0.456, 0.406] + std = [0.229, 0.224, 0.225] + + test_transform = trn.Compose( + [trn.Resize(256), trn.CenterCrop(224), trn.ToTensor(), trn.Normalize(mean, std)]) + + output = net(data)[:,indices_in_1k] + diff --git a/robusta/datasets/imagenetc.py b/robusta/datasets/imagenetc.py new file mode 100644 index 0000000..f10d0e6 --- /dev/null +++ b/robusta/datasets/imagenetc.py @@ -0,0 +1,37 @@ +import os +import torch +import torchvision +from typing import Any, Callable, Optional +from torchvision.datasets.folder import default_loader + + +class ImageNetC(torchvision.datasets.ImageFolder): + num_samples = 50000 + num_classes = 1000 + image_size = (224, 224) + + train_corruptions = ("brightness", "elastic_transform", "impulse_noise", + "pixelate", "snow", "zoom_blur", + "contrast", "fog", "gaussian_noise", + "jpeg_compression", "defocus_blur", "frost", + "glass_blur", "motion_blur", "shot_noise") + test_corruptions = ("gaussian_blur", "saturate", "spatter", "speckle_noise") + severities = ("1", "2", "3", "4", "5") + + def __init__(self, root: str, corruption: str, severity: str, + transform: Optional[Callable] = None, + target_transform: Optional[Callable] = None, + loader: Callable[[str], Any] = default_loader, + is_valid_file: Optional[Callable[[str], bool]] = None + ): + super(ImageNetC, self).__init__( + root=os.path.join(root, corruption, str(severity)), + transform=transform, + target_transform=target_transform, + loader=loader, + is_valid_file=is_valid_file + ) + assert corruption in ImageNetC.train_corruptions or \ + corruption in ImageNetC.test_corruptions + assert str(severity) in ImageNetC.severities + diff --git a/robusta/datasets/imagenetr.py b/robusta/datasets/imagenetr.py new file mode 100644 index 0000000..e7ebe71 --- /dev/null +++ b/robusta/datasets/imagenetr.py @@ -0,0 +1,26 @@ +# Copyright 2020-2021 Evgenia Rusak, Steffen Schneider, George Pachitariu +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# --- +# This licence notice applies to all originally written code by the +# authors. Code taken from other open-source projects is indicated. +# See NOTICE for a list of all third-party licences used in the project. + +import torchvision +from robusta.datasets.base import RemappedImageNet + + +class ImageNetR(torchvision.datasets.ImageFolder, RemappedImageNet): + pass diff --git a/robusta/models/BiT_models.py b/robusta/models/BiT_models.py new file mode 100644 index 0000000..5c5361b --- /dev/null +++ b/robusta/models/BiT_models.py @@ -0,0 +1,300 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Lint as: python3 +"""Bottleneck ResNet v2 with GroupNorm and Weight Standardization.""" + +from collections import OrderedDict # pylint: disable=g-importing-member + +import torch +import torch.nn as nn +import torch.nn.functional as F + + +class StdConv2d(nn.Conv2d): + def forward(self, x): + w = self.weight + v, m = torch.var_mean(w, dim=[1, 2, 3], keepdim=True, unbiased=False) + w = (w - m) / torch.sqrt(v + 1e-10) + return F.conv2d( + x, w, self.bias, self.stride, self.padding, self.dilation, self.groups + ) + + +def conv3x3(cin, cout, stride=1, groups=1, bias=False): + return StdConv2d( + cin, cout, kernel_size=3, stride=stride, padding=1, bias=bias, groups=groups + ) + + +def conv1x1(cin, cout, stride=1, bias=False): + return StdConv2d(cin, cout, kernel_size=1, stride=stride, padding=0, bias=bias) + + +def tf2th(conv_weights): + """Possibly convert HWIO to OIHW.""" + if conv_weights.ndim == 4: + conv_weights = conv_weights.transpose([3, 2, 0, 1]) + return torch.from_numpy(conv_weights) + + +class PreActBottleneck(nn.Module): + """Pre-activation (v2) bottleneck block. + Follows the implementation of "Identity Mappings in Deep Residual Networks": + https://github.com/KaimingHe/resnet-1k-layers/blob/master/resnet-pre-act.lua + Except it puts the stride on 3x3 conv when available. + """ + + def __init__(self, cin, cout=None, cmid=None, stride=1): + super().__init__() + cout = cout or cin + cmid = cmid or cout // 4 + + self.gn1 = nn.GroupNorm(32, cin) + self.conv1 = conv1x1(cin, cmid) + self.gn2 = nn.GroupNorm(32, cmid) + self.conv2 = conv3x3(cmid, cmid, stride) # Original code has it on conv1!! + self.gn3 = nn.GroupNorm(32, cmid) + self.conv3 = conv1x1(cmid, cout) + self.relu = nn.ReLU(inplace=True) + + if stride != 1 or cin != cout: + # Projection also with pre-activation according to paper. + self.downsample = conv1x1(cin, cout, stride) + + def forward(self, x): + out = self.relu(self.gn1(x)) + + # Residual branch + residual = x + if hasattr(self, "downsample"): + residual = self.downsample(out) + + # Unit's branch + out = self.conv1(out) + out = self.conv2(self.relu(self.gn2(out))) + out = self.conv3(self.relu(self.gn3(out))) + + return out + residual + + def load_from(self, weights, prefix=""): + convname = "standardized_conv2d" + with torch.no_grad(): + self.conv1.weight.copy_(tf2th(weights[f"{prefix}a/{convname}/kernel"])) + self.conv2.weight.copy_(tf2th(weights[f"{prefix}b/{convname}/kernel"])) + self.conv3.weight.copy_(tf2th(weights[f"{prefix}c/{convname}/kernel"])) + self.gn1.weight.copy_(tf2th(weights[f"{prefix}a/group_norm/gamma"])) + self.gn2.weight.copy_(tf2th(weights[f"{prefix}b/group_norm/gamma"])) + self.gn3.weight.copy_(tf2th(weights[f"{prefix}c/group_norm/gamma"])) + self.gn1.bias.copy_(tf2th(weights[f"{prefix}a/group_norm/beta"])) + self.gn2.bias.copy_(tf2th(weights[f"{prefix}b/group_norm/beta"])) + self.gn3.bias.copy_(tf2th(weights[f"{prefix}c/group_norm/beta"])) + if hasattr(self, "downsample"): + w = weights[f"{prefix}a/proj/{convname}/kernel"] + self.downsample.weight.copy_(tf2th(w)) + + +class ResNetV2(nn.Module): + """Implementation of Pre-activation (v2) ResNet mode.""" + + def __init__(self, block_units, width_factor, head_size=21843, zero_head=False): + super().__init__() + wf = width_factor # shortcut 'cause we'll use it a lot. + + # The following will be unreadable if we split lines. + # pylint: disable=line-too-long + self.root = nn.Sequential( + OrderedDict( + [ + ( + "conv", + StdConv2d( + 3, 64 * wf, kernel_size=7, stride=2, padding=3, bias=False + ), + ), + ("pad", nn.ConstantPad2d(1, 0)), + ("pool", nn.MaxPool2d(kernel_size=3, stride=2, padding=0)), + # The following is subtly not the same! + # ('pool', nn.MaxPool2d(kernel_size=3, stride=2, padding=1)), + ] + ) + ) + + self.body = nn.Sequential( + OrderedDict( + [ + ( + "block1", + nn.Sequential( + OrderedDict( + [ + ( + "unit01", + PreActBottleneck( + cin=64 * wf, cout=256 * wf, cmid=64 * wf + ), + ) + ] + + [ + ( + f"unit{i:02d}", + PreActBottleneck( + cin=256 * wf, cout=256 * wf, cmid=64 * wf + ), + ) + for i in range(2, block_units[0] + 1) + ], + ) + ), + ), + ( + "block2", + nn.Sequential( + OrderedDict( + [ + ( + "unit01", + PreActBottleneck( + cin=256 * wf, + cout=512 * wf, + cmid=128 * wf, + stride=2, + ), + ) + ] + + [ + ( + f"unit{i:02d}", + PreActBottleneck( + cin=512 * wf, cout=512 * wf, cmid=128 * wf + ), + ) + for i in range(2, block_units[1] + 1) + ], + ) + ), + ), + ( + "block3", + nn.Sequential( + OrderedDict( + [ + ( + "unit01", + PreActBottleneck( + cin=512 * wf, + cout=1024 * wf, + cmid=256 * wf, + stride=2, + ), + ) + ] + + [ + ( + f"unit{i:02d}", + PreActBottleneck( + cin=1024 * wf, cout=1024 * wf, cmid=256 * wf + ), + ) + for i in range(2, block_units[2] + 1) + ], + ) + ), + ), + ( + "block4", + nn.Sequential( + OrderedDict( + [ + ( + "unit01", + PreActBottleneck( + cin=1024 * wf, + cout=2048 * wf, + cmid=512 * wf, + stride=2, + ), + ) + ] + + [ + ( + f"unit{i:02d}", + PreActBottleneck( + cin=2048 * wf, cout=2048 * wf, cmid=512 * wf + ), + ) + for i in range(2, block_units[3] + 1) + ], + ) + ), + ), + ] + ) + ) + # pylint: enable=line-too-long + + self.zero_head = zero_head + self.head = nn.Sequential( + OrderedDict( + [ + ("gn", nn.GroupNorm(32, 2048 * wf)), + ("relu", nn.ReLU(inplace=True)), + ("avg", nn.AdaptiveAvgPool2d(output_size=1)), + ("conv", nn.Conv2d(2048 * wf, head_size, kernel_size=1, bias=True)), + ] + ) + ) + + def forward(self, x): + x = self.head(self.body(self.root(x))) + assert x.shape[-2:] == (1, 1) # We should have no spatial shape left. + return x[..., 0, 0] + + def load_from(self, weights, prefix="resnet/"): + with torch.no_grad(): + self.root.conv.weight.copy_( + tf2th(weights[f"{prefix}root_block/standardized_conv2d/kernel"]) + ) # pylint: disable=line-too-long + self.head.gn.weight.copy_(tf2th(weights[f"{prefix}group_norm/gamma"])) + self.head.gn.bias.copy_(tf2th(weights[f"{prefix}group_norm/beta"])) + if self.zero_head: + nn.init.zeros_(self.head.conv.weight) + nn.init.zeros_(self.head.conv.bias) + else: + self.head.conv.weight.copy_( + tf2th(weights[f"{prefix}head/conv2d/kernel"]) + ) # pylint: disable=line-too-long + self.head.conv.bias.copy_(tf2th(weights[f"{prefix}head/conv2d/bias"])) + + for bname, block in self.body.named_children(): + for uname, unit in block.named_children(): + unit.load_from(weights, prefix=f"{prefix}{bname}/{uname}/") + + +KNOWN_MODELS = OrderedDict( + [ + ("BiT-M-R50x1", lambda *a, **kw: ResNetV2([3, 4, 6, 3], 1, *a, **kw)), + ("BiT-M-R50x3", lambda *a, **kw: ResNetV2([3, 4, 6, 3], 3, *a, **kw)), + ("BiT-M-R101x1", lambda *a, **kw: ResNetV2([3, 4, 23, 3], 1, *a, **kw)), + ("BiT-M-R101x3", lambda *a, **kw: ResNetV2([3, 4, 23, 3], 3, *a, **kw)), + ("BiT-M-R152x2", lambda *a, **kw: ResNetV2([3, 8, 36, 3], 2, *a, **kw)), + ("BiT-M-R152x4", lambda *a, **kw: ResNetV2([3, 8, 36, 3], 4, *a, **kw)), + ("BiT-S-R50x1", lambda *a, **kw: ResNetV2([3, 4, 6, 3], 1, *a, **kw)), + ("BiT-S-R50x3", lambda *a, **kw: ResNetV2([3, 4, 6, 3], 3, *a, **kw)), + ("BiT-S-R101x1", lambda *a, **kw: ResNetV2([3, 4, 23, 3], 1, *a, **kw)), + ("BiT-S-R101x3", lambda *a, **kw: ResNetV2([3, 4, 23, 3], 3, *a, **kw)), + ("BiT-S-R152x2", lambda *a, **kw: ResNetV2([3, 8, 36, 3], 2, *a, **kw)), + ("BiT-S-R152x4", lambda *a, **kw: ResNetV2([3, 8, 36, 3], 4, *a, **kw)), + ] +) diff --git a/robusta/models/__init__.py b/robusta/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/robusta/models/fixup.py b/robusta/models/fixup.py new file mode 100644 index 0000000..777f202 --- /dev/null +++ b/robusta/models/fixup.py @@ -0,0 +1,275 @@ +""" +Reference: +https://github.com/hongyi-zhang/Fixup/blob/master/imagenet/models/fixup_resnet_imagenet.py + +--- + +BSD 3-Clause License + +Copyright (c) 2019, +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" + +import torch +import torch.nn as nn +import numpy as np + + +__all__ = [ + "fixup_resnet18", + "fixup_resnet34", + "fixup_resnet50", + "fixup_resnet101", + "fixup_resnet152", +] + + +def conv3x3(in_planes, out_planes, stride=1): + """3x3 convolution with padding""" + return nn.Conv2d( + in_planes, out_planes, kernel_size=3, stride=stride, padding=1, bias=False + ) + + +def conv1x1(in_planes, out_planes, stride=1): + """1x1 convolution""" + return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False) + + +class FixupBasicBlock(nn.Module): + expansion = 1 + + def __init__(self, inplanes, planes, stride=1, downsample=None): + super(FixupBasicBlock, self).__init__() + # Both self.conv1 and self.downsample layers downsample the input when stride != 1 + self.bias1a = nn.Parameter(torch.zeros(1)) + self.conv1 = conv3x3(inplanes, planes, stride) + self.bias1b = nn.Parameter(torch.zeros(1)) + self.relu = nn.ReLU(inplace=True) + self.bias2a = nn.Parameter(torch.zeros(1)) + self.conv2 = conv3x3(planes, planes) + self.scale = nn.Parameter(torch.ones(1)) + self.bias2b = nn.Parameter(torch.zeros(1)) + self.downsample = downsample + self.stride = stride + + def forward(self, x): + identity = x + + out = self.conv1(x + self.bias1a) + out = self.relu(out + self.bias1b) + + out = self.conv2(out + self.bias2a) + out = out * self.scale + self.bias2b + + if self.downsample is not None: + identity = self.downsample(x + self.bias1a) + + out += identity + out = self.relu(out) + + return out + + +class FixupBottleneck(nn.Module): + expansion = 4 + + def __init__(self, inplanes, planes, stride=1, downsample=None): + super(FixupBottleneck, self).__init__() + # Both self.conv2 and self.downsample layers downsample the input when stride != 1 + self.bias1a = nn.Parameter(torch.zeros(1)) + self.conv1 = conv1x1(inplanes, planes) + self.bias1b = nn.Parameter(torch.zeros(1)) + self.bias2a = nn.Parameter(torch.zeros(1)) + self.conv2 = conv3x3(planes, planes, stride) + self.bias2b = nn.Parameter(torch.zeros(1)) + self.bias3a = nn.Parameter(torch.zeros(1)) + self.conv3 = conv1x1(planes, planes * self.expansion) + self.scale = nn.Parameter(torch.ones(1)) + self.bias3b = nn.Parameter(torch.zeros(1)) + self.relu = nn.ReLU(inplace=True) + self.downsample = downsample + self.stride = stride + + def forward(self, x): + identity = x + + out = self.conv1(x + self.bias1a) + out = self.relu(out + self.bias1b) + + out = self.conv2(out + self.bias2a) + out = self.relu(out + self.bias2b) + + out = self.conv3(out + self.bias3a) + out = out * self.scale + self.bias3b + + if self.downsample is not None: + identity = self.downsample(x + self.bias1a) + + out += identity + out = self.relu(out) + + return out + + +class FixupResNet(nn.Module): + def __init__(self, block, layers, num_classes=1000): + super(FixupResNet, self).__init__() + self.num_layers = sum(layers) + self.inplanes = 64 + self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False) + self.bias1 = nn.Parameter(torch.zeros(1)) + self.relu = nn.ReLU(inplace=True) + self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) + self.layer1 = self._make_layer(block, 64, layers[0]) + self.layer2 = self._make_layer(block, 128, layers[1], stride=2) + self.layer3 = self._make_layer(block, 256, layers[2], stride=2) + self.layer4 = self._make_layer(block, 512, layers[3], stride=2) + self.avgpool = nn.AdaptiveAvgPool2d((1, 1)) + self.bias2 = nn.Parameter(torch.zeros(1)) + self.fc = nn.Linear(512 * block.expansion, num_classes) + + for m in self.modules(): + if isinstance(m, FixupBasicBlock): + nn.init.normal_( + m.conv1.weight, + mean=0, + std=np.sqrt( + 2 + / (m.conv1.weight.shape[0] * np.prod(m.conv1.weight.shape[2:])) + ) + * self.num_layers ** (-0.5), + ) + nn.init.constant_(m.conv2.weight, 0) + if m.downsample is not None: + nn.init.normal_( + m.downsample.weight, + mean=0, + std=np.sqrt( + 2 + / ( + m.downsample.weight.shape[0] + * np.prod(m.downsample.weight.shape[2:]) + ) + ), + ) + elif isinstance(m, FixupBottleneck): + nn.init.normal_( + m.conv1.weight, + mean=0, + std=np.sqrt( + 2 + / (m.conv1.weight.shape[0] * np.prod(m.conv1.weight.shape[2:])) + ) + * self.num_layers ** (-0.25), + ) + nn.init.normal_( + m.conv2.weight, + mean=0, + std=np.sqrt( + 2 + / (m.conv2.weight.shape[0] * np.prod(m.conv2.weight.shape[2:])) + ) + * self.num_layers ** (-0.25), + ) + nn.init.constant_(m.conv3.weight, 0) + if m.downsample is not None: + nn.init.normal_( + m.downsample.weight, + mean=0, + std=np.sqrt( + 2 + / ( + m.downsample.weight.shape[0] + * np.prod(m.downsample.weight.shape[2:]) + ) + ), + ) + elif isinstance(m, nn.Linear): + nn.init.constant_(m.weight, 0) + nn.init.constant_(m.bias, 0) + + def _make_layer(self, block, planes, blocks, stride=1): + downsample = None + if stride != 1 or self.inplanes != planes * block.expansion: + downsample = conv1x1(self.inplanes, planes * block.expansion, stride) + + layers = [] + layers.append(block(self.inplanes, planes, stride, downsample)) + self.inplanes = planes * block.expansion + for _ in range(1, blocks): + layers.append(block(self.inplanes, planes)) + + return nn.Sequential(*layers) + + def forward(self, x): + x = self.conv1(x) + x = self.relu(x + self.bias1) + x = self.maxpool(x) + + x = self.layer1(x) + x = self.layer2(x) + x = self.layer3(x) + x = self.layer4(x) + + x = self.avgpool(x) + x = x.view(x.size(0), -1) + x = self.fc(x + self.bias2) + + return x + + +def fixup_resnet18(**kwargs): + """Constructs a Fixup-ResNet-18 model.""" + model = FixupResNet(FixupBasicBlock, [2, 2, 2, 2], **kwargs) + return model + + +def fixup_resnet34(**kwargs): + """Constructs a Fixup-ResNet-34 model.""" + model = FixupResNet(FixupBasicBlock, [3, 4, 6, 3], **kwargs) + return model + + +def fixup_resnet50(**kwargs): + """Constructs a Fixup-ResNet-50 model.""" + model = FixupResNet(FixupBottleneck, [3, 4, 6, 3], **kwargs) + return model + + +def fixup_resnet101(**kwargs): + """Constructs a Fixup-ResNet-101 model.""" + model = FixupResNet(FixupBottleneck, [3, 4, 23, 3], **kwargs) + return model + + +def fixup_resnet152(**kwargs): + """Constructs a Fixup-ResNet-152 model.""" + model = FixupResNet(FixupBottleneck, [3, 8, 36, 3], **kwargs) + return model diff --git a/robusta/models/imagenet_model.py b/robusta/models/imagenet_model.py new file mode 100644 index 0000000..d7a21e1 --- /dev/null +++ b/robusta/models/imagenet_model.py @@ -0,0 +1,40 @@ +import torch.nn as nn +import torch +import torchvision.models as models + + +class ZeroOneResNet50_parallel(nn.Module): + def __init__(self, device="cuda", pretrained=True): + super().__init__() + self.resnet = models.resnet50(pretrained=pretrained) + + self.mean = nn.Parameter( + torch.FloatTensor([0.485, 0.456, 0.406])[None, :, None, None], + requires_grad=False, + ) # asdf changed + self.std = nn.Parameter( + torch.FloatTensor([0.229, 0.224, 0.225])[None, :, None, None], + requires_grad=False, + ) + + def forward(self, input): + # input = (input - self.mean) / self.std + return self.resnet(input) + + +class ZeroOneInceptionV3(nn.Module): + def __init__(self, device="cuda", pretrained=False): + super().__init__() + self.inception = models.inception_v3(pretrained=True, transform_input=True) + self.mean = nn.Parameter( + torch.FloatTensor([0.485, 0.456, 0.406])[None, :, None, None], + requires_grad=False, + ) + self.std = nn.Parameter( + torch.FloatTensor([0.229, 0.224, 0.225])[None, :, None, None], + requires_grad=False, + ) + + def forward(self, input): + # input = (input - self.mean) / self.std + return self.inception(input) diff --git a/robusta/models/resnet_gn.py b/robusta/models/resnet_gn.py new file mode 100644 index 0000000..48220ca --- /dev/null +++ b/robusta/models/resnet_gn.py @@ -0,0 +1,147 @@ +import torch.nn as nn +import math +import torch.utils.model_zoo as model_zoo + + +__all__ = ["ResNet", "resnet50", "resnet101", "resnet152"] + + +def conv3x3(in_planes, out_planes, stride=1): + "3x3 convolution with padding" + return nn.Conv2d( + in_planes, out_planes, kernel_size=3, stride=stride, padding=1, bias=False + ) + + +class Bottleneck(nn.Module): + expansion = 4 + + def __init__(self, inplanes, planes, stride=1, downsample=None): + super(Bottleneck, self).__init__() + self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False) + self.bn1 = nn.GroupNorm(32, planes) + self.conv2 = nn.Conv2d( + planes, planes, kernel_size=3, stride=stride, padding=1, bias=False + ) + self.bn2 = nn.GroupNorm(32, planes) + self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False) + self.bn3 = nn.GroupNorm(32, planes * 4) + self.relu = nn.ReLU(inplace=True) + self.downsample = downsample + self.stride = stride + + gn_init(self.bn1) + gn_init(self.bn2) + gn_init(self.bn3, zero_init=True) + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + out = self.relu(out) + + out = self.conv3(out) + out = self.bn3(out) + + if self.downsample is not None: + residual = self.downsample(x) + + out += residual + out = self.relu(out) + + return out + + +def conv2d_init(m): + assert isinstance(m, nn.Conv2d) + n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels + m.weight.data.normal_(0, math.sqrt(2.0 / n)) + + +def gn_init(m, zero_init=False): + assert isinstance(m, nn.GroupNorm) + m.weight.data.fill_(0.0 if zero_init else 1.0) + m.bias.data.zero_() + + +class ResNet(nn.Module): + def __init__(self, block, layers, num_classes=1000): + self.inplanes = 64 + super(ResNet, self).__init__() + self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False) + self.bn1 = nn.GroupNorm(32, 64) + self.relu = nn.ReLU(inplace=True) + self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) + self.layer1 = self._make_layer(block, 64, layers[0]) + self.layer2 = self._make_layer(block, 128, layers[1], stride=2) + self.layer3 = self._make_layer(block, 256, layers[2], stride=2) + self.layer4 = self._make_layer(block, 512, layers[3], stride=2) + self.avgpool = nn.AvgPool2d(7, stride=1) + self.fc = nn.Linear(512 * block.expansion, num_classes) + + for m in self.modules(): + if isinstance(m, nn.Conv2d): + conv2d_init(m) + gn_init(self.bn1) + + def _make_layer(self, block, planes, blocks, stride=1): + downsample = None + if stride != 1 or self.inplanes != planes * block.expansion: + downsample = nn.Sequential( + nn.Conv2d( + self.inplanes, + planes * block.expansion, + kernel_size=1, + stride=stride, + bias=False, + ), + nn.GroupNorm(32, planes * block.expansion), + ) + m = downsample[1] + assert isinstance(m, nn.GroupNorm) + gn_init(m) + + layers = [] + layers.append(block(self.inplanes, planes, stride, downsample)) + self.inplanes = planes * block.expansion + for i in range(1, blocks): + layers.append(block(self.inplanes, planes)) + + return nn.Sequential(*layers) + + def forward(self, x): + x = self.conv1(x) + x = self.bn1(x) + x = self.relu(x) + x = self.maxpool(x) + + x = self.layer1(x) + x = self.layer2(x) + x = self.layer3(x) + x = self.layer4(x) + + x = self.avgpool(x) + x = x.view(x.size(0), -1) + x = self.fc(x) + + return x + + +def resnet50(**kwargs): + model = ResNet(Bottleneck, [3, 4, 6, 3], **kwargs) + return model + + +def resnet101(**kwargs): + model = ResNet(Bottleneck, [3, 4, 23, 3], **kwargs) + return model + + +def resnet152(**kwargs): + model = ResNet(Bottleneck, [3, 8, 36, 3], **kwargs) + return model diff --git a/robusta/selflearning/__init__.py b/robusta/selflearning/__init__.py new file mode 100644 index 0000000..ec0a4c0 --- /dev/null +++ b/robusta/selflearning/__init__.py @@ -0,0 +1,38 @@ +# Copyright 2020-2021 Evgenia Rusak, Steffen Schneider, George Pachitariu +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# --- +# This licence notice applies to all originally written code by the +# authors. Code taken from other open-source projects is indicated. +# See NOTICE for a list of all third-party licences used in the project. + +import torch.nn + +from robusta.selflearning import functional +from robusta.selflearning.nn import EntropyLoss +from robusta.selflearning.nn import GeneralizedCrossEntropy + + +def _iter_params(model): + for module in model.modules(): + if isinstance(module, torch.nn.BatchNorm2d): + for parameter in module.parameters(): + yield parameter + + +def adapt(model, adapt_type="affine"): + if adapt_type not in ["affine"]: + raise ValueError(adapt_type) + return iter(_iter_params(model)) diff --git a/robusta/selflearning/functional.py b/robusta/selflearning/functional.py new file mode 100644 index 0000000..ed86b90 --- /dev/null +++ b/robusta/selflearning/functional.py @@ -0,0 +1,39 @@ +# Copyright 2020-2021 Evgenia Rusak, Steffen Schneider, George Pachitariu +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# --- +# This licence notice applies to all originally written code by the +# authors. Code taken from other open-source projects is indicated. +# See NOTICE for a list of all third-party licences used in the project. + +import torch.nn.functional as F + +def gce(logits, target, q = 0.8): + """ Generalized cross entropy. + + Reference: https://arxiv.org/abs/1805.07836 + """ + probs = F.softmax(logits, dim=1) + probs_with_correct_idx = probs.index_select(-1, target).diag() + loss = (1. - probs_with_correct_idx**q) / q + return loss.mean() + +def entropy(logits, target, q = 0.8): + """ Entropy. + + """ + log_probs = F.log_softmax(logits, dim=1) + probs = F.softmax(logits, dim=1) + return -(probs * log_probs).sum(dim=-1).mean() \ No newline at end of file diff --git a/robusta/selflearning/nn.py b/robusta/selflearning/nn.py new file mode 100644 index 0000000..e0117a0 --- /dev/null +++ b/robusta/selflearning/nn.py @@ -0,0 +1,46 @@ +# Copyright 2020-2021 Evgenia Rusak, Steffen Schneider, George Pachitariu +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# --- +# This licence notice applies to all originally written code by the +# authors. Code taken from other open-source projects is indicated. +# See NOTICE for a list of all third-party licences used in the project. + +from torch import nn +import robusta.selflearning.functional as RF + +class GeneralizedCrossEntropy(nn.Module): + + def __init__(self, q = 0.8): + super().__init__() + self.q = q + + def forward(self, logits, target = None): + if target is None: + target = logits.argmax(dim = 1) + return RF.gce(logits, target, self.q) + +class EntropyLoss(nn.Module): + + def __init__(self, stop_teacher_gradient = False): + super().__init__() + self.stop_teacher_gradient = stop_teacher_gradient + + def forward(self, logits, target = None): + if target is None: + target = logits + if self.top_teacher_gradient: + target = target.detach() + return RF.entropy(logits, target, self.q) \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..38ec34a --- /dev/null +++ b/setup.py @@ -0,0 +1,26 @@ +import setuptools + +#with open("README.md", "r", encoding="utf-8") as fh: +# long_description = fh.read() + +setuptools.setup( + name="robusta", + version="0.0.1", + author="Evgenia Rusak, Steffen Schneider, George Pachitariu", + author_email="steffen@bethgelab.org", + description="A pytorch package for robustness and adaptation research", + long_description="# A pytorch package for robustness and adaptation research ☕", + long_description_content_type="text/markdown", + url="https://github.com/bethgelab/robustness", + project_urls={ + "Bug Tracker": "https://github.com/bethgelab/robustness/issues" + }, + classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + ], + package_dir={"": "robusta"}, + packages=setuptools.find_packages(where="robusta"), + python_requires=">=3.6", +) \ No newline at end of file diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..b1dc30c --- /dev/null +++ b/test.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +python -m pytest test \ No newline at end of file diff --git a/test/dummy_datasets/ImageNet-C/gaussian_blur/1/n01440764/ILSVRC2012_val_00000293.jpeg b/test/dummy_datasets/ImageNet-C/gaussian_blur/1/n01440764/ILSVRC2012_val_00000293.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..1a5081b757933e0b6a259d0074bf4a22e6ea0a05 GIT binary patch literal 6277 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≤g$bKIn? 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