diff --git a/.codacy.yaml b/.codacy.yaml new file mode 100644 index 0000000..14735f0 --- /dev/null +++ b/.codacy.yaml @@ -0,0 +1,9 @@ +# https://support.codacy.com/hc/en-us/articles/115002130625-Codacy-Configuration-File +--- +engines: + bandit: + enabled: false # FIXME: make it work +exclude_paths: +- scripts/* +- setup.py +- docker/**/* diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..6b8710a --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +.git diff --git a/.gitignore b/.gitignore index 94ca90d..4a6f60f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,87 +1,131 @@ -# ---> Android -# Built application files -*.apk -*.aar -*.ap_ -*.aab - -# Files for the ART/Dalvik VM -*.dex - -# Java class files -*.class - -# Generated files -bin/ -gen/ -out/ -# Uncomment the following line in case you need and you don't have the release build type files in your app -# release/ - -# Gradle files -.gradle/ +# 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/ +*.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/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 -# Local configuration file (sdk path, etc) -local.properties +# Flask stuff: +instance/ +.webassets-cache -# Proguard folder generated by Eclipse -proguard/ +# Scrapy stuff: +.scrapy -# Log Files -*.log +# Sphinx documentation +docs/_build/ +docs/test_build/ +docs/build_test/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +venv_/ +venv2/ +venv3/ +venv_doc/ +venv_py2/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + + +# IDE Specific directories +.DS_Store +.idea +.vscode/ + +# TensorLayer Directories +checkpoints +data/ +lib_win/ -# Android Studio Navigation editor temp files -.navigation/ - -# Android Studio captures folder -captures/ - -# IntelliJ -*.iml -.idea/workspace.xml -.idea/tasks.xml -.idea/gradle.xml -.idea/assetWizardSettings.xml -.idea/dictionaries -.idea/libraries -# Android Studio 3 in .gitignore file. -.idea/caches -.idea/modules.xml -# Comment next line if keeping position of elements in Navigation Editor is relevant for you -.idea/navEditor.xml - -# Keystore files -# Uncomment the following lines if you do not want to check your keystore files in. -#*.jks -#*.keystore - -# External native build folder generated in Android Studio 2.2 and later -.externalNativeBuild -.cxx/ - -# Google Services (e.g. APIs or Firebase) -# google-services.json - -# Freeline -freeline.py -freeline/ -freeline_project_description.json - -# fastlane -fastlane/report.xml -fastlane/Preview.html -fastlane/screenshots -fastlane/test_output -fastlane/readme.md - -# Version control -vcs.xml - -# lint -lint/intermediates/ -lint/generated/ -lint/outputs/ -lint/tmp/ -# lint/reports/ +# Custom Scripts +update_tl.bat +update_tl.py +# Data Files and ByteCode files +*.gz +*.npz diff --git a/.pyup.yml b/.pyup.yml new file mode 100644 index 0000000..6c96920 --- /dev/null +++ b/.pyup.yml @@ -0,0 +1,75 @@ +############################################################################ +# see https://pyup.io/docs/configuration/ for all available options # +############################################################################ + +# configure updates globally +# default: all +# allowed: all, insecure, False +update: all + +# configure dependency pinning globally +# default: True +# allowed: True, False +pin: False + +# set the default branch +# default: empty, the default branch on GitHub +branch: master + +# update schedule +# default: empty +# allowed: "every day", "every week", .. +schedule: "every day" + +# search for requirement files +# default: True +# allowed: True, False +search: False + +# Specify requirement files by hand, default is empty +# default: empty +# allowed: list +requirements: + # Requirements for the library + - requirements/requirements.txt + + # Requirements for the development + - requirements/requirements_tf_cpu.txt + + # Requirements for the development + - requirements/requirements_tf_gpu.txt + + # Not necessary, but recommended libraries + - requirements/requirements_extra.txt + + # Requirements for contrib loggers + - requirements_contrib_loggers.txt + + # Requirements for the db + - requirements/requirements_db.txt + + # Requirements for the development + - requirements/requirements_dev.txt + + # Requirements for building docs + - requirements/requirements_doc.txt + + # Requirements for running unittests + - requirements/requirements_test.txt + +# add a label to pull requests, default is not set +# requires private repo permissions, even on public repos +# default: empty +#label_prs: update + +# configure the branch prefix the bot is using +# default: pyup- +branch_prefix: pyup- + +# set a global prefix for PRs +# default: empty +pr_prefix: "PyUP - Dependency Update" + +# allow to close stale PRs +# default: True +close_prs: True diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000..0752675 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,14 @@ +# https://docs.readthedocs.io/en/latest/yaml-config.html + +build: + image: latest # For python 3.6 + +formats: + - epub + - pdf + +python: + version: 3.6 + +requirements_file: + requirements/requirements_doc.txt diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..63cda3d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,119 @@ +# https://docs.travis-ci.com/user/languages/python/ +language: python + +# https://docs.travis-ci.com/user/caching/#pip-cache +cache: + directories: + - $HOME/.cache/pip/wheels + +addons: + apt: + update: false + +branches: + only: + - master + - TensorLayer-2.x + - /^\d+\.\d+(\.\d+)?(\S*)?$/ + +python: + - "3.6" + - "3.5" +# - "2.7" # TensorLayer 2.0 does not support python2 now + +env: + + # Backward Compatibility in insured for release less than 1 year old. + # https://pypi.org/project/tensorflow/#history + matrix: + - _TF_VERSION=2.0.0-rc1 +# - _TF_VERSION=1.12.0 # Remove on Oct 22, 2019 +# - _TF_VERSION=1.11.0 # Remove on Sep 28, 2019 +# - _TF_VERSION=1.10.1 # Remove on Aug 24, 2019 +# - _TF_VERSION=1.9.0 # Remove on Jul 10, 2019 +# - _TF_VERSION=1.8.0 # Remove on Apr 28, 2019 +# - _TF_VERSION=1.7.1 # Remove on May 08, 2019 +# - _TF_VERSION=1.7.0 # Remove on Mar 29, 2019 +# - _TF_VERSION=1.6.0 # Remove on Mar 01, 2019 + + global: + + - PYPI_USER='jonathandekhtiar' + + # See https://docs.travis-ci.com/user/encryption-keys/ for more details about secure keys. + + ### == PYPI_PASSWORD === ### + ## To update: travis encrypt PYPI_PASSWORD=################################ + - secure: "fGIRDjfzzP9DhdDshgh/+bWTZ5Y0jTD4aR+gsT1TyAyc6N4f3RRlx70xZZwYMdQ+XC3no/q4na8UzhhuSM0hCCM1EaQ78WF1c6+FBScf4vYGoYgyJ1am+4gu54JXt+4f0bd+s6jyYBafJALUJp5fqHoxCUXqzjrOqGBBU2+JbL71Aaj8yhQuK0VPPABexsQPQM312Gvzg7hy9dh63J0Q02PqINn+CTcwq3gLH9Oua58zWQ7TaT0cdy/hzAc6Yxy3ajo2W5NU+nKROaaG9W57sa7K/v1dshDFFFST2DdGxm9i7vvfPsq0OWM6qWLsec/4mXJWsmai2ygZEv+IhaABb10c7spd2nl7oHFj2UGmldtO5W0zLb1KkCPWDPilFt3lvHM+OS/YaibquL5/5+yGj0LsRJrVyWoMBA8idcQeH4dvTAfySeFpO42VNwW5ez9JaEOh7bBp7naAA8c/fbNJJ5YEW4MEmOZ9dwFTohNNDiN+oITSrcXBS+jukbfTOmtCeYNUker+4G2YwII9cxHXbZeIMrTq9AqTfOVTAYCFaFHKbpSc1+HCyF7n5ZfNC00kBaw93XUnLRzSNKe5Ir791momYL8HecMN3OAI77bz26/pHSfzJnLntq9qx2nLBTnqDuSq5/pHvdZ8hyt+hTDxvF7HJIVMhbnkjoLPxmn4k/I=" + + ### === GITHUB_PERSONAL_TOKEN === ### + ## To update: travis encrypt GITHUB_PERSONAL_TOKEN=################################ + - secure: "kMxg4WfTwhnYMD7WjYk71vgw7XlShPpANauKzfTL6oawDrpQRkBUai4uQwiL3kXVBuVv9rKKKZxxnyAm05iB5wGasPDhlFA1DPF0IbyU3pwQKlw2Xo5qtHdgxBnbms6JJ9z5b+hHCVg+LXYYeUw5qG01Osg5Ue6j1g2juQQHCod00FNuo3fe8ah/Y10Rem6CigH8ofosCrTvN2w1GaetJwVehRYf8JkPC6vQ+Yk8IIjHn2CaVJALbhuchVblxwH0NXXRen915BVBwWRQtsrvEVMXKu7A8sMHmvPz1u3rhXQfjpF2KeVOfy1ZnyiHcLE2HgAPuAGh4kxZAAA8ovmcaCbv8m64bm72BrQApSbt6OEtR9L1UeUwdEgi54FH1XFOHQ9dA6CpiGCyk3wAJZqO0/PkNYVLfb4gPLvnvBRwYBaPgUPvVNhidFu/oODENZmcf8g9ChtuC1GT70EYlVwhgDGqUY7/USZCEvIPe81UToqtIKgcgA+Is51XindumJVMiqTsjgdqeC/wZcw+y37TAjIvvXbtYxeqIKv9zh1JuZppqUhnf+OhI+HHFaY4iu7lQTs3C0WmoLskZAp9srwRtifnVFFkdYzngmPaSjWyko2qiS0cTdFJQB/ljqmnJdksacbv5OOa0Q4qZef/hW774nVx105FlkAIk70D2b5l2pA=" + + ### === GITHUB_PERSONAL_TOKEN === ### + ## To update: travis encrypt HYPERDASH_APIKEY=################################ + - secure: "ez9Ck1VpqWOd2PPu+CMWzd8R4aAIXbjKCk96PCKwWu8VXoHjaPkiy8Nn0LUzSlUg3nKdZmu2JSndwDMy3+lMLG7zE2WlGNY7MF5KM3GrvFpP3cxJQ6OuPcZcEH4j5KtBtNTrNqa8SWglqhc9mr66a92SD8Ydc4aMj6L9nbQvrsvVzIMmMy6weVlpBF35nweYCM8LxlsnqyPLleHPZo3o/k+hsTqQQbiMGjC78tqrGr56u7AjL2+D/m33+dfCGzFvMJFcpLQ5ldbcVU54i5e6V3xJ48P30QOGZaqG3fcpfZsyJEIWjykt6XFA8GfJjaVVbxdlr7zP7Vd9iWBuemnMEX3F9Cy/4x7LmX9PJfsVPC6FQnanDvsZSNO5hpmKe8BTpttJJvxgscOczV4jnI69OzqhSQeyChwtkqhIg1E/53XIO+uLJAAZsCkAco7tjGGXTKyv8ZlpSJwSqsLcmgpmQbfodCoMLcYenTxqKZv78e2B4tOPGQyS2bkSxAqhvAIam7RCq/yEvz5n2/mBFEGwP6OQFIdC7ypO2LyrOlLT7HJjCeYMeKSm+GOD3LW9oIy9QJZpG6N/zAAjnk9C2mYtWRQIBo4qdHjRvyDReevDexI8j0AXySblxREmQ7ZaT6KEDXXZSu5goTlaGm0g2HwAkKu9xYFV/bRtp6+i1mP7CQg=" + +matrix: + include: + - python: '3.6' + env: + - _DOC_AND_YAPF_TEST=True + +install: + - | + if [[ -v _DOC_AND_YAPF_TEST ]]; then + pip install tensorflow==2.0.0-rc1 + pip install yapf + pip install -e .[doc] + else + pip install tensorflow==$_TF_VERSION + pip install -e .[all_cpu_dev] + fi + +script: + # units test + # https://docs.pytest.org/en/latest/ + - rm setup.cfg + - | + if [[ -v _DOC_AND_YAPF_TEST ]]; then + mv setup.travis_doc.cfg setup.cfg + else + mv setup.travis.cfg setup.cfg + fi + - pytest + + +before_deploy: + - python setup.py sdist + - python setup.py bdist_wheel + - python setup.py bdist_wheel --universal + - python setup.py egg_info + + +deploy: + +# Documentation: https://docs.travis-ci.com/user/deployment/pypi/ +- provider: pypi + user: '$PYPI_USER' + password: '$PYPI_PASSWORD' + skip_cleanup: true + on: + tags: true + python: '3.6' + condition: '$_TF_VERSION = 2.0.0-rc1' +# condition: '$_TF_VERSION = 1.11.0' + +# Documentation: https://docs.travis-ci.com/user/deployment/releases/ +- provider: releases + file: + - dist/* + - tensorlayer.egg-info/PKG-INFO + file_glob: true + skip_cleanup: true + api_key: '$GITHUB_PERSONAL_TOKEN' + on: + tags: true + python: '3.6' + condition: '$_TF_VERSION = 2.0.0-rc1' +# condition: '$_TF_VERSION = 1.11.0' diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a19b722 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,585 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + + + + + + +## [Unreleased] + +### Added + +### Changed + +### Dependencies Update + +### Deprecated + +### Fixed + +- Fix README. (#PR 1044) +- Fix package info. (#PR 1046) + +### Removed + +### Security + +### Contributors + +- @luomai (PR #1044, 1046) + + +## [2.2.0] - 2019-09-13 + +TensorLayer 2.2.0 is a maintenance release. +It contains numerous API improvement and bug fixes. +This release is compatible with TensorFlow 2 RC1. + +### Added +- Support nested layer customization (#PR 1015) +- Support string dtype in InputLayer (#PR 1017) +- Support Dynamic RNN in RNN (#PR 1023) +- Add ResNet50 static model (#PR 1030) +- Add performance test code in static model (#PR 1041) + +### Changed + +- `SpatialTransform2dAffine` auto `in_channels` +- support TensorFlow 2.0.0-rc1 +- Update model weights property, now returns its copy (#PR 1010) + +### Fixed +- RNN updates: remove warnings, fix if seq_len=0, unitest (#PR 1033) +- BN updates: fix BatchNorm1d for 2D data, refactored (#PR 1040) + +### Dependencies Update + +### Deprecated + +### Fixed +- Fix `tf.models.Model._construct_graph` for list of outputs, e.g. STN case (PR #1010) +- Enable better `in_channels` exception raise. (PR #1015) +- Set allow_pickle=True in np.load() (#PR 1021) +- Remove `private_method` decorator (#PR 1025) +- Copy original model's `trainable_weights` and `nontrainable_weights` when initializing `ModelLayer` (#PR 1026) +- Copy original model's `trainable_weights` and `nontrainable_weights` when initializing `LayerList` (#PR 1029) +- Remove redundant parts in `model.all_layers` (#PR 1029) +- Replace `tf.image.resize_image_with_crop_or_pad` with `tf.image.resize_with_crop_or_pad` (#PR 1032) +- Fix a bug in `ResNet50` static model (#PR 1041) + +### Removed + +### Security + +### Contributors + +- @zsdonghao +- @luomai +- @ChrisWu1997: #1010 #1015 #1025 #1030 #1040 +- @warshallrho: #1017 #1021 #1026 #1029 #1032 #1041 +- @ArnoldLIULJ: #1023 +- @JingqingZ: #1023 + +## [2.1.0] + +### Changed +- Add version_info in model.config. (PR #992) +- Replace tf.nn.func with tf.nn.func.\_\_name\_\_ in model config. (PR #994) +- Add Reinforcement learning tutorials. (PR #995) +- Add RNN layers with simple rnn cell, GRU cell, LSTM cell. (PR #998) +- Update Seq2seq (#998) +- Add Seq2seqLuongAttention model (#998) + +### Fixed + +### Contributors +- @warshallrho: #992 #994 +- @quantumiracle: #995 +- @Tokarev-TT-33: #995 +- @initial-h: #995 +- @Officium: #995 +- @ArnoldLIULJ: #998 +- @JingqingZ: #998 + + +## [2.0.2] - 2019-6-5 + +### Changed +- change the format of network config, change related code and files; change layer act (PR #980) + +### Fixed +- Fix dynamic model cannot track PRelu weights gradients problem (PR #982) +- Raise .weights warning (commit) + +### Contributors +- @warshallrho: #980 +- @1FengL: #982 + +## [2.0.1] - 2019-5-17 + + +A maintain release. + +### Changed +- remove `tl.layers.initialize_global_variables(sess)` (PR #931) +- support `trainable_weights` (PR #966) + +### Added + - Layer + - `InstanceNorm`, `InstanceNorm1d`, `InstanceNorm2d`, `InstanceNorm3d` (PR #963) + +* Reinforcement learning tutorials. (PR #995) + +### Changed +- remove `tl.layers.initialize_global_variables(sess)` (PR #931) +- update `tutorial_generate_text.py`, `tutorial_ptb_lstm.py`. remove `tutorial_ptb_lstm_state_is_tuple.py` (PR #958) +- change `tl.layers.core`, `tl.models.core` (PR #966) +- change `weights` into `all_weights`, `trainable_weights`, `nontrainable_weights` + +### Dependencies Update +- nltk>=3.3,<3.4 => nltk>=3.3,<3.5 (PR #892) +- pytest>=3.6,<3.11 => pytest>=3.6,<4.1 (PR #889) +- yapf>=0.22,<0.25 => yapf==0.25.0 (PR #896) +- imageio==2.5.0 progressbar2==3.39.3 scikit-learn==0.21.0 scikit-image==0.15.0 scipy==1.2.1 wrapt==1.11.1 pymongo==3.8.0 sphinx==2.0.1 wrapt==1.11.1 opencv-python==4.1.0.25 requests==2.21.0 tqdm==4.31.1 lxml==4.3.3 pycodestyle==2.5.0 sphinx==2.0.1 yapf==0.27.0(PR #967) + +### Fixed +- fix docs of models @zsdonghao #957 +- In `BatchNorm`, keep dimensions of mean and variance to suit `channels first` (PR #963) + +### Contributors +- @warshallrho: #PR966 +- @zsdonghao: #931 +- @yd-yin: #963 +- @Tokarev-TT-33: # 995 +- @initial-h: # 995 +- @quantumiracle: #995 +- @Officium: #995 +- @1FengL: #958 +- @dvklopfenstein: #971 + + +## [2.0.0] - 2019-05-04 + +To many PR for this update, please check [here](https://github.com/tensorlayer/tensorlayer/releases/tag/2.0.0) for more details. + +### Changed +* update for TensorLayer 2.0.0 alpha version (PR #952) +* support TensorFlow 2.0.0-alpha +* support both static and dynamic model building + +### Dependencies Update +- tensorflow>=1.6,<1.13 => tensorflow>=2.0.0-alpha (PR #952) +- h5py>=2.9 (PR #952) +- cloudpickle>=0.8.1 (PR #952) +- remove matplotlib + +### Contributors +- @zsdonghao +- @JingqingZ +- @ChrisWu1997 +- @warshallrho + + +## [1.11.1] - 2018-11-15 + +### Changed +* guide for pose estimation - flipping (PR #884) +* cv2 transform support 2 modes (PR #885) + +### Dependencies Update +- pytest>=3.6,<3.9 => pytest>=3.6,<3.10 (PR #874) +- requests>=2.19,<2.20 => requests>=2.19,<2.21 (PR #874) +- tqdm>=4.23,<4.28 => tqdm>=4.23,<4.29 (PR #878) +- pytest>=3.6,<3.10 => pytest>=3.6,<3.11 (PR #886) +- pytest-xdist>=1.22,<1.24 => pytest-xdist>=1.22,<1.25 (PR #883) +- tensorflow>=1.6,<1.12 => tensorflow>=1.6,<1.13 (PR #886) + +### Contributors +- @zsdonghao: #884 #885 + +## [1.11.0] - 2018-10-18 + +### Added +- Layer: + - Release `GroupNormLayer` (PR #850) +- Image affine transformation APIs + - `affine_rotation_matrix` (PR #857) + - `affine_horizontal_flip_matrix` (PR #857) + - `affine_vertical_flip_matrix` (PR #857) + - `affine_shift_matrix` (PR #857) + - `affine_shear_matrix` (PR #857) + - `affine_zoom_matrix` (PR #857) + - `affine_transform_cv2` (PR #857) + - `affine_transform_keypoints` (PR #857) +- Affine transformation tutorial + - `examples/data_process/tutorial_fast_affine_transform.py` (PR #857) + +### Changed +- BatchNormLayer: support `data_format` + +### Dependencies Update +- matplotlib>=2.2,<2.3 => matplotlib>=2.2,<3.1 (PR #845) +- pydocstyle>=2.1,<2.2 => pydocstyle>=2.1,<3.1 (PR #866) +- scikit-learn>=0.19,<0.20 => scikit-learn>=0.19,<0.21 (PR #851) +- sphinx>=1.7,<1.8 => sphinx>=1.7,<1.9 (PR #842) +- tensorflow>=1.6,<1.11 => tensorflow>=1.6,<1.12 (PR #853) +- tqdm>=4.23,<4.26 => tqdm>=4.23,<4.28 (PR #862 & #868) +- yapf>=0.22,<0.24 => yapf>=0.22,<0.25 (PR #829) + +### Fixed +- Correct offset calculation in `tl.prepro.transform_matrix_offset_center` (PR #855) + +### Contributors +- @2wins: #850 #855 +- @DEKHTIARJonathan: #853 +- @zsdonghao: #857 +- @luomai: #857 + +## [1.10.1] - 2018-09-07 + +### Added +- unittest `tests\test_timeout.py` has been added to ensure the network creation process does not freeze. + +### Changed + - remove 'tensorboard' param, replaced by 'tensorboard_dir' in `tensorlayer/utils.py` with customizable tensorboard directory (PR #819) + +### Removed +- TL Graph API removed. Memory Leaks Issues with this API, will be fixed and integrated in TL 2.0 (PR #818) + +### Fixed +- Issue #817 fixed: TL 1.10.0 - Memory Leaks and very slow network creation. + +### Dependencies Update +- autopep8>=1.3,<1.4 => autopep8>=1.3,<1.5 (PR #815) +- imageio>=2.3,<2.4 => imageio>=2.3,<2.5 (PR #823) +- pytest>=3.6,<3.8 => pytest>=3.6,<3.9 (PR #823) +- pytest-cov>=2.5,<2.6 => pytest-cov>=2.5,<2.7 (PR #820) + +### Contributors +- @DEKHTIARJonathan: #815 #818 #820 #823 +- @ndiy: #819 +- @zsdonghao: #818 + + +## [1.10.0] - 2018-09-02 + +### Added +- API: + - Add `tl.model.vgg19` (PR #698) + - Add `tl.logging.contrib.hyperdash` (PR #739) + - Add `tl.distributed.trainer` (PR #700) + - Add `prefetch_buffer_size` to the `tl.distributed.trainer` (PR #766) + - Add `tl.db.TensorHub` (PR #751) + - Add `tl.files.save_graph` (PR #751) + - Add `tl.files.load_graph_` (PR #751) + - Add `tl.files.save_graph_and_params` (PR #751) + - Add `tl.files.load_graph_and_params` (PR #751) + - Add `tl.prepro.keypoint_random_xxx` (PR #787) +- Documentation: + - Add binary, ternary and dorefa links (PR #711) + - Update input scale of VGG16 and VGG19 to 0~1 (PR #736) + - Update database (PR #751) +- Layer: + - Release SwitchNormLayer (PR #737) + - Release QuanConv2d, QuanConv2dWithBN, QuanDenseLayer, QuanDenseLayerWithBN (PR#735) + - Update Core Layer to support graph (PR #751) + - All Pooling layers support `data_format` (PR #809) +- Setup: + - Creation of installation flaggs `all_dev`, `all_cpu_dev`, and `all_gpu_dev` (PR #739) +- Examples: + - change folder struction (PR #802) + - `tutorial_models_vgg19` has been introduced to show how to use `tl.model.vgg19` (PR #698). + - fix bug of `tutorial_bipedalwalker_a3c_continuous_action.py` (PR #734, Issue #732) + - `tutorial_models_vgg16` and `tutorial_models_vgg19` has been changed the input scale from [0,255] to [0,1](PR #710) + - `tutorial_mnist_distributed_trainer.py` and `tutorial_cifar10_distributed_trainer.py` are added to explain the uses of Distributed Trainer (PR #700) + - add `tutorial_quanconv_cifar10.py` and `tutorial_quanconv_mnist.py` (PR #735) + - add `tutorial_work_with_onnx.py`(PR #775) +- Applications: + - [Arbitrary Style Transfer in Real-time with Adaptive Instance Normalization](https://arxiv.org/abs/1703.06868) (PR #799) + +### Changed + - function minibatches changed to avoid wasting samples.(PR #762) + - all the input scale in both vgg16 and vgg19 has been changed the input scale from [0,255] to [0,1](PR #710) + - Dockerfiles merged and refactored into one file (PR #747) + - LazyImports move to the most **top level** imports as possible (PR #739) + - some new test functions have been added in `test_layers_convolution.py`, `test_layers_normalization.py`, `test_layers_core.py` (PR #735) + - documentation now uses mock imports reducing the number of dependencies to compile the documentation (PR #785) + - fixed and enforced pydocstyle D210, D200, D301, D207, D403, D204, D412, D402, D300, D208 (PR #784) + +### Deprecated + - `tl.logging.warn` has been deprecated in favor of `tl.logging.warning` (PR #739) + +### Removed + - `conv_layers()` has been removed in both vgg16 and vgg19(PR #710) + - graph API (PR #818) + +### Fixed +- import error caused by matplotlib on OSX (PR #705) +- missing import in tl.prepro (PR #712) +- Dockerfiles import error fixed - issue #733 (PR #747) +- Fix a typo in `absolute_difference_error` in file: `tensorlayer/cost.py` - Issue #753 (PR #759) +- Fix the bug of scaling the learning rate of trainer (PR #776) +- log error instead of info when npz file not found. (PR #812) + +### Dependencies Update +- numpy>=1.14,<1.15 => numpy>=1.14,<1.16 (PR #754) +- pymongo>=3.6,<3.7 => pymongo>=3.6,<3.8 (PR #750) +- pytest>=3.6,<3.7 => tqdm>=3.6,<3.8 (PR #798) +- pytest-xdist>=1.22,<1.23 => pytest-xdist>=1.22,<1.24 (PR #805 and #806) +- tensorflow>=1.8,<1.9 => tensorflow>=1.6,<1.11 (PR #739 and PR #798) +- tqdm>=4.23,<4.25 => tqdm>=4.23,<4.26 (PR #798) +- yapf>=0.21,<0.22 => yapf>=0.22,<0.24 (PR #798 #808) + +### Contributors +- @DEKHTIARJonathan: #739 #747 #750 #754 +- @lgarithm: #705 #700 +- @OwenLiuzZ: #698 #710 #775 #776 +- @zsdonghao: #711 #712 #734 #736 #737 #700 #751 #809 #818 +- @luomai: #700 #751 #766 #802 +- @XJTUWYD: #735 +- @mutewall: #735 +- @thangvubk: #759 +- @JunbinWang: #796 +- @boldjoel: #787 + +## [1.9.1] - 2018-07-30 + +### Fixed +- Issue with tensorflow 1.10.0 fixed + +## [1.9.0] - 2018-06-16 + +### Added +- API: + - `tl.alphas` and `tl.alphas_like` added following the tf.ones/zeros and tf.zeros_like/ones_like (PR #580) + - `tl.lazy_imports.LazyImport` to import heavy libraries only when necessary (PR #667) + - `tl.act.leaky_relu6` and `tl.layers.PRelu6Layer` have been deprecated (PR #686) + - `tl.act.leaky_twice_relu6` and `tl.layers.PTRelu6Layer` have been deprecated (PR #686) +- CI Tool: + - [Stale Probot](https://github.com/probot/stale) added to clean stale issues (PR #573) + - [Changelog Probot](https://github.com/mikz/probot-changelog) Configuration added (PR #637) + - Travis Builds now handling a matrix of TF Version from TF==1.6.0 to TF==1.8.0 (PR #644) + - CircleCI added to build and upload Docker Containers for each PR merged and tag release (PR #648) +- Decorator: + - `tl.decorators` API created including `deprecated_alias` and `private_method` (PR #660) + - `tl.decorators` API enriched with `protected_method` (PR #675) + - `tl.decorators` API enriched with `deprecated` directly raising warning and modifying documentation (PR #691) +- Docker: + - Containers for each release and for each PR merged on master built (PR #648) + - Containers built in the following configurations (PR #648): + - py2 + cpu + - py2 + gpu + - py3 + cpu + - py3 + gpu +- Documentation: + - Clean README.md (PR #677) + - Release semantic version added on index page (PR #633) + - Optimizers page added (PR #636) + - `AMSGrad` added on Optimizers page added (PR #636) +- Layer: + - ElementwiseLambdaLayer added to use custom function to connect multiple layer inputs (PR #579) + - AtrousDeConv2dLayer added (PR #662) + - Fix bugs of using `tf.layers` in CNN (PR #686) +- Optimizer: + + - AMSGrad Optimizer added based on `On the Convergence of Adam and Beyond (ICLR 2018)` (PR #636) +- Setup: + + - Creation of installation flaggs `all`, `all_cpu`, and `all_gpu` (PR #660) +- Test: + - `test_utils_predict.py` added to reproduce and fix issue #288 (PR #566) + - `Layer_DeformableConvolution_Test` added to reproduce issue #572 with deformable convolution (PR #573) + - `Array_Op_Alphas_Test` and `Array_Op_Alphas_Like_Test` added to test `tensorlayer/array_ops.py` file (PR #580) + - `test_optimizer_amsgrad.py` added to test `AMSGrad` optimizer (PR #636) + - `test_logging.py` added to insure robustness of the logging API (PR #645) + - `test_decorators.py` added (PR #660) + - `test_activations.py` added (PR #686) +- Tutorials: + - `tutorial_tfslim` has been introduced to show how to use `SlimNetsLayer` (PR #560). + - add the following to all tutorials (PR #697): + ```python + tf.logging.set_verbosity(tf.logging.DEBUG) + tl.logging.set_verbosity(tl.logging.DEBUG) + ``` + +### Changed +- Tensorflow CPU & GPU dependencies moved to separated requirement files in order to allow PyUP.io to parse them (PR #573) +- The document of LambdaLayer for linking it with ElementwiseLambdaLayer (PR #587) +- RTD links point to stable documentation instead of latest used for development (PR #633) +- TF Version older than 1.6.0 are officially unsupported and raises an exception (PR #644) +- README.md Badges Updated with Support Python and Tensorflow Versions (PR #644) +- TL logging API has been consistent with TF logging API and thread-safe (PR #645) +- Relative Imports changed for absolute imports (PR #657) +- `tl.files` refactored into a directory with numerous files (PR #657) +- `tl.files.voc_dataset` fixed because of original Pascal VOC website was down (PR #657) +- extra requirements hidden inside the library added in the project requirements (PR #657) +- requirements files refactored in `requirements/` directory (PR #657) +- README.md and other markdown files have been refactored and cleaned. (PR #639) +- Ternary Convolution Layer added in unittest (PR #658) +- Convolution Layers unittests have been cleaned & refactored (PR #658) +- All the tests are now using a DEBUG level verbosity when run individualy (PR #660) +- `tf.identity` as activation is **ignored**, thus reducing the size of the graph by removing useless operation (PR #667) +- argument dictionaries are now checked and saved within the `Layer` Base Class (PR #667) +- `Layer` Base Class now presenting methods to update faultlessly `all_layers`, `all_params`, and `all_drop` (PR #675) +- Input Layers have been removed from `tl.layers.core` and added to `tl.layers.inputs` (PR #675) +- Input Layers are now considered as true layers in the graph (they represent a placeholder), unittests have been updated (PR #675) +- Layer API is simplified, with automatic feeding `prev_layer` into `self.inputs` (PR #675) +- Complete Documentation Refactoring and Reorganization (namely Layer APIs) (PR #691) + +### Deprecated +- `tl.layers.TimeDistributedLayer` argurment `args` is deprecated in favor of `layer_args` (PR #667) +- `tl.act.leaky_relu` have been deprecated in favor of `tf.nn.leaky_relu` (PR #686) + +### Removed +- `assert()` calls remove and replaced by `raise AssertionError()` (PR #667) +- `tl.identity` is removed, not used anymore and deprecated for a long time (PR #667) +- All Code specific to `TF.__version__ < "1.6"` have been removed (PR #675) + +### Fixed +- Issue #498 - Deprecation Warning Fix in `tl.layers.RNNLayer` with `inspect` (PR #574) +- Issue #498 - Deprecation Warning Fix in `tl.files` with truth value of an empty array is ambiguous (PR #575) +- Issue #565 related to `tl.utils.predict` fixed - `np.hstack` problem in which the results for multiple batches are stacked along `axis=1` (PR #566) +- Issue #572 with `tl.layers.DeformableConv2d` fixed (PR #573) +- Issue #664 with `tl.layers.ConvLSTMLayer` fixed (PR #676) +- Typo of the document of ElementwiseLambdaLayer (PR #588) +- Error in `tl.layers.TernaryConv2d` fixed - self.inputs not defined (PR #658) +- Deprecation warning fixed in `tl.layers.binary._compute_threshold()` (PR #658) +- All references to `tf.logging` replaced by `tl.logging` (PR #661) +- Duplicated code removed when bias was used (PR #667) +- `tensorlayer.third_party.roi_pooling.roi_pooling.roi_pooling_ops` is now lazy loaded to prevent systematic error raised (PR #675) +- Documentation not build in RTD due to old version of theme in docs directory fixed (PR #703) +- Tutorial: + - `tutorial_word2vec_basic.py` saving issue #476 fixed (PR #635) + - All tutorials tested and errors have been fixed (PR #635) + +### Dependencies Update +- Update pytest from 3.5.1 to 3.6.0 (PR #647) +- Update progressbar2 from 3.37.1 to 3.38.0 (PR #651) +- Update scikit-image from 0.13.1 to 0.14.0 (PR #656) +- Update keras from 2.1.6 to 2.2.0 (PR #684) +- Update requests from 2.18.4 to 2.19.0 (PR #695) + +### Contributors +- @lgarithm: #563 +- @DEKHTIARJonathan: #573 #574 #575 #580 #633 #635 #636 #639 #644 #645 #648 #657 #667 #658 #659 #660 #661 #666 #667 #672 #675 #683 #686 #687 #690 #691 #692 #703 +- @2wins: #560 #566 #662 +- @One-sixth: #579 +- @zsdonghao: #587 #588 #639 #685 #697 +- @luomai: #639 #677 +- @dengyueyun666: #676 + +## [1.8.5] - 2018-05-09 + +### Added +- Github Templates added (by @DEKHTIARJonathan) + - New issues Template + - New PR Template +- Travis Deploy Automation on new Tag (by @DEKHTIARJonathan) + - Deploy to PyPI and create a new version. + - Deploy to Github Releases and upload the wheel files +- PyUP.io has been added to ensure we are compatible with the latest libraries (by @DEKHTIARJonathan) +- `deconv2d` now handling dilation_rate (by @zsdonghao) +- Documentation unittest added (by @DEKHTIARJonathan) +- `test_layers_core` has been added to ensure that `LayersConfig` is abstract. + +### Changed +- All Tests Refactored - Now using unittests and runned with PyTest (by @DEKHTIARJonathan) +- Documentation updated (by @zsdonghao) +- Package Setup Refactored (by @DEKHTIARJonathan) +- Dataset Downlaod now using library progressbar2 (by @DEKHTIARJonathan) +- `deconv2d` function transformed into Class (by @zsdonghao) +- `conv1d` function transformed into Class (by @zsdonghao) +- super resolution functions transformed into Class (by @zsdonghao) +- YAPF coding style improved and enforced (by @DEKHTIARJonathan) + +### Fixed +- Backward Compatibility Restored with deprecation warnings (by @DEKHTIARJonathan) +- Tensorflow Deprecation Fix (Issue #498): + - AverageEmbeddingInputlayer (by @zsdonghao) + - load_mpii_pose_dataset (by @zsdonghao) +- maxPool2D initializer issue #551 (by @zsdonghao) +- `LayersConfig` class has been enforced as abstract +- Pooling Layer Issue #557 fixed (by @zsdonghao) + +### Dependencies Update +- scipy>=1.0,<1.1 => scipy>=1.1,<1.2 + +### Contributors +@zsdonghao @luomai @DEKHTIARJonathan + +[Unreleased]: https://github.com/tensorlayer/tensorlayer/compare/2.0....master +[2.2.0]: https://github.com/tensorlayer/tensorlayer/compare/2.2.0...2.2.0 +[2.1.0]: https://github.com/tensorlayer/tensorlayer/compare/2.1.0...2.1.0 +[2.0.2]: https://github.com/tensorlayer/tensorlayer/compare/2.0.2...2.0.2 +[2.0.1]: https://github.com/tensorlayer/tensorlayer/compare/2.0.1...2.0.1 +[2.0.0]: https://github.com/tensorlayer/tensorlayer/compare/2.0.0...2.0.0 +[1.11.1]: https://github.com/tensorlayer/tensorlayer/compare/1.11.0...1.11.0 +[1.11.0]: https://github.com/tensorlayer/tensorlayer/compare/1.10.1...1.11.0 +[1.10.1]: https://github.com/tensorlayer/tensorlayer/compare/1.10.0...1.10.1 +[1.10.0]: https://github.com/tensorlayer/tensorlayer/compare/1.9.1...1.10.0 +[1.9.1]: https://github.com/tensorlayer/tensorlayer/compare/1.9.0...1.9.1 +[1.9.0]: https://github.com/tensorlayer/tensorlayer/compare/1.8.5...1.9.0 +[1.8.5]: https://github.com/tensorlayer/tensorlayer/compare/1.8.4...1.8.5 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..6a010a2 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,199 @@ +# TensorLayer Contributor Guideline + +## Welcome to contribute! +You are more than welcome to contribute to TensorLayer! If you have any improvement, please send us your [pull requests](https://help.github.com/en/articles/about-pull-requests). You may implement your improvement on your [fork](https://help.github.com/en/articles/working-with-forks). + +## Checklist +* Continuous integration +* Build from sources +* Unittest +* Documentation +* General intro to TensorLayer2 +* How to contribute a new `Layer` +* How to contribute a new `Model` +* How to contribute a new example/tutorial + +## Continuous integration + +We appreciate contributions +either by adding / improving examples or extending / fixing the core library. +To make your contributions, you would need to follow the [pep8](https://www.python.org/dev/peps/pep-0008/) coding style and [numpydoc](https://numpydoc.readthedocs.io/en/latest/) document style. +We rely on Continuous Integration (CI) for checking push commits. +The following tools are used to ensure that your commits can pass through the CI test: + +* [yapf](https://github.com/google/yapf) (format code), compulsory +* [isort](https://github.com/timothycrosley/isort) (sort imports), optional +* [autoflake](https://github.com/myint/autoflake) (remove unused imports), optional + +You can simply run + +```bash +make format +``` + +to apply those tools before submitting your PR. + +## Build from sources + +```bash +# First clone the repository and change the current directory to the newly cloned repository +git clone https://github.com/zsdonghao/tensorlayer2.git +cd tensorlayer2 + +# Install virtualenv if necessary +pip install virtualenv + +# Then create a virtualenv called `venv` +virtualenv venv + +# Activate the virtualenv + +## Linux: +source venv/bin/activate + +## Windows: +venv\Scripts\activate.bat + +# ============= IF TENSORFLOW IS NOT ALREADY INSTALLED ============= # + +# basic installation +pip install . + +# advanced: for a machine **without** an NVIDIA GPU +pip install -e ".[all_cpu_dev]" + +# advanced: for a machine **with** an NVIDIA GPU +pip install -e ".[all_gpu_dev]" +``` + +## Unittest + +Launching the unittest for the whole repo: + +```bash +# install pytest +pip install pytest + +# run pytest +pytest +``` + +Running your unittest code on your implemented module only: + +```bash +# install coverage +pip install coverage + +cd /path/to/your/unittest/code +# For example: cd tests/layers/ + +# run unittest +coverage run --source myproject.module -m unittest discover +# For example: coverage run --source tensorlayer.layers -m unittest discover + +# generate html report +coverage html +``` + +## Documentation +Even though you follow [numpydoc](https://numpydoc.readthedocs.io/en/latest/) document style when writing your code, +this does not ensure those lines appear on TensorLayer online documentation. +You need further modify corresponding RST files in `docs/modules`. + +For example, to add your implemented new pooling layer into documentation, modify `docs/modules/layer.rst`. First, insert layer name under Layer list +```rst +Layer list +---------- + +.. autosummary:: + + NewPoolingLayer +``` + +Second, find pooling layer part and add: +```rst +.. ----------------------------------------------------------- +.. Pooling Layers +.. ----------------------------------------------------------- + +Pooling Layers +------------------------ + +New Pooling Layer +^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: NewPoolingLayer +``` + +Finally, test with local documentation: +```bash +cd ./docs + +make clean +make html +# then view generated local documentation by ./html/index.html +``` + +## General intro to TensorLayer2 +* TensorLayer2 is built on [TensorFlow2](https://www.tensorflow.org/alpha), so TensorLayer2 is purely eager, no sessions, no globals. +* TensorLayer2 supports APIs to build static models and dynamic models. Therefore, all `Layers` should be compatible with the two modes. +```python +# An example of a static model +# A static model has inputs and outputs with fixed shape. +inputs = tl.layers.Input([32, 784]) +dense1 = tl.layers.Dense(n_units=800, act=tf.nn.relu, in_channels=784, name='dense1')(inputs) +dense2 = tl.layers.Dense(n_units=10, act=tf.nn.relu, in_channels=800, name='dense2')(dense1) +model = tl.models.Model(inputs=inputs, outputs=dense2) + +# An example of a dynamic model +# A dynamic model has more flexibility. The inputs and outputs may be different in different runs. +class CustomizeModel(tl.models.Model): + def __init__(self): + super(CustomizeModel, self).__init__() + self.dense1 = tl.layers.Dense(n_units=800, act=tf.nn.relu, in_channels=784, name='dense1') + self.dense2 = tl.layers.Dense(n_units=10, act=tf.nn.relu, in_channels=800, name='dense2') + + # a dynamic model allows more flexibility by customising forwarding. + def forward(self, x, bar=None): + d1 = self.dense1(x) + if bar: + return d1 + else: + d2 = self.dense2(d1) + return d1, d2 + +model = CustomizeModel() +``` +* More examples can be found in [examples](examples/) and [tests/layers](tests/layers/). Note that not all of them are completed. + +## How to contribute a new `Layer` +* A `NewLayer` should be a derived from the base class [`Layer`](tensorlayer/layers/core.py). +* Member methods to be overrided: + - `__init__(self, args1, args2, inputs_shape=None, name=None)`: The constructor of the `NewLayer`, which should + - Call `super(NewLayer, self).__init__(name)` to construct the base. + - Define member variables based on the args1, args2 (or even more). + - If the `inputs_shape` is provided, call `self.build(inputs_shape)` and set `self._built=True`. Note that sometimes only `in_channels` should be enough to build the layer like [`Dense`](tensorlayer/layers/dense/base_dense.py). + - Logging by `logging.info(...)`. + - `__repr__(self)`: Return a printable representation of the `NewLayer`. + - `build(self, inputs_shape)`: Build the `NewLayer` by defining weights. + - `forward(self, inputs, **kwargs)`: Forward feeding the `NewLayer`. Note that the forward feeding of some `Layers` may be different during training and testing like [`Dropout`](tensorlayer/layers/dropout.py). +* Unittest: + - Unittest should be done before a pull request. Unittest code can be written in [tests/](tests/) +* Documents: + - Please write a description for each class and method in RST format. The description may include the functionality, arguments, references, examples of the `NewLayer`. +* Examples: [`Dense`](tensorlayer/layers/dense/base_dense.py), [`Dropout`](tensorlayer/layers/dropout.py), [`Conv`](tensorlayer/layers/convolution/simplified_conv.py). + +## How to contribute a new `Model` +* A `NewModel` should be derived from the base class [`Model`](tensorlayer/models/core.py) (if dynamic) or an instance of [`Model`](tensorlayer/models/core.py) (if static). +* A static `NewModel` should have fixed inputs and outputs. Please check the example [`VGG_Static`](tensorlayer/models/vgg.py) +* A dynamic `NewModel` has more flexiblility. Please check the example [`VGG16`](tensorlayer/models/vgg16.py) + +## How to contribute a new example/tutorial +* A new example/tutorial should implement a complete workflow of deep learning which includes (but not limited) + - `Models` construction based on `Layers`. + - Data processing and loading. + - Training and testing. + - Forward feeding by calling the models. + - Loss function. + - Back propagation by `tf.GradientTape()`. + - Model saving and restoring. +* Examples: [MNIST](examples/basic_tutorials/tutorial_mnist_mlp_static.py), [CIFAR10](examples/basic_tutorials/tutorial_cifar10_cnn_static.py), [FastText](examples/text_classification/tutorial_imdb_fasttext.py) diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 4ed90b9..0000000 --- a/LICENSE +++ /dev/null @@ -1,208 +0,0 @@ -Apache License - -Version 2.0, January 2004 - -http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, -AND DISTRIBUTION - - 1. Definitions. - - - -"License" shall mean the terms and conditions for use, reproduction, and distribution -as defined by Sections 1 through 9 of this document. - - - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - - - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct -or indirect, to cause the direction or management of such entity, whether -by contract or otherwise, or (ii) ownership of fifty percent (50%) or more -of the outstanding shares, or (iii) beneficial ownership of such entity. - - - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions -granted by this License. - - - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - - - -"Object" form shall mean any form resulting from mechanical transformation -or translation of a Source form, including but not limited to compiled object -code, generated documentation, and conversions to other media types. - - - -"Work" shall mean the work of authorship, whether in Source or Object form, -made available under the License, as indicated by a copyright notice that -is included in or attached to the work (an example is provided in the Appendix -below). - - - -"Derivative Works" shall mean any work, whether in Source or Object form, -that is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative -Works shall not include works that remain separable from, or merely link (or -bind by name) to the interfaces of, the Work and Derivative Works thereof. - - - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative -Works thereof, that is intentionally submitted to Licensor for inclusion in -the Work by the copyright owner or by an individual or Legal Entity authorized -to submit on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication -sent to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor -for the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - - - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently incorporated -within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this -License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, -no-charge, royalty-free, irrevocable copyright license to reproduce, prepare -Derivative Works of, publicly display, publicly perform, sublicense, and distribute -the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, -each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, -no-charge, royalty-free, irrevocable (except as stated in this section) patent -license to make, have made, use, offer to sell, sell, import, and otherwise -transfer the Work, where such license applies only to those patent claims -licensable by such Contributor that are necessarily infringed by their Contribution(s) -alone or by combination of their Contribution(s) with the Work to which such -Contribution(s) was submitted. If You institute patent litigation against -any entity (including a cross-claim or counterclaim in a lawsuit) alleging -that the Work or a Contribution incorporated within the Work constitutes direct -or contributory patent infringement, then any patent licenses granted to You -under this License for that Work shall terminate as of the date such litigation -is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or -Derivative Works thereof in any medium, with or without modifications, and -in Source or Object form, provided that You meet the following conditions: - -(a) You must give any other recipients of the Work or Derivative Works a copy -of this License; and - -(b) You must cause any modified files to carry prominent notices stating that -You changed the files; and - -(c) You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source -form of the Work, excluding those notices that do not pertain to any part -of the Derivative Works; and - -(d) If the Work includes a "NOTICE" text file as part of its distribution, -then any Derivative Works that You distribute must include a readable copy -of the attribution notices contained within such NOTICE file, excluding those -notices that do not pertain to any part of the Derivative Works, in at least -one of the following places: within a NOTICE text file distributed as part -of the Derivative Works; within the Source form or documentation, if provided -along with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents -of the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works -that You distribute, alongside or as an addendum to the NOTICE text from the -Work, provided that such additional attribution notices cannot be construed -as modifying the License. - -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, -or distribution of Your modifications, or for any such Derivative Works as -a whole, provided Your use, reproduction, and distribution of the Work otherwise -complies with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any -Contribution intentionally submitted for inclusion in the Work by You to the -Licensor shall be under the terms and conditions of this License, without -any additional terms or conditions. Notwithstanding the above, nothing herein -shall supersede or modify the terms of any separate license agreement you -may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, -trademarks, service marks, or product names of the Licensor, except as required -for reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to -in writing, Licensor provides the Work (and each Contributor provides its -Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied, including, without limitation, any warranties -or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR -A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness -of using or redistributing the Work and assume any risks associated with Your -exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether -in tort (including negligence), contract, or otherwise, unless required by -applicable law (such as deliberate and grossly negligent acts) or agreed to -in writing, shall any Contributor be liable to You for damages, including -any direct, indirect, special, incidental, or consequential damages of any -character arising as a result of this License or out of the use or inability -to use the Work (including but not limited to damages for loss of goodwill, -work stoppage, computer failure or malfunction, or any and all other commercial -damages or losses), even if such Contributor has been advised of the possibility -of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work -or Derivative Works thereof, You may choose to offer, and charge a fee for, -acceptance of support, warranty, indemnity, or other liability obligations -and/or rights consistent with this License. However, in accepting such obligations, -You may act only on Your own behalf and on Your sole responsibility, not on -behalf of any other Contributor, and only if You agree to indemnify, defend, -and hold each Contributor harmless for any liability incurred by, or claims -asserted against, such Contributor by reason of your accepting any such warranty -or additional liability. END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own identifying -information. (Don't include the brackets!) The text should be enclosed in -the appropriate comment syntax for the file format. We also recommend that -a file or class name and description of purpose be included on the same "printed -page" as the copyright notice for easier identification within third-party -archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); - -you may not use this file except in compliance with the License. - -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software - -distributed under the License is distributed on an "AS IS" BASIS, - -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - -See the License for the specific language governing permissions and - -limitations under the License. diff --git a/LICENSE.rst b/LICENSE.rst new file mode 100644 index 0000000..b662f1d --- /dev/null +++ b/LICENSE.rst @@ -0,0 +1,211 @@ +License +======= + +Copyright (c) 2016~2018 The TensorLayer contributors. All rights reserved. + + 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 2016, The TensorLayer Authors. + + 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. + + +Contact +======= +Questions? Please contact hao.dong11@imperial.ac.uk diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4fbfd85 --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +default: + @echo "Usage:" + @echo "\tmake lint # run pylint" + @echo "\tmake format # run yapf, autoflake and isort" + @echo "\tmake install3 # install tensorlayer in current workspace with pip3" + +lint: + pylint examples/*.py + pylint tensorlayer + +test: + python3 tests/models/test_model_core.py + python3 tests/layers/test_layernode.py + python3 tests/files/test_utils_saveload.py + +format: + autoflake -i examples/*.py + autoflake -i tensorlayer/*.py + autoflake -i tensorlayer/**/*.py + + isort -rc examples + isort -rc tensorlayer + + yapf -i examples/*.py + yapf -i tensorlayer/*.py + yapf -i tensorlayer/**/*.py + +install3: + pip3 install -U . --user + + +TAG = tensorlayer-docs:snaphot + +doc: + docker build --rm -t $(TAG) -f docker/docs/Dockerfile . diff --git a/README.md b/README.md index 6034f09..13e3ba0 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,181 @@ -#### 从命令行创建一个新的仓库 + +
+ +
+
+ + + + +![GitHub last commit (branch)](https://img.shields.io/github/last-commit/tensorlayer/tensorlayer/master.svg) +[![Supported TF Version](https://img.shields.io/badge/TensorFlow-2.0.0%2B-brightgreen.svg)](https://github.com/tensorflow/tensorflow/releases) +[![Documentation Status](https://readthedocs.org/projects/tensorlayer/badge/)](https://tensorlayer.readthedocs.io/) +[![Build Status](https://travis-ci.org/tensorlayer/tensorlayer.svg?branch=master)](https://travis-ci.org/tensorlayer/tensorlayer) +[![Downloads](http://pepy.tech/badge/tensorlayer)](http://pepy.tech/project/tensorlayer) +[![Docker Pulls](https://img.shields.io/docker/pulls/tensorlayer/tensorlayer.svg)](https://hub.docker.com/r/tensorlayer/tensorlayer/) +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/d6b118784e25435498e7310745adb848)](https://www.codacy.com/app/tensorlayer/tensorlayer) + + + + + +TensorLayer is a novel TensorFlow-based deep learning and reinforcement learning library designed for researchers and engineers. It provides an extensive collection of customizable neural layers to build complex AI models. TensorLayer is awarded the 2017 Best Open Source Software by the [ACM Multimedia Society](https://twitter.com/ImperialDSI/status/923928895325442049). +TensorLayer can also be found at [iHub](https://code.ihub.org.cn/projects/328) and [Gitee](https://gitee.com/organizations/TensorLayer). + +# News + +🔥 Reinforcement Learning Model Zoo: [Low-level APIs for Research](https://github.com/tensorlayer/tensorlayer/tree/master/examples/reinforcement_learning) and [High-level APIs for Production](https://github.com/tensorlayer/RLzoo) + +🔥 [Sipeed Maxi-EMC](https://github.com/sipeed/Maix-EMC): Run TensorLayer models on the **low-cost AI chip** (e.g., K210) (Alpha Version) + + + +🔥 [Free GPU and storage resources](https://github.com/fangde/FreeGPU): TensorLayer users can access to free GPU and storage resources donated by SurgicalAI. Thank you SurgicalAI! + +# Design Features + +TensorLayer is a new deep learning library designed with simplicity, flexibility and high-performance in mind. + +- ***Simplicity*** : TensorLayer has a high-level layer/model abstraction which is effortless to learn. You can learn how deep learning can benefit your AI tasks in minutes through the massive [examples](https://github.com/tensorlayer/awesome-tensorlayer). +- ***Flexibility*** : TensorLayer APIs are transparent and flexible, inspired by the emerging PyTorch library. Compared to the Keras abstraction, TensorLayer makes it much easier to build and train complex AI models. +- ***Zero-cost Abstraction*** : Though simple to use, TensorLayer does not require you to make any compromise in the performance of TensorFlow (Check the following benchmark section for more details). + +TensorLayer stands at a unique spot in the TensorFlow wrappers. Other wrappers like Keras and TFLearn +hide many powerful features of TensorFlow and provide little support for writing custom AI models. Inspired by PyTorch, TensorLayer APIs are simple, flexible and Pythonic, +making it easy to learn while being flexible enough to cope with complex AI tasks. +TensorLayer has a fast-growing community. It has been used by researchers and engineers all over the world, including those from Peking University, +Imperial College London, UC Berkeley, Carnegie Mellon University, Stanford University, and companies like Google, Microsoft, Alibaba, Tencent, Xiaomi, and Bloomberg. + +# Multilingual Documents + +TensorLayer has extensive documentation for both beginners and professionals. The documentation is available in +both English and Chinese. + +[![English Documentation](https://img.shields.io/badge/documentation-english-blue.svg)](https://tensorlayer.readthedocs.io/) +[![Chinese Documentation](https://img.shields.io/badge/documentation-%E4%B8%AD%E6%96%87-blue.svg)](https://tensorlayercn.readthedocs.io/) +[![Chinese Book](https://img.shields.io/badge/book-%E4%B8%AD%E6%96%87-blue.svg)](http://www.broadview.com.cn/book/5059/) + +If you want to try the experimental features on the the master branch, you can find the latest document +[here](https://tensorlayer.readthedocs.io/en/latest/). + +# Extensive Examples + +You can find a large collection of examples that use TensorLayer in [here](examples/) and the following space: + + +
+ +
+
+ +# Getting Start + +TensorLayer 2.0 relies on TensorFlow, numpy, and others. To use GPUs, CUDA and cuDNN are required. + +Install TensorFlow: + +```bash +pip3 install tensorflow-gpu==2.0.0-rc1 # TensorFlow GPU (version 2.0 RC1) +pip3 install tensorflow # CPU version +``` + +Install the stable release of TensorLayer: + +```bash +pip3 install tensorlayer +``` + +Install the unstable development version of TensorLayer: ```bash -touch README.md -git init -git add README.md -git commit -m "first commit" -git remote add origin https://git.trustie.net/TensorLayer/tensorlayer3.git -git push -u origin master +pip3 install git+https://github.com/tensorlayer/tensorlayer.git +``` +If you want to install the additional dependencies, you can also run +```bash +pip3 install --upgrade tensorlayer[all] # all additional dependencies +pip3 install --upgrade tensorlayer[extra] # only the `extra` dependencies +pip3 install --upgrade tensorlayer[contrib_loggers] # only the `contrib_loggers` dependencies ``` -#### 从命令行推送已经创建的仓库 +If you are TensorFlow 1.X users, you can use TensorLayer 1.11.0: ```bash -git remote add origin https://git.trustie.net/TensorLayer/tensorlayer3.git -git push -u origin master +# For last stable version of TensorLayer 1.X +pip3 install --upgrade tensorlayer==1.11.0 +``` + + + +# Performance Benchmark + +The following table shows the training speeds of [VGG16](http://www.robots.ox.ac.uk/~vgg/research/very_deep/) using TensorLayer and native TensorFlow on a TITAN Xp. + +| Mode | Lib | Data Format | Max GPU Memory Usage(MB) |Max CPU Memory Usage(MB) | Avg CPU Memory Usage(MB) | Runtime (sec) | +| :-------: | :-------------: | :-----------: | :-----------------: | :-----------------: | :-----------------: | :-----------: | +| AutoGraph | TensorFlow 2.0 | channel last | 11833 | 2161 | 2136 | 74 | +| | Tensorlayer 2.0 | channel last | 11833 | 2187 | 2169 | 76 | +| Graph | Keras | channel last | 8677 | 2580 | 2576 | 101 | +| Eager | TensorFlow 2.0 | channel last | 8723 | 2052 | 2024 | 97 | +| | TensorLayer 2.0 | channel last | 8723 | 2010 | 2007 | 95 | + +# Getting Involved + +Please read the [Contributor Guideline](CONTRIBUTING.md) before submitting your PRs. + +We suggest users to report bugs using Github issues. Users can also discuss how to use TensorLayer in the following slack channel. + +
+ + +
+ +
+
+ +
+ +# Citing TensorLayer + +If you find TensorLayer useful for your project, please cite the following paper: + +``` +@article{tensorlayer2017, + author = {Dong, Hao and Supratak, Akara and Mai, Luo and Liu, Fangde and Oehmichen, Axel and Yu, Simiao and Guo, Yike}, + journal = {ACM Multimedia}, + title = {{TensorLayer: A Versatile Library for Efficient Deep Learning Development}}, + url = {http://tensorlayer.org}, + year = {2017} +} +``` \ No newline at end of file diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..5f424cf --- /dev/null +++ b/README.rst @@ -0,0 +1,201 @@ +|TENSORLAYER-LOGO| + + +|Awesome| |Documentation-EN| |Documentation-CN| |Book-CN| |Downloads| + +|PyPI| |PyPI-Prerelease| |Commits-Since| |Python| |TensorFlow| + +|Travis| |Docker| |RTD-EN| |RTD-CN| |PyUP| |Docker-Pulls| |Code-Quality| + + +|JOIN-SLACK-LOGO| + +TensorLayer is a novel TensorFlow-based deep learning and reinforcement +learning library designed for researchers and engineers. It provides a +large collection of customizable neural layers / functions that are key +to build real-world AI applications. TensorLayer is awarded the 2017 +Best Open Source Software by the `ACM Multimedia +Society `__. + +Why another deep learning library: TensorLayer +============================================== + +As deep learning practitioners, we have been looking for a library that +can address various development purposes. This library is easy to adopt +by providing diverse examples, tutorials and pre-trained models. Also, +it allow users to easily fine-tune TensorFlow; while being suitable for +production deployment. TensorLayer aims to satisfy all these purposes. +It has three key features: + +- **Simplicity** : TensorLayer lifts the low-level dataflow interface + of TensorFlow to *high-level* layers / models. It is very easy to + learn through the rich `example + codes `__ + contributed by a wide community. +- **Flexibility** : TensorLayer APIs are transparent: it does not + mask TensorFlow from users; but leaving massive hooks that help + *low-level tuning* and *deep customization*. +- **Zero-cost Abstraction** : TensorLayer can achieve the *full + power* of TensorFlow. The following table shows the training speeds + of classic models using TensorLayer and native TensorFlow on a Titan + X Pascal GPU. + + +---------------+-----------------+-----------------+-----------------+ + | | CIFAR-10 | PTB LSTM | Word2Vec | + +===============+=================+=================+=================+ + | TensorLayer | 2528 images/s | 18063 words/s | 58167 words/s | + +---------------+-----------------+-----------------+-----------------+ + | TensorFlow | 2530 images/s | 18075 words/s | 58181 words/s | + +---------------+-----------------+-----------------+-----------------+ + +TensorLayer stands at a unique spot in the library landscape. Other +wrapper libraries like Keras and TFLearn also provide high-level +abstractions. They, however, often hide the underlying engine from +users, which make them hard to customize and fine-tune. On the contrary, +TensorLayer APIs are generally flexible and transparent. Users often +find it easy to start with the examples and tutorials, and then dive +into TensorFlow seamlessly. In addition, TensorLayer does not create +library lock-in through native supports for importing components from +Keras, TFSlim and TFLearn. + +TensorLayer has a fast growing usage among top researchers and +engineers, from universities like Imperial College London, UC Berkeley, +Carnegie Mellon University, Stanford University, and University of +Technology of Compiegne (UTC), and companies like Google, Microsoft, +Alibaba, Tencent, Xiaomi, and Bloomberg. + +Install +======= + +TensorLayer has pre-requisites including TensorFlow, numpy, and others. For GPU support, CUDA and cuDNN are required. +The simplest way to install TensorLayer is to use the Python Package Index (PyPI): + +.. code:: bash + + # for last stable version + pip install --upgrade tensorlayer + + # for latest release candidate + pip install --upgrade --pre tensorlayer + + # if you want to install the additional dependencies, you can also run + pip install --upgrade tensorlayer[all] # all additional dependencies + pip install --upgrade tensorlayer[extra] # only the `extra` dependencies + pip install --upgrade tensorlayer[contrib_loggers] # only the `contrib_loggers` dependencies + +Alternatively, you can install the latest or development version by directly pulling from github: + +.. code:: bash + + pip install https://github.com/tensorlayer/tensorlayer/archive/master.zip + # or + # pip install https://github.com/tensorlayer/tensorlayer/archive/.zip + +Using Docker - a ready-to-use environment +----------------------------------------- + +The `TensorLayer +containers `__ are +built on top of the official `TensorFlow +containers `__: + +Containers with CPU support +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: bash + + # for CPU version and Python 2 + docker pull tensorlayer/tensorlayer:latest + docker run -it --rm -p 8888:8888 -p 6006:6006 -e PASSWORD=JUPYTER_NB_PASSWORD tensorlayer/tensorlayer:latest + + # for CPU version and Python 3 + docker pull tensorlayer/tensorlayer:latest-py3 + docker run -it --rm -p 8888:8888 -p 6006:6006 -e PASSWORD=JUPYTER_NB_PASSWORD tensorlayer/tensorlayer:latest-py3 + +Containers with GPU support +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +NVIDIA-Docker is required for these containers to work: `Project +Link `__ + +.. code:: bash + + # for GPU version and Python 2 + docker pull tensorlayer/tensorlayer:latest-gpu + nvidia-docker run -it --rm -p 8888:88888 -p 6006:6006 -e PASSWORD=JUPYTER_NB_PASSWORD tensorlayer/tensorlayer:latest-gpu + + # for GPU version and Python 3 + docker pull tensorlayer/tensorlayer:latest-gpu-py3 + nvidia-docker run -it --rm -p 8888:8888 -p 6006:6006 -e PASSWORD=JUPYTER_NB_PASSWORD tensorlayer/tensorlayer:latest-gpu-py3 + +Contribute +========== + +Please read the `Contributor +Guideline `__ +before submitting your PRs. + +Cite +==== + +If you find this project useful, we would be grateful if you cite the +TensorLayer paper: + +:: + + @article{tensorlayer2017, + author = {Dong, Hao and Supratak, Akara and Mai, Luo and Liu, Fangde and Oehmichen, Axel and Yu, Simiao and Guo, Yike}, + journal = {ACM Multimedia}, + title = {{TensorLayer: A Versatile Library for Efficient Deep Learning Development}}, + url = {http://tensorlayer.org}, + year = {2017} + } + +License +======= + +TensorLayer is released under the Apache 2.0 license. + + +.. |TENSORLAYER-LOGO| image:: https://raw.githubusercontent.com/tensorlayer/tensorlayer/master/img/tl_transparent_logo.png + :target: https://tensorlayer.readthedocs.io/ +.. |JOIN-SLACK-LOGO| image:: https://raw.githubusercontent.com/tensorlayer/tensorlayer/master/img/join_slack.png + :target: https://join.slack.com/t/tensorlayer/shared_invite/enQtMjUyMjczMzU2Njg4LWI0MWU0MDFkOWY2YjQ4YjVhMzI5M2VlZmE4YTNhNGY1NjZhMzUwMmQ2MTc0YWRjMjQzMjdjMTg2MWQ2ZWJhYzc + +.. |Awesome| image:: https://awesome.re/mentioned-badge.svg + :target: https://github.com/tensorlayer/awesome-tensorlayer +.. |Documentation-EN| image:: https://img.shields.io/badge/documentation-english-blue.svg + :target: https://tensorlayer.readthedocs.io/ +.. |Documentation-CN| image:: https://img.shields.io/badge/documentation-%E4%B8%AD%E6%96%87-blue.svg + :target: https://tensorlayercn.readthedocs.io/ +.. |Book-CN| image:: https://img.shields.io/badge/book-%E4%B8%AD%E6%96%87-blue.svg + :target: http://www.broadview.com.cn/book/5059/ +.. |Downloads| image:: http://pepy.tech/badge/tensorlayer + :target: http://pepy.tech/project/tensorlayer + + +.. |PyPI| image:: http://ec2-35-178-47-120.eu-west-2.compute.amazonaws.com/github/release/tensorlayer/tensorlayer.svg?label=PyPI%20-%20Release + :target: https://pypi.org/project/tensorlayer/ +.. |PyPI-Prerelease| image:: http://ec2-35-178-47-120.eu-west-2.compute.amazonaws.com/github/release/tensorlayer/tensorlayer/all.svg?label=PyPI%20-%20Pre-Release + :target: https://pypi.org/project/tensorlayer/ +.. |Commits-Since| image:: http://ec2-35-178-47-120.eu-west-2.compute.amazonaws.com/github/commits-since/tensorlayer/tensorlayer/latest.svg + :target: https://github.com/tensorlayer/tensorlayer/compare/1.10.1...master +.. |Python| image:: http://ec2-35-178-47-120.eu-west-2.compute.amazonaws.com/pypi/pyversions/tensorlayer.svg + :target: https://pypi.org/project/tensorlayer/ +.. |TensorFlow| image:: https://img.shields.io/badge/tensorflow-1.6.0+-blue.svg + :target: https://github.com/tensorflow/tensorflow/releases + +.. |Travis| image:: http://ec2-35-178-47-120.eu-west-2.compute.amazonaws.com/travis/tensorlayer/tensorlayer/master.svg?label=Travis + :target: https://travis-ci.org/tensorlayer/tensorlayer +.. |Docker| image:: http://ec2-35-178-47-120.eu-west-2.compute.amazonaws.com/circleci/project/github/tensorlayer/tensorlayer/master.svg?label=Docker%20Build + :target: https://circleci.com/gh/tensorlayer/tensorlayer/tree/master +.. |RTD-EN| image:: http://ec2-35-178-47-120.eu-west-2.compute.amazonaws.com/readthedocs/tensorlayer/latest.svg?label=ReadTheDocs-EN + :target: https://tensorlayer.readthedocs.io/ +.. |RTD-CN| image:: http://ec2-35-178-47-120.eu-west-2.compute.amazonaws.com/readthedocs/tensorlayercn/latest.svg?label=ReadTheDocs-CN + :target: https://tensorlayercn.readthedocs.io/ +.. |PyUP| image:: https://pyup.io/repos/github/tensorlayer/tensorlayer/shield.svg + :target: https://pyup.io/repos/github/tensorlayer/tensorlayer/ +.. |Docker-Pulls| image:: http://ec2-35-178-47-120.eu-west-2.compute.amazonaws.com/docker/pulls/tensorlayer/tensorlayer.svg + :target: https://hub.docker.com/r/tensorlayer/tensorlayer/ +.. |Code-Quality| image:: https://api.codacy.com/project/badge/Grade/d6b118784e25435498e7310745adb848 + :target: https://www.codacy.com/app/tensorlayer/tensorlayer diff --git a/docker/DockerLint.bat b/docker/DockerLint.bat new file mode 100644 index 0000000..3a4b1f8 --- /dev/null +++ b/docker/DockerLint.bat @@ -0,0 +1,4 @@ +docker pull hadolint/hadolint:latest +docker run --rm -i hadolint/hadolint hadolint --ignore DL3007 - < Dockerfile + +PAUSE; \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..2d3b689 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,35 @@ +# Build args. +# * Accepted Values: +# - Python 2 + CPU: "latest" => --build-arg TF_CONTAINER_VERSION="latest" +# - Python 2 + GPU: "latest-gpu" => --build-arg TF_CONTAINER_VERSION="latest-gpu" +# - Python 3 + CPU: "latest-py3" => --build-arg TF_CONTAINER_VERSION="latest-py3" +# - Python 3 + GPU: "latest-gpu-py3" => --build-arg TF_CONTAINER_VERSION="latest-gpu-py3" + +ARG TF_CONTAINER_VERSION + +FROM tensorflow/tensorflow:${TF_CONTAINER_VERSION} + +LABEL version="1.0" maintainer="Jonathan DEKHTIAR " + +ARG TL_VERSION +ARG TF_CONTAINER_VERSION + +RUN echo "Container Tag: ${TF_CONTAINER_VERSION}" \ + && apt-get update \ + && case $TF_CONTAINER_VERSION in \ + latest-py3 | latest-gpu-py3) apt-get install -y python3-tk ;; \ + *) apt-get install -y python-tk ;; \ + esac \ + && if [ -z "$TL_VERSION" ]; then \ + echo "Building a Nightly Release" \ + && apt-get install -y git \ + && mkdir /dist/ && cd /dist/ \ + && git clone https://github.com/tensorlayer/tensorlayer.git \ + && cd tensorlayer \ + && pip install --disable-pip-version-check --no-cache-dir --upgrade -e .[all]; \ + else \ + echo "Building Tag Release: $TL_VERSION" \ + && pip install --disable-pip-version-check --no-cache-dir --upgrade tensorlayer[all]=="$TL_VERSION"; \ + fi \ + && apt-get autoremove -y \ + && rm -rf /var/lib/apt/lists/* diff --git a/docker/docs/Dockerfile b/docker/docs/Dockerfile new file mode 100644 index 0000000..a20020b --- /dev/null +++ b/docker/docs/Dockerfile @@ -0,0 +1,14 @@ +FROM ubuntu:bionic + +ADD docker/docs/sources.list.ustc /etc/apt/sources.list +ENV DEBIAN_FRONTEND=noninteractive +RUN apt update && \ + apt install -y python3-pip python3-tk python-qt4 wget && \ + pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple tensorflow +ADD . /tensorlayer +WORKDIR /tensorlayer +RUN ln -s `which pip3` /usr/bin/pip && \ + ./scripts/install-horovod-for-doc-test.sh +RUN pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple . +RUN pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple -e .[all] +RUN make -C docs html diff --git a/docker/docs/sources.list.ustc b/docker/docs/sources.list.ustc new file mode 100644 index 0000000..e46be90 --- /dev/null +++ b/docker/docs/sources.list.ustc @@ -0,0 +1,15 @@ +deb http://mirrors.ustc.edu.cn/ubuntu/ bionic main restricted + +deb http://mirrors.ustc.edu.cn/ubuntu/ bionic-updates main restricted + +deb http://mirrors.ustc.edu.cn/ubuntu/ bionic universe +deb http://mirrors.ustc.edu.cn/ubuntu/ bionic-updates universe + +deb http://mirrors.ustc.edu.cn/ubuntu/ bionic multiverse +deb http://mirrors.ustc.edu.cn/ubuntu/ bionic-updates multiverse + +deb http://mirrors.ustc.edu.cn/ubuntu/ bionic-backports main restricted universe multiverse + +deb http://mirrors.ustc.edu.cn/ubuntu bionic-security main restricted +deb http://mirrors.ustc.edu.cn/ubuntu bionic-security universe +deb http://mirrors.ustc.edu.cn/ubuntu bionic-security multiverse diff --git a/docker/pypi_list.py b/docker/pypi_list.py new file mode 100644 index 0000000..69a2bad --- /dev/null +++ b/docker/pypi_list.py @@ -0,0 +1,68 @@ +import argparse +import requests +import logging + +import pip._internal + +if __name__ == "__main__": + + parser = argparse.ArgumentParser(description='Get the nth version of a given package') + parser.add_argument('--package', type=str, required=True, help='The PyPI you want to inspect') + parser.add_argument('--nth_last_version', type=int, default=1, help='The nth last package will be retrieved') + parser.add_argument('--prerelease', help='Get PreRelease Package Version', action='store_true') + parser.add_argument('--debug', help='Print debug information', action='store_true') + + args = parser.parse_args() + + # create logger + logger = logging.getLogger("PyPI_CLI") + + formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') + + ch = logging.StreamHandler() + ch.setLevel(logging.DEBUG) + ch.setFormatter(formatter) + logger.addHandler(ch) + + if args.debug: + logger.setLevel(logging.DEBUG) + + logger.debug("Package: %s" % args.package) + logger.debug("nth_last_version: %s" % args.nth_last_version) + logger.debug("prerelease: %s" % args.prerelease) + logger.debug("debug: %s" % args.debug) + + finder = pip._internal.index.PackageFinder( + [], + ['https://pypi.python.org/simple'], + session=requests.Session() + ) + results = finder.find_all_candidates(args.package) + tmp_versions = [str(p.version) for p in results] + + logger.debug("%s" % tmp_versions) + + versions = list() + for el in tmp_versions: + if el not in versions: + versions.append(el) + + pos = -1 + nth_version = 1 + + while True: + fetched_version = versions[pos] + + logger.debug("Version: %s" % fetched_version) + + if nth_version == args.nth_last_version: + if args.prerelease or not ("rc" in fetched_version or "a" in fetched_version or "b" in fetched_version): + break + else: + pos -= 1 + continue + + pos -= 1 + nth_version += 1 + + print(fetched_version) diff --git a/docker/version_prefix.py b/docker/version_prefix.py new file mode 100644 index 0000000..7ee648d --- /dev/null +++ b/docker/version_prefix.py @@ -0,0 +1,37 @@ +import argparse +import logging + +if __name__ == "__main__": + + parser = argparse.ArgumentParser(description='Determine the version prefix to apply depending on the version name') + + parser.add_argument( + '--version', + type=str, + required=True, + help='The Package Version to be installed in the container' + ) + + parser.add_argument('--debug', help='Print debug information', action='store_true') + + args = parser.parse_args() + + # create logger + logger = logging.getLogger("VERSION_PREFIX_CLI") + + formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') + + ch = logging.StreamHandler() + ch.setLevel(logging.DEBUG) + ch.setFormatter(formatter) + logger.addHandler(ch) + + if args.debug: + logger.setLevel(logging.DEBUG) + + logger.debug("Package Version: %s" % args.version) + + if "rc" in args.version or "a" in args.version or "b" in args.version: + print("latest-dev") + else: + print("latest") diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..6024bcf --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,225 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " epub3 to make an epub3" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + @echo " dummy to check syntax errors of document sources" + +.PHONY: clean +clean: + rm -rf $(BUILDDIR)/* + +.PHONY: html +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +.PHONY: dirhtml +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +.PHONY: singlehtml +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +.PHONY: pickle +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +.PHONY: json +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +.PHONY: htmlhelp +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +.PHONY: qthelp +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/TLayer.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/TLayer.qhc" + +.PHONY: applehelp +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +.PHONY: devhelp +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/TLayer" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/TLayer" + @echo "# devhelp" + +.PHONY: epub +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +.PHONY: epub3 +epub3: + $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 + @echo + @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." + +.PHONY: latex +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +.PHONY: latexpdf +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: latexpdfja +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: text +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +.PHONY: man +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +.PHONY: texinfo +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +.PHONY: info +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +.PHONY: gettext +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +.PHONY: changes +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +.PHONY: linkcheck +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +.PHONY: doctest +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +.PHONY: coverage +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +.PHONY: xml +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +.PHONY: pseudoxml +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." + +.PHONY: dummy +dummy: + $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy + @echo + @echo "Build finished. Dummy builder generates no files." diff --git a/docs/_static/fix_rtd.css b/docs/_static/fix_rtd.css new file mode 100644 index 0000000..83623a0 --- /dev/null +++ b/docs/_static/fix_rtd.css @@ -0,0 +1,8 @@ +/* work around https://github.com/snide/sphinx_rtd_theme/issues/149 */ +.rst-content table.field-list .field-body { + padding-top: 8px; +} + +/*.section #basic-2-flip-flop-synchronizer{ + text-align:justify; +}*/ diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..89f1601 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,469 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# TensorLayer documentation build configuration file, created by +# sphinx-quickstart on Tue Aug 2 15:30:55 2016. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os, sys, datetime +sys.path.insert(0, os.path.abspath("../")) # Important +sys.path.insert(0, os.path.abspath(os.path.join("..", "tensorlayer"))) # Important + +from package_info import __shortversion__ +from package_info import __version__ + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. + +# extensions = [ +# 'sphinx.ext.coverage', +# 'sphinx.ext.githubpages', +# 'numpydoc', +# ] + +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.autosummary', + 'sphinx.ext.doctest', + 'sphinx.ext.ifconfig', + 'sphinx.ext.inheritance_diagram', + 'sphinx.ext.intersphinx', + 'sphinx.ext.mathjax', + 'sphinx.ext.napoleon', + 'sphinx.ext.todo', + 'sphinx.ext.viewcode', +] + +autodoc_mock_imports = [ + 'cv2', + 'gridfs', + 'horovod', + 'hyperdash', + 'imageio', + 'lxml', + 'matplotlib', + 'nltk', + 'numpy', + 'PIL', + 'progressbar', + 'pymongo', + 'scipy', + 'skimage', + 'sklearn', + 'tensorflow', + 'tqdm', + 'h5py', + + # TL C++ Packages + 'tensorlayer.third_party.roi_pooling.roi_pooling.roi_pooling_ops', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The encoding of source files. +# +# source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = 'TensorLayer' +copyright = '2016~%s, TensorLayer Contributors' % (str(datetime.datetime.now().year)) +author = 'TensorLayer Contributors' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = __shortversion__ +# The full version, including alpha/beta/rc tags. +release = __version__ + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# +# today = '' +# +# Else, today_fmt is used as the format for a strftime call. +# +# today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +# +# default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +# +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +# +# show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +# modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +# keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +# html_theme = 'alabaster' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +# html_theme_path = [] + +# The name for this set of Sphinx documents. +# " v documentation" by default. +# +# html_title = 'TensorLayer' + +# A shorter title for the navigation bar. Default is the same as html_title. +# +# html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +# +# html_logo = None + +# The name of an image file (relative to this directory) to use as a favicon of +# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# +# html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = [] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +# +# html_extra_path = [] + +# If not None, a 'Last updated on:' timestamp is inserted at every page +# bottom, using the given strftime format. +# The empty string is equivalent to '%b %d, %Y'. +# +# html_last_updated_fmt = None + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# +# html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +# +# html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# +# html_additional_pages = {} + +# If false, no module index is generated. +# +# html_domain_indices = True + +# If false, no index is generated. +# +# html_use_index = True + +# If true, the index is split into individual pages for each letter. +# +# html_split_index = False + +# If true, links to the reST sources are added to the pages. +# +# html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +# +# html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +# +# html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# +# html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +# html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh' +# +# html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# 'ja' uses this config value. +# 'zh' user can custom change `jieba` dictionary path. +# +# html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +# +# html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'TensorLayerdoc' + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'TensorLayer.tex', 'TensorLayer Documentation', + 'TensorLayer contributors', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +# +# latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +# +# latex_use_parts = False + +# If true, show page references after internal links. +# +# latex_show_pagerefs = False + +# If true, show URL addresses after external links. +# +# latex_show_urls = False + +# Documents to append as an appendix to all manuals. +# +# latex_appendices = [] + +# If false, no module index is generated. +# +# latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'tensorlayer', 'TensorLayer Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +# +# man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'TensorLayer', 'TensorLayer Documentation', + author, 'TensorLayer', 'Deep learning and Reinforcement learning library for Researchers and Engineers.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +# +# texinfo_appendices = [] + +# If false, no module index is generated. +# +# texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +# +# texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +# +# texinfo_no_detailmenu = False + + +# -- Options for Epub output ---------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project +epub_author = author +epub_publisher = author +epub_copyright = copyright + +# The basename for the epub file. It defaults to the project name. +# epub_basename = project + +# The HTML theme for the epub output. Since the default themes are not +# optimized for small screen space, using the same theme for HTML and epub +# output is usually not wise. This defaults to 'epub', a theme designed to save +# visual space. +# +# epub_theme = 'epub' + +# The language of the text. It defaults to the language option +# or 'en' if the language is not set. +# +# epub_language = '' + +# The scheme of the identifier. Typical schemes are ISBN or URL. +# epub_scheme = '' + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +# +# epub_identifier = '' + +# A unique identification for the text. +# +# epub_uid = '' + +# A tuple containing the cover image and cover page html template filenames. +# +# epub_cover = () + +# A sequence of (type, uri, title) tuples for the guide element of content.opf. +# +# epub_guide = () + +# HTML files that should be inserted before the pages created by sphinx. +# The format is a list of tuples containing the path and title. +# +# epub_pre_files = [] + +# HTML files that should be inserted after the pages created by sphinx. +# The format is a list of tuples containing the path and title. +# +# epub_post_files = [] + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ['search.html'] + +# The depth of the table of contents in toc.ncx. +# +# epub_tocdepth = 3 + +# Allow duplicate toc entries. +# +# epub_tocdup = True + +# Choose between 'default' and 'includehidden'. +# +# epub_tocscope = 'default' + +# Fix unsupported image types using the Pillow. +# +# epub_fix_images = False + +# Scale large images. +# +# epub_max_image_width = 0 + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +# +# epub_show_urls = 'inline' + +# If false, no index is generated. +# +# epub_use_index = True + +pygments_style = 'sphinx' + +html_theme = "sphinx_rtd_theme" + +html_theme_path = [] diff --git a/docs/images/affine_transform_comparison.jpg b/docs/images/affine_transform_comparison.jpg new file mode 100644 index 0000000..f7da0b8 Binary files /dev/null and b/docs/images/affine_transform_comparison.jpg differ diff --git a/docs/images/affine_transform_why.jpg b/docs/images/affine_transform_why.jpg new file mode 100644 index 0000000..bd3b905 Binary files /dev/null and b/docs/images/affine_transform_why.jpg differ diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..977f337 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,100 @@ +Welcome to TensorLayer +======================================= + + +.. image:: user/my_figs/tl_transparent_logo.png + :width: 30 % + :align: center + :target: https://github.com/tensorlayer/tensorlayer + +**Documentation Version:** |release| + +**Jun 2019** `Deep Reinforcement Learning Model ZOO Release !! `__. + +**Good News:** We won the **Best Open Source Software Award** `@ACM Multimedia (MM) 2017 `_. + +`TensorLayer`_ is a Deep Learning (DL) and Reinforcement Learning (RL) library extended from `Google TensorFlow `_. It provides popular DL and RL modules that can be easily customized and assembled for tackling real-world machine learning problems. +More details can be found `here `_. + +.. note:: + If you got problem to read the docs online, you could download the repository + on `GitHub`_, then go to ``/docs/_build/html/index.html`` to read the docs + offline. The ``_build`` folder can be generated in ``docs`` using ``make html``. + +User Guide +------------ + +The TensorLayer user guide explains how to install TensorFlow, CUDA and cuDNN, +how to build and train neural networks using TensorLayer, and how to contribute +to the library as a developer. + +.. toctree:: + :maxdepth: 2 + + user/installation + user/examples + user/contributing + user/get_involved + user/faq + +.. toctree:: + :maxdepth: 2 + :caption: Getting started + + user/get_start_model + user/get_start_advance + +API Reference +------------- + +If you are looking for information on a specific function, class or +method, this part of the documentation is for you. + +.. toctree:: + :maxdepth: 2 + :caption: Stable Functionalities + + modules/activation + modules/array_ops + modules/cost + modules/prepro + modules/files + modules/iterate + modules/layers + modules/models + modules/nlp + modules/initializers + modules/rein + modules/utils + modules/visualize + +.. toctree:: + :maxdepth: 2 + :caption: Alpha Version Functionalities + + modules/db + modules/optimizers + modules/distributed + +Command-line Reference +---------------------- + +TensorLayer provides a handy command-line tool `tl` to perform some common tasks. + +.. toctree:: + :maxdepth: 2 + :caption: Command Line Interface + + modules/cli + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + + +.. _GitHub: https://github.com/tensorlayer/tensorlayer +.. _TensorLayer: https://github.com/tensorlayer/tensorlayer/ diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..2daa20d --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,281 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. epub3 to make an epub3 + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + echo. coverage to run coverage check of the documentation if enabled + echo. dummy to check syntax errors of document sources + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +REM Check if sphinx-build is available and fallback to Python version if any +%SPHINXBUILD% 1>NUL 2>NUL +if errorlevel 9009 goto sphinx_python +goto sphinx_ok + +:sphinx_python + +set SPHINXBUILD=python -m sphinx.__init__ +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +:sphinx_ok + + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\TLayer.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\TLayer.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "epub3" ( + %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3 + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub3 file is in %BUILDDIR%/epub3. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "coverage" ( + %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage + if errorlevel 1 exit /b 1 + echo. + echo.Testing of coverage in the sources finished, look at the ^ +results in %BUILDDIR%/coverage/python.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +if "%1" == "dummy" ( + %SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. Dummy builder generates no files. + goto end +) + +:end diff --git a/docs/modules/activation.rst b/docs/modules/activation.rst new file mode 100644 index 0000000..3965bd0 --- /dev/null +++ b/docs/modules/activation.rst @@ -0,0 +1,73 @@ +API - Activations +========================= + +To make TensorLayer simple, we minimize the number of activation functions as much as +we can. So we encourage you to use TensorFlow's function. TensorFlow provides +``tf.nn.relu``, ``tf.nn.relu6``, ``tf.nn.elu``, ``tf.nn.softplus``, +``tf.nn.softsign`` and so on. +For parametric activation, please read the layer APIs. + +The shortcut of ``tensorlayer.activation`` is ``tensorlayer.act``. + +Your activation +------------------- + +Customizes activation function in TensorLayer is very easy. +The following example implements an activation that multiplies its input by 2. +For more complex activation, TensorFlow API will be required. + +.. code-block:: python + + def double_activation(x): + return x * 2 + + double_activation = lambda x: x * 2 + +.. automodule:: tensorlayer.activation + +.. autosummary:: + + leaky_relu + leaky_relu6 + leaky_twice_relu6 + ramp + swish + sign + hard_tanh + pixel_wise_softmax + +Ramp +------ +.. autofunction:: ramp + +Leaky ReLU +------------ +.. autofunction:: leaky_relu + +Leaky ReLU6 +------------ +.. autofunction:: leaky_relu6 + +Twice Leaky ReLU6 +----------------- +.. autofunction:: leaky_twice_relu6 + +Swish +------------ +.. autofunction:: swish + +Sign +--------------------- +.. autofunction:: sign + +Hard Tanh +--------------------- +.. autofunction:: hard_tanh + +Pixel-wise softmax +-------------------- +.. autofunction:: pixel_wise_softmax + +Parametric activation +------------------------------ +See ``tensorlayer.layers``. diff --git a/docs/modules/array_ops.rst b/docs/modules/array_ops.rst new file mode 100644 index 0000000..ee0aff7 --- /dev/null +++ b/docs/modules/array_ops.rst @@ -0,0 +1,20 @@ +API - Array Operations +====================== + +.. automodule:: tensorlayer.array_ops + +.. autosummary:: + + alphas + alphas_like + +Tensorflow Tensor Operations +---------------------------- + +tl.alphas +^^^^^^^^^ +.. autofunction:: alphas + +tl.alphas_like +^^^^^^^^^^^^^^ +.. autofunction:: alphas_like diff --git a/docs/modules/cli.rst b/docs/modules/cli.rst new file mode 100644 index 0000000..7fd5af6 --- /dev/null +++ b/docs/modules/cli.rst @@ -0,0 +1,6 @@ +CLI - Command Line Interface +============================== + +.. automodule:: tensorlayer.cli + +.. automodule:: tensorlayer.cli.train diff --git a/docs/modules/cost.rst b/docs/modules/cost.rst new file mode 100644 index 0000000..eba52f4 --- /dev/null +++ b/docs/modules/cost.rst @@ -0,0 +1,100 @@ +API - Cost +================== + +To make TensorLayer simple, we minimize the number of cost functions as much as +we can. So we encourage you to use TensorFlow's function, , see `TensorFlow API `_. + +.. note:: + Please refer to `Getting Started `_ for getting specific weights for weight regularization. + +.. automodule:: tensorlayer.cost + +.. autosummary:: + + cross_entropy + sigmoid_cross_entropy + binary_cross_entropy + mean_squared_error + normalized_mean_square_error + absolute_difference_error + dice_coe + dice_hard_coe + iou_coe + cross_entropy_seq + cross_entropy_seq_with_mask + cosine_similarity + li_regularizer + lo_regularizer + maxnorm_regularizer + maxnorm_o_regularizer + maxnorm_i_regularizer + huber_loss + + +Softmax cross entropy +---------------------- +.. autofunction:: cross_entropy + +Sigmoid cross entropy +---------------------- +.. autofunction:: sigmoid_cross_entropy + +Binary cross entropy +------------------------- +.. autofunction:: binary_cross_entropy + +Mean squared error (L2) +------------------------- +.. autofunction:: mean_squared_error + +Normalized mean square error +-------------------------------- +.. autofunction:: normalized_mean_square_error + +Absolute difference error (L1) +-------------------------------- +.. autofunction:: absolute_difference_error + +Dice coefficient +------------------------- +.. autofunction:: dice_coe + +Hard Dice coefficient +------------------------- +.. autofunction:: dice_hard_coe + +IOU coefficient +------------------------- +.. autofunction:: iou_coe + +Cross entropy for sequence +----------------------------- +.. autofunction:: cross_entropy_seq + +Cross entropy with mask for sequence +---------------------------------------- +.. autofunction:: cross_entropy_seq_with_mask + +Cosine similarity +------------------- +.. autofunction:: cosine_similarity + +Regularization functions +-------------------------- + +For ``tf.nn.l2_loss``, ``tf.contrib.layers.l1_regularizer``, ``tf.contrib.layers.l2_regularizer`` and +``tf.contrib.layers.sum_regularizer``, see tensorflow API. +Maxnorm +^^^^^^^^^^ +.. autofunction:: maxnorm_regularizer + +Special +^^^^^^^^^^ +.. autofunction:: li_regularizer +.. autofunction:: lo_regularizer +.. autofunction:: maxnorm_o_regularizer +.. autofunction:: maxnorm_i_regularizer + +Huber Loss +^^^^^^^^^^ +.. autofunction:: huber_loss \ No newline at end of file diff --git a/docs/modules/db.rst b/docs/modules/db.rst new file mode 100644 index 0000000..53a89ac --- /dev/null +++ b/docs/modules/db.rst @@ -0,0 +1,260 @@ +API - Database +========================= + +This is the alpha version of database management system. +If you have any trouble, please ask for help at `tensorlayer@gmail.com `_ . + +Why Database +---------------- + +TensorLayer is designed for real world production, capable of large scale machine learning applications. +TensorLayer database is introduced to address the many data management challenges in the large scale machine learning projects, such as: + +1. Finding training data from an enterprise data warehouse. +2. Loading large datasets that are beyond the storage limitation of one computer. +3. Managing different models with version control, and comparing them(e.g. accuracy). +4. Automating the process of training, evaluating and deploying machine learning models. + +With the TensorLayer system, we introduce this database technology to address the challenges above. + +The database management system is designed with the following three principles in mind. + +Everything is Data +^^^^^^^^^^^^^^^^^^ + +Data warehouses can store and capture the entire machine learning development process. The data can be categorized as: + +1. Dataset: This includes all the data used for training, validation and prediction. The labels can be manually specified or generated by model prediction. +2. Model architecture: The database includes a table that stores different model architectures, enabling users to reuse the many model development works. +3. Model parameters: This database stores all the model parameters of each epoch in the training step. +4. Tasks: A project usually include many small tasks. Each task contains the necessary information such as hyper-parameters for training or validation. For a training task, typical information includes training data, the model parameter, the model architecture, how many epochs the training task has. Validation, testing and inference are also supported by the task system. +5. Loggings: The logs store all the metrics of each machine learning model, such as the time stamp, loss and accuracy of each batch or epoch. + +TensorLayer database in principle is a keyword based search engine. Each model, parameter, or training data is assigned many tags. +The storage system organizes data into two layers: the index layer, and the blob layer. The index layer stores all the tags and references to the blob storage. The index layer is implemented based on NoSQL document database such as MongoDB. The blob layer stores videos, medical images or label masks in large chunk size, which is usually implemented based on a file system. Our database is based on MongoDB. The blob system is based on the GridFS while the indexes are stored as documents. + + +Everything is identified by Query +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Within the database framework, any entity within the data warehouse, such as the data, model or tasks is specified by the database query language. +As a reference, the query is more space efficient for storage and it can specify multiple objects in a concise way. +Another advantage of such a design is enabling a highly flexible software system. +Many system can be implemented by simply rewriting different components, with many new applications can be implemented just by update the query without modification of any application code. + +.. + A pulling based Stream processing pipeline + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + For large training datasets, we provide a stream interface, which can in theory support an unlimitedly large dataset. + A stream interface, implemented as a python generators, keeps on generating new data during training. + When using the stream interface, the idea of epoch does not apply anymore, instead, we specify the batch size and image a epoch will have a fixed large number of steps. + + Many techniques are introduced behind the stream interface for performance optimization. + The stream interface is based on the database cursor technology. + For every data query, only the cursors are returned immediately, not the actual query results. + The actual data are loaded later when the generators are evaluated. + + The data loading is further optimized in many ways: + + 1. Data are compressed and decompressed, + 2. The data are loaded in bulk model to further optimize the IO traffic + 3. The data argumentation or random sampling are computed on the fly, only after the data are loaded into the local computer memory. + 4. We also introduced simple cache system that stores the recent blob data. + + Based on the stream interface, a continuos machine learning system can be easily implemented. + On a distributed system, the model training, validation and deployment can be run by different computing node which are all running continuously. + The trainer keeps on optimizing the models with new added data, the evaluation node keeps on evaluating the recent generated models and the deployment system keeps pulling the best models from the our database warehouse for application. + +Preparation +-------------- + +In principle, the database can be implemented by any document oriented NoSQL database system. +The existing implementation is based on MongoDB. +Further implementations on other databases will be released depending on the progress. +It will be straightforward to port our database system to Google Cloud, AWS and Azure. +The following tutorials are based on the MongoDB implementation. + +Installing and running MongoDB +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The installation instruction of MongoDB can be found at +`MongoDB Docs `__. +There are also many MongoDB services from Amazon or GCP, such as Mongo Atlas from MongoDB +User can also use docker, which is a powerful tool for `deploying software `_ . +After installing MongoDB, a MongoDB management tool with graphic user interface will be extremely useful. +Users can also install Studio3T(MongoChef), which is powerful user interface tool for MongoDB and is free for non-commercial use `studio3t `_. + +Tutorials +---------- + +Connect to the database +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Similar with MongoDB management tools, IP and port number are required for connecting to the database. +To distinguish the different projects, the database instances have a ``project_name`` argument. +In the following example, we connect to MongoDB on a local machine with the IP ``localhost``, and port ``27017`` (this is the default port number of MongoDB). + +.. code-block:: python + + db = tl.db.TensorHub(ip='localhost', port=27017, dbname='temp', + username=None, password='password', project_name='tutorial') + +Dataset management +^^^^^^^^^^^^^^^^^^^^ + +You can save a dataset into the database and allow all machines to access it. +Apart from the dataset key, you can also insert a custom argument such as version and description, for better managing the datasets. +Note that, all saving functions will automatically save a timestamp, allowing you to load staff (data, model, task) using the timestamp. + +.. code-block:: python + + db.save_dataset(dataset=[X_train, y_train, X_test, y_test], dataset_name='mnist', description='this is a tutorial') + +After saving the dataset, others can access the dataset as followed: + +.. code-block:: python + + dataset = db.find_dataset('mnist') + dataset = db.find_dataset('mnist', version='1.0') + +If you have multiple datasets that use the same dataset key, you can get all of them as followed: + +.. code-block:: python + + datasets = db.find_all_datasets('mnist') + + +Model management +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Save model architecture and parameters into database. +The model architecture is represented by a TL graph, and the parameters are stored as a list of array. + +.. code-block:: python + + db.save_model(net, accuracy=0.8, loss=2.3, name='second_model') + +After saving the model into database, we can load it as follow: + +.. code-block:: python + + net = db.find_model(sess=sess, accuracy=0.8, loss=2.3) + +If there are many models, you can use MongoDB's 'sort' method to find the model you want. +To get the newest or oldest model, you can sort by time: + +.. code-block:: python + + ## newest model + + net = db.find_model(sess=sess, sort=[("time", pymongo.DESCENDING)]) + net = db.find_model(sess=sess, sort=[("time", -1)]) + + ## oldest model + + net = db.find_model(sess=sess, sort=[("time", pymongo.ASCENDING)]) + net = db.find_model(sess=sess, sort=[("time", 1)]) + +If you save the model along with accuracy, you can get the model with the best accuracy as followed: + +.. code-block:: python + + net = db.find_model(sess=sess, sort=[("test_accuracy", -1)]) + +To delete all models in a project: + +.. code-block:: python + + db.delete_model() + +If you want to specify which model you want to delete, you need to put arguments inside. + + +Event / Logging management +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Save training log: + +.. code-block:: python + + db.save_training_log(accuracy=0.33) + db.save_training_log(accuracy=0.44) + +Delete logs that match the requirement: + +.. code-block:: python + + db.delete_training_log(accuracy=0.33) + +Delete all logging of this project: + +.. code-block:: python + + db.delete_training_log() + db.delete_validation_log() + db.delete_testing_log() + +Task distribution +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A project usually consists of many tasks such as hyper parameter selection. +To make it easier, we can distribute these tasks to several GPU servers. +A task consists of a task script, hyper parameters, desired result and a status. + +A task distributor can push both dataset and tasks into a database, allowing task runners on GPU servers to pull and run. +The following is an example that pushes 3 tasks with different hyper parameters. + +.. code-block:: python + + ## save dataset into database, then allow other servers to use it + X_train, y_train, X_val, y_val, X_test, y_test = tl.files.load_mnist_dataset(shape=(-1, 784)) + db.save_dataset((X_train, y_train, X_val, y_val, X_test, y_test), 'mnist', description='handwriting digit') + + ## push tasks into database, then allow other servers pull tasks to run + db.create_task( + task_name='mnist', script='task_script.py', hyper_parameters=dict(n_units1=800, n_units2=800), + saved_result_keys=['test_accuracy'], description='800-800' + ) + + db.create_task( + task_name='mnist', script='task_script.py', hyper_parameters=dict(n_units1=600, n_units2=600), + saved_result_keys=['test_accuracy'], description='600-600' + ) + + db.create_task( + task_name='mnist', script='task_script.py', hyper_parameters=dict(n_units1=400, n_units2=400), + saved_result_keys=['test_accuracy'], description='400-400' + ) + + ## wait for tasks to finish + while db.check_unfinished_task(task_name='mnist'): + print("waiting runners to finish the tasks") + time.sleep(1) + + ## you can get the model and result from database and do some analysis at the end + +The task runners on GPU servers can monitor the database, and run the tasks immediately when they are made available. +In the task script, we can save the final model and results to the database, this allows task distributors to get the desired model and results. + +.. code-block:: python + + ## monitors the database and pull tasks to run + while True: + print("waiting task from distributor") + db.run_task(task_name='mnist', sort=[("time", -1)]) + time.sleep(1) + +Example codes +^^^^^^^^^^^^^^^^ + +See `here `__. + + +TensorHub API +--------------------- + +.. automodule:: tensorlayer.db + +.. autoclass:: TensorHub + :members: diff --git a/docs/modules/distributed.rst b/docs/modules/distributed.rst new file mode 100644 index 0000000..5e30325 --- /dev/null +++ b/docs/modules/distributed.rst @@ -0,0 +1,23 @@ +API - Distributed Training +============================= + +(Alpha release - usage might change later) + +Helper API to run a distributed training. +Check these `examples `_. + +.. automodule:: tensorlayer.distributed + +.. autosummary:: + + Trainer + +Distributed training +-------------------- + +Trainer +^^^^^^^^^^^ + +.. autofunction:: Trainer + + diff --git a/docs/modules/files.rst b/docs/modules/files.rst new file mode 100644 index 0000000..c823b4d --- /dev/null +++ b/docs/modules/files.rst @@ -0,0 +1,295 @@ +API - Files +=================================== + +A collections of helper functions to work with dataset. +Load benchmark dataset, save and restore model, save and load variables. + +.. automodule:: tensorlayer.files + +.. autosummary:: + + load_mnist_dataset + load_fashion_mnist_dataset + load_cifar10_dataset + load_cropped_svhn + load_ptb_dataset + load_matt_mahoney_text8_dataset + load_imdb_dataset + load_nietzsche_dataset + load_wmt_en_fr_dataset + load_flickr25k_dataset + load_flickr1M_dataset + load_cyclegan_dataset + load_celebA_dataset + load_voc_dataset + load_mpii_pose_dataset + download_file_from_google_drive + + save_npz + load_npz + assign_weights + load_and_assign_npz + save_npz_dict + load_and_assign_npz_dict + save_weights_to_hdf5 + load_hdf5_to_weights_in_order + load_hdf5_to_weights + + save_any_to_npy + load_npy_to_any + + file_exists + folder_exists + del_file + del_folder + read_file + load_file_list + load_folder_list + exists_or_mkdir + maybe_download_and_extract + + natural_keys + +.. + save_ckpt + load_ckpt + save_graph + load_graph + save_graph_and_params + load_graph_and_params + + npz_to_W_pdf + + +Load dataset functions +------------------------ + +MNIST +^^^^^^^ +.. autofunction:: load_mnist_dataset + +Fashion-MNIST +^^^^^^^^^^^^^^^^ +.. autofunction:: load_fashion_mnist_dataset + +CIFAR-10 +^^^^^^^^^^^^ +.. autofunction:: load_cifar10_dataset + +SVHN +^^^^^^^ +.. autofunction:: load_cropped_svhn + +Penn TreeBank (PTB) +^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: load_ptb_dataset + +Matt Mahoney's text8 +^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: load_matt_mahoney_text8_dataset + +IMBD +^^^^^^^^^^^ +.. autofunction:: load_imdb_dataset + +Nietzsche +^^^^^^^^^^^^^^ +.. autofunction:: load_nietzsche_dataset + +English-to-French translation data from the WMT'15 Website +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: load_wmt_en_fr_dataset + +Flickr25k +^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: load_flickr25k_dataset + +Flickr1M +^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: load_flickr1M_dataset + +CycleGAN +^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: load_cyclegan_dataset + +CelebA +^^^^^^^^^ +.. autofunction:: load_celebA_dataset + +VOC 2007/2012 +^^^^^^^^^^^^^^^^ +.. autofunction:: load_voc_dataset + +MPII +^^^^^^^^^^^^^^^^ +.. autofunction:: load_mpii_pose_dataset + +Google Drive +^^^^^^^^^^^^^^^^ +.. autofunction:: download_file_from_google_drive + + + + + +Load and save network +---------------------- + +TensorFlow provides ``.ckpt`` file format to save and restore the models, while +we suggest to use standard python file format ``hdf5`` to save models for the +sake of cross-platform. Other file formats such as ``.npz`` are also available. + +.. code-block:: python + + ## save model as .h5 + tl.files.save_weights_to_hdf5('model.h5', network.all_weights) + # restore model from .h5 (in order) + tl.files.load_hdf5_to_weights_in_order('model.h5', network.all_weights) + # restore model from .h5 (by name) + tl.files.load_hdf5_to_weights('model.h5', network.all_weights) + + ## save model as .npz + tl.files.save_npz(network.all_weights , name='model.npz') + # restore model from .npz (method 1) + load_params = tl.files.load_npz(name='model.npz') + tl.files.assign_weights(sess, load_params, network) + # restore model from .npz (method 2) + tl.files.load_and_assign_npz(sess=sess, name='model.npz', network=network) + + ## you can assign the pre-trained parameters as follow + # 1st parameter + tl.files.assign_weights(sess, [load_params[0]], network) + # the first three parameters + tl.files.assign_weights(sess, load_params[:3], network) + +Save network into list (npz) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: save_npz + +Load network from list (npz) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: load_npz + +Assign a list of parameters to network +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: assign_weights + +Load and assign a list of parameters to network +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: load_and_assign_npz + + +Save network into dict (npz) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: save_npz_dict + +Load network from dict (npz) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: load_and_assign_npz_dict + +Save network into OrderedDict (hdf5) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: save_weights_to_hdf5 + +Load network from hdf5 in order +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: load_hdf5_to_weights_in_order + +Load network from hdf5 by name +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: load_hdf5_to_weights + +.. + Save network architecture as a graph + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + .. autofunction:: save_graph + + Load network architecture from a graph + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + .. autofunction:: load_graph + + Save network architecture and parameters + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + .. autofunction:: save_graph_and_params + + Load network architecture and parameters + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + .. autofunction:: load_graph_and_params + +.. + Save network into ckpt + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + .. autofunction:: save_ckpt + + Load network from ckpt + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + .. autofunction:: load_ckpt + + + + +Load and save variables +------------------------ + +Save variables as .npy +^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: save_any_to_npy + +Load variables from .npy +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: load_npy_to_any + + + + +Folder/File functions +------------------------ + +Check file exists +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: file_exists + +Check folder exists +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: folder_exists + +Delete file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: del_file + +Delete folder +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: del_folder + +Read file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: read_file + +Load file list from folder +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: load_file_list + +Load folder list from folder +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: load_folder_list + +Check and Create folder +^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: exists_or_mkdir + +Download or extract +^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: maybe_download_and_extract + + + +Sort +------- + +List of string with number in human order +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: natural_keys + +Visualizing npz file +---------------------- +.. autofunction:: npz_to_W_pdf diff --git a/docs/modules/initializers.rst b/docs/modules/initializers.rst new file mode 100644 index 0000000..6311619 --- /dev/null +++ b/docs/modules/initializers.rst @@ -0,0 +1,51 @@ +API - Initializers +========================= + +To make TensorLayer simple, TensorLayer only warps some basic initializers. For more advanced initializer, +e.g. ``tf.initializers.he_normal``, please refer to TensorFlow provided initializers +`here `_. + +.. automodule:: tensorlayer.initializers + +.. autosummary:: + + Initializer + Zeros + Ones + Constant + RandomUniform + RandomNormal + TruncatedNormal + deconv2d_bilinear_upsampling_initializer + +Initializer +------------ +.. autoclass:: Initializer + +Zeros +------------ +.. autoclass:: Zeros + +Ones +------------ +.. autoclass:: Ones + +Constant +----------------- +.. autoclass:: Constant + +RandomUniform +-------------- +.. autoclass:: RandomUniform + +RandomNormal +--------------------- +.. autoclass:: RandomNormal + +TruncatedNormal +--------------------- +.. autoclass:: TruncatedNormal + +deconv2d_bilinear_upsampling_initializer +------------------------------------------ +.. autofunction:: deconv2d_bilinear_upsampling_initializer diff --git a/docs/modules/iterate.rst b/docs/modules/iterate.rst new file mode 100644 index 0000000..57d2ddc --- /dev/null +++ b/docs/modules/iterate.rst @@ -0,0 +1,36 @@ +API - Iteration +========================== + +Data iteration. + + +.. automodule:: tensorlayer.iterate + +.. autosummary:: + + minibatches + seq_minibatches + seq_minibatches2 + ptb_iterator + + +Non-time series +-------------------- + +.. autofunction:: minibatches + + +Time series +---------------------- + +Sequence iteration 1 +^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: seq_minibatches + +Sequence iteration 2 +^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: seq_minibatches2 + +PTB dataset iteration +^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: ptb_iterator diff --git a/docs/modules/layers.rst b/docs/modules/layers.rst new file mode 100644 index 0000000..f6c86a5 --- /dev/null +++ b/docs/modules/layers.rst @@ -0,0 +1,701 @@ +API - Layers +============ + +.. automodule:: tensorlayer.layers + +.. ----------------------------------------------------------- +.. Layer List +.. ----------------------------------------------------------- + +Layer list +---------- + +.. autosummary:: + + Layer + + Input + + OneHot + Word2vecEmbedding + Embedding + AverageEmbedding + + Dense + Dropout + GaussianNoise + DropconnectDense + + UpSampling2d + DownSampling2d + + Conv1d + Conv2d + Conv3d + DeConv2d + DeConv3d + DepthwiseConv2d + SeparableConv1d + SeparableConv2d + DeformableConv2d + GroupConv2d + + PadLayer + PoolLayer + ZeroPad1d + ZeroPad2d + ZeroPad3d + MaxPool1d + MeanPool1d + MaxPool2d + MeanPool2d + MaxPool3d + MeanPool3d + GlobalMaxPool1d + GlobalMeanPool1d + GlobalMaxPool2d + GlobalMeanPool2d + GlobalMaxPool3d + GlobalMeanPool3d + CornerPool2d + + SubpixelConv1d + SubpixelConv2d + + SpatialTransformer2dAffine + transformer + batch_transformer + + BatchNorm + BatchNorm1d + BatchNorm2d + BatchNorm3d + LocalResponseNorm + InstanceNorm + InstanceNorm1d + InstanceNorm2d + InstanceNorm3d + LayerNorm + GroupNorm + SwitchNorm + + RNN + SimpleRNN + GRURNN + LSTMRNN + BiRNN + + retrieve_seq_length_op + retrieve_seq_length_op2 + retrieve_seq_length_op3 + target_mask_op + + Flatten + Reshape + Transpose + Shuffle + + Lambda + + Concat + Elementwise + ElementwiseLambda + + ExpandDims + Tile + + Stack + UnStack + + Sign + Scale + BinaryDense + BinaryConv2d + TernaryDense + TernaryConv2d + DorefaDense + DorefaConv2d + + PRelu + PRelu6 + PTRelu6 + + flatten_reshape + initialize_rnn_state + list_remove_repeat + +.. ----------------------------------------------------------- +.. Basic Layers +.. ----------------------------------------------------------- + +Base Layer +----------- + +.. autoclass:: Layer + +.. ----------------------------------------------------------- +.. Input Layer +.. ----------------------------------------------------------- + +Input Layers +--------------- + +Input Layer +^^^^^^^^^^^^^^^^ +.. autofunction:: Input + +.. ----------------------------------------------------------- +.. Embedding Layers +.. ----------------------------------------------------------- + + +One-hot Layer +^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: OneHot + +Word2Vec Embedding Layer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: Word2vecEmbedding + +Embedding Layer +^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: Embedding + +Average Embedding Layer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: AverageEmbedding + +.. ----------------------------------------------------------- +.. Activation Layers +.. ----------------------------------------------------------- + + +Activation Layers +--------------------------- + +PReLU Layer +^^^^^^^^^^^^^^^^^ +.. autoclass:: PRelu + + +PReLU6 Layer +^^^^^^^^^^^^^^^^^^ +.. autoclass:: PRelu6 + + +PTReLU6 Layer +^^^^^^^^^^^^^^^^^^^ +.. autoclass:: PTRelu6 + + +.. ----------------------------------------------------------- +.. Convolutional Layers +.. ----------------------------------------------------------- + +Convolutional Layers +--------------------- + +Convolutions +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Conv1d +""""""""""""""""""""" +.. autoclass:: Conv1d + +Conv2d +""""""""""""""""""""" +.. autoclass:: Conv2d + +Conv3d +""""""""""""""""""""" +.. autoclass:: Conv3d + +Deconvolutions +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +DeConv2d +""""""""""""""""""""" +.. autoclass:: DeConv2d + +DeConv3d +""""""""""""""""""""" +.. autoclass:: DeConv3d + + +Deformable Convolutions +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +DeformableConv2d +""""""""""""""""""""" +.. autoclass:: DeformableConv2d + + +Depthwise Convolutions +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +DepthwiseConv2d +""""""""""""""""""""" +.. autoclass:: DepthwiseConv2d + + +Group Convolutions +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +GroupConv2d +""""""""""""""""""""" +.. autoclass:: GroupConv2d + + +Separable Convolutions +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +SeparableConv1d +""""""""""""""""""""" +.. autoclass:: SeparableConv1d + +SeparableConv2d +""""""""""""""""""""" +.. autoclass:: SeparableConv2d + + +SubPixel Convolutions +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +SubpixelConv1d +""""""""""""""""""""" +.. autoclass:: SubpixelConv1d + +SubpixelConv2d +""""""""""""""""""""" +.. autoclass:: SubpixelConv2d + + +.. ----------------------------------------------------------- +.. Dense Layers +.. ----------------------------------------------------------- + +Dense Layers +------------- + +Dense Layer +^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: Dense + +Drop Connect Dense Layer +^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: DropconnectDense + + +.. ----------------------------------------------------------- +.. Dropout Layer +.. ----------------------------------------------------------- + +Dropout Layers +------------------- +.. autoclass:: Dropout + +.. ----------------------------------------------------------- +.. Extend Layers +.. ----------------------------------------------------------- + +Extend Layers +------------------- + +Expand Dims Layer +^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: ExpandDims + + +Tile layer +^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: Tile + +.. ----------------------------------------------------------- +.. Image Resampling Layers +.. ----------------------------------------------------------- + +Image Resampling Layers +------------------------- + +2D UpSampling +^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: UpSampling2d + +2D DownSampling +^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: DownSampling2d + +.. ----------------------------------------------------------- +.. Lambda Layer +.. ----------------------------------------------------------- + +Lambda Layers +--------------- + +Lambda Layer +^^^^^^^^^^^^^^^^^^^ +.. autoclass:: Lambda + +ElementWise Lambda Layer +^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: ElementwiseLambda + +.. ----------------------------------------------------------- +.. Merge Layer +.. ----------------------------------------------------------- + +Merge Layers +--------------- + +Concat Layer +^^^^^^^^^^^^^^^^^^^ +.. autoclass:: Concat + +ElementWise Layer +^^^^^^^^^^^^^^^^^^^ +.. autoclass:: Elementwise + +.. ----------------------------------------------------------- +.. Noise Layers +.. ----------------------------------------------------------- + +Noise Layer +--------------- +.. autoclass:: GaussianNoise + +.. ----------------------------------------------------------- +.. Normalization Layers +.. ----------------------------------------------------------- + +Normalization Layers +-------------------- + +Batch Normalization +^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: BatchNorm + +Batch Normalization 1D +^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: BatchNorm1d + +Batch Normalization 2D +^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: BatchNorm2d + +Batch Normalization 3D +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: BatchNorm3d + +Local Response Normalization +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: LocalResponseNorm + +Instance Normalization +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: InstanceNorm + +Instance Normalization 1D +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: InstanceNorm1d + +Instance Normalization 2D +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: InstanceNorm2d + +Instance Normalization 3D +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: InstanceNorm3d + +Layer Normalization +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: LayerNorm + +Group Normalization +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: GroupNorm + +Switch Normalization +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: SwitchNorm + +.. ----------------------------------------------------------- +.. Padding Layers +.. ----------------------------------------------------------- + +Padding Layers +------------------------ + +Pad Layer (Expert API) +^^^^^^^^^^^^^^^^^^^^^^^^^ +Padding layer for any modes. + +.. autoclass:: PadLayer + +1D Zero padding +^^^^^^^^^^^^^^^^^^^ +.. autoclass:: ZeroPad1d + +2D Zero padding +^^^^^^^^^^^^^^^^^^^ +.. autoclass:: ZeroPad2d + +3D Zero padding +^^^^^^^^^^^^^^^^^^^ +.. autoclass:: ZeroPad3d + +.. ----------------------------------------------------------- +.. Pooling Layers +.. ----------------------------------------------------------- + +Pooling Layers +------------------------ + +Pool Layer (Expert API) +^^^^^^^^^^^^^^^^^^^^^^^^^ +Pooling layer for any dimensions and any pooling functions. + +.. autoclass:: PoolLayer + +1D Max pooling +^^^^^^^^^^^^^^^^^^^ +.. autoclass:: MaxPool1d + +1D Mean pooling +^^^^^^^^^^^^^^^^^^^ +.. autoclass:: MeanPool1d + +2D Max pooling +^^^^^^^^^^^^^^^^^^^ +.. autoclass:: MaxPool2d + +2D Mean pooling +^^^^^^^^^^^^^^^^^^^ +.. autoclass:: MeanPool2d + +3D Max pooling +^^^^^^^^^^^^^^^^^^^ +.. autoclass:: MaxPool3d + +3D Mean pooling +^^^^^^^^^^^^^^^^^^^ +.. autoclass:: MeanPool3d + +1D Global Max pooling +^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: GlobalMaxPool1d + +1D Global Mean pooling +^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: GlobalMeanPool1d + +2D Global Max pooling +^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: GlobalMaxPool2d + +2D Global Mean pooling +^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: GlobalMeanPool2d + +3D Global Max pooling +^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: GlobalMaxPool3d + +3D Global Mean pooling +^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: GlobalMeanPool3d + +2D Corner pooling +^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: CornerPool2d + +.. ----------------------------------------------------------- +.. Quantized Layers +.. ----------------------------------------------------------- + +Quantized Nets +------------------ + +This is an experimental API package for building Quantized Neural Networks. We are using matrix multiplication rather than add-minus and bit-count operation at the moment. Therefore, these APIs would not speed up the inferencing, for production, you can train model via TensorLayer and deploy the model into other customized C/C++ implementation (We probably provide users an extra C/C++ binary net framework that can load model from TensorLayer). + +Note that, these experimental APIs can be changed in the future. + + +Sign +^^^^^^^^^^^^^^ +.. autoclass:: Sign + +Scale +^^^^^^^^^^^^^^ +.. autoclass:: Scale + +Binary Dense Layer +^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: BinaryDense + +Binary (De)Convolutions +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +BinaryConv2d +""""""""""""""""""""" +.. autoclass:: BinaryConv2d + +Ternary Dense Layer +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +TernaryDense +""""""""""""""""""""" +.. autoclass:: TernaryDense + +Ternary Convolutions +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +TernaryConv2d +""""""""""""""""""""" +.. autoclass:: TernaryConv2d + +DoReFa Convolutions +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +DorefaConv2d +""""""""""""""""""""" +.. autoclass:: DorefaConv2d + +DoReFa Convolutions +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +DorefaConv2d +""""""""""""""""""""" +.. autoclass:: DorefaConv2d + + +.. ----------------------------------------------------------- +.. Recurrent Layers +.. ----------------------------------------------------------- + +Recurrent Layers +--------------------- + +Common Recurrent layer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +All recurrent layers can implement any type of RNN cell by feeding different cell function (LSTM, GRU etc). + +RNN layer +"""""""""""""""""""""""""" +.. autoclass:: RNN + +RNN layer with Simple RNN Cell +"""""""""""""""""""""""""""""""""" +.. autoclass:: SimpleRNN + +RNN layer with GRU Cell +"""""""""""""""""""""""""""""""""" +.. autoclass:: GRURNN + +RNN layer with LSTM Cell +"""""""""""""""""""""""""""""""""" +.. autoclass:: LSTMRNN + +Bidirectional layer +""""""""""""""""""""""""""""""""" +.. autoclass:: BiRNN + +Advanced Ops for Dynamic RNN +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +These operations usually be used inside Dynamic RNN layer, they can +compute the sequence lengths for different situation and get the last RNN outputs by indexing. + +Compute Sequence length 1 +"""""""""""""""""""""""""" +.. autofunction:: retrieve_seq_length_op + +Compute Sequence length 2 +""""""""""""""""""""""""""""" +.. autofunction:: retrieve_seq_length_op2 + +Compute Sequence length 3 +"""""""""""""""""""""""""""" +.. autofunction:: retrieve_seq_length_op3 + +Compute mask of the target sequence +""""""""""""""""""""""""""""""""""""""" +.. autofunction:: target_mask_op + + + +.. ----------------------------------------------------------- +.. Shape Layers +.. ----------------------------------------------------------- + +Shape Layers +------------ + +Flatten Layer +^^^^^^^^^^^^^^^ +.. autoclass:: Flatten + +Reshape Layer +^^^^^^^^^^^^^^^ +.. autoclass:: Reshape + +Transpose Layer +^^^^^^^^^^^^^^^^^ +.. autoclass:: Transpose + +Shuffle Layer +^^^^^^^^^^^^^^^^^ +.. autoclass:: Shuffle + +.. ----------------------------------------------------------- +.. Spatial Transformer Layers +.. ----------------------------------------------------------- + +Spatial Transformer +----------------------- + +2D Affine Transformation +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: SpatialTransformer2dAffine + +2D Affine Transformation function +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: transformer + +Batch 2D Affine Transformation function +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: batch_transformer + +.. ----------------------------------------------------------- +.. Stack Layers +.. ----------------------------------------------------------- + +Stack Layer +------------- + +Stack Layer +^^^^^^^^^^^^^^ +.. autoclass:: Stack + +Unstack Layer +^^^^^^^^^^^^^^^ +.. autoclass:: UnStack + + +.. ----------------------------------------------------------- +.. Helper Functions +.. ----------------------------------------------------------- + +Helper Functions +------------------------ + +Flatten tensor +^^^^^^^^^^^^^^^^^ +.. autofunction:: flatten_reshape + +Initialize RNN state +^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: initialize_rnn_state + +Remove repeated items in a list +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: list_remove_repeat + diff --git a/docs/modules/models.rst b/docs/modules/models.rst new file mode 100644 index 0000000..272f1d9 --- /dev/null +++ b/docs/modules/models.rst @@ -0,0 +1,59 @@ +API - Models +================================ + +TensorLayer provides many pretrained models, you can easily use the whole or a part of the pretrained models via these APIs. + +.. automodule:: tensorlayer.models + +.. autosummary:: + + Model + + VGG16 + VGG19 + SqueezeNetV1 + MobileNetV1 + ResNet50 + Seq2seq + Seq2seqLuongAttention + + +Base Model +----------- + +.. autoclass:: Model + +VGG16 +---------------------- + +.. autofunction:: VGG16 + +VGG19 +---------------------- + +.. autofunction:: VGG19 + +SqueezeNetV1 +---------------- +.. autofunction:: SqueezeNetV1 + +MobileNetV1 +---------------- + +.. autofunction:: MobileNetV1 + +ResNet50 +---------------- + +.. autofunction:: ResNet50 + +Seq2seq +------------------------ + +.. autoclass:: Seq2seq + + +Seq2seq Luong Attention +------------------------ + +.. autoclass:: Seq2seqLuongAttention diff --git a/docs/modules/nlp.rst b/docs/modules/nlp.rst new file mode 100644 index 0000000..16486a6 --- /dev/null +++ b/docs/modules/nlp.rst @@ -0,0 +1,148 @@ +API - Natural Language Processing +================================== + +Natural Language Processing and Word Representation. + +.. automodule:: tensorlayer.nlp + +.. autosummary:: + + generate_skip_gram_batch + + sample + sample_top + + SimpleVocabulary + Vocabulary + process_sentence + create_vocab + + simple_read_words + read_words + read_analogies_file + build_vocab + build_reverse_dictionary + build_words_dataset + save_vocab + + words_to_word_ids + word_ids_to_words + + basic_tokenizer + create_vocabulary + initialize_vocabulary + sentence_to_token_ids + data_to_token_ids + + moses_multi_bleu + + +Iteration function for training embedding matrix +------------------------------------------------- +.. autofunction:: generate_skip_gram_batch + + +Sampling functions +------------------- + +Simple sampling +^^^^^^^^^^^^^^^^^^^ +.. autofunction:: sample + +Sampling from top k +^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: sample_top + +Vector representations of words +------------------------------- + +Simple vocabulary class +^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: SimpleVocabulary + +Vocabulary class +^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: Vocabulary + +Process sentence +^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: process_sentence + +Create vocabulary +^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: create_vocab + +Read words from file +---------------------- + +Simple read file +^^^^^^^^^^^^^^^^^^ +.. autofunction:: simple_read_words + +Read file +^^^^^^^^^^^^^^^^^^ +.. autofunction:: read_words + + +Read analogy question file +----------------------------- +.. autofunction:: read_analogies_file + +Build vocabulary, word dictionary and word tokenization +-------------------------------------------------------- + +Build dictionary from word to id +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: build_vocab + +Build dictionary from id to word +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: build_reverse_dictionary + +Build dictionaries for id to word etc +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: build_words_dataset + +Save vocabulary +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: save_vocab + + +Convert words to IDs and IDs to words +-------------------------------------------------------- +These functions can be done by ``Vocabulary`` class. + +List of Words to IDs +^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: words_to_word_ids + +List of IDs to Words +^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: word_ids_to_words + + + +Functions for translation +--------------------------- + +Word Tokenization +^^^^^^^^^^^^^^^^^^^ +.. autofunction:: basic_tokenizer + +Create or read vocabulary +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: create_vocabulary +.. autofunction:: initialize_vocabulary + +Convert words to IDs and IDs to words +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: sentence_to_token_ids +.. autofunction:: data_to_token_ids + + +Metrics +--------------------------- + +BLEU +^^^^^^^^^^^^^^^^^^^ +.. autofunction:: moses_multi_bleu diff --git a/docs/modules/optimizers.rst b/docs/modules/optimizers.rst new file mode 100644 index 0000000..0ababc8 --- /dev/null +++ b/docs/modules/optimizers.rst @@ -0,0 +1,19 @@ +API - Optimizers +================ + +.. automodule:: tensorlayer.optimizers + +TensorLayer provides simple API and tools to ease research, development and reduce the time to production. +Therefore, we provide the latest state of the art optimizers that work with Tensorflow. + +Optimizers List +--------------- + +.. autosummary:: + + AMSGrad + +AMSGrad Optimizer +----------------- +.. autoclass:: AMSGrad + :members: \ No newline at end of file diff --git a/docs/modules/prepro.rst b/docs/modules/prepro.rst new file mode 100644 index 0000000..ec65da0 --- /dev/null +++ b/docs/modules/prepro.rst @@ -0,0 +1,641 @@ +API - Data Pre-Processing +========================= + +.. automodule:: tensorlayer.prepro + +.. autosummary:: + + affine_rotation_matrix + affine_horizontal_flip_matrix + affine_vertical_flip_matrix + affine_shift_matrix + affine_shear_matrix + affine_zoom_matrix + affine_respective_zoom_matrix + + transform_matrix_offset_center + affine_transform + affine_transform_cv2 + affine_transform_keypoints + projective_transform_by_points + + rotation + rotation_multi + crop + crop_multi + flip_axis + flip_axis_multi + shift + shift_multi + + shear + shear_multi + shear2 + shear_multi2 + swirl + swirl_multi + elastic_transform + elastic_transform_multi + + zoom + respective_zoom + zoom_multi + + brightness + brightness_multi + + illumination + + rgb_to_hsv + hsv_to_rgb + adjust_hue + + imresize + + pixel_value_scale + + samplewise_norm + featurewise_norm + + channel_shift + channel_shift_multi + + drop + + array_to_img + + find_contours + pt2map + binary_dilation + dilation + binary_erosion + erosion + + + obj_box_coord_rescale + obj_box_coords_rescale + obj_box_coord_scale_to_pixelunit + obj_box_coord_centroid_to_upleft_butright + obj_box_coord_upleft_butright_to_centroid + obj_box_coord_centroid_to_upleft + obj_box_coord_upleft_to_centroid + + parse_darknet_ann_str_to_list + parse_darknet_ann_list_to_cls_box + + obj_box_horizontal_flip + obj_box_imresize + obj_box_crop + obj_box_shift + obj_box_zoom + + keypoint_random_crop + keypoint_resize_random_crop + keypoint_random_rotate + keypoint_random_flip + keypoint_random_resize + keypoint_random_resize_shortestedge + + pad_sequences + remove_pad_sequences + process_sequences + sequences_add_start_id + sequences_add_end_id + sequences_add_end_id_after_pad + sequences_get_mask + + +.. + Threading + ------------ + .. autofunction:: threading_data + + +Affine Transform +---------------- + + +Python can be FAST +^^^^^^^^^^^^^^^^^^ + +Image augmentation is a critical step in deep learning. +Though TensorFlow has provided ``tf.image``, +image augmentation often remains as a key bottleneck. +``tf.image`` has three limitations: + +- Real-world visual tasks such as object detection, segmentation, and pose estimation + must cope with image meta-data (e.g., coordinates). + These data are beyond ``tf.image`` + which processes images as tensors. + +- ``tf.image`` operators + breaks the pure Python programing experience (i.e., users have to + use ``tf.py_func`` in order to call image functions written in Python); however, + frequent uses of ``tf.py_func`` slow down TensorFlow, + making users hard to balance flexibility and performance. + +- ``tf.image`` API is inflexible. Image operations are + performed in an order. They are hard to jointly optimize. More importantly, + sequential image operations can significantly + reduces the quality of images, thus affecting training accuracy. + + +TensorLayer addresses these limitations by providing a +high-performance image augmentation API in Python. +This API bases on affine transformation and ``cv2.wrapAffine``. +It allows you to combine multiple image processing functions into +a single matrix operation. This combined operation +is executed by the fast ``cv2`` library, offering 78x performance improvement (observed in +`openpose-plus `_ for example). +The following example illustrates the rationale +behind this tremendous speed up. + + +Example +^^^^^^^ + +The source code of complete examples can be found \ +`here `__. +The following is a typical Python program that applies rotation, shifting, flipping, zooming and shearing to an image, + +.. code-block:: python + + image = tl.vis.read_image('tiger.jpeg') + + xx = tl.prepro.rotation(image, rg=-20, is_random=False) + xx = tl.prepro.flip_axis(xx, axis=1, is_random=False) + xx = tl.prepro.shear2(xx, shear=(0., -0.2), is_random=False) + xx = tl.prepro.zoom(xx, zoom_range=0.8) + xx = tl.prepro.shift(xx, wrg=-0.1, hrg=0, is_random=False) + + tl.vis.save_image(xx, '_result_slow.png') + + +However, by leveraging affine transformation, image operations can be combined into one: + +.. code-block:: python + + # 1. Create required affine transformation matrices + M_rotate = tl.prepro.affine_rotation_matrix(angle=20) + M_flip = tl.prepro.affine_horizontal_flip_matrix(prob=1) + M_shift = tl.prepro.affine_shift_matrix(wrg=0.1, hrg=0, h=h, w=w) + M_shear = tl.prepro.affine_shear_matrix(x_shear=0.2, y_shear=0) + M_zoom = tl.prepro.affine_zoom_matrix(zoom_range=0.8) + + # 2. Combine matrices + # NOTE: operations are applied in a reversed order (i.e., rotation is performed first) + M_combined = M_shift.dot(M_zoom).dot(M_shear).dot(M_flip).dot(M_rotate) + + # 3. Convert the matrix from Cartesian coordinates (the origin in the middle of image) + # to image coordinates (the origin on the top-left of image) + transform_matrix = tl.prepro.transform_matrix_offset_center(M_combined, x=w, y=h) + + # 4. Transform the image using a single operation + result = tl.prepro.affine_transform_cv2(image, transform_matrix) # 76 times faster + + tl.vis.save_image(result, '_result_fast.png') + + +The following figure illustrates the rational behind combined affine transformation. + +.. image:: ../images/affine_transform_why.jpg + :width: 100 % + :align: center + + +Using combined affine transformation has two key benefits. First, it allows \ +you to leverage a pure Python API to achieve orders of magnitudes of speed up in image augmentation, +and thus prevent data pre-processing from becoming a bottleneck in training. \ +Second, performing sequential image transformation requires multiple image interpolations. \ +This produces low-quality input images. In contrast, a combined transformation performs the \ +interpolation only once, and thus +preserve the content in an image. The following figure illustrates these two benefits: + +.. image:: ../images/affine_transform_comparison.jpg + :width: 100 % + :align: center + +The major reason for combined affine transformation being fast is because it has lower computational complexity. +Assume we have ``k`` affine transformations ``T1, ..., Tk``, where ``Ti`` can be represented by 3x3 matrixes. +The sequential transformation can be represented as ``y = Tk (... T1(x))``, +and the time complexity is ``O(k N)`` where ``N`` is the cost of applying one transformation to image ``x``. +``N`` is linear to the size of ``x``. +For the combined transformation ``y = (Tk ... T1) (x)`` +the time complexity is ``O(27(k - 1) + N) = max{O(27k), O(N)} = O(N)`` (assuming 27k << N) where 27 = 3^3 is the cost for combining two transformations. + + +Get rotation matrix +^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: affine_rotation_matrix + +Get horizontal flipping matrix +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: affine_horizontal_flip_matrix + +Get vertical flipping matrix +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: affine_vertical_flip_matrix + +Get shifting matrix +^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: affine_shift_matrix + +Get shearing matrix +^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: affine_shear_matrix + +Get zooming matrix +^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: affine_zoom_matrix + +Get respective zooming matrix +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: affine_respective_zoom_matrix + +Cartesian to image coordinates +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: transform_matrix_offset_center + +.. + Apply image transform + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + .. autofunction:: affine_transform + +Apply image transform +^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: affine_transform_cv2 + +Apply keypoint transform +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: affine_transform_keypoints + + +Images +----------- + +Projective transform by points +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: projective_transform_by_points + +Rotation +^^^^^^^^^ +.. autofunction:: rotation +.. autofunction:: rotation_multi + +Crop +^^^^^^^^^ +.. autofunction:: crop +.. autofunction:: crop_multi + +Flip +^^^^^^^^^ +.. autofunction:: flip_axis +.. autofunction:: flip_axis_multi + +Shift +^^^^^^^^^ +.. autofunction:: shift +.. autofunction:: shift_multi + +Shear +^^^^^^^^^ +.. autofunction:: shear +.. autofunction:: shear_multi + +Shear V2 +^^^^^^^^^^^ +.. autofunction:: shear2 +.. autofunction:: shear_multi2 + +Swirl +^^^^^^^^^ +.. autofunction:: swirl +.. autofunction:: swirl_multi + +Elastic transform +^^^^^^^^^^^^^^^^^^ +.. autofunction:: elastic_transform +.. autofunction:: elastic_transform_multi + +Zoom +^^^^^^^^^ +.. autofunction:: zoom +.. autofunction:: zoom_multi + +Respective Zoom +^^^^^^^^^^^^^^^^^ +.. autofunction:: respective_zoom + +Brightness +^^^^^^^^^^^^ +.. autofunction:: brightness +.. autofunction:: brightness_multi + +Brightness, contrast and saturation +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: illumination + +RGB to HSV +^^^^^^^^^^^^^^ +.. autofunction:: rgb_to_hsv + +HSV to RGB +^^^^^^^^^^^^^^ +.. autofunction:: hsv_to_rgb + +Adjust Hue +^^^^^^^^^^^^^^ +.. autofunction:: adjust_hue + +Resize +^^^^^^^^^^^^ +.. autofunction:: imresize + +Pixel value scale +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: pixel_value_scale + +Normalization +^^^^^^^^^^^^^^^ +.. autofunction:: samplewise_norm +.. autofunction:: featurewise_norm + +Channel shift +^^^^^^^^^^^^^^ +.. autofunction:: channel_shift +.. autofunction:: channel_shift_multi + +Noise +^^^^^^^^^^^^^^ +.. autofunction:: drop + +Numpy and PIL +^^^^^^^^^^^^^^ +.. autofunction:: array_to_img + +Find contours +^^^^^^^^^^^^^^ +.. autofunction:: find_contours + +Points to Image +^^^^^^^^^^^^^^^^^ +.. autofunction:: pt2map + +Binary dilation +^^^^^^^^^^^^^^^^^ +.. autofunction:: binary_dilation + +Greyscale dilation +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: dilation + +Binary erosion +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: binary_erosion + +Greyscale erosion +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: erosion + + + +Object detection +------------------- + +Tutorial for Image Aug +^^^^^^^^^^^^^^^^^^^^^^^ + +Hi, here is an example for image augmentation on VOC dataset. + +.. code-block:: python + + import tensorlayer as tl + + ## download VOC 2012 dataset + imgs_file_list, _, _, _, classes, _, _,\ + _, objs_info_list, _ = tl.files.load_voc_dataset(dataset="2012") + + ## parse annotation and convert it into list format + ann_list = [] + for info in objs_info_list: + ann = tl.prepro.parse_darknet_ann_str_to_list(info) + c, b = tl.prepro.parse_darknet_ann_list_to_cls_box(ann) + ann_list.append([c, b]) + + # read and save one image + idx = 2 # you can select your own image + image = tl.vis.read_image(imgs_file_list[idx]) + tl.vis.draw_boxes_and_labels_to_image(image, ann_list[idx][0], + ann_list[idx][1], [], classes, True, save_name='_im_original.png') + + # left right flip + im_flip, coords = tl.prepro.obj_box_horizontal_flip(image, + ann_list[idx][1], is_rescale=True, is_center=True, is_random=False) + tl.vis.draw_boxes_and_labels_to_image(im_flip, ann_list[idx][0], + coords, [], classes, True, save_name='_im_flip.png') + + # resize + im_resize, coords = tl.prepro.obj_box_imresize(image, + coords=ann_list[idx][1], size=[300, 200], is_rescale=True) + tl.vis.draw_boxes_and_labels_to_image(im_resize, ann_list[idx][0], + coords, [], classes, True, save_name='_im_resize.png') + + # crop + im_crop, clas, coords = tl.prepro.obj_box_crop(image, ann_list[idx][0], + ann_list[idx][1], wrg=200, hrg=200, + is_rescale=True, is_center=True, is_random=False) + tl.vis.draw_boxes_and_labels_to_image(im_crop, clas, coords, [], + classes, True, save_name='_im_crop.png') + + # shift + im_shfit, clas, coords = tl.prepro.obj_box_shift(image, ann_list[idx][0], + ann_list[idx][1], wrg=0.1, hrg=0.1, + is_rescale=True, is_center=True, is_random=False) + tl.vis.draw_boxes_and_labels_to_image(im_shfit, clas, coords, [], + classes, True, save_name='_im_shift.png') + + # zoom + im_zoom, clas, coords = tl.prepro.obj_box_zoom(image, ann_list[idx][0], + ann_list[idx][1], zoom_range=(1.3, 0.7), + is_rescale=True, is_center=True, is_random=False) + tl.vis.draw_boxes_and_labels_to_image(im_zoom, clas, coords, [], + classes, True, save_name='_im_zoom.png') + + +In practice, you may want to use threading method to process a batch of images as follows. + +.. code-block:: python + + import tensorlayer as tl + import random + + batch_size = 64 + im_size = [416, 416] + n_data = len(imgs_file_list) + jitter = 0.2 + def _data_pre_aug_fn(data): + im, ann = data + clas, coords = ann + ## change image brightness, contrast and saturation randomly + im = tl.prepro.illumination(im, gamma=(0.5, 1.5), + contrast=(0.5, 1.5), saturation=(0.5, 1.5), is_random=True) + ## flip randomly + im, coords = tl.prepro.obj_box_horizontal_flip(im, coords, + is_rescale=True, is_center=True, is_random=True) + ## randomly resize and crop image, it can have same effect as random zoom + tmp0 = random.randint(1, int(im_size[0]*jitter)) + tmp1 = random.randint(1, int(im_size[1]*jitter)) + im, coords = tl.prepro.obj_box_imresize(im, coords, + [im_size[0]+tmp0, im_size[1]+tmp1], is_rescale=True, + interp='bicubic') + im, clas, coords = tl.prepro.obj_box_crop(im, clas, coords, + wrg=im_size[1], hrg=im_size[0], is_rescale=True, + is_center=True, is_random=True) + ## rescale value from [0, 255] to [-1, 1] (optional) + im = im / 127.5 - 1 + return im, [clas, coords] + + # randomly read a batch of image and the corresponding annotations + idexs = tl.utils.get_random_int(min=0, max=n_data-1, number=batch_size) + b_im_path = [imgs_file_list[i] for i in idexs] + b_images = tl.prepro.threading_data(b_im_path, fn=tl.vis.read_image) + b_ann = [ann_list[i] for i in idexs] + + # threading process + data = tl.prepro.threading_data([_ for _ in zip(b_images, b_ann)], + _data_pre_aug_fn) + b_images2 = [d[0] for d in data] + b_ann = [d[1] for d in data] + + # save all images + for i in range(len(b_images)): + tl.vis.draw_boxes_and_labels_to_image(b_images[i], + ann_list[idexs[i]][0], ann_list[idexs[i]][1], [], + classes, True, save_name='_bbox_vis_%d_original.png' % i) + tl.vis.draw_boxes_and_labels_to_image((b_images2[i]+1)*127.5, + b_ann[i][0], b_ann[i][1], [], classes, True, + save_name='_bbox_vis_%d.png' % i) + +Image Aug with TF Dataset API +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Example code for VOC `here `__. + +Coordinate pixel unit to percentage +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: obj_box_coord_rescale + +Coordinates pixel unit to percentage +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: obj_box_coords_rescale + +Coordinate percentage to pixel unit +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: obj_box_coord_scale_to_pixelunit + +Coordinate [x_center, x_center, w, h] to up-left button-right +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: obj_box_coord_centroid_to_upleft_butright + +Coordinate up-left button-right to [x_center, x_center, w, h] +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: obj_box_coord_upleft_butright_to_centroid + +Coordinate [x_center, x_center, w, h] to up-left-width-high +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: obj_box_coord_centroid_to_upleft + +Coordinate up-left-width-high to [x_center, x_center, w, h] +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: obj_box_coord_upleft_to_centroid + +Darknet format string to list +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: parse_darknet_ann_str_to_list + +Darknet format split class and coordinate +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: parse_darknet_ann_list_to_cls_box + +Image Aug - Flip +^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: obj_box_horizontal_flip + +Image Aug - Resize +^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: obj_box_imresize + +Image Aug - Crop +^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: obj_box_crop + +Image Aug - Shift +^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: obj_box_shift + +Image Aug - Zoom +^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: obj_box_zoom + +Keypoints +------------ + +Image Aug - Crop +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: keypoint_random_crop + +Image Aug - Resize then Crop +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: keypoint_resize_random_crop + +Image Aug - Rotate +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: keypoint_random_rotate + +Image Aug - Flip +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: keypoint_random_flip + +Image Aug - Resize +^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: keypoint_random_resize + +Image Aug - Resize Shortest Edge +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: keypoint_random_resize_shortestedge + + +Sequence +--------- + +More related functions can be found in ``tensorlayer.nlp``. + +Padding +^^^^^^^^^ +.. autofunction:: pad_sequences + +Remove Padding +^^^^^^^^^^^^^^^^^ +.. autofunction:: remove_pad_sequences + + +Process +^^^^^^^^^ +.. autofunction:: process_sequences + +Add Start ID +^^^^^^^^^^^^^^^ +.. autofunction:: sequences_add_start_id + + +Add End ID +^^^^^^^^^^^^^^^ +.. autofunction:: sequences_add_end_id + +Add End ID after pad +^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: sequences_add_end_id_after_pad + +Get Mask +^^^^^^^^^ +.. autofunction:: sequences_get_mask diff --git a/docs/modules/rein.rst b/docs/modules/rein.rst new file mode 100644 index 0000000..9ee16a6 --- /dev/null +++ b/docs/modules/rein.rst @@ -0,0 +1,33 @@ +API - Reinforcement Learning +============================== + +Reinforcement Learning. + +.. automodule:: tensorlayer.rein + +.. autosummary:: + + discount_episode_rewards + cross_entropy_reward_loss + log_weight + choice_action_by_probs + + +Reward functions +--------------------- +.. autofunction:: discount_episode_rewards + +Cost functions +--------------------- + +Weighted Cross Entropy +^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: cross_entropy_reward_loss + +Log weight +^^^^^^^^^^^^^^ +.. autofunction:: log_weight + +Sampling functions +--------------------- +.. autofunction:: choice_action_by_probs diff --git a/docs/modules/utils.rst b/docs/modules/utils.rst new file mode 100644 index 0000000..dfc5f6b --- /dev/null +++ b/docs/modules/utils.rst @@ -0,0 +1,73 @@ +API - Utility +======================== + +.. automodule:: tensorlayer.utils + +.. autosummary:: + + fit + test + predict + evaluation + class_balancing_oversample + get_random_int + dict_to_one + list_string_to_dict + flatten_list + exit_tensorflow + open_tensorboard + set_gpu_fraction + +Training, testing and predicting +---------------------------------- + +Training +^^^^^^^^^^^ +.. autofunction:: fit + +Evaluation +^^^^^^^^^^^^^ +.. autofunction:: test + +Prediction +^^^^^^^^^^^^ +.. autofunction:: predict + +Evaluation functions +--------------------- +.. autofunction:: evaluation + +Class balancing functions +---------------------------- +.. autofunction:: class_balancing_oversample + +Random functions +---------------------------- +.. autofunction:: get_random_int + +Dictionary and list +-------------------- + +Set all items in dictionary to one +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: dict_to_one + +Convert list of string to dictionary +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: list_string_to_dict + +Flatten a list +^^^^^^^^^^^^^^^^^^^ +.. autofunction:: flatten_list + +Close TF session and associated processes +----------------------------------------- +.. autofunction:: exit_tensorflow + +Open TensorBoard +---------------- +.. autofunction:: open_tensorboard + +Set GPU functions +----------------- +.. autofunction:: set_gpu_fraction diff --git a/docs/modules/visualize.rst b/docs/modules/visualize.rst new file mode 100644 index 0000000..0bbe028 --- /dev/null +++ b/docs/modules/visualize.rst @@ -0,0 +1,76 @@ +API - Visualization +================================ + +TensorFlow provides `TensorBoard `_ +to visualize the model, activations etc. Here we provide more functions for data visualization. + +.. automodule:: tensorlayer.visualize + +.. autosummary:: + + read_image + read_images + save_image + save_images + draw_boxes_and_labels_to_image + draw_mpii_pose_to_image + draw_weights + CNN2d + frame + images2d + tsne_embedding + + +Save and read images +---------------------- + +Read one image +^^^^^^^^^^^^^^^^^ +.. autofunction:: read_image + +Read multiple images +^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: read_images + +Save one image +^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: save_image + +Save multiple images +^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: save_images + +Save image for object detection +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: draw_boxes_and_labels_to_image + + +Save image for pose estimation (MPII) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: draw_mpii_pose_to_image + +Visualize model parameters +------------------------------ + +Visualize CNN 2d filter +^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: CNN2d + +Visualize weights +^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: draw_weights + +Visualize images +----------------- + +Image by matplotlib +^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: frame + +Images by matplotlib +^^^^^^^^^^^^^^^^^^^^^ +.. autofunction:: images2d + +Visualize embeddings +-------------------- +.. autofunction:: tsne_embedding diff --git a/docs/user/contributing.rst b/docs/user/contributing.rst new file mode 100644 index 0000000..a83767a --- /dev/null +++ b/docs/user/contributing.rst @@ -0,0 +1,184 @@ +.. _contributing: + +=============== +Contributing +=============== + +TensorLayer 2.0 is a major ongoing research project in CFCS, Peking University, the first version was established at Imperial College London in 2016. The goal of the project is to develop a compositional language while complex learning systems +can be built through composition of neural network modules. + +Numerous contributors come from various horizons such as: Imperial College London, Tsinghua University, Carnegie Mellon University, Stanford, University of Technology of Compiegne, Google, Microsoft, Bloomberg and etc. + +You can easily open a Pull Request (PR) on `GitHub`_, every little step counts and will be credited. +As an open-source project, we highly welcome and value contributions! + +**If you are interested in working with us, please contact us at:** `tensorlayer@gmail.com `_. + +.. image:: ../../img/join_slack.png + :width: 30 % + :align: center + :target: https://join.slack.com/t/tensorlayer/shared_invite/enQtMjUyMjczMzU2Njg4LWI0MWU0MDFkOWY2YjQ4YjVhMzI5M2VlZmE4YTNhNGY1NjZhMzUwMmQ2MTc0YWRjMjQzMjdjMTg2MWQ2ZWJhYzc + + +Project Maintainers +-------------------------- + +The TensorLayer project was started by `Hao Dong `_ at Imperial College London in June 2016. + +For TensorLayer 2.x, it is now actively developing and maintaining by the following people who has more than 50 contributions: + +- **Hao Dong** (`@zsdonghao `_) - ``_ +- **Jingqing Zhang** (`@JingqingZ `_) - ``_ +- **Rundi Wu** (`@ChrisWu1997 `_) - ``_ +- **Ruihai Wu** (`@warshallrho `_) - ``_ + +For TensorLayer 1.x, it was actively developed and maintained by the following people *(in alphabetical order)*: + +- **Akara Supratak** (`@akaraspt `_) - ``_ +- **Fangde Liu** (`@fangde `_) - ``_ +- **Guo Li** (`@lgarithm `_) - ``_ +- **Hao Dong** (`@zsdonghao `_) - ``_ +- **Jonathan Dekhtiar** (`@DEKHTIARJonathan `_) - ``_ +- **Luo Mai** (`@luomai `_) - ``_ +- **Simiao Yu** (`@nebulaV `_) - ``_ + +Numerous other contributors can be found in the `Github Contribution Graph `_. + + +What to contribute +------------------ + +Your method and example +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you have a new method or example in terms of Deep learning or Reinforcement learning, you are welcome to contribute. + +* Provide your layers or examples, so everyone can use it. +* Explain how it would work, and link to a scientific paper if applicable. +* Keep the scope as narrow as possible, to make it easier to implement. + + +Report bugs +~~~~~~~~~~~ + +Report bugs at the `GitHub`_, we normally will fix it in 5 hours. +If you are reporting a bug, please include: + +* your TensorLayer, TensorFlow and Python version. +* steps to reproduce the bug, ideally reduced to a few Python commands. +* the results you obtain, and the results you expected instead. + +If you are unsure whether the behavior you experience is a bug, or if you are +unsure whether it is related to TensorLayer or TensorFlow, please just ask on `our +mailing list`_ first. + + +Fix bugs +~~~~~~~~ + +Look through the GitHub issues for bug reports. Anything tagged with "bug" is +open to whoever wants to implement it. If you discover a bug in TensorLayer you can +fix yourself, by all means feel free to just implement a fix and not report it +first. + + +Write documentation +~~~~~~~~~~~~~~~~~~~ + +Whenever you find something not explained well, misleading, glossed over or +just wrong, please update it! The *Edit on GitHub* link on the top right of +every documentation page and the *[source]* link for every documented entity +in the API reference will help you to quickly locate the origin of any text. + + + +How to contribute +----------------- + +Edit on GitHub +~~~~~~~~~~~~~~ + +As a very easy way of just fixing issues in the documentation, use the *Edit +on GitHub* link on the top right of a documentation page or the *[source]* link +of an entity in the API reference to open the corresponding source file in +GitHub, then click the *Edit this file* link to edit the file in your browser +and send us a Pull Request. All you need for this is a free GitHub account. + +For any more substantial changes, please follow the steps below to setup +TensorLayer for development. + + +Documentation +~~~~~~~~~~~~~ + +The documentation is generated with `Sphinx +`_. To build it locally, run the +following commands: + +.. code:: bash + + pip install Sphinx + sphinx-quickstart + + cd docs + make html + +If you want to re-generate the whole docs, run the following commands: + +.. code :: bash + + cd docs + make clean + make html + + +To write the docs, we recommend you to install `Local RTD VM `_. + + + + +Afterwards, open ``docs/_build/html/index.html`` to view the documentation as +it would appear on `readthedocs `_. If you +changed a lot and seem to get misleading error messages or warnings, run +``make clean html`` to force Sphinx to recreate all files from scratch. + +When writing docstrings, follow existing documentation as much as possible to +ensure consistency throughout the library. For additional information on the +syntax and conventions used, please refer to the following documents: + +* `reStructuredText Primer `_ +* `Sphinx reST markup constructs `_ +* `A Guide to NumPy/SciPy Documentation `_ + + +Testing +~~~~~~~ + +TensorLayer has a code coverage of 100%, which has proven very helpful in the past, +but also creates some duties: + +* Whenever you change any code, you should test whether it breaks existing + features by just running the test scripts. +* Every bug you fix indicates a missing test case, so a proposed bug fix should + come with a new test that fails without your fix. + + +Sending Pull Requests +~~~~~~~~~~~~~~~~~~~~~ + +When you're satisfied with your addition, the tests pass and the documentation +looks good without any markup errors, commit your changes to a new branch, push +that branch to your fork and send us a Pull Request via GitHub's web interface. + +All these steps are nicely explained on GitHub: +https://guides.github.com/introduction/flow/ + +When filing your Pull Request, please include a description of what it does, to +help us reviewing it. If it is fixing an open issue, say, issue #123, add +*Fixes #123*, *Resolves #123* or *Closes #123* to the description text, so +GitHub will close it when your request is merged. + + +.. _Release: https://github.com/tensorlayer/tensorlayer/releases +.. _GitHub: https://github.com/tensorlayer/tensorlayer +.. _our mailing list: hao.dong11@imperial.ac.uk diff --git a/docs/user/examples.rst b/docs/user/examples.rst new file mode 100644 index 0000000..91971c0 --- /dev/null +++ b/docs/user/examples.rst @@ -0,0 +1,121 @@ +.. _example: + +============ +Examples +============ + +We list some examples here, but more tutorials and applications can be found in `Github examples `__ and `Awesome-TensorLayer `_. + +Basics +============ + + - Multi-layer perceptron (MNIST), simple usage. Classification task, see `tutorial_mnist_simple.py `__. + - Multi-layer perceptron (MNIST), dynamic model. Classification with dropout using iterator, see `tutorial_mnist_mlp_dynamic.py method2 `__. + - Multi-layer perceptron (MNIST), static model. Classification with dropout using iterator, see `tutorial_mnist_mlp_static.py `__. + - Convolutional Network (CIFAR-10). Classification task, see `tutorial_cifar10_cnn_static.py `_. + - TensorFlow dataset API for object detection see `here `__. + - Data augmentation with TFRecord. Effective way to load and pre-process data, see `tutorial_tfrecord*.py `__ and `tutorial_cifar10_tfrecord.py `__. + - Data augmentation with TensorLayer. See `tutorial_fast_affine_transform.py `__ (for quick test only). + +Pretrained Models +================== + + - VGG 16 (ImageNet). Classification task, see `tutorial_models_vgg16 `__. + - VGG 19 (ImageNet). Classification task, see `tutorial_models_vgg19.py `__. + - SqueezeNet (ImageNet). Model compression, see `tutorial_models_squeezenetv1.py `__. + - MobileNet (ImageNet). Model compression, see `tutorial_models_mobilenetv1.py `__. + - All pretrained models in `pretrained-models `__. + +Vision +================== + + - Arbitrary Style Transfer in Real-time with Adaptive Instance Normalization, see `examples `__. + - ArcFace: Additive Angular Margin Loss for Deep Face Recognition, see `InsignFace `__. + - BinaryNet. Model compression, see `mnist `__ `cifar10 `__. + - Ternary Weight Network. Model compression, see `mnist `__ `cifar10 `__. + - DoReFa-Net. Model compression, see `mnist `__ `cifar10 `__. + - QuanCNN. Model compression, sees `mnist `__ `cifar10 `__. + - Wide ResNet (CIFAR) by `ritchieng `__. + - `Spatial Transformer Networks `__ by `zsdonghao `__. + - `U-Net for brain tumor segmentation `__ by `zsdonghao `__. + - Variational Autoencoder (VAE) for (CelebA) by `yzwxx `__. + - Variational Autoencoder (VAE) for (MNIST) by `BUPTLdy `__. + - Image Captioning - Reimplementation of Google's `im2txt `__ by `zsdonghao `__. + +Adversarial Learning +======================== + - DCGAN (CelebA). Generating images by `Deep Convolutional Generative Adversarial Networks `__ by `zsdonghao `__. + - `Generative Adversarial Text to Image Synthesis `__ by `zsdonghao `__. + - `Unsupervised Image to Image Translation with Generative Adversarial Networks `__ by `zsdonghao `__. + - `Improved CycleGAN `__ with resize-convolution by `luoxier `__. + - `Super Resolution GAN `__ by `zsdonghao `__. + - `BEGAN: Boundary Equilibrium Generative Adversarial Networks `__ by `2wins `__. + - `DAGAN: Fast Compressed Sensing MRI Reconstruction `__ by `nebulaV `__. + +Natural Language Processing +============================== + + - Recurrent Neural Network (LSTM). Apply multiple LSTM to PTB dataset for language modeling, see `tutorial_ptb_lstm_state_is_tuple.py `__. + - Word Embedding (Word2vec). Train a word embedding matrix, see `tutorial_word2vec_basic.py `__. + - Restore Embedding matrix. Restore a pre-train embedding matrix, see `tutorial_generate_text.py `__. + - Text Generation. Generates new text scripts, using LSTM network, see `tutorial_generate_text.py `__. + - Chinese Text Anti-Spam by `pakrchen `__. + - `Chatbot in 200 lines of code `__ for `Seq2Seq `__. + - FastText Sentence Classification (IMDB), see `tutorial_imdb_fasttext.py `__ by `tomtung `__. + +Reinforcement Learning +============================== + + - Policy Gradient / Network (Atari Ping Pong), see `tutorial_atari_pong.py `__. + - Deep Q-Network (Frozen lake), see `tutorial_frozenlake_dqn.py `__. + - Q-Table learning algorithm (Frozen lake), see `tutorial_frozenlake_q_table.py `__. + - Asynchronous Policy Gradient using TensorDB (Atari Ping Pong) by `nebulaV `__. + - AC for discrete action space (Cartpole), see `tutorial_cartpole_ac.py `__. + - A3C for continuous action space (Bipedal Walker), see `tutorial_bipedalwalker_a3c*.py `__. + - `DAGGER `__ for (`Gym Torcs `__) by `zsdonghao `__. + - `TRPO `__ for continuous and discrete action space by `jjkke88 `__. + +Miscellaneous +================= + +- `Sipeed `__ : Run TensorLayer on AI Chips + +.. + - TensorDB by `fangde `__ see `tl_paper `__. + - A simple web service - `TensorFlask `__ by `JoelKronander `__. + +.. + Applications + ============= + + There are some good applications implemented by TensorLayer. + You may able to find some useful examples for your project. + If you want to share your application, please contact tensorlayer@gmail.com. + + 1D CNN + LSTM for Biosignal + --------------------------------- + + Author : `Akara Supratak `__ + + Introduction + ^^^^^^^^^^^^ + + Implementation + ^^^^^^^^^^^^^^ + + Citation + ^^^^^^^^ + + + + + +.. _GitHub: https://github.com/tensorlayer/tensorlayer +.. _Deeplearning Tutorial: http://deeplearning.stanford.edu/tutorial/ +.. _Convolutional Neural Networks for Visual Recognition: http://cs231n.github.io/ +.. _Neural Networks and Deep Learning: http://neuralnetworksanddeeplearning.com/ +.. _TensorFlow tutorial: https://www.tensorflow.org/versions/r0.9/tutorials/index.html +.. _Understand Deep Reinforcement Learning: http://karpathy.github.io/2016/05/31/rl/ +.. _Understand Recurrent Neural Network: http://karpathy.github.io/2015/05/21/rnn-effectiveness/ +.. _Understand LSTM Network: http://colah.github.io/posts/2015-08-Understanding-LSTMs/ +.. _Word Representations: http://colah.github.io/posts/2014-07-NLP-RNNs-Representations/ diff --git a/docs/user/faq.rst b/docs/user/faq.rst new file mode 100644 index 0000000..0c05033 --- /dev/null +++ b/docs/user/faq.rst @@ -0,0 +1,79 @@ +.. _faq: + +============ +FAQ +============ + + +How to effectively learn TensorLayer +===================================== + +No matter what stage you are in, we recommend you to spend just 10 minutes to +read the source code of TensorLayer and the `Understand layer / Your layer `__ +in this website, you will find the abstract methods are very simple for everyone. +Reading the source codes helps you to better understand TensorFlow and allows +you to implement your own methods easily. For discussion, we recommend +`Gitter `__, +`Help Wanted Issues `__, +`QQ group `__ +and `Wechat group `__. + +Beginner +----------- +For people who new to deep learning, the contributors provided a number of tutorials in this website, these tutorials will guide you to understand autoencoder, convolutional neural network, recurrent neural network, word embedding and deep reinforcement learning and etc. If your already understand the basic of deep learning, we recommend you to skip the tutorials and read the example codes on `Github `__ , then implement an example from scratch. + +Engineer +------------ +For people from industry, the contributors provided mass format-consistent examples covering computer vision, natural language processing and reinforcement learning. Besides, there are also many TensorFlow users already implemented product-level examples including image captioning, semantic/instance segmentation, machine translation, chatbot and etc., which can be found online. +It is worth noting that a wrapper especially for computer vision `Tf-Slim `__ can be connected with TensorLayer seamlessly. +Therefore, you may able to find the examples that can be used in your project. + +Researcher +------------- +For people from academia, TensorLayer was originally developed by PhD students who facing issues with other libraries on implement novel algorithm. Installing TensorLayer in editable mode is recommended, so you can extend your methods in TensorLayer. +For research related to image processing such as image captioning, visual QA and etc., you may find it is very helpful to use the existing `Tf-Slim pre-trained models `__ with TensorLayer (a specially layer for connecting Tf-Slim is provided). + + +Install Master Version +======================== + +To use all new features of TensorLayer, you need to install the master version from Github. +Before that, you need to make sure you already installed git. + +.. code-block:: bash + + [stable version] pip install tensorlayer + [master version] pip install git+https://github.com/tensorlayer/tensorlayer.git + +Editable Mode +=============== + +- 1. Download the TensorLayer folder from Github. +- 2. Before editing the TensorLayer ``.py`` file. + + - If your script and TensorLayer folder are in the same folder, when you edit the ``.py`` inside TensorLayer folder, your script can access the new features. + - If your script and TensorLayer folder are not in the same folder, you need to run the following command in the folder contains ``setup.py`` before you edit ``.py`` inside TensorLayer folder. + + .. code-block:: bash + + pip install -e . + + +Load Model +=========== + +Note that, the ``tl.files.load_npz()`` can only able to load the npz model saved by ``tl.files.save_npz()``. +If you have a model want to load into your TensorLayer network, you can first assign your parameters into a list in order, +then use ``tl.files.assign_params()`` to load the parameters into your TensorLayer model. + + + +.. _GitHub: https://github.com/tensorlayer/tensorlayer +.. _Deeplearning Tutorial: http://deeplearning.stanford.edu/tutorial/ +.. _Convolutional Neural Networks for Visual Recognition: http://cs231n.github.io/ +.. _Neural Networks and Deep Learning: http://neuralnetworksanddeeplearning.com/ +.. _TensorFlow tutorial: https://www.tensorflow.org/versions/r0.9/tutorials/index.html +.. _Understand Deep Reinforcement Learning: http://karpathy.github.io/2016/05/31/rl/ +.. _Understand Recurrent Neural Network: http://karpathy.github.io/2015/05/21/rnn-effectiveness/ +.. _Understand LSTM Network: http://colah.github.io/posts/2015-08-Understanding-LSTMs/ +.. _Word Representations: http://colah.github.io/posts/2014-07-NLP-RNNs-Representations/ diff --git a/docs/user/get_involved.rst b/docs/user/get_involved.rst new file mode 100644 index 0000000..3fe4131 --- /dev/null +++ b/docs/user/get_involved.rst @@ -0,0 +1,76 @@ + + +========================= +Get Involved in Research +========================= + +Ph.D. Postition @ PKU +============================================================= + + +Hi, I am `Hao Dong `__, the founder of this project and a new faculty member in EECS, Peking University. I now have a few Ph.D. positions per year open for international students who would like to study AI. If you or your friends are interested in it, feel free to contact me. +PKU is a top 30 university in the global ranking. The application is competitive, apply early is recommended. For the application of next year, please note that the DDL of Chinese Government Scholarship is in the end of each year, please check this `link `__ for more details. + +My homepage: `https://zsdonghao.github.io `__ + +Contact: hao.dong [AT] pku.edu.cn + + + +Faculty Postition @ PKU +============================================================= + +The Center on Frontiers of Computing Studies (CFCS), Peking University (PKU), China, is a university new initiative co-founded by Professors John Hopcroft (Turing Awardee) and Wen Gao (CAE, ACM/IEEE Fellow). The center aims at developing the excellence on two fronts: research and education. On the research front, the center will provide a world-class research environment, where innovation and impactful research is the central aim, measured by professional reputation among world scholars, not by counting the number of publications and research funding. On the education front, the center deeply involves in the Turing Class, an elite undergraduate program that draws the cream of the crop from the PKU undergraduate talent pool. New curriculum and pedagogy are designed and practiced in this program, with the aim to cultivate a new generation of computer scientist/engineers that are solid in both theories and practices. + +**Positions and Qualification** + +The center invites applications for tenured/tenure-track faculty positions. We are seeking applicants from all areas of Computer Science, spanning theoretical foundations, systems, software, and applications, with special interests in artificial intelligence and machine learning. We are especially interested in applicants conducting research at the frontiers of Computer Science with other disciplines, such as data sciences, engineering, as well as mathematical, medical, physical, and social sciences. + +Applicants are expected to have completed (or be completing) a Ph.D., have demonstrated the ability to pursue a program of research, and have a strong commitment to undergraduate and graduate teaching. A successful candidate will be expected to teach one to two courses at the undergraduate and graduate levels in each semester, and to build and lead a team of undergraduate and graduate students in innovative research. + +We are also seeking qualified candidates for postdoctoral positions. Candidates should have a Ph.D. in a relevant discipline or expect a Ph. D within a year, with a substantive record of research accomplishments, and the ability to work collaboratively with faculty members in the center. + +**To Apply** + +Applicants should send a full curriculum vitae; copies of 3-5 key publications; 3-5 names and contact information of references; and a statement of research and teaching to: CFCS_recruiting[at]pku[dot]edu[dot]cn . To expedite the process, please arrange to have the reference letters sent directly to the above email address. + +Application for a postdoctoral position should include a curriculum vita, brief statement of research, and three to five names and contact information of recommendation, and can be directly addressed to an individual faculty member. + +We conduct review of applications monthly, immediately upon the recipient of all application materials at the beginning of each month. However, it is highly recommended that applicants submit complete applications sooner than later, as the positions are to be filled quickly. + + +Postdoc Postition @ ICL +================================================== + +Data science is therefore by nature at the core of all modern transdisciplinary scientific activities, as it involves the whole life cycle of data, from acquisition and exploration to analysis and communication of the results. Data science is not only concerned with the tools and methods to obtain, manage and analyse data: it is also about extracting value from data and translating it from asset to product. + +Launched on 1st April 2014, the Data Science Institute (DSI) at Imperial College London aims to enhance Imperial's excellence in data-driven research across its faculties by fulfilling the following objectives. + +The Data Science Institute is housed in purpose built facilities in the heart of the Imperial College campus in South Kensington. Such a central location provides excellent access to collabroators across the College and across London. + + - To act as a focal point for coordinating data science research at Imperial College by facilitating access to funding, engaging with global partners, and stimulating cross-disciplinary collaboration. + - To develop data management and analysis technologies and services for supporting data driven research in the College. + - To promote the training and education of the new generation of data scientist by developing and coordinating new degree courses, and conducting public outreach programmes on data science. + - To advise College on data strategy and policy by providing world-class data science expertise. + - To enable the translation of data science innovation by close collaboration with industry and supporting commercialization. + +If you are interested in working with us, please check our +`vacancies `__ +and other ways to +`get involved `__ +, or feel free to +`contact us `__. + +Software Engineer @ SurgicalAI.cn +============================================================= +SurgicalAI is a startup founded by the data scientists and surgical robot experts from Imperial College. Our objective is AI democratise Surgery. By combining 5G, AI and Cloud Computing, SurgicalAI is building a platform enable junor surgeons to perfom complex procedures. As one of the most impactful startup, SurgicalAI is supported by Nvidia, AWS and top surgeons around the world. + +Currently based in Hangzhou, China, we are building digital solution for cardiac surgery like TAVR, LAA and Orthopedidcs like TKA and UNA. A demo can be found at here + +We are activly looking for experts in robotic navigation, computer graphics and medical image analysis experts to join us, building digitalized surgical service platform for the aging world. + +Home Page: http://www.surgicalai.cn + +Demo Page: http://demo5g.surgicalai.cn + +Contact: liufangde@surgicalai.cn diff --git a/docs/user/get_start_advance.rst b/docs/user/get_start_advance.rst new file mode 100644 index 0000000..db3441c --- /dev/null +++ b/docs/user/get_start_advance.rst @@ -0,0 +1,217 @@ +.. _getstartadvance: + +================== +Advanced features +================== + + +Customizing layer +================== + +Layers with weights +---------------------- + +The fully-connected layer is `a = f(x*W+b)`, the most simple implementation is as follow, which can only support static model. + +.. code-block:: python + + class Dense(Layer): + """The :class:`Dense` class is a fully connected layer. + + Parameters + ---------- + n_units : int + The number of units of this layer. + act : activation function + The activation function of this layer. + name : None or str + A unique layer name. If None, a unique name will be automatically generated. + """ + + def __init__( + self, + n_units, # the number of units/channels of this layer + act=None, # None: no activation, tf.nn.relu or 'relu': ReLU ... + name=None, # the name of this layer (optional) + ): + super(Dense, self).__init__(name, act=act) # auto naming, dense_1, dense_2 ... + self.n_units = n_units + + def build(self, inputs_shape): # initialize the model weights here + shape = [inputs_shape[1], self.n_units] + self.W = self._get_weights("weights", shape=tuple(shape), init=self.W_init) + self.b = self._get_weights("biases", shape=(self.n_units, ), init=self.b_init) + + def forward(self, inputs): # call function + z = tf.matmul(inputs, self.W) + self.b + if self.act: # is not None + z = self.act(z) + return z + +The full implementation is as follow, which supports both static and dynamic models and allows users to control whether to use the bias, how to initialize the weight values. + +.. code-block:: python + + class Dense(Layer): + """The :class:`Dense` class is a fully connected layer. + + Parameters + ---------- + n_units : int + The number of units of this layer. + act : activation function + The activation function of this layer. + W_init : initializer + The initializer for the weight matrix. + b_init : initializer or None + The initializer for the bias vector. If None, skip biases. + in_channels: int + The number of channels of the previous layer. + If None, it will be automatically detected when the layer is forwarded for the first time. + name : None or str + A unique layer name. If None, a unique name will be automatically generated. + """ + + def __init__( + self, + n_units, + act=None, + W_init=tl.initializers.truncated_normal(stddev=0.1), + b_init=tl.initializers.constant(value=0.0), + in_channels=None, # the number of units/channels of the previous layer + name=None, + ): + # we feed activation function to the base layer, `None` denotes identity function + # string (e.g., relu, sigmoid) will be converted into function. + super(Dense, self).__init__(name, act=act) + + self.n_units = n_units + self.W_init = W_init + self.b_init = b_init + self.in_channels = in_channels + + # in dynamic model, the number of input channel is given, we initialize the weights here + if self.in_channels is not None: + self.build(self.in_channels) + self._built = True + + logging.info( + "Dense %s: %d %s" % + (self.name, self.n_units, self.act.__name__ if self.act is not None else 'No Activation') + ) + + def __repr__(self): # optional, for printing information + actstr = self.act.__name__ if self.act is not None else 'No Activation' + s = ('{classname}(n_units={n_units}, ' + actstr) + if self.in_channels is not None: + s += ', in_channels=\'{in_channels}\'' + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): # initialize the model weights here + if self.in_channels: # if the number of input channel is given, use it + shape = [self.in_channels, self.n_units] + else: # otherwise, get it from static model + self.in_channels = inputs_shape[1] + shape = [inputs_shape[1], self.n_units] + self.W = self._get_weights("weights", shape=tuple(shape), init=self.W_init) + if self.b_init: # if b_init is None, no bias is applied + self.b = self._get_weights("biases", shape=(self.n_units, ), init=self.b_init) + + def forward(self, inputs): + z = tf.matmul(inputs, self.W) + if self.b_init: + z = tf.add(z, self.b) + if self.act: + z = self.act(z) + return z + + +Layers with train/test modes +------------------------------ + +We use Dropout as an example here: + +.. code-block:: python + + class Dropout(Layer): + """ + The :class:`Dropout` class is a noise layer which randomly set some + activations to zero according to a keeping probability. + Parameters + ---------- + keep : float + The keeping probability. + The lower the probability it is, the more activations are set to zero. + name : None or str + A unique layer name. + """ + + def __init__(self, keep, name=None): + super(Dropout, self).__init__(name) + self.keep = keep + + self.build() + self._built = True + + logging.info("Dropout %s: keep: %f " % (self.name, self.keep)) + + def build(self, inputs_shape=None): + pass # no weights in dropout layer + + def forward(self, inputs): + if self.is_train: # this attribute is changed by Model.train() and Model.eval() described above + outputs = tf.nn.dropout(inputs, rate=1 - (self.keep), name=self.name) + else: + outputs = inputs + return outputs + +Pre-trained CNN +================ + +Get entire CNN +--------------- + +.. code-block:: python + + import tensorflow as tf + import tensorlayer as tl + import numpy as np + from tensorlayer.models.imagenet_classes import class_names + + vgg = tl.models.vgg16(pretrained=True) + img = tl.vis.read_image('data/tiger.jpeg') + img = tl.prepro.imresize(img, (224, 224)).astype(np.float32) / 255 + output = vgg(img, is_train=False) + +Get a part of CNN +------------------ + +.. code-block:: python + + # get VGG without the last layer + cnn = tl.models.vgg16(end_with='fc2_relu', mode='static').as_layer() + # add one more layer and build a new model + ni = tl.layers.Input([None, 224, 224, 3], name="inputs") + nn = cnn(ni) + nn = tl.layers.Dense(n_units=100, name='out')(nn) + model = tl.models.Model(inputs=ni, outputs=nn) + # train your own classifier (only update the last layer) + train_weights = model.get_layer('out').all_weights + +Reuse CNN +------------------ + +.. code-block:: python + + # in dynamic model, we can directly use the same model + # in static model + vgg_layer = tl.models.vgg16().as_layer() + ni_1 = tl.layers.Input([None, 224, 224, 3]) + ni_2 = tl.layers.Input([None, 224, 224, 3]) + a_1 = vgg_layer(ni_1) + a_2 = vgg_layer(ni_2) + M = Model(inputs=[ni_1, ni_2], outputs=[a_1, a_2]) + diff --git a/docs/user/get_start_model.rst b/docs/user/get_start_model.rst new file mode 100644 index 0000000..2337a7d --- /dev/null +++ b/docs/user/get_start_model.rst @@ -0,0 +1,249 @@ +.. _getstartmodel: + +=============== +Define a model +=============== + +TensorLayer provides two ways to define a model. +Static model allows you to build model in a fluent way while dynamic model allows you to fully control the forward process. + +Static model +=============== + +.. code-block:: python + + import tensorflow as tf + from tensorlayer.layers import Input, Dropout, Dense + from tensorlayer.models import Model + + def get_model(inputs_shape): + ni = Input(inputs_shape) + nn = Dropout(keep=0.8)(ni) + nn = Dense(n_units=800, act=tf.nn.relu, name="dense1")(nn) # “name" is optional + nn = Dropout(keep=0.8)(nn) + nn = Dense(n_units=800, act=tf.nn.relu)(nn) + nn = Dropout(keep=0.8)(nn) + nn = Dense(n_units=10, act=None)(nn) + M = Model(inputs=ni, outputs=nn, name="mlp") # “name" is optional + return M + + MLP = get_model([None, 784]) + MLP.eval() + outputs = MLP(data) + +Dynamic model +======================= + + +In this case, you need to manually input the output shape of the previous layer to the new layer. + +.. code-block:: python + + class CustomModel(Model): + + def __init__(self): + super(CustomModel, self).__init__() + + self.dropout1 = Dropout(keep=0.8) + self.dense1 = Dense(n_units=800, act=tf.nn.relu, in_channels=784) + self.dropout2 = Dropout(keep=0.8) + self.dense2 = Dense(n_units=800, act=tf.nn.relu, in_channels=800) + self.dropout3 = Dropout(keep=0.8) + self.dense3 = Dense(n_units=10, act=None, in_channels=800) + + def forward(self, x, foo=False): + z = self.dropout1(x) + z = self.dense1(z) + z = self.dropout2(z) + z = self.dense2(z) + z = self.dropout3(z) + out = self.dense3(z) + if foo: + out = tf.nn.softmax(out) + return out + + MLP = CustomModel() + MLP.eval() + outputs = MLP(data, foo=True) # controls the forward here + outputs = MLP(data, foo=False) + + +Switching train/test modes +============================= + +.. code-block:: python + + # method 1: switch before forward + Model.train() # enable dropout, batch norm moving avg ... + output = Model(train_data) + ... # training code here + Model.eval() # disable dropout, batch norm moving avg ... + output = Model(test_data) + ... # testing code here + + # method 2: switch while forward + output = Model(train_data, is_train=True) + output = Model(test_data, is_train=False) + +Reuse weights +======================= + +For static model, call the layer multiple time in model creation + +.. code-block:: python + + # create siamese network + + def create_base_network(input_shape): + '''Base network to be shared (eq. to feature extraction). + ''' + input = Input(shape=input_shape) + x = Flatten()(input) + x = Dense(128, act=tf.nn.relu)(x) + x = Dropout(0.9)(x) + x = Dense(128, act=tf.nn.relu)(x) + x = Dropout(0.9)(x) + x = Dense(128, act=tf.nn.relu)(x) + return Model(input, x) + + + def get_siamese_network(input_shape): + """Create siamese network with shared base network as layer + """ + base_layer = create_base_network(input_shape).as_layer() # convert model as layer + + ni_1 = Input(input_shape) + ni_2 = Input(input_shape) + nn_1 = base_layer(ni_1) # call base_layer twice + nn_2 = base_layer(ni_2) + return Model(inputs=[ni_1, ni_2], outputs=[nn_1, nn_2]) + + siamese_net = get_siamese_network([None, 784]) + +For dynamic model, call the layer multiple time in forward function + +.. code-block:: python + + class MyModel(Model): + def __init__(self): + super(MyModel, self).__init__() + self.dense_shared = Dense(n_units=800, act=tf.nn.relu, in_channels=784) + self.dense1 = Dense(n_units=10, act=tf.nn.relu, in_channels=800) + self.dense2 = Dense(n_units=10, act=tf.nn.relu, in_channels=800) + self.cat = Concat() + + def forward(self, x): + x1 = self.dense_shared(x) # call dense_shared twice + x2 = self.dense_shared(x) + x1 = self.dense1(x1) + x2 = self.dense2(x2) + out = self.cat([x1, x2]) + return out + + model = MyModel() + +Print model information +======================= + +.. code-block:: python + + print(MLP) # simply call print function + + # Model( + # (_inputlayer): Input(shape=[None, 784], name='_inputlayer') + # (dropout): Dropout(keep=0.8, name='dropout') + # (dense): Dense(n_units=800, relu, in_channels='784', name='dense') + # (dropout_1): Dropout(keep=0.8, name='dropout_1') + # (dense_1): Dense(n_units=800, relu, in_channels='800', name='dense_1') + # (dropout_2): Dropout(keep=0.8, name='dropout_2') + # (dense_2): Dense(n_units=10, None, in_channels='800', name='dense_2') + # ) + + import pprint + pprint.pprint(MLP.config) # print the model architecture + # {'inputs': '_inputlayer_1_node_0', + # 'model_architecture': [{'args': {'dtype': tf.float32, + # 'layer_type': 'normal', + # 'name': '_inputlayer_1', + # 'shape': [None, 784]}, + # 'class': '_InputLayer', + # 'prev_layer': None}, + # {'args': {'keep': 0.8, + # 'layer_type': 'normal', + # 'name': 'dropout_1'}, + # 'class': 'Dropout', + # 'prev_layer': ['_inputlayer_1_node_0']}, + # {'args': {'act': 'relu', + # 'layer_type': 'normal', + # 'n_units': 800, + # 'name': 'dense_1'}, + # 'class': 'Dense', + # 'prev_layer': ['dropout_1_node_0']}, + # {'args': {'keep': 0.8, + # 'layer_type': 'normal', + # 'name': 'dropout_2'}, + # 'class': 'Dropout', + # 'prev_layer': ['dense_1_node_0']}, + # {'args': {'act': 'relu', + # 'layer_type': 'normal', + # 'n_units': 800, + # 'name': 'dense_2'}, + # 'class': 'Dense', + # 'prev_layer': ['dropout_2_node_0']}, + # {'args': {'keep': 0.8, + # 'layer_type': 'normal', + # 'name': 'dropout_3'}, + # 'class': 'Dropout', + # 'prev_layer': ['dense_2_node_0']}, + # {'args': {'act': None, + # 'layer_type': 'normal', + # 'n_units': 10, + # 'name': 'dense_3'}, + # 'class': 'Dense', + # 'prev_layer': ['dropout_3_node_0']}], + # 'name': 'mlp', + # 'outputs': 'dense_3_node_0', + # 'version_info': {'backend': 'tensorflow', + # 'backend_version': '2.0.0-alpha0', + # 'save_date': None, + # 'tensorlayer_version': '2.1.0', + # 'training_device': 'gpu'}} + +Get specific weights +======================= + +We can get the specific weights by indexing or naming. + +.. code-block:: python + + # indexing + all_weights = MLP.all_weights + some_weights = MLP.all_weights[1:3] + + # naming + some_weights = MLP.get_layer('dense1').all_weights + + +Save and restore model +======================= + +We provide two ways to save and restore models + + +Save weights only +------------------ + +.. code-block:: python + + MLP.save_weights('model_weights.h5') # by default, file will be in hdf5 format + MLP.load_weights('model_weights.h5') + +Save model architecture and weights (optional) +----------------------------------------------- + +.. code-block:: python + + # When using Model.load(), there is no need to reimplement or declare the architecture of the model explicitly in code + MLP.save('model.h5', save_weights=True) + MLP = Model.load('model.h5', load_weights=True) + diff --git a/docs/user/installation.rst b/docs/user/installation.rst new file mode 100644 index 0000000..bb86054 --- /dev/null +++ b/docs/user/installation.rst @@ -0,0 +1,210 @@ +.. _installation: + +============ +Installation +============ + +TensorLayer has some prerequisites that need to be installed first, including +`TensorFlow`_ , numpy and matplotlib. For GPU +support CUDA and cuDNN are required. + +If you run into any trouble, please check the `TensorFlow installation +instructions `_ +which cover installing the TensorFlow for a range of operating systems including +Mac OX, Linux and Windows, or ask for help on `tensorlayer@gmail.com `_ +or `FAQ `_. + + +Install TensorFlow +========================= + +.. code-block:: bash + + pip3 install tensorflow-gpu==2.0.0-beta1 # specific version (YOU SHOULD INSTALL THIS ONE NOW) + pip3 install tensorflow-gpu # GPU version + pip3 install tensorflow # CPU version + +The installation instructions of TensorFlow are written to be very detailed on `TensorFlow`_ website. +However, there are something need to be considered. For example, `TensorFlow`_ officially supports GPU acceleration for Linux, Mac OX and Windows at present. For ARM processor architecture, you need to install TensorFlow from source. + +Install TensorLayer +========================= + +For stable version: + +.. code-block:: bash + + pip3 install tensorlayer + +For latest version, please install from Github. + +.. code-block:: bash + + pip3 install git+https://github.com/tensorlayer/tensorlayer.git + or + pip3 install https://github.com/tensorlayer/tensorlayer/archive/master.zip + +For developers, you should clone the folder to your local machine and put it along with your project scripts. + +.. code-block:: bash + + git clone https://github.com/tensorlayer/tensorlayer.git + + +Alternatively, you can build from the source. + +.. code-block:: bash + + # First clone the repository and change the current directory to the newly cloned repository + git clone https://github.com/tensorlayer/tensorlayer.git + cd tensorlayer + + # Install virtualenv if necessary + pip install virtualenv + # Then create a virtualenv called `venv` + virtualenv venv + + # Activate the virtualenv + + ## Linux: + source venv/bin/activate + + ## Windows: + venv\Scripts\activate.bat + + # basic installation + pip install . + + # ============= IF TENSORFLOW IS NOT ALREADY INSTALLED ============= # + + # for a machine **without** an NVIDIA GPU + pip install -e ".[all_cpu_dev]" + + # for a machine **with** an NVIDIA GPU + pip install -e ".[all_gpu_dev]" + +If you want install TensorLayer 1.X, the simplest way to install TensorLayer 1.X is as follow. It will also install the numpy and matplotlib automatically. + +.. code-block:: bash + + [stable version] pip install tensorlayer==1.x.x + +However, if you want to modify or extend TensorLayer 1.X, you can download the repository from +`Github`_ and install it as follow. + +.. code-block:: bash + + cd to the root of the git tree + pip install -e . + +This command will run the ``setup.py`` to install TensorLayer. The ``-e`` reflects +editable, then you can edit the source code in ``tensorlayer`` folder, and ``import`` the edited +TensorLayer. + + +GPU support +========================== + +Thanks to NVIDIA supports, training a fully connected network on a +GPU, which may be 10 to 20 times faster than training them on a CPU. +For convolutional network, may have 50 times faster. +This requires an NVIDIA GPU with CUDA and cuDNN support. + + +CUDA +---- + +The TensorFlow website also teach how to install the CUDA and cuDNN, please see +`TensorFlow GPU Support `_. + +Download and install the latest CUDA is available from NVIDIA website: + + - `CUDA download and install `_ + + +.. + After installation, make sure ``/usr/local/cuda/bin`` is in your ``PATH`` (use ``echo #PATH`` to check), and + ``nvcc --version`` works. Also ensure ``/usr/local/cuda/lib64`` is in your + ``LD_LIBRARY_PATH``, so the CUDA libraries can be found. + +If CUDA is set up correctly, the following command should print some GPU information on +the terminal: + +.. code-block:: bash + + python -c "import tensorflow" + + +cuDNN +-------- + +Apart from CUDA, NVIDIA also provides a library for common neural network operations that especially +speeds up Convolutional Neural Networks (CNNs). Again, it can be obtained from +NVIDIA after registering as a developer (it take a while): + +Download and install the latest cuDNN is available from NVIDIA website: + + - `cuDNN download and install `_ + + +To install it, copy the ``*.h`` files to ``/usr/local/cuda/include`` and the +``lib*`` files to ``/usr/local/cuda/lib64``. + +.. _TensorFlow: https://www.tensorflow.org/versions/master/get_started/os_setup.html +.. _GitHub: https://github.com/tensorlayer/tensorlayer +.. _TensorLayer: https://github.com/tensorlayer/tensorlayer/ + + + +Windows User +============== + +TensorLayer is built on the top of Python-version TensorFlow, so please install Python first. +Note:We highly recommend installing Anaconda. The lowest version requirements of Python is py35. + +`Anaconda download `_ + +GPU support +------------ +Thanks to NVIDIA supports, training a fully connected network on a GPU, which may be 10 to 20 times faster than training them on a CPU. For convolutional network, may have 50 times faster. This requires an NVIDIA GPU with CUDA and cuDNN support. + +1. Installing Microsoft Visual Studio +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +You should preinstall Microsoft Visual Studio (VS) before installing CUDA. The lowest version requirements is VS2010. We recommend installing VS2015 or VS2013. CUDA7.5 supports VS2010, VS2012 and VS2013. CUDA8.0 also supports VS2015. + +2. Installing CUDA +^^^^^^^^^^^^^^^^^^^^^^^ +Download and install the latest CUDA is available from NVIDIA website: + +`CUDA download `_ + +We do not recommend modifying the default installation directory. + +3. Installing cuDNN +^^^^^^^^^^^^^^^^^^^^^^ +The NVIDIA CUDA® Deep Neural Network library (cuDNN) is a GPU-accelerated library of primitives for deep neural networks. Download and extract the latest cuDNN is available from NVIDIA website: + +`cuDNN download `_ + +After extracting cuDNN, you will get three folders (bin, lib, include). Then these folders should be copied to CUDA installation. (The default installation directory is `C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v8.0`) + +Installing TensorLayer +------------------------ +For TensorLayer, please refer to the steps mentioned above. + +.. code-block:: bash + + pip install tensorflow #CPU version + pip install tensorflow-gpu   #GPU version (GPU version and CPU version just choose one) + pip install tensorlayer       #Install tensorlayer + + + +Issue +======= + +If you get the following output when import tensorlayer, please read `FQA `_. + +.. code-block:: bash + + _tkinter.TclError: no display name and no $DISPLAY environment variable diff --git a/docs/user/my_figs/basic_seq2seq.png b/docs/user/my_figs/basic_seq2seq.png new file mode 100644 index 0000000..4e59bcf Binary files /dev/null and b/docs/user/my_figs/basic_seq2seq.png differ diff --git a/docs/user/my_figs/img_tensorlayer.png b/docs/user/my_figs/img_tensorlayer.png new file mode 100644 index 0000000..c6b1f7c Binary files /dev/null and b/docs/user/my_figs/img_tensorlayer.png differ diff --git a/docs/user/my_figs/img_tlayer.png b/docs/user/my_figs/img_tlayer.png new file mode 100644 index 0000000..7b998ea Binary files /dev/null and b/docs/user/my_figs/img_tlayer.png differ diff --git a/docs/user/my_figs/img_tlayer_big.png b/docs/user/my_figs/img_tlayer_big.png new file mode 100644 index 0000000..3679b70 Binary files /dev/null and b/docs/user/my_figs/img_tlayer_big.png differ diff --git a/docs/user/my_figs/img_tunelayer.png b/docs/user/my_figs/img_tunelayer.png new file mode 100644 index 0000000..7b998ea Binary files /dev/null and b/docs/user/my_figs/img_tunelayer.png differ diff --git a/docs/user/my_figs/karpathy_rnn.jpeg b/docs/user/my_figs/karpathy_rnn.jpeg new file mode 100644 index 0000000..479a125 Binary files /dev/null and b/docs/user/my_figs/karpathy_rnn.jpeg differ diff --git a/docs/user/my_figs/mnist.jpeg b/docs/user/my_figs/mnist.jpeg new file mode 100644 index 0000000..0803233 Binary files /dev/null and b/docs/user/my_figs/mnist.jpeg differ diff --git a/docs/user/my_figs/pong_game.jpeg b/docs/user/my_figs/pong_game.jpeg new file mode 100644 index 0000000..c6ee4b3 Binary files /dev/null and b/docs/user/my_figs/pong_game.jpeg differ diff --git a/docs/user/my_figs/seq2seq.png b/docs/user/my_figs/seq2seq.png new file mode 100644 index 0000000..41e902c Binary files /dev/null and b/docs/user/my_figs/seq2seq.png differ diff --git a/docs/user/my_figs/tl_black_logo.png b/docs/user/my_figs/tl_black_logo.png new file mode 100644 index 0000000..8bf892d Binary files /dev/null and b/docs/user/my_figs/tl_black_logo.png differ diff --git a/docs/user/my_figs/tl_transparent_logo.png b/docs/user/my_figs/tl_transparent_logo.png new file mode 100644 index 0000000..359a232 Binary files /dev/null and b/docs/user/my_figs/tl_transparent_logo.png differ diff --git a/docs/user/my_figs/tl_white_logo.png b/docs/user/my_figs/tl_white_logo.png new file mode 100644 index 0000000..a42806d Binary files /dev/null and b/docs/user/my_figs/tl_white_logo.png differ diff --git a/docs/user/my_figs/tsne.png b/docs/user/my_figs/tsne.png new file mode 100644 index 0000000..848d7af Binary files /dev/null and b/docs/user/my_figs/tsne.png differ diff --git a/docs/user/my_figs/word2vec_basic.pdf b/docs/user/my_figs/word2vec_basic.pdf new file mode 100644 index 0000000..6dc5b92 Binary files /dev/null and b/docs/user/my_figs/word2vec_basic.pdf differ diff --git a/examples/basic_tutorials/tutorial_LayerList.py b/examples/basic_tutorials/tutorial_LayerList.py new file mode 100644 index 0000000..2b60fec --- /dev/null +++ b/examples/basic_tutorials/tutorial_LayerList.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from tensorlayer.layers import LayerList +from tensorlayer.layers import Dense +import tensorlayer as tl +import numpy as np + +layer_list = [] +layer_list.append(Dense(n_units=800, act=tl.ReLU, in_channels=784, name='Dense1')) +layer_list.append(Dense(n_units=800, act=tl.ReLU, in_channels=800, name='Dense2')) +layer_list.append(Dense(n_units=10, act=tl.ReLU, in_channels=800, name='Dense3')) +MLP = LayerList(layer_list) + +X_train, y_train, X_val, y_val, X_test, y_test = tl.files.load_mnist_dataset(shape=(-1, 784)) + +def generator_train(): + inputs = X_train + targets = y_train + if len(inputs) != len(targets): + raise AssertionError("The length of inputs and targets should be equal") + for _input, _target in zip(inputs, targets): + yield (_input, np.array(_target)) + +n_epoch = 50 +batch_size = 128 +print_freq = 2 +shuffle_buffer_size = 128 + +# train_weights = MLP.trainable_weights +# print(train_weights) +optimizer = tl.optimizers.Momentum(0.05, 0.9) +train_ds = tl.dataflow.FromGenerator( + generator_train, output_types=(tl.float32, tl.int32) , column_names=['data', 'label'] +) +train_ds = tl.dataflow.Shuffle(train_ds,shuffle_buffer_size) +train_ds = tl.dataflow.Batch(train_ds,batch_size) + + +model = tl.models.Model(network=MLP, loss_fn=tl.cost.cross_entropy, optimizer=optimizer) +model.train(n_epoch=n_epoch, train_dataset=train_ds, print_freq=print_freq, print_train_batch=False) +model.save_weights('./model.npz', format='npz_dict') +model.load_weights('./model.npz', format='npz_dict') \ No newline at end of file diff --git a/examples/basic_tutorials/tutorial_cifar10_cnn_dynamic_MS_backend.py b/examples/basic_tutorials/tutorial_cifar10_cnn_dynamic_MS_backend.py new file mode 100644 index 0000000..02ab3e8 --- /dev/null +++ b/examples/basic_tutorials/tutorial_cifar10_cnn_dynamic_MS_backend.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import time +import numpy as np +import multiprocessing +import tensorflow as tf +from tensorlayer.layers import Module +import tensorlayer as tl +from tensorlayer.layers import (Conv2d, Dense, Flatten, MaxPool2d, BatchNorm2d) + +from mindspore.nn import Momentum, WithLossCell +from mindspore import ParameterTuple +import mindspore.nn as nn +import mindspore as ms +from mindspore.ops import composite as C +import mindspore.ops.operations as P + +# enable debug logging +tl.logging.set_verbosity(tl.logging.DEBUG) +tl.logging.set_verbosity(tl.logging.DEBUG) + +class CNN(Module): + def __init__(self): + super(CNN, self).__init__() + self.conv1 = Conv2d(64, (5, 5), (2, 2), padding='SAME', b_init=None, name='conv1', in_channels=3, act=tl.ReLU, data_format='channels_first') + self.bn = BatchNorm2d(num_features=64, act=tl.ReLU, data_format='channels_first') + self.maxpool1 = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool1', data_format='channels_first') + self.conv2 = Conv2d(128, (5, 5), (2, 2), padding='SAME', act=tl.ReLU, b_init=None, name='conv2', in_channels=64, data_format='channels_first') + self.maxpool2 = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool2', data_format='channels_first') + + self.flatten = Flatten(name='flatten') + self.dense1 = Dense(120, act=tl.ReLU, name='dense1relu', in_channels=4608) + self.dense2 = Dense(84, act=tl.ReLU, name='dense2relu', in_channels=120) + self.dense3 = Dense(10, act=None, name='output', in_channels=84) + + + def forward(self, x): + z = self.conv1(x) + z = self.bn(z) + z = self.maxpool1(z) + z = self.conv2(z) + z = self.maxpool2(z) + z = self.flatten(z) + z = self.dense1(z) + z = self.dense2(z) + z = self.dense3(z) + return z + +# training settings +batch_size = 128 +n_epoch = 500 +shuffle_buffer_size = 128 + + +# prepare cifar10 data +X_train, y_train, X_test, y_test = tl.files.load_cifar10_dataset(shape=(-1, 32, 32, 3), plotable=False) +def generator_train(): + inputs = X_train + targets = y_train + if len(inputs) != len(targets): + raise AssertionError("The length of inputs and targets should be equal") + for _input, _target in zip(inputs, targets): + yield _input, _target + + +def generator_test(): + inputs = X_test + targets = y_test + if len(inputs) != len(targets): + raise AssertionError("The length of inputs and targets should be equal") + for _input, _target in zip(inputs, targets): + # yield _input.encode('utf-8'), _target.encode('utf-8') + yield _input, _target + +def _map_fn_train(img, target): + # 1. Randomly crop a [height, width] section of the image. + img = tf.image.random_crop(img, [24, 24, 3]) + # 2. Randomly flip the image horizontally. + img = tf.image.random_flip_left_right(img) + # 3. Randomly change brightness. + img = tf.image.random_brightness(img, max_delta=63) + # 4. Randomly change contrast. + img = tf.image.random_contrast(img, lower=0.2, upper=1.8) + # 5. Subtract off the mean and divide by the variance of the pixels. + img = tf.image.per_image_standardization(img) + target = tf.reshape(target, ()) + return img, target + + +class GradWrap(Module): + """ GradWrap definition """ + + def __init__(self, network): + super(GradWrap, self).__init__(auto_prefix=False) + self.network = network + self.weights = ParameterTuple(filter(lambda x: x.requires_grad, network.get_parameters())) + + def forward(self, x, label): + return C.GradOperation(get_by_list=True)(self.network, self.weights)(x, label) + + +# dataset API and augmentation +train_ds = tf.data.Dataset.from_generator( + generator_train, output_types=(tf.float32, tf.int32) +) # , output_shapes=((24, 24, 3), (1))) +train_ds = train_ds.map(_map_fn_train, num_parallel_calls=multiprocessing.cpu_count()) +# train_ds = train_ds.repeat(n_epoch) +train_ds = train_ds.shuffle(shuffle_buffer_size) +train_ds = train_ds.prefetch(buffer_size=4096) +train_ds = train_ds.batch(batch_size) + +# get the network +net = CNN() +train_weights = net.trainable_weights +# optimizer = Adam(train_weights, learning_rate=0.01) +optimizer = Momentum(train_weights, 0.01, 0.5) +criterion = nn.SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean') +net_with_criterion = WithLossCell(net, criterion) +train_network = GradWrap(net_with_criterion) +train_network.set_train() +# print(train_weights) +for epoch in range(n_epoch): + start_time = time.time() + train_network.set_train() + train_loss, train_acc, n_iter = 0, 0, 0 + for X_batch, y_batch in train_ds: + X_batch = ms.Tensor(X_batch.numpy(), dtype=ms.float32) + y_batch = ms.Tensor(y_batch.numpy(), dtype=ms.int32) + X_batch = tl.nhwc_to_nchw(X_batch) + y_batch = tl.nhwc_to_nchw(y_batch) + output = net(X_batch) + loss_output = criterion(output, y_batch) + grads = train_network(X_batch, y_batch) + success = optimizer(grads) + loss = loss_output.asnumpy() + train_loss += loss + n_iter += 1 + train_acc += np.mean((P.Equal()(P.Argmax(axis=1)(output), y_batch).asnumpy())) + print("Epoch {} of {} took {}".format(epoch + 1, n_epoch, time.time() - start_time)) + print(" train loss: {}".format(train_loss / n_iter)) + print(" train acc: {}".format(train_acc / n_iter)) + print(" loss ", loss) + +# start_time = time.time() + +# train_loss, train_acc, n_iter = 0, 0, 0 +# for X_batch, y_batch in train_ds: +# net.set_train() + +# with tf.GradientTape() as tape: +# # compute outputs +# _logits = net(X_batch) +# # compute loss and update model +# _loss_ce = tl.cost.cross_entropy(_logits, y_batch, name='train_loss') + +# grad = tape.gradient(_loss_ce, train_weights) +# optimizer.apply_gradients(zip(grad, train_weights)) + +# train_loss += _loss_ce +# train_acc += np.mean(np.equal(np.argmax(_logits, 1), y_batch)) +# n_iter += 1 + +# print("Epoch {} of {} took {}".format(epoch + 1, n_epoch, time.time() - start_time)) +# print(" train loss: {}".format(train_loss / n_iter)) +# print(" train acc: {}".format(train_acc / n_iter)) diff --git a/examples/basic_tutorials/tutorial_cifar10_cnn_dynamic_TF_backend.py b/examples/basic_tutorials/tutorial_cifar10_cnn_dynamic_TF_backend.py new file mode 100644 index 0000000..f399bef --- /dev/null +++ b/examples/basic_tutorials/tutorial_cifar10_cnn_dynamic_TF_backend.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import time +import numpy as np +import multiprocessing +import tensorflow as tf + +from tensorlayer.layers import Module +import tensorlayer as tl +from tensorlayer.layers import (Conv2d, Dense, Flatten, MaxPool2d, BatchNorm2d) + +# enable debug logging +tl.logging.set_verbosity(tl.logging.DEBUG) +tl.logging.set_verbosity(tl.logging.DEBUG) + +# prepare cifar10 data +X_train, y_train, X_test, y_test = tl.files.load_cifar10_dataset(shape=(-1, 32, 32, 3), plotable=False) + + +class CNN(Module): + + def __init__(self): + super(CNN, self).__init__() + # weights init + W_init = tl.initializers.truncated_normal(stddev=5e-2) + W_init2 = tl.initializers.truncated_normal(stddev=0.04) + b_init2 = tl.initializers.constant(value=0.1) + + self.conv1 = Conv2d(64, (5, 5), (1, 1), padding='SAME', W_init=W_init, b_init=None, name='conv1', in_channels=3) + self.bn = BatchNorm2d(num_features=64, act=tl.ReLU) + self.maxpool1 = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool1') + + self.conv2 = Conv2d( + 64, (5, 5), (1, 1), padding='SAME', act=tl.ReLU, W_init=W_init, b_init=None, name='conv2', in_channels=64 + ) + self.maxpool2 = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool2') + + self.flatten = Flatten(name='flatten') + self.dense1 = Dense(384, act=tl.ReLU, W_init=W_init2, b_init=b_init2, name='dense1relu', in_channels=2304) + self.dense2 = Dense(192, act=tl.ReLU, W_init=W_init2, b_init=b_init2, name='dense2relu', in_channels=384) + self.dense3 = Dense(10, act=None, W_init=W_init2, name='output', in_channels=192) + + def forward(self, x): + z = self.conv1(x) + z = self.bn(z) + z = self.maxpool1(z) + z = self.conv2(z) + z = self.maxpool2(z) + z = self.flatten(z) + z = self.dense1(z) + z = self.dense2(z) + z = self.dense3(z) + return z + + +# get the network +net = CNN() + +# training settings +batch_size = 128 +n_epoch = 500 +learning_rate = 0.0001 +print_freq = 5 +n_step_epoch = int(len(y_train) / batch_size) +n_step = n_epoch * n_step_epoch +shuffle_buffer_size = 128 + +train_weights = net.trainable_weights +optimizer = tl.optimizers.Adam(learning_rate) +# looking for decay learning rate? see https://github.com/tensorlayer/srgan/blob/master/train.py + + +def generator_train(): + inputs = X_train + targets = y_train + if len(inputs) != len(targets): + raise AssertionError("The length of inputs and targets should be equal") + for _input, _target in zip(inputs, targets): + # yield _input.encode('utf-8'), _target.encode('utf-8') + yield _input, _target + + +def generator_test(): + inputs = X_test + targets = y_test + if len(inputs) != len(targets): + raise AssertionError("The length of inputs and targets should be equal") + for _input, _target in zip(inputs, targets): + # yield _input.encode('utf-8'), _target.encode('utf-8') + yield _input, _target + + +def _map_fn_train(img, target): + # 1. Randomly crop a [height, width] section of the image. + img = tf.image.random_crop(img, [24, 24, 3]) + # 2. Randomly flip the image horizontally. + img = tf.image.random_flip_left_right(img) + # 3. Randomly change brightness. + img = tf.image.random_brightness(img, max_delta=63) + # 4. Randomly change contrast. + img = tf.image.random_contrast(img, lower=0.2, upper=1.8) + # 5. Subtract off the mean and divide by the variance of the pixels. + img = tf.image.per_image_standardization(img) + target = tf.reshape(target, ()) + return img, target + + +def _map_fn_test(img, target): + # 1. Crop the central [height, width] of the image. + img = tf.image.resize_with_pad(img, 24, 24) + # 2. Subtract off the mean and divide by the variance of the pixels. + img = tf.image.per_image_standardization(img) + img = tf.reshape(img, (24, 24, 3)) + target = tf.reshape(target, ()) + return img, target + + +# dataset API and augmentation +train_ds = tf.data.Dataset.from_generator( + generator_train, output_types=(tf.float32, tf.int32) +) # , output_shapes=((24, 24, 3), (1))) +train_ds = train_ds.map(_map_fn_train, num_parallel_calls=multiprocessing.cpu_count()) +# train_ds = train_ds.repeat(n_epoch) +train_ds = train_ds.shuffle(shuffle_buffer_size) +train_ds = train_ds.prefetch(buffer_size=4096) +train_ds = train_ds.batch(batch_size) +# value = train_ds.make_one_shot_iterator().get_next() + +test_ds = tf.data.Dataset.from_generator( + generator_test, output_types=(tf.float32, tf.int32) +) # , output_shapes=((24, 24, 3), (1))) +# test_ds = test_ds.shuffle(shuffle_buffer_size) +test_ds = test_ds.map(_map_fn_test, num_parallel_calls=multiprocessing.cpu_count()) +# test_ds = test_ds.repeat(n_epoch) +test_ds = test_ds.prefetch(buffer_size=4096) +test_ds = test_ds.batch(batch_size) +# value_test = test_ds.make_one_shot_iterator().get_next() + +for epoch in range(n_epoch): + start_time = time.time() + + train_loss, train_acc, n_iter = 0, 0, 0 + for X_batch, y_batch in train_ds: + net.set_train() + + with tf.GradientTape() as tape: + # compute outputs + _logits = net(X_batch) + # compute loss and update model + _loss_ce = tl.cost.cross_entropy(_logits, y_batch, name='train_loss') + + grad = tape.gradient(_loss_ce, train_weights) + optimizer.apply_gradients(zip(grad, train_weights)) + + train_loss += _loss_ce + train_acc += np.mean(np.equal(np.argmax(_logits, 1), y_batch)) + n_iter += 1 + + print("Epoch {} of {} took {}".format(epoch + 1, n_epoch, time.time() - start_time)) + print(" train loss: {}".format(train_loss / n_iter)) + print(" train acc: {}".format(train_acc / n_iter)) + + # use training and evaluation sets to evaluate the model every print_freq epoch + if epoch + 1 == 1 or (epoch + 1) % print_freq == 0: + + net.eval() + val_loss, val_acc, n_iter = 0, 0, 0 + for X_batch, y_batch in test_ds: + _logits = net(X_batch) # is_train=False, disable dropout + val_loss += tl.cost.cross_entropy(_logits, y_batch, name='eval_loss') + val_acc += np.mean(np.equal(np.argmax(_logits, 1), y_batch)) + n_iter += 1 + print(" val loss: {}".format(val_loss / n_iter)) + print(" val acc: {}".format(val_acc / n_iter)) + +# use testing data to evaluate the model +net.eval() +test_loss, test_acc, n_iter = 0, 0, 0 +for X_batch, y_batch in test_ds: + _logits = net(X_batch) + test_loss += tl.cost.cross_entropy(_logits, y_batch, name='test_loss') + test_acc += np.mean(np.equal(np.argmax(_logits, 1), y_batch)) + n_iter += 1 +print(" test loss: {}".format(test_loss / n_iter)) +print(" test acc: {}".format(test_acc / n_iter)) diff --git a/examples/basic_tutorials/tutorial_mnist_mlp_dynamci_dragon.py b/examples/basic_tutorials/tutorial_mnist_mlp_dynamci_dragon.py new file mode 100644 index 0000000..9c06ec5 --- /dev/null +++ b/examples/basic_tutorials/tutorial_mnist_mlp_dynamci_dragon.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os +os.environ['TL_BACKEND'] = 'dragon' + +from tensorlayer.layers import Module +from tensorlayer.layers import Dense +import tensorlayer as tl +import dragon as dg +import time +import argparse +import numpy as np + +X_train, y_train, X_val, y_val, X_test, y_test = tl.files.load_mnist_dataset(shape=(-1, 784)) + + +class CustomModel(Module): + + def __init__(self): + super(CustomModel, self).__init__() + self.dense1 = Dense(n_units=800, act=tl.ReLU, in_channels=784) + self.dense2 = Dense(n_units=800, act=tl.ReLU, in_channels=800) + self.dense3 = Dense(n_units=10, act=tl.ReLU, in_channels=800) + + def forward(self, x, foo=None): + z = self.dense1(x) + z = self.dense2(z) + out = self.dense3(z) + return out + + +def parse_args(): + """Parse the arguments.""" + parser = argparse.ArgumentParser(description='Train a cifar10 resnet') + parser.add_argument('--execution', default='EAGER_MODE', type=str, help='The execution mode') + parser.add_argument('--seed', default=1337, type=int, help='The random seed') + parser.add_argument('--cuda', default=-1, type=int, help='The cuda device to use') + return parser.parse_args() + + +class Classifier(object): + """The base classifier class.""" + + # TensorSpec for graph execution + image_spec = dg.Tensor([None, 3, 32, 32], 'float32') + label_spec = dg.Tensor([None], 'int64') + + def __init__(self, optimizer): + super(Classifier, self).__init__() + self.net = CustomModel() + self.optimizer = optimizer + self.params = self.net.trainable_weights + + def step(self, image, label): + with dg.GradientTape() as tape: + logit = self.net(image) + # logit = dg.cast(logit, 'float64') + logit = dg.cast(dg.math.argmax(logit, -1), 'int64') + label = dg.cast(label, 'int64') + # print("logit :\n", logit, label) + # loss = dg.losses.smooth_l1_loss([logit, label]) + loss = dg.math.sum(logit - label) # dg.losses.sparse_softmax_cross_entropy([logit, label]) + accuracy = dg.math.mean(dg.math.equal([logit, label]).astype('float32')) + grads = tape.gradient(loss, self.params) + self.optimizer.apply_gradients(zip(self.params, grads)) + return loss, accuracy, self.optimizer + + +if __name__ == '__main__': + args = parse_args() + dg.logging.info('Called with args:\n' + str(args)) + + np.random.seed(args.seed) + dg.autograph.set_execution(args.execution) + dg.cuda.set_default_device(args.cuda) + + # Define the model + model = Classifier(dg.optimizers.SGD(base_lr=0.01, momentum=0.9, weight_decay=1e-4)) + + # Compile for graph execution if necessary + if args.execution == 'GRAPH_MODE': + model.step = dg.function( + func=model.step, + input_signature=[model.image_spec, model.label_spec], + ) + + # Main loop + import tensorflow as tf + batch_size = 200 + for i in range(50): + for X_batch, y_batch in tl.iterate.minibatches(X_train, y_train, batch_size, shuffle=True): + image = dg.EagerTensor(X_batch, copy=False) + label = dg.EagerTensor(y_batch, copy=False, dtype='float32') + loss, accuracy, _ = model.step(image, label) + if i % 20 == 0: + dg.logging.info( + 'Iteration %d, lr = %s, loss = %.5f, accuracy = %.3f' % + (i, str(model.optimizer.base_lr), loss, accuracy) + ) diff --git a/examples/basic_tutorials/tutorial_mnist_mlp_dynamic_MS_backend.py b/examples/basic_tutorials/tutorial_mnist_mlp_dynamic_MS_backend.py new file mode 100644 index 0000000..e480221 --- /dev/null +++ b/examples/basic_tutorials/tutorial_mnist_mlp_dynamic_MS_backend.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import mindspore.nn as nn +import mindspore.ops.operations as P +from mindspore.ops import composite as C +from mindspore.common import dtype as mstype +from mindspore import context, Tensor, ParameterTuple +from mindspore.common.initializer import TruncatedNormal +from mindspore.nn import SoftmaxCrossEntropyWithLogits, Momentum, WithLossCell + +import numpy as np +import tensorlayer as tl +import mindspore as ms +import tensorflow as tf +import time +from tensorlayer.layers import Module +from tensorlayer.layers import Dense +import mindspore.nn as nn + + +class MLP(Module): + + def __init__(self): + super(MLP, self).__init__() + self.dense1 = Dense(n_units=800, act=tl.ReLU, in_channels=784) + self.dense2 = Dense(n_units=800, act=tl.ReLU, in_channels=800) + self.dense3 = Dense(n_units=10, act=tl.ReLU, in_channels=800) + + def forward(self, x): + z = self.dense1(x) + z = self.dense2(z) + out = self.dense3(z) + return out + + +class GradWrap(Module): + """ GradWrap definition """ + + def __init__(self, network): + super(GradWrap, self).__init__(auto_prefix=False) + self.network = network + self.weights = ParameterTuple(filter(lambda x: x.requires_grad, network.get_parameters())) + + def forward(self, x, label): + return C.GradOperation(get_by_list=True)(self.network, self.weights)(x, label) + + +def generator_train(): + inputs = X_train + targets = y_train + if len(inputs) != len(targets): + raise AssertionError("The length of inputs and targets should be equal") + for _input, _target in zip(inputs, targets): + yield _input, _target + + +net = MLP() +train_weights = list(filter(lambda x: x.requires_grad, net.get_parameters())) +optimizer = Momentum(filter(lambda x: x.requires_grad, net.get_parameters()), 0.15, 0.8) + +criterion = nn.SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean') +net_with_criterion = WithLossCell(net, criterion) +train_network = GradWrap(net_with_criterion) +train_network.set_train() + +X_train, y_train, X_val, y_val, X_test, y_test = tl.files.load_mnist_dataset(shape=(-1, 784)) +train_ds = tf.data.Dataset.from_generator(generator_train, output_types=(tf.float32, tf.int32)) +shuffle_buffer_size = 128 +batch_size = 128 +train_ds = train_ds.shuffle(shuffle_buffer_size) +train_ds = train_ds.batch(batch_size) +n_epoch = 50 + +for epoch in range(n_epoch): + start_time = time.time() + train_network.set_train() + train_loss, train_acc, n_iter = 0, 0, 0 + for X_batch, y_batch in train_ds: + X_batch = ms.Tensor(X_batch.numpy(), dtype=ms.float32) + y_batch = ms.Tensor(y_batch.numpy(), dtype=ms.int32) + output = net(X_batch) + loss_output = criterion(output, y_batch) + grads = train_network(X_batch, y_batch) + success = optimizer(grads) + loss = loss_output.asnumpy() + train_loss += loss + n_iter += 1 + train_acc += np.mean((P.Equal()(P.Argmax(axis=1)(output), y_batch).asnumpy())) + print("Epoch {} of {} took {}".format(epoch + 1, n_epoch, time.time() - start_time)) + print(" train loss: {}".format(train_loss / n_iter)) + print(" train acc: {}".format(train_acc / n_iter)) + print(" loss ", loss) diff --git a/examples/basic_tutorials/tutorial_mnist_mlp_dynamic_TF_backend.py b/examples/basic_tutorials/tutorial_mnist_mlp_dynamic_TF_backend.py new file mode 100644 index 0000000..1287391 --- /dev/null +++ b/examples/basic_tutorials/tutorial_mnist_mlp_dynamic_TF_backend.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import numpy as np +import time + +import tensorflow as tf +import tensorlayer as tl +from tensorlayer.layers import Module +from tensorlayer.layers import Dense, Dropout, BatchNorm1d + +X_train, y_train, X_val, y_val, X_test, y_test = tl.files.load_mnist_dataset(shape=(-1, 784)) + + +class CustomModel(Module): + + def __init__(self): + super(CustomModel, self).__init__() + self.dropout1 = Dropout(keep=0.8) + self.dense1 = Dense(n_units=800, in_channels=784) + self.batchnorm = BatchNorm1d(act=tl.ReLU, num_features=800) + self.dropout2 = Dropout(keep=0.8) + self.dense2 = Dense(n_units=800, act=tl.ReLU, in_channels=800) + self.dropout3 = Dropout(keep=0.8) + self.dense3 = Dense(n_units=10, act=tl.ReLU, in_channels=800) + + def forward(self, x, foo=None): + z = self.dropout1(x) + z = self.dense1(z) + z = self.batchnorm(z) + z = self.dropout2(z) + z = self.dense2(z) + z = self.dropout3(z) + out = self.dense3(z) + if foo is not None: + out = tl.ops.relu(out) + return out + + +MLP = CustomModel() +n_epoch = 50 +batch_size = 500 +print_freq = 5 +train_weights = MLP.trainable_weights +optimizer = tl.optimizers.Adam(lr=0.0001) + +for epoch in range(n_epoch): ## iterate the dataset n_epoch times + start_time = time.time() + ## iterate over the entire training set once (shuffle the data via training) + for X_batch, y_batch in tl.iterate.minibatches(X_train, y_train, batch_size, shuffle=True): + MLP.set_train() # enable dropout + with tf.GradientTape() as tape: + ## compute outputs + _logits = MLP(X_batch) + ## compute loss and update model + _loss = tl.cost.cross_entropy(_logits, y_batch, name='train_loss') + grad = tape.gradient(_loss, train_weights) + optimizer.apply_gradients(zip(grad, train_weights)) + + ## use training and evaluation sets to evaluate the model every print_freq epoch + if epoch + 1 == 1 or (epoch + 1) % print_freq == 0: + MLP.set_train() + print("Epoch {} of {} took {}".format(epoch + 1, n_epoch, time.time() - start_time)) + train_loss, train_acc, n_iter = 0, 0, 0 + for X_batch, y_batch in tl.iterate.minibatches(X_train, y_train, batch_size, shuffle=False): + _logits = MLP(X_batch) + train_loss += tl.cost.cross_entropy(_logits, y_batch, name='eval_loss') + train_acc += np.mean(np.equal(np.argmax(_logits, 1), y_batch)) + n_iter += 1 + print(" train loss: {}".format(train_loss / n_iter)) + print(" train acc: {}".format(train_acc / n_iter)) + + val_loss, val_acc, n_iter = 0, 0, 0 + for X_batch, y_batch in tl.iterate.minibatches(X_val, y_val, batch_size, shuffle=False): + _logits = MLP(X_batch) # is_train=False, disable dropout + val_loss += tl.cost.cross_entropy(_logits, y_batch, name='eval_loss') + val_acc += np.mean(np.equal(np.argmax(_logits, 1), y_batch)) + n_iter += 1 + print(" val loss: {}".format(val_loss / n_iter)) + print(" val acc: {}".format(val_acc / n_iter)) + +## use testing data to evaluate the model +MLP.eval() +test_loss, test_acc, n_iter = 0, 0, 0 +for X_batch, y_batch in tl.iterate.minibatches(X_test, y_test, batch_size, shuffle=False): + _logits = MLP(X_batch, foo=1) + test_loss += tl.cost.cross_entropy(_logits, y_batch, name='test_loss') + test_acc += np.mean(np.equal(np.argmax(_logits, 1), y_batch)) + n_iter += 1 +print(" test foo=1 loss: {}".format(val_loss / n_iter)) +print(" test foo=1 acc: {}".format(val_acc / n_iter)) diff --git a/examples/basic_tutorials/tutorial_mnist_mlp_mindspore.py b/examples/basic_tutorials/tutorial_mnist_mlp_mindspore.py new file mode 100644 index 0000000..3e552d3 --- /dev/null +++ b/examples/basic_tutorials/tutorial_mnist_mlp_mindspore.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import numpy as np +import mindspore.nn as nn +import mindspore.ops.operations as P +from mindspore.ops import composite as C +from mindspore.common import dtype as mstype +from mindspore import context, Tensor, ParameterTuple +from mindspore.common.initializer import TruncatedNormal +from mindspore.nn import Dense, WithLossCell, SoftmaxCrossEntropyWithLogits, Momentum +import tensorlayer as tl +import mindspore as ms +import tensorflow as tf +import time + +context.set_context(mode=context.PYNATIVE_MODE, device_target="GPU") + + +def fc_with_initialize(input_channels, out_channels): + """weight initial for fc layer""" + weight = weight_variable() + bias = weight_variable() + return nn.Dense(input_channels, out_channels, weight, bias) + + +def weight_variable(): + """weight initial""" + return TruncatedNormal(0.02) + + +class MLP(nn.Cell): + """ + Lenet network + Args: + num_class (int): Num classes. Default: 10. + + Returns: + Tensor, output tensor + + Examples: + >>> MLP(num_class=10) + """ + + def __init__(self, num_class=10): + super(MLP, self).__init__() + self.num_class = num_class + self.fc1 = fc_with_initialize(784, 800) + self.fc2 = fc_with_initialize(800, 800) + self.fc3 = fc_with_initialize(800, self.num_class) + self.relu = nn.ReLU() + + def construct(self, x): + x = self.fc1(x) + x = self.relu(x) + x = self.fc2(x) + x = self.relu(x) + x = self.fc3(x) + return x + + +class GradWrap(nn.Cell): + """ GradWrap definition """ + + def __init__(self, network): + super(GradWrap, self).__init__(auto_prefix=False) + self.network = network + self.weights = ParameterTuple(filter(lambda x: x.requires_grad, network.get_parameters())) + + def construct(self, x, label): + weights = self.weights + return C.GradOperation('get_by_list', get_by_list=True)(self.network, weights)(x, label) + + +def generator_train(): + inputs = X_train + targets = y_train + if len(inputs) != len(targets): + raise AssertionError("The length of inputs and targets should be equal") + for _input, _target in zip(inputs, targets): + yield _input, _target + + +net = MLP() +optimizer = Momentum(filter(lambda x: x.requires_grad, net.get_parameters()), 0.1, 0.9) +criterion = nn.SoftmaxCrossEntropyWithLogits(is_grad=False, sparse=True) +net_with_criterion = WithLossCell(net, criterion) +train_network = GradWrap(net_with_criterion) +train_network.set_train() + +X_train, y_train, X_val, y_val, X_test, y_test = tl.files.load_mnist_dataset(shape=(-1, 784)) +train_ds = tf.data.Dataset.from_generator(generator_train, output_types=(tf.float32, tf.int32)) +shuffle_buffer_size = 128 +batch_size = 128 +train_ds = train_ds.shuffle(shuffle_buffer_size) +train_ds = train_ds.batch(batch_size) +n_epoch = 50 + +for epoch in range(n_epoch): + start_time = time.time() + train_network.set_train() + train_loss, train_acc, n_iter = 0, 0, 0 + for X_batch, y_batch in train_ds: + X_batch = ms.Tensor(X_batch.numpy(), dtype=ms.float32) + y_batch = ms.Tensor(y_batch.numpy(), dtype=ms.int32) + output = net(X_batch) + loss_output = criterion(output, y_batch) + grads = train_network(X_batch, y_batch) + success = optimizer(grads) + loss = loss_output.asnumpy() + train_loss += loss + n_iter += 1 + # train_acc += np.mean((P.Equal()(P.Argmax(axis=1)(output), y_batch).asnumpy())) + print("Epoch {} of {} took {}".format(epoch + 1, n_epoch, time.time() - start_time)) + print(" train loss: {}".format(train_loss / n_iter)) + # print(" train acc: {}".format(train_acc / n_iter)) + print(" triain weights ", train_network.trainable_params()[0].data) diff --git a/examples/basic_tutorials/tutorial_mnist_simple.py b/examples/basic_tutorials/tutorial_mnist_simple.py new file mode 100644 index 0000000..4d2bc7c --- /dev/null +++ b/examples/basic_tutorials/tutorial_mnist_simple.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import numpy as np +import os +os.environ['TL_BACKEND'] = 'tensorflow' +# os.environ['TL_BACKEND'] = 'mindspore' + +import tensorlayer as tl +from tensorlayer.layers import Module +from tensorlayer.layers import Dense, Dropout + +X_train, y_train, X_val, y_val, X_test, y_test = tl.files.load_mnist_dataset(shape=(-1, 784)) + + +class CustomModel(Module): + + def __init__(self): + super(CustomModel, self).__init__() + self.dropout1 = Dropout(keep=0.8) + self.dense1 = Dense(n_units=800, act=tl.ReLU, in_channels=784) + self.dropout2 = Dropout(keep=0.8) + self.dense2 = Dense(n_units=800, act=tl.ReLU, in_channels=800) + self.dropout3 = Dropout(keep=0.8) + self.dense3 = Dense(n_units=10, act=tl.ReLU, in_channels=800) + + def forward(self, x, foo=None): + z = self.dropout1(x) + z = self.dense1(z) + # z = self.bn(z) + z = self.dropout2(z) + z = self.dense2(z) + z = self.dropout3(z) + out = self.dense3(z) + if foo is not None: + out = tl.ops.relu(out) + return out + + +def generator_train(): + inputs = X_train + targets = y_train + if len(inputs) != len(targets): + raise AssertionError("The length of inputs and targets should be equal") + for _input, _target in zip(inputs, targets): + yield (_input, np.array(_target)) + + +MLP = CustomModel() + +n_epoch = 50 +batch_size = 128 +print_freq = 2 +shuffle_buffer_size = 128 + +train_weights = MLP.trainable_weights +optimizer = tl.optimizers.Momentum(0.05, 0.9) +train_ds = tl.dataflow.FromGenerator( + generator_train, output_types=(tl.float32, tl.int32) , column_names=['data', 'label'] +) +train_ds = tl.dataflow.Shuffle(train_ds,shuffle_buffer_size) +train_ds = tl.dataflow.Batch(train_ds,batch_size) + + +model = tl.models.Model(network=MLP, loss_fn=tl.cost.cross_entropy, optimizer=optimizer) +model.train(n_epoch=n_epoch, train_dataset=train_ds, print_freq=print_freq, print_train_batch=False) +model.save_weights('./model.npz', format='npz_dict') +model.load_weights('./model.npz', format='npz_dict') diff --git a/examples/basic_tutorials/tutorial_nested_usage_of_Layer.py b/examples/basic_tutorials/tutorial_nested_usage_of_Layer.py new file mode 100644 index 0000000..27ae9be --- /dev/null +++ b/examples/basic_tutorials/tutorial_nested_usage_of_Layer.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import time +import numpy as np +import multiprocessing +import tensorflow as tf + +from tensorlayer.layers import Module, SequentialLayer +import tensorlayer as tl +from tensorlayer.layers import (Conv2d, Dense, Flatten, MaxPool2d, BatchNorm2d, Elementwise) + +X_train, y_train, X_test, y_test = tl.files.load_cifar10_dataset(shape=(-1, 32, 32, 3), plotable=False) + +class Block(Module): + def __init__(self, in_channels): + super(Block, self).__init__() + self.dense1 = Dense(in_channels=in_channels, n_units=256) + self.dense2 = Dense(in_channels=256, n_units=384) + self.dense3 = Dense(in_channels=in_channels, n_units=384) + self.concat = Elementwise(combine_fn=tl.ops.add) + + def forward(self, inputs): + z = self.dense1(inputs) + z1 = self.dense2(z) + + z2 = self.dense3(inputs) + out = self.concat([z1, z2]) + return out + + +class CNN(Module): + + def __init__(self): + super(CNN, self).__init__() + # weights init + W_init = tl.initializers.truncated_normal(stddev=5e-2) + W_init2 = tl.initializers.truncated_normal(stddev=0.04) + b_init2 = tl.initializers.constant(value=0.1) + + self.conv1 = Conv2d(64, (5, 5), (1, 1), padding='SAME', W_init=W_init, b_init=None, name='conv1', in_channels=3) + self.bn = BatchNorm2d(num_features=64, act=tl.ReLU) + self.maxpool1 = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool1') + + self.conv2 = Conv2d( + 64, (5, 5), (1, 1), padding='SAME', act=tl.ReLU, W_init=W_init, b_init=None, name='conv2', in_channels=64 + ) + self.maxpool2 = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool2') + + self.flatten = Flatten(name='flatten') + self.dense1 = Dense(384, act=tl.ReLU, W_init=W_init2, b_init=b_init2, name='dense1relu', in_channels=2304) + self.dense_add = self.make_layer(in_channel=384) + + self.dense2 = Dense(192, act=tl.ReLU, W_init=W_init2, b_init=b_init2, name='dense2relu', in_channels=384) + self.dense3 = Dense(10, act=None, W_init=W_init2, name='output', in_channels=192) + + def forward(self, x): + z = self.conv1(x) + z = self.bn(z) + z = self.maxpool1(z) + z = self.conv2(z) + z = self.maxpool2(z) + z = self.flatten(z) + z = self.dense1(z) + z = self.dense_add(z) + + z = self.dense2(z) + z = self.dense3(z) + return z + + def make_layer(self, in_channel): + layers = [] + + _block = Block(in_channel) + layers.append(_block) + + for _ in range(1, 3): + range_block = Block(in_channel) + layers.append(range_block) + + return SequentialLayer(layers) + + +# get the network +net = CNN() +# training settings +batch_size = 128 +n_epoch = 500 +learning_rate = 0.0001 +print_freq = 5 +n_step_epoch = int(len(y_train) / batch_size) +n_step = n_epoch * n_step_epoch +shuffle_buffer_size = 128 + +train_weights = net.trainable_weights +optimizer = tl.optimizers.Adam(learning_rate) + + +def generator_train(): + inputs = X_train + targets = y_train + if len(inputs) != len(targets): + raise AssertionError("The length of inputs and targets should be equal") + for _input, _target in zip(inputs, targets): + # yield _input.encode('utf-8'), _target.encode('utf-8') + yield _input, _target + + +def generator_test(): + inputs = X_test + targets = y_test + if len(inputs) != len(targets): + raise AssertionError("The length of inputs and targets should be equal") + for _input, _target in zip(inputs, targets): + # yield _input.encode('utf-8'), _target.encode('utf-8') + yield _input, _target + + +def _map_fn_train(img, target): + # 1. Randomly crop a [height, width] section of the image. + img = tf.image.random_crop(img, [24, 24, 3]) + # 2. Randomly flip the image horizontally. + img = tf.image.random_flip_left_right(img) + # 3. Randomly change brightness. + img = tf.image.random_brightness(img, max_delta=63) + # 4. Randomly change contrast. + img = tf.image.random_contrast(img, lower=0.2, upper=1.8) + # 5. Subtract off the mean and divide by the variance of the pixels. + img = tf.image.per_image_standardization(img) + target = tf.reshape(target, ()) + return img, target + + +def _map_fn_test(img, target): + # 1. Crop the central [height, width] of the image. + img = tf.image.resize_with_pad(img, 24, 24) + # 2. Subtract off the mean and divide by the variance of the pixels. + img = tf.image.per_image_standardization(img) + img = tf.reshape(img, (24, 24, 3)) + target = tf.reshape(target, ()) + return img, target + + +# dataset API and augmentation +train_ds = tf.data.Dataset.from_generator( + generator_train, output_types=(tf.float32, tf.int32) +) # , output_shapes=((24, 24, 3), (1))) +train_ds = train_ds.map(_map_fn_train, num_parallel_calls=multiprocessing.cpu_count()) +# train_ds = train_ds.repeat(n_epoch) +train_ds = train_ds.shuffle(shuffle_buffer_size) +train_ds = train_ds.prefetch(buffer_size=4096) +train_ds = train_ds.batch(batch_size) +# value = train_ds.make_one_shot_iterator().get_next() + +test_ds = tf.data.Dataset.from_generator( + generator_test, output_types=(tf.float32, tf.int32) +) # , output_shapes=((24, 24, 3), (1))) +# test_ds = test_ds.shuffle(shuffle_buffer_size) +test_ds = test_ds.map(_map_fn_test, num_parallel_calls=multiprocessing.cpu_count()) +# test_ds = test_ds.repeat(n_epoch) +test_ds = test_ds.prefetch(buffer_size=4096) +test_ds = test_ds.batch(batch_size) +# value_test = test_ds.make_one_shot_iterator().get_next() + +for epoch in range(n_epoch): + start_time = time.time() + + train_loss, train_acc, n_iter = 0, 0, 0 + for X_batch, y_batch in train_ds: + net.set_train() + + with tf.GradientTape() as tape: + # compute outputs + _logits = net(X_batch) + # compute loss and update model + _loss_ce = tl.cost.cross_entropy(_logits, y_batch, name='train_loss') + + grad = tape.gradient(_loss_ce, train_weights) + optimizer.apply_gradients(zip(grad, train_weights)) + + train_loss += _loss_ce + train_acc += np.mean(np.equal(np.argmax(_logits, 1), y_batch)) + n_iter += 1 + + print("Epoch {} of {} took {}".format(epoch + 1, n_epoch, time.time() - start_time)) + print(" train loss: {}".format(train_loss / n_iter)) + print(" train acc: {}".format(train_acc / n_iter)) + + # use training and evaluation sets to evaluate the model every print_freq epoch + if epoch + 1 == 1 or (epoch + 1) % print_freq == 0: + + net.eval() + val_loss, val_acc, n_iter = 0, 0, 0 + for X_batch, y_batch in test_ds: + _logits = net(X_batch) # is_train=False, disable dropout + val_loss += tl.cost.cross_entropy(_logits, y_batch, name='eval_loss') + val_acc += np.mean(np.equal(np.argmax(_logits, 1), y_batch)) + n_iter += 1 + print(" val loss: {}".format(val_loss / n_iter)) + print(" val acc: {}".format(val_acc / n_iter)) + +# use testing data to evaluate the model +net.eval() +test_loss, test_acc, n_iter = 0, 0, 0 +for X_batch, y_batch in test_ds: + _logits = net(X_batch) + test_loss += tl.cost.cross_entropy(_logits, y_batch, name='test_loss') + test_acc += np.mean(np.equal(np.argmax(_logits, 1), y_batch)) + n_iter += 1 +print(" test loss: {}".format(test_loss / n_iter)) +print(" test acc: {}".format(test_acc / n_iter)) \ No newline at end of file diff --git a/examples/basic_tutorials/tutorial_paddle_tensorlayer_mlp.py b/examples/basic_tutorials/tutorial_paddle_tensorlayer_mlp.py new file mode 100644 index 0000000..96d57b5 --- /dev/null +++ b/examples/basic_tutorials/tutorial_paddle_tensorlayer_mlp.py @@ -0,0 +1,57 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +import os +os.environ['TL_BACKEND'] = 'paddle' + +import paddle.nn.functional as F +from paddle.vision.transforms import Compose, Normalize +import paddle + +import tensorlayer as tl +from tensorlayer.layers import Module +from tensorlayer.layers import Dense, Flatten + +transform = Compose([Normalize(mean=[127.5], + std=[127.5], + data_format='CHW')]) +print('download training data and load training data') +train_dataset = paddle.vision.datasets.MNIST(mode='train', transform=transform) +test_dataset = paddle.vision.datasets.MNIST(mode='test', transform=transform) +print('load finished') + +class MLP(Module): + def __init__(self): + super(MLP, self).__init__() + self.linear1 = Dense(n_units=120, in_channels=784, act=tl.ReLU) + self.linear2 = Dense(n_units=84, in_channels=120, act=tl.ReLU) + self.linear3 = Dense(n_units=10, in_channels=84) + self.flatten = Flatten() + + def forward(self, x): + x = self.flatten(x) + x = self.linear1(x) + x = self.linear2(x) + x = self.linear3(x) + return x + +train_loader = paddle.io.DataLoader(train_dataset, batch_size=64, shuffle=True) + +def train(model): + model.train() + epochs = 2 + optim = paddle.optimizer.Adam(learning_rate=0.001, parameters=model.trainable_weights) + for epoch in range(epochs): + for batch_id, data in enumerate(train_loader()): + x_data = data[0] + y_data = data[1] + predicts = model(x_data) + loss = tl.cost.mean_squared_error(predicts, y_data) + acc = paddle.metric.accuracy(predicts, y_data) + loss.backward() + if batch_id % 5 == 0: + print("epoch: {}, batch_id: {}, loss is: {}, acc is: {}".format(epoch, batch_id, loss.numpy(), acc.numpy())) + optim.step() + optim.clear_grad() +model = MLP() +train(model) + diff --git a/examples/model_zoo/__init__.py b/examples/model_zoo/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/model_zoo/common.py b/examples/model_zoo/common.py new file mode 100644 index 0000000..7bc1bfd --- /dev/null +++ b/examples/model_zoo/common.py @@ -0,0 +1,287 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorflow as tf +import colorsys, random, cv2 +import numpy as np +from tensorlayer.visualize import save_image + +def decode_tf(conv_output, output_size, NUM_CLASS, STRIDES, ANCHORS, i=0, XYSCALE=[1, 1, 1]): + batch_size = tf.shape(conv_output)[0] + conv_output = tf.reshape(conv_output, (batch_size, output_size, output_size, 3, 5 + NUM_CLASS)) + + conv_raw_dxdy, conv_raw_dwdh, conv_raw_conf, conv_raw_prob = tf.split(conv_output, (2, 2, 1, NUM_CLASS), axis=-1) + + xy_grid = tf.meshgrid(tf.range(output_size), tf.range(output_size)) + xy_grid = tf.expand_dims(tf.stack(xy_grid, axis=-1), axis=2) # [gx, gy, 1, 2] + xy_grid = tf.tile(tf.expand_dims(xy_grid, axis=0), [batch_size, 1, 1, 3, 1]) + + xy_grid = tf.cast(xy_grid, tf.float32) + + pred_xy = ((tf.sigmoid(conv_raw_dxdy) * XYSCALE[i]) - 0.5 * (XYSCALE[i] - 1) + xy_grid) * \ + STRIDES[i] + pred_wh = (tf.exp(conv_raw_dwdh) * ANCHORS[i]) + pred_xywh = tf.concat([pred_xy, pred_wh], axis=-1) + + pred_conf = tf.sigmoid(conv_raw_conf) + pred_prob = tf.sigmoid(conv_raw_prob) + + pred_prob = pred_conf * pred_prob + pred_prob = tf.reshape(pred_prob, (batch_size, -1, NUM_CLASS)) + pred_xywh = tf.reshape(pred_xywh, (batch_size, -1, 4)) + + return pred_xywh, pred_prob + + +def decode(conv_output, output_size, NUM_CLASS, STRIDES, ANCHORS, i, XYSCALE=[1, 1, 1]): + return decode_tf(conv_output, output_size, NUM_CLASS, STRIDES, ANCHORS, i=i, XYSCALE=XYSCALE) + + +def filter_boxes(box_xywh, scores, score_threshold=0.4, input_shape=tf.constant([416, 416])): + scores_max = tf.math.reduce_max(scores, axis=-1) + + mask = scores_max >= score_threshold + class_boxes = tf.boolean_mask(box_xywh, mask) + pred_conf = tf.boolean_mask(scores, mask) + class_boxes = tf.reshape(class_boxes, [tf.shape(scores)[0], -1, tf.shape(class_boxes)[-1]]) + pred_conf = tf.reshape(pred_conf, [tf.shape(scores)[0], -1, tf.shape(pred_conf)[-1]]) + + box_xy, box_wh = tf.split(class_boxes, (2, 2), axis=-1) + + input_shape = tf.cast(input_shape, dtype=tf.float32) + box_yx = box_xy[..., ::-1] + box_hw = box_wh[..., ::-1] + + box_mins = (box_yx - (box_hw / 2.)) / input_shape + box_maxes = (box_yx + (box_hw / 2.)) / input_shape + boxes = tf.concat( + [ + box_mins[..., 0:1], # y_min + box_mins[..., 1:2], # x_min + box_maxes[..., 0:1], # y_max + box_maxes[..., 1:2] # x_max + ], + axis=-1 + ) + # return tf.concat([boxes, pred_conf], axis=-1) + return (boxes, pred_conf) + + +def read_class_names(class_file_name): + names = {} + with open(class_file_name, 'r') as data: + for ID, name in enumerate(data): + names[ID] = name.strip('\n') + return names + + +def draw_bbox(image, bboxes, show_label=True): + classes = read_class_names('model/coco.names') + num_classes = len(classes) + image_h, image_w, _ = image.shape + hsv_tuples = [(1.0 * x / num_classes, 1., 1.) for x in range(num_classes)] + colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples)) + colors = list(map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)), colors)) + + random.seed(0) + random.shuffle(colors) + random.seed(None) + + out_boxes, out_scores, out_classes, num_boxes = bboxes + for i in range(num_boxes[0]): + if int(out_classes[0][i]) < 0 or int(out_classes[0][i]) > num_classes: continue + coor = out_boxes[0][i] + coor[0] = int(coor[0] * image_h) + coor[2] = int(coor[2] * image_h) + coor[1] = int(coor[1] * image_w) + coor[3] = int(coor[3] * image_w) + + fontScale = 0.5 + score = out_scores[0][i] + class_ind = int(out_classes[0][i]) + bbox_color = colors[class_ind] + bbox_thick = int(0.6 * (image_h + image_w) / 600) + c1, c2 = (coor[1], coor[0]), (coor[3], coor[2]) + cv2.rectangle(image, c1, c2, bbox_color, bbox_thick) + + if show_label: + bbox_mess = '%s: %.2f' % (classes[class_ind], score) + t_size = cv2.getTextSize(bbox_mess, 0, fontScale, thickness=bbox_thick // 2)[0] + c3 = (c1[0] + t_size[0], c1[1] - t_size[1] - 3) + cv2.rectangle(image, c1, (np.float32(c3[0]), np.float32(c3[1])), bbox_color, -1) #filled + + cv2.putText( + image, bbox_mess, (c1[0], np.float32(c1[1] - 2)), cv2.FONT_HERSHEY_SIMPLEX, fontScale, (0, 0, 0), + bbox_thick // 2, lineType=cv2.LINE_AA + ) + return image + + +def get_anchors(anchors_path, tiny=False): + anchors = np.array(anchors_path) + if tiny: + return anchors.reshape(2, 3, 2) + else: + return anchors.reshape(3, 3, 2) + + +def decode_train(conv_output, output_size, NUM_CLASS, STRIDES, ANCHORS, i=0, XYSCALE=[1, 1, 1]): + conv_output = tf.reshape(conv_output, (tf.shape(conv_output)[0], output_size, output_size, 3, 5 + NUM_CLASS)) + + conv_raw_dxdy, conv_raw_dwdh, conv_raw_conf, conv_raw_prob = tf.split(conv_output, (2, 2, 1, NUM_CLASS), axis=-1) + + xy_grid = tf.meshgrid(tf.range(output_size), tf.range(output_size)) + xy_grid = tf.expand_dims(tf.stack(xy_grid, axis=-1), axis=2) # [gx, gy, 1, 2] + xy_grid = tf.tile(tf.expand_dims(xy_grid, axis=0), [tf.shape(conv_output)[0], 1, 1, 3, 1]) + + xy_grid = tf.cast(xy_grid, tf.float32) + + pred_xy = ((tf.sigmoid(conv_raw_dxdy) * XYSCALE[i]) - 0.5 * (XYSCALE[i] - 1) + xy_grid) * \ + STRIDES[i] + pred_wh = (tf.exp(conv_raw_dwdh) * ANCHORS[i]) + pred_xywh = tf.concat([pred_xy, pred_wh], axis=-1) + + pred_conf = tf.sigmoid(conv_raw_conf) + pred_prob = tf.sigmoid(conv_raw_prob) + + return tf.concat([pred_xywh, pred_conf, pred_prob], axis=-1) + + +def yolo4_input_processing(original_image): + image_data = cv2.resize(original_image, (416, 416)) + image_data = image_data / 255. + images_data = [] + for i in range(1): + images_data.append(image_data) + images_data = np.asarray(images_data).astype(np.float32) + batch_data = tf.constant(images_data) + return batch_data + + +def yolo4_output_processing(feature_maps): + STRIDES = [8, 16, 32] + ANCHORS = get_anchors([12, 16, 19, 36, 40, 28, 36, 75, 76, 55, 72, 146, 142, 110, 192, 243, 459, 401]) + NUM_CLASS = 80 + XYSCALE = [1.2, 1.1, 1.05] + iou_threshold = 0.45 + score_threshold = 0.25 + + bbox_tensors = [] + prob_tensors = [] + score_thres = 0.2 + for i, fm in enumerate(feature_maps): + if i == 0: + output_tensors = decode(fm, 416 // 8, NUM_CLASS, STRIDES, ANCHORS, i, XYSCALE) + elif i == 1: + output_tensors = decode(fm, 416 // 16, NUM_CLASS, STRIDES, ANCHORS, i, XYSCALE) + else: + output_tensors = decode(fm, 416 // 32, NUM_CLASS, STRIDES, ANCHORS, i, XYSCALE) + bbox_tensors.append(output_tensors[0]) + prob_tensors.append(output_tensors[1]) + pred_bbox = tf.concat(bbox_tensors, axis=1) + pred_prob = tf.concat(prob_tensors, axis=1) + boxes, pred_conf = filter_boxes( + pred_bbox, pred_prob, score_threshold=score_thres, input_shape=tf.constant([416, 416]) + ) + pred = {'concat': tf.concat([boxes, pred_conf], axis=-1)} + + for key, value in pred.items(): + boxes = value[:, :, 0:4] + pred_conf = value[:, :, 4:] + + boxes, scores, classes, valid_detections = tf.image.combined_non_max_suppression( + boxes=tf.reshape(boxes, (tf.shape(boxes)[0], -1, 1, 4)), + scores=tf.reshape(pred_conf, (tf.shape(pred_conf)[0], -1, tf.shape(pred_conf)[-1])), + max_output_size_per_class=50, max_total_size=50, iou_threshold=iou_threshold, score_threshold=score_threshold + ) + output = [boxes.numpy(), scores.numpy(), classes.numpy(), valid_detections.numpy()] + return output + + +def result_to_json(image, pred_bbox): + image_h, image_w, _ = image.shape + out_boxes, out_scores, out_classes, num_boxes = pred_bbox + class_names = {} + json_result = [] + with open('model/coco.names', 'r') as data: + for ID, name in enumerate(data): + class_names[ID] = name.strip('\n') + nums_class = len(class_names) + + for i in range(num_boxes[0]): + if int(out_classes[0][i]) < 0 or int(out_classes[0][i]) > nums_class: continue + coor = out_boxes[0][i] + coor[0] = int(coor[0] * image_h) + coor[2] = int(coor[2] * image_h) + coor[1] = int(coor[1] * image_w) + coor[3] = int(coor[3] * image_w) + + score = float(out_scores[0][i]) + class_ind = int(out_classes[0][i]) + bbox = np.array([coor[1], coor[0], coor[3], coor[2]]).tolist() # [x1,y1,x2,y2] + json_result.append({'image': None, 'category_id': class_ind, 'bbox': bbox, 'score': score}) + + return json_result + + +def draw_boxes_and_labels_to_image_with_json(image, json_result, class_list, save_name=None): + """Draw bboxes and class labels on image. Return the image with bboxes. + + Parameters + ----------- + image : numpy.array + The RGB image [height, width, channel]. + json_result : list of dict + The object detection result with json format. + classes_list : list of str + For converting ID to string on image. + save_name : None or str + The name of image file (i.e. image.png), if None, not to save image. + + Returns + ------- + numpy.array + The saved image. + + References + ----------- + - OpenCV rectangle and putText. + - `scikit-image `__. + + """ + image_h, image_w, _ = image.shape + num_classes = len(class_list) + hsv_tuples = [(1.0 * x / num_classes, 1., 1.) for x in range(num_classes)] + colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples)) + colors = list(map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)), colors)) + random.seed(0) + random.shuffle(colors) + random.seed(None) + bbox_thick = int(0.6 * (image_h + image_w) / 600) + fontScale = 0.5 + + for bbox_info in json_result: + image_name = bbox_info['image'] + category_id = bbox_info['category_id'] + if category_id < 0 or category_id > num_classes: continue + bbox = bbox_info['bbox'] # the order of coordinates is [x1, y2, x2, y2] + score = bbox_info['score'] + + bbox_color = colors[category_id] + c1, c2 = (int(bbox[0]), int(bbox[1])), (int(bbox[2]), int(bbox[3])) + cv2.rectangle(image, c1, c2, bbox_color, bbox_thick) + + bbox_mess = '%s: %.2f' % (class_list[category_id], score) + t_size = cv2.getTextSize(bbox_mess, 0, fontScale, thickness=bbox_thick // 2)[0] + c3 = (c1[0] + t_size[0], c1[1] - t_size[1] - 3) + cv2.rectangle(image, c1, (np.float32(c3[0]), np.float32(c3[1])), bbox_color, -1) + + cv2.putText( + image, bbox_mess, (c1[0], np.float32(c1[1] - 2)), cv2.FONT_HERSHEY_SIMPLEX, fontScale, (0, 0, 0), + bbox_thick // 2, lineType=cv2.LINE_AA + ) + + if save_name is not None: + save_image(image, save_name) + + return image \ No newline at end of file diff --git a/examples/model_zoo/imagenet_classes.py b/examples/model_zoo/imagenet_classes.py new file mode 100644 index 0000000..d13cfda --- /dev/null +++ b/examples/model_zoo/imagenet_classes.py @@ -0,0 +1,1003 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +class_names = '''tench, Tinca tinca +goldfish, Carassius auratus +great white shark, white shark, man-eater, man-eating shark, Carcharodon carcharias +tiger shark, Galeocerdo cuvieri +hammerhead, hammerhead shark +electric ray, crampfish, numbfish, torpedo +stingray +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 +great grey owl, great gray owl, Strix nebulosa +European fire salamander, Salamandra salamandra +common newt, Triturus vulgaris +eft +spotted salamander, Ambystoma maculatum +axolotl, mud puppy, Ambystoma mexicanum +bullfrog, Rana catesbeiana +tree frog, tree-frog +tailed frog, bell toad, ribbed toad, tailed toad, Ascaphus trui +loggerhead, loggerhead turtle, Caretta caretta +leatherback turtle, leatherback, leathery turtle, Dermochelys coriacea +mud turtle +terrapin +box turtle, box tortoise +banded gecko +common iguana, iguana, Iguana iguana +American chameleon, anole, Anolis carolinensis +whiptail, whiptail lizard +agama +frilled lizard, Chlamydosaurus kingi +alligator lizard +Gila monster, Heloderma suspectum +green lizard, Lacerta viridis +African chameleon, Chamaeleo chamaeleon +Komodo dragon, Komodo lizard, dragon lizard, giant lizard, Varanus komodoensis +African crocodile, Nile crocodile, Crocodylus niloticus +American alligator, Alligator mississipiensis +triceratops +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 +trilobite +harvestman, daddy longlegs, Phalangium opilio +scorpion +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 +centipede +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 +tusker +echidna, spiny anteater, anteater +platypus, duckbill, duckbilled platypus, duck-billed platypus, Ornithorhynchus anatinus +wallaby, brush kangaroo +koala, koala bear, kangaroo bear, native bear, Phascolarctos cinereus +wombat +jellyfish +sea anemone, anemone +brain coral +flatworm, platyhelminth +nematode, nematode worm, roundworm +conch +snail +slug +sea slug, nudibranch +chiton, coat-of-mail shell, sea cradle, polyplacophore +chambered nautilus, pearly nautilus, nautilus +Dungeness crab, Cancer magister +rock crab, Cancer irroratus +fiddler crab +king crab, Alaska crab, Alaskan king crab, Alaska king crab, Paralithodes camtschatica +American lobster, Northern lobster, Maine lobster, Homarus americanus +spiny lobster, langouste, rock lobster, crawfish, crayfish, sea crawfish +crayfish, crawfish, crawdad, crawdaddy +hermit crab +isopod +white stork, Ciconia ciconia +black stork, Ciconia nigra +spoonbill +flamingo +little blue heron, Egretta caerulea +American egret, great white heron, Egretta albus +bittern +crane +limpkin, Aramus pictus +European gallinule, Porphyrio porphyrio +American coot, marsh hen, mud hen, water hen, Fulica americana +bustard +ruddy turnstone, Arenaria interpres +red-backed sandpiper, dunlin, Erolia alpina +redshank, Tringa totanus +dowitcher +oystercatcher, oyster catcher +pelican +king penguin, Aptenodytes patagonica +albatross, mollymawk +grey whale, gray whale, devilfish, Eschrichtius gibbosus, Eschrichtius robustus +killer whale, killer, orca, grampus, sea wolf, Orcinus orca +dugong, Dugong dugon +sea lion +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 +timber wolf, grey wolf, gray wolf, Canis lupus +white wolf, Arctic wolf, Canis lupus tundrarum +red wolf, maned wolf, Canis rufus, Canis niger +coyote, prairie wolf, brush wolf, Canis latrans +dingo, warrigal, warragal, Canis dingo +dhole, Cuon alpinus +African hunting dog, hyena dog, Cape hunting dog, Lycaon pictus +hyena, hyaena +red fox, Vulpes vulpes +kit fox, Vulpes macrotis +Arctic fox, white fox, Alopex lagopus +grey fox, gray fox, Urocyon cinereoargenteus +tabby, tabby cat +tiger cat +Persian cat +Siamese cat, Siamese +Egyptian cat +cougar, puma, catamount, mountain lion, painter, panther, Felis concolor +lynx, catamount +leopard, Panthera pardus +snow leopard, ounce, Panthera uncia +jaguar, panther, Panthera onca, Felis onca +lion, king of beasts, Panthera leo +tiger, Panthera tigris +cheetah, chetah, Acinonyx jubatus +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 +mongoose +meerkat, mierkat +tiger beetle +ladybug, ladybeetle, lady beetle, ladybird, ladybird beetle +ground beetle, carabid beetle +long-horned beetle, longicorn, longicorn beetle +leaf beetle, chrysomelid +dung beetle +rhinoceros beetle +weevil +fly +bee +ant, emmet, pismire +grasshopper, hopper +cricket +walking stick, walkingstick, stick insect +cockroach, roach +mantis, mantid +cicada, cicala +leafhopper +lacewing, lacewing fly +dragonfly, darning needle, devil's darning needle, sewing needle, snake feeder, snake doctor, mosquito hawk, skeeter hawk +damselfly +admiral +ringlet, ringlet butterfly +monarch, monarch butterfly, milkweed butterfly, Danaus plexippus +cabbage butterfly +sulphur butterfly, sulfur butterfly +lycaenid, lycaenid butterfly +starfish, sea star +sea urchin +sea cucumber, holothurian +wood rabbit, cottontail, cottontail rabbit +hare +Angora, Angora rabbit +hamster +porcupine, hedgehog +fox squirrel, eastern fox squirrel, Sciurus niger +marmot +beaver +guinea pig, Cavia cobaya +sorrel +zebra +hog, pig, grunter, squealer, Sus scrofa +wild boar, boar, Sus scrofa +warthog +hippopotamus, hippo, river horse, Hippopotamus amphibius +ox +water buffalo, water ox, Asiatic buffalo, Bubalus bubalis +bison +ram, tup +bighorn, bighorn sheep, cimarron, Rocky Mountain bighorn, Rocky Mountain sheep, Ovis canadensis +ibex, Capra ibex +hartebeest +impala, Aepyceros melampus +gazelle +Arabian camel, dromedary, Camelus dromedarius +llama +weasel +mink +polecat, fitch, foulmart, foumart, Mustela putorius +black-footed ferret, ferret, Mustela nigripes +otter +skunk, polecat, wood pussy +badger +armadillo +three-toed sloth, ai, Bradypus tridactylus +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 +Indian elephant, Elephas maximus +African elephant, Loxodonta africana +lesser panda, red panda, panda, bear cat, cat bear, Ailurus fulgens +giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca +barracouta, snoek +eel +coho, cohoe, coho salmon, blue jack, silver salmon, Oncorhynchus kisutch +rock beauty, Holocanthus tricolor +anemone fish +sturgeon +gar, garfish, garpike, billfish, Lepisosteus osseus +lionfish +puffer, pufferfish, blowfish, globefish +abacus +abaya +academic gown, academic robe, judge's robe +accordion, piano accordion, squeeze box +acoustic guitar +aircraft carrier, carrier, flattop, attack aircraft carrier +airliner +airship, dirigible +altar +ambulance +amphibian, amphibious vehicle +analog clock +apiary, bee house +apron +ashcan, trash can, garbage can, wastebin, ash bin, ash-bin, ashbin, dustbin, trash barrel, trash bin +assault rifle, assault gun +backpack, back pack, knapsack, packsack, rucksack, haversack +bakery, bakeshop, bakehouse +balance beam, beam +balloon +ballpoint, ballpoint pen, ballpen, Biro +Band Aid +banjo +bannister, banister, balustrade, balusters, handrail +barbell +barber chair +barbershop +barn +barometer +barrel, cask +barrow, garden cart, lawn cart, wheelbarrow +baseball +basketball +bassinet +bassoon +bathing cap, swimming cap +bath towel +bathtub, bathing tub, bath, tub +beach wagon, station wagon, wagon, estate car, beach waggon, station waggon, waggon +beacon, lighthouse, beacon light, pharos +beaker +bearskin, busby, shako +beer bottle +beer glass +bell cote, bell cot +bib +bicycle-built-for-two, tandem bicycle, tandem +bikini, two-piece +binder, ring-binder +binoculars, field glasses, opera glasses +birdhouse +boathouse +bobsled, bobsleigh, bob +bolo tie, bolo, bola tie, bola +bonnet, poke bonnet +bookcase +bookshop, bookstore, bookstall +bottlecap +bow +bow tie, bow-tie, bowtie +brass, memorial tablet, plaque +brassiere, bra, bandeau +breakwater, groin, groyne, mole, bulwark, seawall, jetty +breastplate, aegis, egis +broom +bucket, pail +buckle +bulletproof vest +bullet train, bullet +butcher shop, meat market +cab, hack, taxi, taxicab +caldron, cauldron +candle, taper, wax light +cannon +canoe +can opener, tin opener +cardigan +car mirror +carousel, carrousel, merry-go-round, roundabout, whirligig +carpenter's kit, tool kit +carton +car wheel +cash machine, cash dispenser, automated teller machine, automatic teller machine, automated teller, automatic teller, ATM +cassette +cassette player +castle +catamaran +CD player +cello, violoncello +cellular telephone, cellular phone, cellphone, cell, mobile phone +chain +chainlink fence +chain mail, ring mail, mail, chain armor, chain armour, ring armor, ring armour +chain saw, chainsaw +chest +chiffonier, commode +chime, bell, gong +china cabinet, china closet +Christmas stocking +church, church building +cinema, movie theater, movie theatre, movie house, picture palace +cleaver, meat cleaver, chopper +cliff dwelling +cloak +clog, geta, patten, sabot +cocktail shaker +coffee mug +coffeepot +coil, spiral, volute, whorl, helix +combination lock +computer keyboard, keypad +confectionery, confectionary, candy store +container ship, containership, container vessel +convertible +corkscrew, bottle screw +cornet, horn, trumpet, trump +cowboy boot +cowboy hat, ten-gallon hat +cradle +crane +crash helmet +crate +crib, cot +Crock Pot +croquet ball +crutch +cuirass +dam, dike, dyke +desk +desktop computer +dial telephone, dial phone +diaper, nappy, napkin +digital clock +digital watch +dining table, board +dishrag, dishcloth +dishwasher, dish washer, dishwashing machine +disk brake, disc brake +dock, dockage, docking facility +dogsled, dog sled, dog sleigh +dome +doormat, welcome mat +drilling platform, offshore rig +drum, membranophone, tympan +drumstick +dumbbell +Dutch oven +electric fan, blower +electric guitar +electric locomotive +entertainment center +envelope +espresso maker +face powder +feather boa, boa +file, file cabinet, filing cabinet +fireboat +fire engine, fire truck +fire screen, fireguard +flagpole, flagstaff +flute, transverse flute +folding chair +football helmet +forklift +fountain +fountain pen +four-poster +freight car +French horn, horn +frying pan, frypan, skillet +fur coat +garbage truck, dustcart +gasmask, respirator, gas helmet +gas pump, gasoline pump, petrol pump, island dispenser +goblet +go-kart +golf ball +golfcart, golf cart +gondola +gong, tam-tam +gown +grand piano, grand +greenhouse, nursery, glasshouse +grille, radiator grille +grocery store, grocery, food market, market +guillotine +hair slide +hair spray +half track +hammer +hamper +hand blower, blow dryer, blow drier, hair dryer, hair drier +hand-held computer, hand-held microcomputer +handkerchief, hankie, hanky, hankey +hard disc, hard disk, fixed disk +harmonica, mouth organ, harp, mouth harp +harp +harvester, reaper +hatchet +holster +home theater, home theatre +honeycomb +hook, claw +hoopskirt, crinoline +horizontal bar, high bar +horse cart, horse-cart +hourglass +iPod +iron, smoothing iron +jack-o'-lantern +jean, blue jean, denim +jeep, landrover +jersey, T-shirt, tee shirt +jigsaw puzzle +jinrikisha, ricksha, rickshaw +joystick +kimono +knee pad +knot +lab coat, laboratory coat +ladle +lampshade, lamp shade +laptop, laptop computer +lawn mower, mower +lens cap, lens cover +letter opener, paper knife, paperknife +library +lifeboat +lighter, light, igniter, ignitor +limousine, limo +liner, ocean liner +lipstick, lip rouge +Loafer +lotion +loudspeaker, speaker, speaker unit, loudspeaker system, speaker system +loupe, jeweler's loupe +lumbermill, sawmill +magnetic compass +mailbag, postbag +mailbox, letter box +maillot +maillot, tank suit +manhole cover +maraca +marimba, xylophone +mask +matchstick +maypole +maze, labyrinth +measuring cup +medicine chest, medicine cabinet +megalith, megalithic structure +microphone, mike +microwave, microwave oven +military uniform +milk can +minibus +miniskirt, mini +minivan +missile +mitten +mixing bowl +mobile home, manufactured home +Model T +modem +monastery +monitor +moped +mortar +mortarboard +mosque +mosquito net +motor scooter, scooter +mountain bike, all-terrain bike, off-roader +mountain tent +mouse, computer mouse +mousetrap +moving van +muzzle +nail +neck brace +necklace +nipple +notebook, notebook computer +obelisk +oboe, hautboy, hautbois +ocarina, sweet potato +odometer, hodometer, mileometer, milometer +oil filter +organ, pipe organ +oscilloscope, scope, cathode-ray oscilloscope, CRO +overskirt +oxcart +oxygen mask +packet +paddle, boat paddle +paddlewheel, paddle wheel +padlock +paintbrush +pajama, pyjama, pj's, jammies +palace +panpipe, pandean pipe, syrinx +paper towel +parachute, chute +parallel bars, bars +park bench +parking meter +passenger car, coach, carriage +patio, terrace +pay-phone, pay-station +pedestal, plinth, footstall +pencil box, pencil case +pencil sharpener +perfume, essence +Petri dish +photocopier +pick, plectrum, plectron +pickelhaube +picket fence, paling +pickup, pickup truck +pier +piggy bank, penny bank +pill bottle +pillow +ping-pong ball +pinwheel +pirate, pirate ship +pitcher, ewer +plane, carpenter's plane, woodworking plane +planetarium +plastic bag +plate rack +plow, plough +plunger, plumber's helper +Polaroid camera, Polaroid Land camera +pole +police van, police wagon, paddy wagon, patrol wagon, wagon, black Maria +poncho +pool table, billiard table, snooker table +pop bottle, soda bottle +pot, flowerpot +potter's wheel +power drill +prayer rug, prayer mat +printer +prison, prison house +projectile, missile +projector +puck, hockey puck +punching bag, punch bag, punching ball, punchball +purse +quill, quill pen +quilt, comforter, comfort, puff +racer, race car, racing car +racket, racquet +radiator +radio, wireless +radio telescope, radio reflector +rain barrel +recreational vehicle, RV, R.V. +reel +reflex camera +refrigerator, icebox +remote control, remote +restaurant, eating house, eating place, eatery +revolver, six-gun, six-shooter +rifle +rocking chair, rocker +rotisserie +rubber eraser, rubber, pencil eraser +rugby ball +rule, ruler +running shoe +safe +safety pin +saltshaker, salt shaker +sandal +sarong +sax, saxophone +scabbard +scale, weighing machine +school bus +schooner +scoreboard +screen, CRT screen +screw +screwdriver +seat belt, seatbelt +sewing machine +shield, buckler +shoe shop, shoe-shop, shoe store +shoji +shopping basket +shopping cart +shovel +shower cap +shower curtain +ski +ski mask +sleeping bag +slide rule, slipstick +sliding door +slot, one-armed bandit +snorkel +snowmobile +snowplow, snowplough +soap dispenser +soccer ball +sock +solar dish, solar collector, solar furnace +sombrero +soup bowl +space bar +space heater +space shuttle +spatula +speedboat +spider web, spider's web +spindle +sports car, sport car +spotlight, spot +stage +steam locomotive +steel arch bridge +steel drum +stethoscope +stole +stone wall +stopwatch, stop watch +stove +strainer +streetcar, tram, tramcar, trolley, trolley car +stretcher +studio couch, day bed +stupa, tope +submarine, pigboat, sub, U-boat +suit, suit of clothes +sundial +sunglass +sunglasses, dark glasses, shades +sunscreen, sunblock, sun blocker +suspension bridge +swab, swob, mop +sweatshirt +swimming trunks, bathing trunks +swing +switch, electric switch, electrical switch +syringe +table lamp +tank, army tank, armored combat vehicle, armoured combat vehicle +tape player +teapot +teddy, teddy bear +television, television system +tennis ball +thatch, thatched roof +theater curtain, theatre curtain +thimble +thresher, thrasher, threshing machine +throne +tile roof +toaster +tobacco shop, tobacconist shop, tobacconist +toilet seat +torch +totem pole +tow truck, tow car, wrecker +toyshop +tractor +trailer truck, tractor trailer, trucking rig, rig, articulated lorry, semi +tray +trench coat +tricycle, trike, velocipede +trimaran +tripod +triumphal arch +trolleybus, trolley coach, trackless trolley +trombone +tub, vat +turnstile +typewriter keyboard +umbrella +unicycle, monocycle +upright, upright piano +vacuum, vacuum cleaner +vase +vault +velvet +vending machine +vestment +viaduct +violin, fiddle +volleyball +waffle iron +wall clock +wallet, billfold, notecase, pocketbook +wardrobe, closet, press +warplane, military plane +washbasin, handbasin, washbowl, lavabo, wash-hand basin +washer, automatic washer, washing machine +water bottle +water jug +water tower +whiskey jug +whistle +wig +window screen +window shade +Windsor tie +wine bottle +wing +wok +wooden spoon +wool, woolen, woollen +worm fence, snake fence, snake-rail fence, Virginia fence +wreck +yawl +yurt +web site, website, internet site, site +comic book +crossword puzzle, crossword +street sign +traffic light, traffic signal, stoplight +book jacket, dust cover, dust jacket, dust wrapper +menu +plate +guacamole +consomme +hot pot, hotpot +trifle +ice cream, icecream +ice lolly, lolly, lollipop, popsicle +French loaf +bagel, beigel +pretzel +cheeseburger +hotdog, hot dog, red hot +mashed potato +head cabbage +broccoli +cauliflower +zucchini, courgette +spaghetti squash +acorn squash +butternut squash +cucumber, cuke +artichoke, globe artichoke +bell pepper +cardoon +mushroom +Granny Smith +strawberry +orange +lemon +fig +pineapple, ananas +banana +jackfruit, jak, jack +custard apple +pomegranate +hay +carbonara +chocolate sauce, chocolate syrup +dough +meat loaf, meatloaf +pizza, pizza pie +potpie +burrito +red wine +espresso +cup +eggnog +alp +bubble +cliff, drop, drop-off +coral reef +geyser +lakeside, lakeshore +promontory, headland, head, foreland +sandbar, sand bar +seashore, coast, seacoast, sea-coast +valley, vale +volcano +ballplayer, baseball player +groom, bridegroom +scuba diver +rapeseed +daisy +yellow lady's slipper, yellow lady-slipper, Cypripedium calceolus, Cypripedium parviflorum +corn +acorn +hip, rose hip, rosehip +buckeye, horse chestnut, conker +coral fungus +agaric +gyromitra +stinkhorn, carrion fungus +earthstar +hen-of-the-woods, hen of the woods, Polyporus frondosus, Grifola frondosa +bolete +ear, spike, capitulum +toilet tissue, toilet paper, bathroom tissue'''.split("\n") diff --git a/examples/model_zoo/model/coco.names b/examples/model_zoo/model/coco.names new file mode 100644 index 0000000..ec82f0f --- /dev/null +++ b/examples/model_zoo/model/coco.names @@ -0,0 +1,80 @@ +person +bicycle +car +motorbike +aeroplane +bus +train +truck +boat +traffic light +fire hydrant +stop sign +parking meter +bench +bird +cat +dog +horse +sheep +cow +elephant +bear +zebra +giraffe +backpack +umbrella +handbag +tie +suitcase +frisbee +skis +snowboard +sports ball +kite +baseball bat +baseball glove +skateboard +surfboard +tennis racket +bottle +wine glass +cup +fork +knife +spoon +bowl +banana +apple +sandwich +orange +broccoli +carrot +hot dog +pizza +donut +cake +chair +sofa +potted plant +bed +dining table +toilet +tvmonitor +laptop +mouse +remote +keyboard +cell phone +microwave +oven +toaster +sink +refrigerator +book +clock +vase +scissors +teddy bear +hair drier +toothbrush diff --git a/examples/model_zoo/model/weights_2.txt b/examples/model_zoo/model/weights_2.txt new file mode 100644 index 0000000..42cc499 --- /dev/null +++ b/examples/model_zoo/model/weights_2.txt @@ -0,0 +1,541 @@ +conv2d_1/filters:0 +batchnorm2d_1/beta:0 +batchnorm2d_1/gamma:0 +batchnorm2d_1/moving_mean:0 +batchnorm2d_1/moving_var:0 +conv2d_2/filters:0 +batchnorm2d_2/beta:0 +batchnorm2d_2/gamma:0 +batchnorm2d_2/moving_mean:0 +batchnorm2d_2/moving_var:0 +conv_rote_block_1/filters:0 +conv2d_3/filters:0 +batchnorm2d_3/beta:0 +batchnorm2d_3/gamma:0 +batchnorm2d_3/moving_mean:0 +batchnorm2d_3/moving_var:0 +batchnorm2d_4/beta:0 +batchnorm2d_4/gamma:0 +batchnorm2d_4/moving_mean:0 +batchnorm2d_4/moving_var:0 +conv2d_4/filters:0 +batchnorm2d_5/beta:0 +batchnorm2d_5/gamma:0 +batchnorm2d_5/moving_mean:0 +batchnorm2d_5/moving_var:0 +conv2d_5/filters:0 +batchnorm2d_6/beta:0 +batchnorm2d_6/gamma:0 +batchnorm2d_6/moving_mean:0 +batchnorm2d_6/moving_var:0 +conv2d_6/filters:0 +batchnorm2d_7/beta:0 +batchnorm2d_7/gamma:0 +batchnorm2d_7/moving_mean:0 +batchnorm2d_7/moving_var:0 +conv2d_7/filters:0 +batchnorm2d_8/beta:0 +batchnorm2d_8/gamma:0 +batchnorm2d_8/moving_mean:0 +batchnorm2d_8/moving_var:0 +conv2d_8/filters:0 +batchnorm2d_9/beta:0 +batchnorm2d_9/gamma:0 +batchnorm2d_9/moving_mean:0 +batchnorm2d_9/moving_var:0 +conv_rote_block_2/filters:0 +conv2d_9/filters:0 +batchnorm2d_10/beta:0 +batchnorm2d_10/gamma:0 +batchnorm2d_10/moving_mean:0 +batchnorm2d_10/moving_var:0 +batchnorm2d_11/beta:0 +batchnorm2d_11/gamma:0 +batchnorm2d_11/moving_mean:0 +batchnorm2d_11/moving_var:0 +conv2d_10/filters:0 +batchnorm2d_12/beta:0 +batchnorm2d_12/gamma:0 +batchnorm2d_12/moving_mean:0 +batchnorm2d_12/moving_var:0 +conv2d_11/filters:0 +batchnorm2d_13/beta:0 +batchnorm2d_13/gamma:0 +batchnorm2d_13/moving_mean:0 +batchnorm2d_13/moving_var:0 +conv2d_12/filters:0 +batchnorm2d_14/beta:0 +batchnorm2d_14/gamma:0 +batchnorm2d_14/moving_mean:0 +batchnorm2d_14/moving_var:0 +conv2d_13/filters:0 +batchnorm2d_15/beta:0 +batchnorm2d_15/gamma:0 +batchnorm2d_15/moving_mean:0 +batchnorm2d_15/moving_var:0 +conv2d_14/filters:0 +batchnorm2d_16/beta:0 +batchnorm2d_16/gamma:0 +batchnorm2d_16/moving_mean:0 +batchnorm2d_16/moving_var:0 +conv2d_15/filters:0 +batchnorm2d_17/beta:0 +batchnorm2d_17/gamma:0 +batchnorm2d_17/moving_mean:0 +batchnorm2d_17/moving_var:0 +conv2d_16/filters:0 +batchnorm2d_18/beta:0 +batchnorm2d_18/gamma:0 +batchnorm2d_18/moving_mean:0 +batchnorm2d_18/moving_var:0 +conv_rote_block_3/filters:0 +conv2d_17/filters:0 +batchnorm2d_19/beta:0 +batchnorm2d_19/gamma:0 +batchnorm2d_19/moving_mean:0 +batchnorm2d_19/moving_var:0 +batchnorm2d_20/beta:0 +batchnorm2d_20/gamma:0 +batchnorm2d_20/moving_mean:0 +batchnorm2d_20/moving_var:0 +conv2d_18/filters:0 +batchnorm2d_21/beta:0 +batchnorm2d_21/gamma:0 +batchnorm2d_21/moving_mean:0 +batchnorm2d_21/moving_var:0 +conv2d_19/filters:0 +batchnorm2d_22/beta:0 +batchnorm2d_22/gamma:0 +batchnorm2d_22/moving_mean:0 +batchnorm2d_22/moving_var:0 +conv2d_20/filters:0 +batchnorm2d_23/beta:0 +batchnorm2d_23/gamma:0 +batchnorm2d_23/moving_mean:0 +batchnorm2d_23/moving_var:0 +conv2d_21/filters:0 +batchnorm2d_24/beta:0 +batchnorm2d_24/gamma:0 +batchnorm2d_24/moving_mean:0 +batchnorm2d_24/moving_var:0 +conv2d_22/filters:0 +batchnorm2d_25/beta:0 +batchnorm2d_25/gamma:0 +batchnorm2d_25/moving_mean:0 +batchnorm2d_25/moving_var:0 +conv2d_23/filters:0 +batchnorm2d_26/beta:0 +batchnorm2d_26/gamma:0 +batchnorm2d_26/moving_mean:0 +batchnorm2d_26/moving_var:0 +conv2d_24/filters:0 +batchnorm2d_27/beta:0 +batchnorm2d_27/gamma:0 +batchnorm2d_27/moving_mean:0 +batchnorm2d_27/moving_var:0 +conv2d_25/filters:0 +batchnorm2d_28/beta:0 +batchnorm2d_28/gamma:0 +batchnorm2d_28/moving_mean:0 +batchnorm2d_28/moving_var:0 +conv2d_26/filters:0 +batchnorm2d_29/beta:0 +batchnorm2d_29/gamma:0 +batchnorm2d_29/moving_mean:0 +batchnorm2d_29/moving_var:0 +conv2d_27/filters:0 +batchnorm2d_30/beta:0 +batchnorm2d_30/gamma:0 +batchnorm2d_30/moving_mean:0 +batchnorm2d_30/moving_var:0 +conv2d_28/filters:0 +batchnorm2d_31/beta:0 +batchnorm2d_31/gamma:0 +batchnorm2d_31/moving_mean:0 +batchnorm2d_31/moving_var:0 +conv2d_29/filters:0 +batchnorm2d_32/beta:0 +batchnorm2d_32/gamma:0 +batchnorm2d_32/moving_mean:0 +batchnorm2d_32/moving_var:0 +conv2d_30/filters:0 +batchnorm2d_33/beta:0 +batchnorm2d_33/gamma:0 +batchnorm2d_33/moving_mean:0 +batchnorm2d_33/moving_var:0 +conv2d_31/filters:0 +batchnorm2d_34/beta:0 +batchnorm2d_34/gamma:0 +batchnorm2d_34/moving_mean:0 +batchnorm2d_34/moving_var:0 +conv2d_32/filters:0 +batchnorm2d_35/beta:0 +batchnorm2d_35/gamma:0 +batchnorm2d_35/moving_mean:0 +batchnorm2d_35/moving_var:0 +conv2d_33/filters:0 +batchnorm2d_36/beta:0 +batchnorm2d_36/gamma:0 +batchnorm2d_36/moving_mean:0 +batchnorm2d_36/moving_var:0 +conv2d_34/filters:0 +batchnorm2d_37/beta:0 +batchnorm2d_37/gamma:0 +batchnorm2d_37/moving_mean:0 +batchnorm2d_37/moving_var:0 +conv2d_35/filters:0 +batchnorm2d_38/beta:0 +batchnorm2d_38/gamma:0 +batchnorm2d_38/moving_mean:0 +batchnorm2d_38/moving_var:0 +conv_yolo_2/filters:0 +batchnorm2d_87/beta:0 +batchnorm2d_87/gamma:0 +batchnorm2d_87/moving_mean:0 +batchnorm2d_87/moving_var:0 +conv2d_36/filters:0 +batchnorm2d_39/beta:0 +batchnorm2d_39/gamma:0 +batchnorm2d_39/moving_mean:0 +batchnorm2d_39/moving_var:0 +conv_rote_block_4/filters:0 +conv2d_37/filters:0 +batchnorm2d_40/beta:0 +batchnorm2d_40/gamma:0 +batchnorm2d_40/moving_mean:0 +batchnorm2d_40/moving_var:0 +batchnorm2d_41/beta:0 +batchnorm2d_41/gamma:0 +batchnorm2d_41/moving_mean:0 +batchnorm2d_41/moving_var:0 +conv2d_38/filters:0 +batchnorm2d_42/beta:0 +batchnorm2d_42/gamma:0 +batchnorm2d_42/moving_mean:0 +batchnorm2d_42/moving_var:0 +conv2d_39/filters:0 +batchnorm2d_43/beta:0 +batchnorm2d_43/gamma:0 +batchnorm2d_43/moving_mean:0 +batchnorm2d_43/moving_var:0 +conv2d_40/filters:0 +batchnorm2d_44/beta:0 +batchnorm2d_44/gamma:0 +batchnorm2d_44/moving_mean:0 +batchnorm2d_44/moving_var:0 +conv2d_41/filters:0 +batchnorm2d_45/beta:0 +batchnorm2d_45/gamma:0 +batchnorm2d_45/moving_mean:0 +batchnorm2d_45/moving_var:0 +conv2d_42/filters:0 +batchnorm2d_46/beta:0 +batchnorm2d_46/gamma:0 +batchnorm2d_46/moving_mean:0 +batchnorm2d_46/moving_var:0 +conv2d_43/filters:0 +batchnorm2d_47/beta:0 +batchnorm2d_47/gamma:0 +batchnorm2d_47/moving_mean:0 +batchnorm2d_47/moving_var:0 +conv2d_44/filters:0 +batchnorm2d_48/beta:0 +batchnorm2d_48/gamma:0 +batchnorm2d_48/moving_mean:0 +batchnorm2d_48/moving_var:0 +conv2d_45/filters:0 +batchnorm2d_49/beta:0 +batchnorm2d_49/gamma:0 +batchnorm2d_49/moving_mean:0 +batchnorm2d_49/moving_var:0 +conv2d_46/filters:0 +batchnorm2d_50/beta:0 +batchnorm2d_50/gamma:0 +batchnorm2d_50/moving_mean:0 +batchnorm2d_50/moving_var:0 +conv2d_47/filters:0 +batchnorm2d_51/beta:0 +batchnorm2d_51/gamma:0 +batchnorm2d_51/moving_mean:0 +batchnorm2d_51/moving_var:0 +conv2d_48/filters:0 +batchnorm2d_52/beta:0 +batchnorm2d_52/gamma:0 +batchnorm2d_52/moving_mean:0 +batchnorm2d_52/moving_var:0 +conv2d_49/filters:0 +batchnorm2d_53/beta:0 +batchnorm2d_53/gamma:0 +batchnorm2d_53/moving_mean:0 +batchnorm2d_53/moving_var:0 +conv2d_50/filters:0 +batchnorm2d_54/beta:0 +batchnorm2d_54/gamma:0 +batchnorm2d_54/moving_mean:0 +batchnorm2d_54/moving_var:0 +conv2d_51/filters:0 +batchnorm2d_55/beta:0 +batchnorm2d_55/gamma:0 +batchnorm2d_55/moving_mean:0 +batchnorm2d_55/moving_var:0 +conv2d_52/filters:0 +batchnorm2d_56/beta:0 +batchnorm2d_56/gamma:0 +batchnorm2d_56/moving_mean:0 +batchnorm2d_56/moving_var:0 +conv2d_53/filters:0 +batchnorm2d_57/beta:0 +batchnorm2d_57/gamma:0 +batchnorm2d_57/moving_mean:0 +batchnorm2d_57/moving_var:0 +conv2d_54/filters:0 +batchnorm2d_58/beta:0 +batchnorm2d_58/gamma:0 +batchnorm2d_58/moving_mean:0 +batchnorm2d_58/moving_var:0 +conv2d_55/filters:0 +batchnorm2d_59/beta:0 +batchnorm2d_59/gamma:0 +batchnorm2d_59/moving_mean:0 +batchnorm2d_59/moving_var:0 +conv_yolo_1/filters:0 +batchnorm2d_80/beta:0 +batchnorm2d_80/gamma:0 +batchnorm2d_80/moving_mean:0 +batchnorm2d_80/moving_var:0 +conv2d_56/filters:0 +batchnorm2d_60/beta:0 +batchnorm2d_60/gamma:0 +batchnorm2d_60/moving_mean:0 +batchnorm2d_60/moving_var:0 +conv_rote_block_5/filters:0 +conv2d_57/filters:0 +batchnorm2d_61/beta:0 +batchnorm2d_61/gamma:0 +batchnorm2d_61/moving_mean:0 +batchnorm2d_61/moving_var:0 +batchnorm2d_62/beta:0 +batchnorm2d_62/gamma:0 +batchnorm2d_62/moving_mean:0 +batchnorm2d_62/moving_var:0 +conv2d_58/filters:0 +batchnorm2d_63/beta:0 +batchnorm2d_63/gamma:0 +batchnorm2d_63/moving_mean:0 +batchnorm2d_63/moving_var:0 +conv2d_59/filters:0 +batchnorm2d_64/beta:0 +batchnorm2d_64/gamma:0 +batchnorm2d_64/moving_mean:0 +batchnorm2d_64/moving_var:0 +conv2d_60/filters:0 +batchnorm2d_65/beta:0 +batchnorm2d_65/gamma:0 +batchnorm2d_65/moving_mean:0 +batchnorm2d_65/moving_var:0 +conv2d_61/filters:0 +batchnorm2d_66/beta:0 +batchnorm2d_66/gamma:0 +batchnorm2d_66/moving_mean:0 +batchnorm2d_66/moving_var:0 +conv2d_62/filters:0 +batchnorm2d_67/beta:0 +batchnorm2d_67/gamma:0 +batchnorm2d_67/moving_mean:0 +batchnorm2d_67/moving_var:0 +conv2d_63/filters:0 +batchnorm2d_68/beta:0 +batchnorm2d_68/gamma:0 +batchnorm2d_68/moving_mean:0 +batchnorm2d_68/moving_var:0 +conv2d_64/filters:0 +batchnorm2d_69/beta:0 +batchnorm2d_69/gamma:0 +batchnorm2d_69/moving_mean:0 +batchnorm2d_69/moving_var:0 +conv2d_65/filters:0 +batchnorm2d_70/beta:0 +batchnorm2d_70/gamma:0 +batchnorm2d_70/moving_mean:0 +batchnorm2d_70/moving_var:0 +conv2d_66/filters:0 +batchnorm2d_71/beta:0 +batchnorm2d_71/gamma:0 +batchnorm2d_71/moving_mean:0 +batchnorm2d_71/moving_var:0 +conv2d_67/filters:0 +batchnorm2d_72/beta:0 +batchnorm2d_72/gamma:0 +batchnorm2d_72/moving_mean:0 +batchnorm2d_72/moving_var:0 +conv2d_68/filters:0 +batchnorm2d_73/beta:0 +batchnorm2d_73/gamma:0 +batchnorm2d_73/moving_mean:0 +batchnorm2d_73/moving_var:0 +conv2d_69/filters:0 +batchnorm2d_74/beta:0 +batchnorm2d_74/gamma:0 +batchnorm2d_74/moving_mean:0 +batchnorm2d_74/moving_var:0 +conv2d_70/filters:0 +batchnorm2d_75/beta:0 +batchnorm2d_75/gamma:0 +batchnorm2d_75/moving_mean:0 +batchnorm2d_75/moving_var:0 +conv2d_71/filters:0 +batchnorm2d_76/beta:0 +batchnorm2d_76/gamma:0 +batchnorm2d_76/moving_mean:0 +batchnorm2d_76/moving_var:0 +conv2d_72/filters:0 +batchnorm2d_77/beta:0 +batchnorm2d_77/gamma:0 +batchnorm2d_77/moving_mean:0 +batchnorm2d_77/moving_var:0 +conv2d_73/filters:0 +batchnorm2d_78/beta:0 +batchnorm2d_78/gamma:0 +batchnorm2d_78/moving_mean:0 +batchnorm2d_78/moving_var:0 +conv2d_74/filters:0 +batchnorm2d_79/beta:0 +batchnorm2d_79/gamma:0 +batchnorm2d_79/moving_mean:0 +batchnorm2d_79/moving_var:0 +conv2d_75/filters:0 +batchnorm2d_81/beta:0 +batchnorm2d_81/gamma:0 +batchnorm2d_81/moving_mean:0 +batchnorm2d_81/moving_var:0 +conv2d_76/filters:0 +batchnorm2d_82/beta:0 +batchnorm2d_82/gamma:0 +batchnorm2d_82/moving_mean:0 +batchnorm2d_82/moving_var:0 +conv2d_77/filters:0 +batchnorm2d_83/beta:0 +batchnorm2d_83/gamma:0 +batchnorm2d_83/moving_mean:0 +batchnorm2d_83/moving_var:0 +conv2d_78/filters:0 +batchnorm2d_84/beta:0 +batchnorm2d_84/gamma:0 +batchnorm2d_84/moving_mean:0 +batchnorm2d_84/moving_var:0 +conv2d_79/filters:0 +batchnorm2d_85/beta:0 +batchnorm2d_85/gamma:0 +batchnorm2d_85/moving_mean:0 +batchnorm2d_85/moving_var:0 +conv2d_80/filters:0 +batchnorm2d_86/beta:0 +batchnorm2d_86/gamma:0 +batchnorm2d_86/moving_mean:0 +batchnorm2d_86/moving_var:0 +conv2d_81/filters:0 +batchnorm2d_88/beta:0 +batchnorm2d_88/gamma:0 +batchnorm2d_88/moving_mean:0 +batchnorm2d_88/moving_var:0 +conv2d_82/filters:0 +batchnorm2d_89/beta:0 +batchnorm2d_89/gamma:0 +batchnorm2d_89/moving_mean:0 +batchnorm2d_89/moving_var:0 +conv2d_83/filters:0 +batchnorm2d_90/beta:0 +batchnorm2d_90/gamma:0 +batchnorm2d_90/moving_mean:0 +batchnorm2d_90/moving_var:0 +conv2d_84/filters:0 +batchnorm2d_91/beta:0 +batchnorm2d_91/gamma:0 +batchnorm2d_91/moving_mean:0 +batchnorm2d_91/moving_var:0 +conv2d_85/filters:0 +batchnorm2d_92/beta:0 +batchnorm2d_92/gamma:0 +batchnorm2d_92/moving_mean:0 +batchnorm2d_92/moving_var:0 +conv_route_1/filters:0 +batchnorm2d_93/beta:0 +batchnorm2d_93/gamma:0 +batchnorm2d_93/moving_mean:0 +batchnorm2d_93/moving_var:0 +conv_route_2/filters:0 +conv2d_86/filters:0 +conv2d_86/biases:0 +batchnorm2d_94/beta:0 +batchnorm2d_94/gamma:0 +batchnorm2d_94/moving_mean:0 +batchnorm2d_94/moving_var:0 +conv2d_87/filters:0 +batchnorm2d_95/beta:0 +batchnorm2d_95/gamma:0 +batchnorm2d_95/moving_mean:0 +batchnorm2d_95/moving_var:0 +conv2d_88/filters:0 +batchnorm2d_96/beta:0 +batchnorm2d_96/gamma:0 +batchnorm2d_96/moving_mean:0 +batchnorm2d_96/moving_var:0 +conv2d_89/filters:0 +batchnorm2d_97/beta:0 +batchnorm2d_97/gamma:0 +batchnorm2d_97/moving_mean:0 +batchnorm2d_97/moving_var:0 +conv2d_90/filters:0 +batchnorm2d_98/beta:0 +batchnorm2d_98/gamma:0 +batchnorm2d_98/moving_mean:0 +batchnorm2d_98/moving_var:0 +conv2d_91/filters:0 +batchnorm2d_99/beta:0 +batchnorm2d_99/gamma:0 +batchnorm2d_99/moving_mean:0 +batchnorm2d_99/moving_var:0 +conv_route_3/filters:0 +batchnorm2d_100/beta:0 +batchnorm2d_100/gamma:0 +batchnorm2d_100/moving_mean:0 +batchnorm2d_100/moving_var:0 +conv_route_4/filters:0 +conv2d_92/filters:0 +conv2d_92/biases:0 +batchnorm2d_101/beta:0 +batchnorm2d_101/gamma:0 +batchnorm2d_101/moving_mean:0 +batchnorm2d_101/moving_var:0 +conv2d_93/filters:0 +batchnorm2d_102/beta:0 +batchnorm2d_102/gamma:0 +batchnorm2d_102/moving_mean:0 +batchnorm2d_102/moving_var:0 +conv2d_94/filters:0 +batchnorm2d_103/beta:0 +batchnorm2d_103/gamma:0 +batchnorm2d_103/moving_mean:0 +batchnorm2d_103/moving_var:0 +conv2d_95/filters:0 +batchnorm2d_104/beta:0 +batchnorm2d_104/gamma:0 +batchnorm2d_104/moving_mean:0 +batchnorm2d_104/moving_var:0 +conv2d_96/filters:0 +batchnorm2d_105/beta:0 +batchnorm2d_105/gamma:0 +batchnorm2d_105/moving_mean:0 +batchnorm2d_105/moving_var:0 +conv2d_97/filters:0 +batchnorm2d_106/beta:0 +batchnorm2d_106/gamma:0 +batchnorm2d_106/moving_mean:0 +batchnorm2d_106/moving_var:0 +conv2d_98/filters:0 +batchnorm2d_107/beta:0 +batchnorm2d_107/gamma:0 +batchnorm2d_107/moving_mean:0 +batchnorm2d_107/moving_var:0 +conv2d_99/filters:0 +conv2d_99/biases:0 \ No newline at end of file diff --git a/examples/model_zoo/model/weights_3.txt b/examples/model_zoo/model/weights_3.txt new file mode 100644 index 0000000..b9ff6e1 --- /dev/null +++ b/examples/model_zoo/model/weights_3.txt @@ -0,0 +1,541 @@ +conv2d_1/filters:0 +batchnorm2d_1/beta:0 +batchnorm2d_1/gamma:0 +batchnorm2d_1/moving_mean:0 +batchnorm2d_1/moving_var:0 +conv2d_2/filters:0 +batchnorm2d_2/beta:0 +batchnorm2d_2/gamma:0 +batchnorm2d_2/moving_mean:0 +batchnorm2d_2/moving_var:0 +conv_rote_block_1/filters:0 +batchnorm2d_3/beta:0 +batchnorm2d_3/gamma:0 +batchnorm2d_3/moving_mean:0 +batchnorm2d_3/moving_var:0 +conv2d_3/filters:0 +batchnorm2d_4/beta:0 +batchnorm2d_4/gamma:0 +batchnorm2d_4/moving_mean:0 +batchnorm2d_4/moving_var:0 +conv2d_4/filters:0 +batchnorm2d_5/beta:0 +batchnorm2d_5/gamma:0 +batchnorm2d_5/moving_mean:0 +batchnorm2d_5/moving_var:0 +conv2d_5/filters:0 +batchnorm2d_6/beta:0 +batchnorm2d_6/gamma:0 +batchnorm2d_6/moving_mean:0 +batchnorm2d_6/moving_var:0 +conv2d_6/filters:0 +batchnorm2d_7/beta:0 +batchnorm2d_7/gamma:0 +batchnorm2d_7/moving_mean:0 +batchnorm2d_7/moving_var:0 +conv2d_7/filters:0 +batchnorm2d_8/beta:0 +batchnorm2d_8/gamma:0 +batchnorm2d_8/moving_mean:0 +batchnorm2d_8/moving_var:0 +conv2d_8/filters:0 +batchnorm2d_9/beta:0 +batchnorm2d_9/gamma:0 +batchnorm2d_9/moving_mean:0 +batchnorm2d_9/moving_var:0 +conv_rote_block_2/filters:0 +batchnorm2d_10/beta:0 +batchnorm2d_10/gamma:0 +batchnorm2d_10/moving_mean:0 +batchnorm2d_10/moving_var:0 +conv2d_9/filters:0 +batchnorm2d_11/beta:0 +batchnorm2d_11/gamma:0 +batchnorm2d_11/moving_mean:0 +batchnorm2d_11/moving_var:0 +conv2d_10/filters:0 +batchnorm2d_12/beta:0 +batchnorm2d_12/gamma:0 +batchnorm2d_12/moving_mean:0 +batchnorm2d_12/moving_var:0 +conv2d_11/filters:0 +batchnorm2d_13/beta:0 +batchnorm2d_13/gamma:0 +batchnorm2d_13/moving_mean:0 +batchnorm2d_13/moving_var:0 +conv2d_12/filters:0 +batchnorm2d_14/beta:0 +batchnorm2d_14/gamma:0 +batchnorm2d_14/moving_mean:0 +batchnorm2d_14/moving_var:0 +conv2d_13/filters:0 +batchnorm2d_15/beta:0 +batchnorm2d_15/gamma:0 +batchnorm2d_15/moving_mean:0 +batchnorm2d_15/moving_var:0 +conv2d_14/filters:0 +batchnorm2d_16/beta:0 +batchnorm2d_16/gamma:0 +batchnorm2d_16/moving_mean:0 +batchnorm2d_16/moving_var:0 +conv2d_15/filters:0 +batchnorm2d_17/beta:0 +batchnorm2d_17/gamma:0 +batchnorm2d_17/moving_mean:0 +batchnorm2d_17/moving_var:0 +conv2d_16/filters:0 +batchnorm2d_18/beta:0 +batchnorm2d_18/gamma:0 +batchnorm2d_18/moving_mean:0 +batchnorm2d_18/moving_var:0 +conv_rote_block_3/filters:0 +batchnorm2d_19/beta:0 +batchnorm2d_19/gamma:0 +batchnorm2d_19/moving_mean:0 +batchnorm2d_19/moving_var:0 +conv2d_17/filters:0 +batchnorm2d_20/beta:0 +batchnorm2d_20/gamma:0 +batchnorm2d_20/moving_mean:0 +batchnorm2d_20/moving_var:0 +conv2d_18/filters:0 +batchnorm2d_21/beta:0 +batchnorm2d_21/gamma:0 +batchnorm2d_21/moving_mean:0 +batchnorm2d_21/moving_var:0 +conv2d_19/filters:0 +batchnorm2d_22/beta:0 +batchnorm2d_22/gamma:0 +batchnorm2d_22/moving_mean:0 +batchnorm2d_22/moving_var:0 +conv2d_20/filters:0 +batchnorm2d_23/beta:0 +batchnorm2d_23/gamma:0 +batchnorm2d_23/moving_mean:0 +batchnorm2d_23/moving_var:0 +conv2d_21/filters:0 +batchnorm2d_24/beta:0 +batchnorm2d_24/gamma:0 +batchnorm2d_24/moving_mean:0 +batchnorm2d_24/moving_var:0 +conv2d_22/filters:0 +batchnorm2d_25/beta:0 +batchnorm2d_25/gamma:0 +batchnorm2d_25/moving_mean:0 +batchnorm2d_25/moving_var:0 +conv2d_23/filters:0 +batchnorm2d_26/beta:0 +batchnorm2d_26/gamma:0 +batchnorm2d_26/moving_mean:0 +batchnorm2d_26/moving_var:0 +conv2d_24/filters:0 +batchnorm2d_27/beta:0 +batchnorm2d_27/gamma:0 +batchnorm2d_27/moving_mean:0 +batchnorm2d_27/moving_var:0 +conv2d_25/filters:0 +batchnorm2d_28/beta:0 +batchnorm2d_28/gamma:0 +batchnorm2d_28/moving_mean:0 +batchnorm2d_28/moving_var:0 +conv2d_26/filters:0 +batchnorm2d_29/beta:0 +batchnorm2d_29/gamma:0 +batchnorm2d_29/moving_mean:0 +batchnorm2d_29/moving_var:0 +conv2d_27/filters:0 +batchnorm2d_30/beta:0 +batchnorm2d_30/gamma:0 +batchnorm2d_30/moving_mean:0 +batchnorm2d_30/moving_var:0 +conv2d_28/filters:0 +batchnorm2d_31/beta:0 +batchnorm2d_31/gamma:0 +batchnorm2d_31/moving_mean:0 +batchnorm2d_31/moving_var:0 +conv2d_29/filters:0 +batchnorm2d_32/beta:0 +batchnorm2d_32/gamma:0 +batchnorm2d_32/moving_mean:0 +batchnorm2d_32/moving_var:0 +conv2d_30/filters:0 +batchnorm2d_33/beta:0 +batchnorm2d_33/gamma:0 +batchnorm2d_33/moving_mean:0 +batchnorm2d_33/moving_var:0 +conv2d_31/filters:0 +batchnorm2d_34/beta:0 +batchnorm2d_34/gamma:0 +batchnorm2d_34/moving_mean:0 +batchnorm2d_34/moving_var:0 +conv2d_32/filters:0 +batchnorm2d_35/beta:0 +batchnorm2d_35/gamma:0 +batchnorm2d_35/moving_mean:0 +batchnorm2d_35/moving_var:0 +conv2d_33/filters:0 +batchnorm2d_36/beta:0 +batchnorm2d_36/gamma:0 +batchnorm2d_36/moving_mean:0 +batchnorm2d_36/moving_var:0 +conv2d_34/filters:0 +batchnorm2d_37/beta:0 +batchnorm2d_37/gamma:0 +batchnorm2d_37/moving_mean:0 +batchnorm2d_37/moving_var:0 +conv2d_35/filters:0 +batchnorm2d_38/beta:0 +batchnorm2d_38/gamma:0 +batchnorm2d_38/moving_mean:0 +batchnorm2d_38/moving_var:0 +conv2d_36/filters:0 +batchnorm2d_39/beta:0 +batchnorm2d_39/gamma:0 +batchnorm2d_39/moving_mean:0 +batchnorm2d_39/moving_var:0 +conv_rote_block_4/filters:0 +batchnorm2d_40/beta:0 +batchnorm2d_40/gamma:0 +batchnorm2d_40/moving_mean:0 +batchnorm2d_40/moving_var:0 +conv2d_37/filters:0 +batchnorm2d_41/beta:0 +batchnorm2d_41/gamma:0 +batchnorm2d_41/moving_mean:0 +batchnorm2d_41/moving_var:0 +conv2d_38/filters:0 +batchnorm2d_42/beta:0 +batchnorm2d_42/gamma:0 +batchnorm2d_42/moving_mean:0 +batchnorm2d_42/moving_var:0 +conv2d_39/filters:0 +batchnorm2d_43/beta:0 +batchnorm2d_43/gamma:0 +batchnorm2d_43/moving_mean:0 +batchnorm2d_43/moving_var:0 +conv2d_40/filters:0 +batchnorm2d_44/beta:0 +batchnorm2d_44/gamma:0 +batchnorm2d_44/moving_mean:0 +batchnorm2d_44/moving_var:0 +conv2d_41/filters:0 +batchnorm2d_45/beta:0 +batchnorm2d_45/gamma:0 +batchnorm2d_45/moving_mean:0 +batchnorm2d_45/moving_var:0 +conv2d_42/filters:0 +batchnorm2d_46/beta:0 +batchnorm2d_46/gamma:0 +batchnorm2d_46/moving_mean:0 +batchnorm2d_46/moving_var:0 +conv2d_43/filters:0 +batchnorm2d_47/beta:0 +batchnorm2d_47/gamma:0 +batchnorm2d_47/moving_mean:0 +batchnorm2d_47/moving_var:0 +conv2d_44/filters:0 +batchnorm2d_48/beta:0 +batchnorm2d_48/gamma:0 +batchnorm2d_48/moving_mean:0 +batchnorm2d_48/moving_var:0 +conv2d_45/filters:0 +batchnorm2d_49/beta:0 +batchnorm2d_49/gamma:0 +batchnorm2d_49/moving_mean:0 +batchnorm2d_49/moving_var:0 +conv2d_46/filters:0 +batchnorm2d_50/beta:0 +batchnorm2d_50/gamma:0 +batchnorm2d_50/moving_mean:0 +batchnorm2d_50/moving_var:0 +conv2d_47/filters:0 +batchnorm2d_51/beta:0 +batchnorm2d_51/gamma:0 +batchnorm2d_51/moving_mean:0 +batchnorm2d_51/moving_var:0 +conv2d_48/filters:0 +batchnorm2d_52/beta:0 +batchnorm2d_52/gamma:0 +batchnorm2d_52/moving_mean:0 +batchnorm2d_52/moving_var:0 +conv2d_49/filters:0 +batchnorm2d_53/beta:0 +batchnorm2d_53/gamma:0 +batchnorm2d_53/moving_mean:0 +batchnorm2d_53/moving_var:0 +conv2d_50/filters:0 +batchnorm2d_54/beta:0 +batchnorm2d_54/gamma:0 +batchnorm2d_54/moving_mean:0 +batchnorm2d_54/moving_var:0 +conv2d_51/filters:0 +batchnorm2d_55/beta:0 +batchnorm2d_55/gamma:0 +batchnorm2d_55/moving_mean:0 +batchnorm2d_55/moving_var:0 +conv2d_52/filters:0 +batchnorm2d_56/beta:0 +batchnorm2d_56/gamma:0 +batchnorm2d_56/moving_mean:0 +batchnorm2d_56/moving_var:0 +conv2d_53/filters:0 +batchnorm2d_57/beta:0 +batchnorm2d_57/gamma:0 +batchnorm2d_57/moving_mean:0 +batchnorm2d_57/moving_var:0 +conv2d_54/filters:0 +batchnorm2d_58/beta:0 +batchnorm2d_58/gamma:0 +batchnorm2d_58/moving_mean:0 +batchnorm2d_58/moving_var:0 +conv2d_55/filters:0 +batchnorm2d_59/beta:0 +batchnorm2d_59/gamma:0 +batchnorm2d_59/moving_mean:0 +batchnorm2d_59/moving_var:0 +conv2d_56/filters:0 +batchnorm2d_60/beta:0 +batchnorm2d_60/gamma:0 +batchnorm2d_60/moving_mean:0 +batchnorm2d_60/moving_var:0 +conv_rote_block_5/filters:0 +batchnorm2d_61/beta:0 +batchnorm2d_61/gamma:0 +batchnorm2d_61/moving_mean:0 +batchnorm2d_61/moving_var:0 +conv2d_57/filters:0 +batchnorm2d_62/beta:0 +batchnorm2d_62/gamma:0 +batchnorm2d_62/moving_mean:0 +batchnorm2d_62/moving_var:0 +conv2d_58/filters:0 +batchnorm2d_63/beta:0 +batchnorm2d_63/gamma:0 +batchnorm2d_63/moving_mean:0 +batchnorm2d_63/moving_var:0 +conv2d_59/filters:0 +batchnorm2d_64/beta:0 +batchnorm2d_64/gamma:0 +batchnorm2d_64/moving_mean:0 +batchnorm2d_64/moving_var:0 +conv2d_60/filters:0 +batchnorm2d_65/beta:0 +batchnorm2d_65/gamma:0 +batchnorm2d_65/moving_mean:0 +batchnorm2d_65/moving_var:0 +conv2d_61/filters:0 +batchnorm2d_66/beta:0 +batchnorm2d_66/gamma:0 +batchnorm2d_66/moving_mean:0 +batchnorm2d_66/moving_var:0 +conv2d_62/filters:0 +batchnorm2d_67/beta:0 +batchnorm2d_67/gamma:0 +batchnorm2d_67/moving_mean:0 +batchnorm2d_67/moving_var:0 +conv2d_63/filters:0 +batchnorm2d_68/beta:0 +batchnorm2d_68/gamma:0 +batchnorm2d_68/moving_mean:0 +batchnorm2d_68/moving_var:0 +conv2d_64/filters:0 +batchnorm2d_69/beta:0 +batchnorm2d_69/gamma:0 +batchnorm2d_69/moving_mean:0 +batchnorm2d_69/moving_var:0 +conv2d_65/filters:0 +batchnorm2d_70/beta:0 +batchnorm2d_70/gamma:0 +batchnorm2d_70/moving_mean:0 +batchnorm2d_70/moving_var:0 +conv2d_66/filters:0 +batchnorm2d_71/beta:0 +batchnorm2d_71/gamma:0 +batchnorm2d_71/moving_mean:0 +batchnorm2d_71/moving_var:0 +conv2d_67/filters:0 +batchnorm2d_72/beta:0 +batchnorm2d_72/gamma:0 +batchnorm2d_72/moving_mean:0 +batchnorm2d_72/moving_var:0 +conv2d_68/filters:0 +batchnorm2d_73/beta:0 +batchnorm2d_73/gamma:0 +batchnorm2d_73/moving_mean:0 +batchnorm2d_73/moving_var:0 +conv2d_69/filters:0 +batchnorm2d_74/beta:0 +batchnorm2d_74/gamma:0 +batchnorm2d_74/moving_mean:0 +batchnorm2d_74/moving_var:0 +conv2d_70/filters:0 +batchnorm2d_75/beta:0 +batchnorm2d_75/gamma:0 +batchnorm2d_75/moving_mean:0 +batchnorm2d_75/moving_var:0 +conv2d_71/filters:0 +batchnorm2d_76/beta:0 +batchnorm2d_76/gamma:0 +batchnorm2d_76/moving_mean:0 +batchnorm2d_76/moving_var:0 +conv2d_72/filters:0 +batchnorm2d_77/beta:0 +batchnorm2d_77/gamma:0 +batchnorm2d_77/moving_mean:0 +batchnorm2d_77/moving_var:0 +conv2d_73/filters:0 +batchnorm2d_78/beta:0 +batchnorm2d_78/gamma:0 +batchnorm2d_78/moving_mean:0 +batchnorm2d_78/moving_var:0 +conv2d_74/filters:0 +batchnorm2d_79/beta:0 +batchnorm2d_79/gamma:0 +batchnorm2d_79/moving_mean:0 +batchnorm2d_79/moving_var:0 +conv_yolo_1/filters:0 +batchnorm2d_80/beta:0 +batchnorm2d_80/gamma:0 +batchnorm2d_80/moving_mean:0 +batchnorm2d_80/moving_var:0 +conv2d_75/filters:0 +batchnorm2d_81/beta:0 +batchnorm2d_81/gamma:0 +batchnorm2d_81/moving_mean:0 +batchnorm2d_81/moving_var:0 +conv2d_76/filters:0 +batchnorm2d_82/beta:0 +batchnorm2d_82/gamma:0 +batchnorm2d_82/moving_mean:0 +batchnorm2d_82/moving_var:0 +conv2d_77/filters:0 +batchnorm2d_83/beta:0 +batchnorm2d_83/gamma:0 +batchnorm2d_83/moving_mean:0 +batchnorm2d_83/moving_var:0 +conv2d_78/filters:0 +batchnorm2d_84/beta:0 +batchnorm2d_84/gamma:0 +batchnorm2d_84/moving_mean:0 +batchnorm2d_84/moving_var:0 +conv2d_79/filters:0 +batchnorm2d_85/beta:0 +batchnorm2d_85/gamma:0 +batchnorm2d_85/moving_mean:0 +batchnorm2d_85/moving_var:0 +conv2d_80/filters:0 +batchnorm2d_86/beta:0 +batchnorm2d_86/gamma:0 +batchnorm2d_86/moving_mean:0 +batchnorm2d_86/moving_var:0 +conv_yolo_2/filters:0 +batchnorm2d_87/beta:0 +batchnorm2d_87/gamma:0 +batchnorm2d_87/moving_mean:0 +batchnorm2d_87/moving_var:0 +conv2d_81/filters:0 +batchnorm2d_88/beta:0 +batchnorm2d_88/gamma:0 +batchnorm2d_88/moving_mean:0 +batchnorm2d_88/moving_var:0 +conv2d_82/filters:0 +batchnorm2d_89/beta:0 +batchnorm2d_89/gamma:0 +batchnorm2d_89/moving_mean:0 +batchnorm2d_89/moving_var:0 +conv2d_83/filters:0 +batchnorm2d_90/beta:0 +batchnorm2d_90/gamma:0 +batchnorm2d_90/moving_mean:0 +batchnorm2d_90/moving_var:0 +conv2d_84/filters:0 +batchnorm2d_91/beta:0 +batchnorm2d_91/gamma:0 +batchnorm2d_91/moving_mean:0 +batchnorm2d_91/moving_var:0 +conv2d_85/filters:0 +batchnorm2d_92/beta:0 +batchnorm2d_92/gamma:0 +batchnorm2d_92/moving_mean:0 +batchnorm2d_92/moving_var:0 +conv_route_1/filters:0 +batchnorm2d_93/beta:0 +batchnorm2d_93/gamma:0 +batchnorm2d_93/moving_mean:0 +batchnorm2d_93/moving_var:0 +conv2d_86/filters:0 +conv2d_86/biases:0 +conv_route_2/filters:0 +batchnorm2d_94/beta:0 +batchnorm2d_94/gamma:0 +batchnorm2d_94/moving_mean:0 +batchnorm2d_94/moving_var:0 +conv2d_87/filters:0 +batchnorm2d_95/beta:0 +batchnorm2d_95/gamma:0 +batchnorm2d_95/moving_mean:0 +batchnorm2d_95/moving_var:0 +conv2d_88/filters:0 +batchnorm2d_96/beta:0 +batchnorm2d_96/gamma:0 +batchnorm2d_96/moving_mean:0 +batchnorm2d_96/moving_var:0 +conv2d_89/filters:0 +batchnorm2d_97/beta:0 +batchnorm2d_97/gamma:0 +batchnorm2d_97/moving_mean:0 +batchnorm2d_97/moving_var:0 +conv2d_90/filters:0 +batchnorm2d_98/beta:0 +batchnorm2d_98/gamma:0 +batchnorm2d_98/moving_mean:0 +batchnorm2d_98/moving_var:0 +conv2d_91/filters:0 +batchnorm2d_99/beta:0 +batchnorm2d_99/gamma:0 +batchnorm2d_99/moving_mean:0 +batchnorm2d_99/moving_var:0 +conv_route_3/filters:0 +batchnorm2d_100/beta:0 +batchnorm2d_100/gamma:0 +batchnorm2d_100/moving_mean:0 +batchnorm2d_100/moving_var:0 +conv2d_92/filters:0 +conv2d_92/biases:0 +conv_route_4/filters:0 +batchnorm2d_101/beta:0 +batchnorm2d_101/gamma:0 +batchnorm2d_101/moving_mean:0 +batchnorm2d_101/moving_var:0 +conv2d_93/filters:0 +batchnorm2d_102/beta:0 +batchnorm2d_102/gamma:0 +batchnorm2d_102/moving_mean:0 +batchnorm2d_102/moving_var:0 +conv2d_94/filters:0 +batchnorm2d_103/beta:0 +batchnorm2d_103/gamma:0 +batchnorm2d_103/moving_mean:0 +batchnorm2d_103/moving_var:0 +conv2d_95/filters:0 +batchnorm2d_104/beta:0 +batchnorm2d_104/gamma:0 +batchnorm2d_104/moving_mean:0 +batchnorm2d_104/moving_var:0 +conv2d_96/filters:0 +batchnorm2d_105/beta:0 +batchnorm2d_105/gamma:0 +batchnorm2d_105/moving_mean:0 +batchnorm2d_105/moving_var:0 +conv2d_97/filters:0 +batchnorm2d_106/beta:0 +batchnorm2d_106/gamma:0 +batchnorm2d_106/moving_mean:0 +batchnorm2d_106/moving_var:0 +conv2d_98/filters:0 +batchnorm2d_107/beta:0 +batchnorm2d_107/gamma:0 +batchnorm2d_107/moving_mean:0 +batchnorm2d_107/moving_var:0 +conv2d_99/filters:0 +conv2d_99/biases:0 \ No newline at end of file diff --git a/examples/model_zoo/model/yolov4_weights3_config.txt b/examples/model_zoo/model/yolov4_weights3_config.txt new file mode 100644 index 0000000..5f31bb5 --- /dev/null +++ b/examples/model_zoo/model/yolov4_weights3_config.txt @@ -0,0 +1,541 @@ +layer_with_weights-0/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-1/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-1/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-1/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-1/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-2/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-3/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-3/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-3/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-3/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-11/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-13/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-13/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-13/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-13/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-4/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-5/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-5/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-5/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-5/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-6/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-7/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-7/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-7/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-7/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-8/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-9/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-9/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-9/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-9/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-10/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-12/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-12/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-12/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-12/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-14/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-15/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-15/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-15/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-15/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-16/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-17/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-17/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-17/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-17/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-29/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-31/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-31/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-31/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-31/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-18/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-19/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-19/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-19/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-19/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-20/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-21/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-21/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-21/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-21/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-22/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-23/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-23/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-23/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-23/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-24/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-25/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-25/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-25/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-25/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-26/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-27/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-27/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-27/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-27/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-28/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-30/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-30/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-30/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-30/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-32/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-33/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-33/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-33/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-33/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-34/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-35/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-35/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-35/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-35/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-71/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-73/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-73/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-73/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-73/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-36/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-37/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-37/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-37/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-37/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-38/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-39/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-39/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-39/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-39/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-40/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-41/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-41/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-41/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-41/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-42/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-43/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-43/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-43/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-43/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-44/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-45/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-45/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-45/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-45/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-46/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-47/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-47/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-47/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-47/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-48/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-49/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-49/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-49/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-49/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-50/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-51/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-51/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-51/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-51/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-52/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-53/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-53/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-53/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-53/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-54/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-55/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-55/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-55/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-55/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-56/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-57/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-57/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-57/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-57/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-58/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-59/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-59/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-59/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-59/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-60/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-61/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-61/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-61/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-61/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-62/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-63/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-63/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-63/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-63/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-64/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-65/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-65/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-65/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-65/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-66/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-67/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-67/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-67/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-67/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-68/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-69/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-69/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-69/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-69/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-70/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-72/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-72/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-72/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-72/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-74/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-75/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-75/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-75/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-75/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-76/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-77/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-77/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-77/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-77/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-113/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-115/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-115/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-115/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-115/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-78/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-79/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-79/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-79/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-79/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-80/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-81/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-81/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-81/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-81/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-82/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-83/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-83/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-83/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-83/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-84/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-85/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-85/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-85/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-85/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-86/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-87/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-87/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-87/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-87/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-88/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-89/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-89/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-89/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-89/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-90/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-91/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-91/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-91/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-91/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-92/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-93/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-93/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-93/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-93/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-94/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-95/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-95/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-95/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-95/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-96/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-97/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-97/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-97/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-97/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-98/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-99/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-99/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-99/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-99/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-100/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-101/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-101/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-101/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-101/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-102/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-103/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-103/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-103/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-103/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-104/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-105/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-105/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-105/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-105/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-106/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-107/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-107/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-107/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-107/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-108/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-109/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-109/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-109/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-109/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-110/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-111/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-111/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-111/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-111/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-112/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-114/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-114/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-114/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-114/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-116/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-117/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-117/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-117/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-117/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-118/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-119/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-119/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-119/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-119/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-139/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-141/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-141/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-141/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-141/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-120/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-121/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-121/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-121/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-121/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-122/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-123/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-123/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-123/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-123/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-124/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-125/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-125/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-125/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-125/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-126/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-127/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-127/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-127/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-127/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-128/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-129/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-129/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-129/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-129/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-130/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-131/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-131/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-131/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-131/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-132/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-133/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-133/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-133/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-133/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-134/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-135/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-135/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-135/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-135/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-136/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-137/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-137/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-137/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-137/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-138/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-140/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-140/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-140/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-140/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-142/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-143/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-143/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-143/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-143/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-144/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-145/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-145/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-145/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-145/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-146/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-147/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-147/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-147/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-147/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-148/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-149/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-149/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-149/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-149/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-150/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-151/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-151/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-151/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-151/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-152/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-153/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-153/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-153/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-153/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-154/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-155/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-155/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-155/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-155/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-156/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-158/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-158/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-158/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-158/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-157/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-159/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-159/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-159/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-159/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-160/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-161/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-161/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-161/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-161/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-162/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-163/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-163/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-163/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-163/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-164/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-165/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-165/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-165/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-165/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-166/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-167/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-167/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-167/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-167/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-168/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-169/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-169/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-169/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-169/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-170/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-172/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-172/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-172/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-172/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-171/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-173/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-173/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-173/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-173/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-174/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-175/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-175/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-175/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-175/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-176/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-177/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-177/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-177/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-177/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-178/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-179/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-179/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-179/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-179/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-180/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-181/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-181/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-181/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-181/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-182/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-183/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-183/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-183/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-183/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-208/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-211/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-211/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-211/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-211/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-214/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-214/bias/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-184/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-185/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-185/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-185/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-185/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-186/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-187/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-187/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-187/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-187/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-188/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-189/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-189/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-189/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-189/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-190/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-191/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-191/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-191/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-191/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-192/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-193/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-193/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-193/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-193/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-194/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-195/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-195/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-195/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-195/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-209/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-212/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-212/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-212/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-212/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-215/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-215/bias/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-196/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-197/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-197/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-197/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-197/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-198/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-199/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-199/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-199/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-199/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-200/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-201/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-201/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-201/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-201/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-202/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-203/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-203/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-203/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-203/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-204/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-205/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-205/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-205/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-205/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-206/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-207/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-207/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-207/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-207/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-210/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-213/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-213/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-213/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-213/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-216/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-216/bias/.ATTRIBUTES/VARIABLE_VALUE \ No newline at end of file diff --git a/examples/model_zoo/model/yolov4_weights_config.txt b/examples/model_zoo/model/yolov4_weights_config.txt new file mode 100644 index 0000000..2c28be0 --- /dev/null +++ b/examples/model_zoo/model/yolov4_weights_config.txt @@ -0,0 +1,541 @@ +layer_with_weights-0/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-1/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-1/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-1/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-1/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-2/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-3/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-3/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-3/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-3/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-11/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-4/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-13/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-13/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-13/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-13/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-5/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-5/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-5/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-5/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-6/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-7/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-7/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-7/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-7/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-8/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-9/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-9/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-9/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-9/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-10/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-12/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-12/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-12/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-12/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-14/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-15/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-15/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-15/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-15/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-16/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-17/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-17/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-17/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-17/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-29/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-18/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-31/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-31/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-31/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-31/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-19/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-19/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-19/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-19/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-20/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-21/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-21/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-21/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-21/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-22/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-23/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-23/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-23/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-23/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-24/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-25/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-25/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-25/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-25/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-26/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-27/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-27/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-27/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-27/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-28/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-30/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-30/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-30/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-30/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-32/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-33/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-33/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-33/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-33/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-34/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-35/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-35/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-35/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-35/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-71/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-36/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-73/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-73/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-73/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-73/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-37/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-37/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-37/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-37/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-38/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-39/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-39/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-39/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-39/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-40/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-41/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-41/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-41/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-41/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-42/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-43/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-43/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-43/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-43/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-44/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-45/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-45/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-45/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-45/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-46/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-47/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-47/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-47/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-47/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-48/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-49/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-49/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-49/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-49/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-50/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-51/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-51/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-51/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-51/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-52/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-53/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-53/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-53/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-53/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-54/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-55/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-55/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-55/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-55/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-56/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-57/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-57/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-57/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-57/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-58/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-59/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-59/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-59/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-59/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-60/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-61/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-61/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-61/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-61/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-62/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-63/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-63/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-63/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-63/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-64/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-65/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-65/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-65/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-65/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-66/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-67/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-67/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-67/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-67/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-68/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-69/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-69/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-69/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-69/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-70/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-72/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-72/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-72/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-72/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-74/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-75/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-75/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-75/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-75/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-171/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-173/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-173/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-173/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-173/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-76/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-77/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-77/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-77/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-77/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-113/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-78/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-115/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-115/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-115/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-115/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-79/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-79/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-79/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-79/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-80/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-81/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-81/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-81/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-81/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-82/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-83/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-83/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-83/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-83/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-84/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-85/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-85/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-85/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-85/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-86/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-87/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-87/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-87/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-87/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-88/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-89/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-89/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-89/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-89/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-90/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-91/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-91/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-91/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-91/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-92/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-93/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-93/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-93/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-93/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-94/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-95/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-95/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-95/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-95/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-96/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-97/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-97/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-97/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-97/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-98/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-99/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-99/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-99/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-99/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-100/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-101/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-101/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-101/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-101/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-102/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-103/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-103/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-103/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-103/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-104/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-105/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-105/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-105/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-105/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-106/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-107/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-107/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-107/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-107/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-108/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-109/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-109/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-109/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-109/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-110/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-111/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-111/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-111/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-111/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-112/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-114/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-114/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-114/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-114/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-116/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-117/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-117/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-117/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-117/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-157/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-159/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-159/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-159/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-159/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-118/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-119/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-119/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-119/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-119/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-139/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-120/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-141/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-141/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-141/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-141/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-121/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-121/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-121/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-121/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-122/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-123/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-123/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-123/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-123/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-124/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-125/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-125/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-125/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-125/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-126/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-127/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-127/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-127/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-127/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-128/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-129/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-129/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-129/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-129/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-130/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-131/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-131/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-131/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-131/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-132/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-133/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-133/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-133/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-133/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-134/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-135/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-135/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-135/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-135/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-136/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-137/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-137/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-137/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-137/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-138/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-140/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-140/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-140/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-140/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-142/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-143/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-143/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-143/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-143/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-144/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-145/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-145/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-145/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-145/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-146/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-147/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-147/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-147/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-147/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-148/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-149/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-149/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-149/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-149/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-150/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-151/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-151/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-151/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-151/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-152/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-153/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-153/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-153/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-153/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-154/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-155/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-155/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-155/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-155/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-156/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-158/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-158/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-158/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-158/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-160/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-161/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-161/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-161/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-161/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-162/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-163/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-163/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-163/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-163/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-164/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-165/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-165/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-165/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-165/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-166/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-167/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-167/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-167/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-167/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-168/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-169/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-169/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-169/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-169/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-170/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-172/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-172/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-172/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-172/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-174/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-175/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-175/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-175/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-175/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-176/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-177/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-177/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-177/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-177/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-178/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-179/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-179/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-179/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-179/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-180/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-181/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-181/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-181/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-181/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-182/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-183/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-183/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-183/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-183/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-208/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-211/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-211/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-211/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-211/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-184/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-214/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-214/bias/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-185/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-185/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-185/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-185/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-186/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-187/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-187/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-187/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-187/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-188/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-189/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-189/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-189/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-189/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-190/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-191/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-191/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-191/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-191/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-192/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-193/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-193/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-193/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-193/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-194/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-195/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-195/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-195/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-195/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-209/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-212/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-212/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-212/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-212/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-196/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-215/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-215/bias/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-197/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-197/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-197/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-197/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-198/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-199/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-199/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-199/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-199/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-200/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-201/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-201/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-201/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-201/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-202/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-203/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-203/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-203/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-203/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-204/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-205/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-205/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-205/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-205/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-206/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-207/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-207/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-207/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-207/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-210/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-213/beta/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-213/gamma/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-213/moving_mean/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-213/moving_variance/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-216/kernel/.ATTRIBUTES/VARIABLE_VALUE +layer_with_weights-216/bias/.ATTRIBUTES/VARIABLE_VALUE \ No newline at end of file diff --git a/examples/model_zoo/pretrained_resnet50.py b/examples/model_zoo/pretrained_resnet50.py new file mode 100644 index 0000000..cac33eb --- /dev/null +++ b/examples/model_zoo/pretrained_resnet50.py @@ -0,0 +1,32 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +""" +ResNet50 for ImageNet using TL models + +""" + +import time +import numpy as np +import tensorlayer as tl +from examples.model_zoo.imagenet_classes import class_names +from examples.model_zoo.resnet import ResNet50 + +tl.logging.set_verbosity(tl.logging.DEBUG) + +# get the whole model +resnet = ResNet50(pretrained=False) +resnet.set_eval() + +img1 = tl.vis.read_image('data/tiger.jpeg') +img1 = tl.prepro.imresize(img1, (224, 224))[:, :, ::-1] +img1 = img1 - np.array([103.939, 116.779, 123.68]).reshape((1, 1, 3)) + +img1 = img1.astype(np.float32)[np.newaxis, ...] + +start_time = time.time() +output = resnet(img1) +prob = tl.ops.softmax(output)[0].numpy() +print(" End time : %.5ss" % (time.time() - start_time)) +preds = (np.argsort(prob)[::-1])[0:5] +for p in preds: + print(class_names[p], prob[p]) diff --git a/examples/model_zoo/pretrained_vgg16.py b/examples/model_zoo/pretrained_vgg16.py new file mode 100644 index 0000000..9bf4264 --- /dev/null +++ b/examples/model_zoo/pretrained_vgg16.py @@ -0,0 +1,29 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +"""VGG-16 for ImageNet using TL models.""" + +import time + +import numpy as np +import tensorflow as tf + +import tensorlayer as tl +from examples.model_zoo.imagenet_classes import class_names +from examples.model_zoo.vgg import vgg16 + +tl.logging.set_verbosity(tl.logging.DEBUG) + +# get the whole model +vgg = vgg16(pretrained=True) +vgg.set_eval() + +img = tl.vis.read_image('data/tiger.jpeg') +img = tl.prepro.imresize(img, (224, 224)).astype(np.float32) / 255 + +start_time = time.time() +output = vgg(img) +probs = tf.nn.softmax(output)[0].numpy() +print(" End time : %.5ss" % (time.time() - start_time)) +preds = (np.argsort(probs)[::-1])[0:5] +for p in preds: + print(class_names[p], probs[p]) diff --git a/examples/model_zoo/pretrained_yolov4.py b/examples/model_zoo/pretrained_yolov4.py new file mode 100644 index 0000000..c8d3908 --- /dev/null +++ b/examples/model_zoo/pretrained_yolov4.py @@ -0,0 +1,28 @@ +import numpy as np +import cv2 +from PIL import Image +from examples.model_zoo.common import yolo4_input_processing, yolo4_output_processing, \ + result_to_json, read_class_names, draw_boxes_and_labels_to_image_with_json +from examples.model_zoo.yolo import YOLOv4 +import tensorlayer as tl + +tl.logging.set_verbosity(tl.logging.DEBUG) + +INPUT_SIZE = 416 +image_path = './data/kite.jpg' + +class_names = read_class_names('./model/coco.names') +original_image = cv2.imread(image_path) +image = cv2.cvtColor(np.array(original_image), cv2.COLOR_BGR2RGB) + +model = YOLOv4(NUM_CLASS=80, pretrained=True) +model.set_eval() + +batch_data = yolo4_input_processing(original_image) +feature_maps = model(batch_data) +pred_bbox = yolo4_output_processing(feature_maps) +json_result = result_to_json(image, pred_bbox) + +image = draw_boxes_and_labels_to_image_with_json(image, json_result, class_names) +image = Image.fromarray(image.astype(np.uint8)) +image.show() \ No newline at end of file diff --git a/examples/model_zoo/resnet.py b/examples/model_zoo/resnet.py new file mode 100644 index 0000000..c57bef9 --- /dev/null +++ b/examples/model_zoo/resnet.py @@ -0,0 +1,225 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +"""ResNet for ImageNet. + +# Reference: +- [Deep Residual Learning for Image Recognition]( + https://arxiv.org/abs/1512.03385) (CVPR 2016 Best Paper Award) + +""" + +import os + +import tensorlayer as tl + +from tensorlayer import logging +from tensorlayer.files import (assign_weights, maybe_download_and_extract) +from tensorlayer.layers import (BatchNorm, Conv2d, Dense, Elementwise, GlobalMeanPool2d, Input, MaxPool2d) +from tensorlayer.layers import Module, SequentialLayer + +__all__ = [ + 'ResNet50', +] + +block_names = ['2a', '2b', '2c', '3a', '3b', '3c', '3d', '4a', '4b', '4c', '4d', '4e', '4f', '5a', '5b', '5c' + ] + ['avg_pool', 'fc1000'] +block_filters = [[64, 64, 256], [128, 128, 512], [256, 256, 1024], [512, 512, 2048]] +in_channels_conv = [64, 256, 512, 1024] +in_channels_identity = [256, 512, 1024, 2048] +henorm = tl.initializers.he_normal() + +class identity_block(Module): + """The identity block where there is no conv layer at shortcut. + + Parameters + ---------- + input : tf tensor + Input tensor from above layer. + kernel_size : int + The kernel size of middle conv layer at main path. + n_filters : list of integers + The numbers of filters for 3 conv layer at main path. + stage : int + Current stage label. + block : str + Current block label. + + Returns + ------- + Output tensor of this block. + + """ + def __init__(self, kernel_size, n_filters, stage, block): + super(identity_block, self).__init__() + filters1, filters2, filters3 = n_filters + _in_channels = in_channels_identity[stage-2] + conv_name_base = 'res' + str(stage) + block + '_branch' + bn_name_base = 'bn' + str(stage) + block + '_branch' + + self.conv1 = Conv2d(filters1, (1, 1), W_init=henorm, name=conv_name_base + '2a', in_channels=_in_channels) + self.bn1 = BatchNorm(name=bn_name_base + '2a', act='relu', num_features=filters1) + + ks = (kernel_size, kernel_size) + self.conv2 = Conv2d(filters2, ks, padding='SAME', W_init=henorm, name=conv_name_base + '2b', in_channels=filters1) + self.bn2 = BatchNorm(name=bn_name_base + '2b', act='relu', num_features=filters2) + + self.conv3 = Conv2d(filters3, (1, 1), W_init=henorm, name=conv_name_base + '2c', in_channels=filters2) + self.bn3 = BatchNorm(name=bn_name_base + '2c', num_features=filters3) + + self.add = Elementwise(tl.add, act='relu') + + def forward(self, inputs): + output = self.conv1(inputs) + output = self.bn1(output) + output = self.conv2(output) + output = self.bn2(output) + output = self.conv3(output) + output = self.bn3(output) + result = self.add([output, inputs]) + return result + + +class conv_block(Module): + def __init__(self, kernel_size, n_filters, stage, block, strides=(2, 2)): + super(conv_block, self).__init__() + filters1, filters2, filters3 = n_filters + _in_channels = in_channels_conv[stage-2] + conv_name_base = 'res' + str(stage) + block + '_branch' + bn_name_base = 'bn' + str(stage) + block + '_branch' + self.conv1 = Conv2d(filters1, (1, 1), strides=strides, W_init=henorm, name=conv_name_base + '2a', in_channels=_in_channels) + self.bn1 = BatchNorm(name=bn_name_base + '2a', act='relu', num_features=filters1) + + ks = (kernel_size, kernel_size) + self.conv2 = Conv2d(filters2, ks, padding='SAME', W_init=henorm, name=conv_name_base + '2b', in_channels=filters1) + self.bn2 = BatchNorm(name=bn_name_base + '2b', act='relu', num_features=filters2) + + self.conv3 = Conv2d(filters3, (1, 1), W_init=henorm, name=conv_name_base + '2c', in_channels=filters2) + self.bn3 = BatchNorm(name=bn_name_base + '2c', num_features=filters3) + + self.shortcut_conv = Conv2d(filters3, (1, 1), strides=strides, W_init=henorm, name=conv_name_base + '1', in_channels=_in_channels) + self.shortcut_bn = BatchNorm(name=bn_name_base + '1', num_features=filters3) + + self.add = Elementwise(tl.add, act='relu') + + def forward(self, inputs): + output = self.conv1(inputs) + output = self.bn1(output) + output = self.conv2(output) + output = self.bn2(output) + output = self.conv3(output) + output = self.bn3(output) + + shortcut = self.shortcut_conv(inputs) + shortcut = self.shortcut_bn(shortcut) + + result = self.add([output, shortcut]) + return result + + +class ResNet50_model(Module): + def __init__(self, end_with='fc1000', n_classes=1000): + super(ResNet50_model, self).__init__() + self.end_with = end_with + self.n_classes = n_classes + self.conv1 = Conv2d(64, (7, 7), in_channels=3, strides=(2, 2), padding='SAME', W_init=henorm, name='conv1') + self.bn_conv1 = BatchNorm(name='bn_conv1', act="relu", num_features=64) + self.max_pool1 = MaxPool2d((3, 3), strides=(2, 2), name='max_pool1') + self.res_layer = self.make_layer() + + def forward(self, inputs): + z = self.conv1(inputs) + z = self.bn_conv1(z) + z = self.max_pool1(z) + z = self.res_layer(z) + return z + + def make_layer(self): + layer_list = [] + for i, block_name in enumerate(block_names): + if len(block_name) == 2: + stage = int(block_name[0]) + block = block_name[1] + if block == 'a': + strides = (1, 1) if stage == 2 else (2, 2) + layer_list.append(conv_block(3, block_filters[stage - 2], stage=stage, block=block, strides=strides)) + else: + layer_list.append(identity_block(3, block_filters[stage - 2], stage=stage, block=block)) + elif block_name == 'avg_pool': + layer_list.append(GlobalMeanPool2d(name='avg_pool')) + elif block_name == 'fc1000': + layer_list.append(Dense(self.n_classes, name='fc1000', in_channels=2048)) + + if block_name == self.end_with: + break + return SequentialLayer(layer_list) + + +def ResNet50(pretrained=False, end_with='fc1000', n_classes=1000): + """Pre-trained MobileNetV1 model (static mode). Input shape [?, 224, 224, 3]. + To use pretrained model, input should be in BGR format and subtracted from ImageNet mean [103.939, 116.779, 123.68]. + + Parameters + ---------- + pretrained : boolean + Whether to load pretrained weights. Default False. + end_with : str + The end point of the model [conv, depth1, depth2 ... depth13, globalmeanpool, out]. + Default ``out`` i.e. the whole model. + n_classes : int + Number of classes in final prediction. + name : None or str + Name for this model. + + Examples + --------- + Classify ImageNet classes, see `tutorial_models_resnet50.py` + TODO Modify the usage example according to the model storage location + >>> # get the whole model with pretrained weights + >>> resnet = tl.models.ResNet50(pretrained=True) + >>> # use for inferencing + >>> output = resnet(img1, is_train=False) + >>> prob = tf.nn.softmax(output)[0].numpy() + + Extract the features before fc layer + >>> resnet = tl.models.ResNet50(pretrained=True, end_with='5c') + >>> output = resnet(img1, is_train=False) + + Returns + ------- + ResNet50 model. + + """ + + network = ResNet50_model(end_with=end_with, n_classes=n_classes) + + if pretrained: + restore_params(network) + + return network + + +def restore_params(network, path='models'): + logging.info("Restore pre-trained parameters") + maybe_download_and_extract( + 'resnet50_weights_tf_dim_ordering_tf_kernels.h5', + path, + 'https://github.com/fchollet/deep-learning-models/releases/download/v0.2/', + ) # ls -al + try: + import h5py + except Exception: + raise ImportError('h5py not imported') + + f = h5py.File(os.path.join(path, 'resnet50_weights_tf_dim_ordering_tf_kernels.h5'), 'r') + + for layer in network.all_layers: + if len(layer.all_weights) == 0: + continue + w_names = list(f[layer.name]) + params = [f[layer.name][n][:] for n in w_names] + # if 'bn' in layer.name: + # params = [x.reshape(1, 1, 1, -1) for x in params] + assign_weights(params, layer) + del params + + f.close() diff --git a/examples/model_zoo/vgg.py b/examples/model_zoo/vgg.py new file mode 100644 index 0000000..779635d --- /dev/null +++ b/examples/model_zoo/vgg.py @@ -0,0 +1,347 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +""" +VGG for ImageNet. + +Introduction +---------------- +VGG is a convolutional neural network model proposed by K. Simonyan and A. Zisserman +from the University of Oxford in the paper "Very Deep Convolutional Networks for +Large-Scale Image Recognition" . The model achieves 92.7% top-5 test accuracy in ImageNet, +which is a dataset of over 14 million images belonging to 1000 classes. + +Download Pre-trained Model +---------------------------- +- Model weights in this example - vgg16_weights.npz : http://www.cs.toronto.edu/~frossard/post/vgg16/ +- Model weights in this example - vgg19.npy : https://media.githubusercontent.com/media/tensorlayer/pretrained-models/master/models/ +- Caffe VGG 16 model : https://gist.github.com/ksimonyan/211839e770f7b538e2d8#file-readme-md +- Tool to convert the Caffe models to TensorFlow's : https://github.com/ethereon/caffe-tensorflow + +Note +------ +- For simplified CNN layer see "Convolutional layer (Simplified)" +in read the docs website. +- When feeding other images to the model be sure to properly resize or crop them +beforehand. Distorted images might end up being misclassified. One way of safely +feeding images of multiple sizes is by doing center cropping. + +""" + +import os + +import numpy as np + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.files import assign_weights, maybe_download_and_extract +from tensorlayer.layers import (BatchNorm, Conv2d, Dense, Flatten, Input, SequentialLayer, MaxPool2d) +from tensorlayer.layers import Module + +__all__ = [ + 'VGG', + 'vgg16', + 'vgg19', + 'VGG16', + 'VGG19', + # 'vgg11', 'vgg11_bn', 'vgg13', 'vgg13_bn', 'vgg16', 'vgg16_bn', + # 'vgg19_bn', 'vgg19', +] + +layer_names = [ + ['conv1_1', 'conv1_2'], 'pool1', ['conv2_1', 'conv2_2'], 'pool2', + ['conv3_1', 'conv3_2', 'conv3_3', 'conv3_4'], 'pool3', ['conv4_1', 'conv4_2', 'conv4_3', 'conv4_4'], 'pool4', + ['conv5_1', 'conv5_2', 'conv5_3', 'conv5_4'], 'pool5', 'flatten', 'fc1_relu', 'fc2_relu', 'outputs' +] + +cfg = { + 'A': [[64], 'M', [128], 'M', [256, 256], 'M', [512, 512], 'M', [512, 512], 'M', 'F', 'fc1', 'fc2', 'O'], + 'B': [[64, 64], 'M', [128, 128], 'M', [256, 256], 'M', [512, 512], 'M', [512, 512], 'M', 'F', 'fc1', 'fc2', 'O'], + 'D': + [ + [64, 64], 'M', [128, 128], 'M', [256, 256, 256], 'M', [512, 512, 512], 'M', [512, 512, 512], 'M', 'F', + 'fc1', 'fc2', 'O' + ], + 'E': + [ + [64, 64], 'M', [128, 128], 'M', [256, 256, 256, 256], 'M', [512, 512, 512, 512], 'M', [512, 512, 512, 512], + 'M', 'F', 'fc1', 'fc2', 'O' + ], +} + +mapped_cfg = { + 'vgg11': 'A', + 'vgg11_bn': 'A', + 'vgg13': 'B', + 'vgg13_bn': 'B', + 'vgg16': 'D', + 'vgg16_bn': 'D', + 'vgg19': 'E', + 'vgg19_bn': 'E' +} + +model_urls = { + 'vgg16': 'http://www.cs.toronto.edu/~frossard/vgg16/', + 'vgg19': 'https://media.githubusercontent.com/media/tensorlayer/pretrained-models/master/models/' +} + +model_saved_name = {'vgg16': 'vgg16_weights.npz', 'vgg19': 'vgg19.npy'} + + +class VGG(Module): + + def __init__(self, layer_type, batch_norm=False, end_with='outputs', name=None): + super(VGG, self).__init__(name=name) + self.end_with = end_with + + config = cfg[mapped_cfg[layer_type]] + self.make_layer = make_layers(config, batch_norm, end_with) + + def forward(self, inputs): + """ + inputs : tensor + Shape [None, 224, 224, 3], value range [0, 1]. + """ + + inputs = inputs * 255 - np.array([123.68, 116.779, 103.939], dtype=np.float32).reshape([1, 1, 1, 3]) + out = self.make_layer(inputs) + return out + + +def make_layers(config, batch_norm=False, end_with='outputs'): + layer_list = [] + is_end = False + for layer_group_idx, layer_group in enumerate(config): + if isinstance(layer_group, list): + for idx, layer in enumerate(layer_group): + layer_name = layer_names[layer_group_idx][idx] + n_filter = layer + if idx == 0: + if layer_group_idx > 0: + in_channels = config[layer_group_idx - 2][-1] + else: + in_channels = 3 + else: + in_channels = layer_group[idx - 1] + layer_list.append( + Conv2d( + n_filter=n_filter, filter_size=(3, 3), strides=(1, 1), act=tl.ReLU, padding='SAME', + in_channels=in_channels, name=layer_name + ) + ) + if batch_norm: + layer_list.append(BatchNorm(num_features=n_filter)) + if layer_name == end_with: + is_end = True + break + else: + layer_name = layer_names[layer_group_idx] + if layer_group == 'M': + layer_list.append(MaxPool2d(filter_size=(2, 2), strides=(2, 2), padding='SAME', name=layer_name)) + elif layer_group == 'O': + layer_list.append(Dense(n_units=1000, in_channels=4096, name=layer_name)) + elif layer_group == 'F': + layer_list.append(Flatten(name='flatten')) + elif layer_group == 'fc1': + layer_list.append(Dense(n_units=4096, act=tl.ReLU, in_channels=512 * 7 * 7, name=layer_name)) + elif layer_group == 'fc2': + layer_list.append(Dense(n_units=4096, act=tl.ReLU, in_channels=4096, name=layer_name)) + if layer_name == end_with: + is_end = True + if is_end: + break + return SequentialLayer(layer_list) + +def restore_model(model, layer_type): + logging.info("Restore pre-trained weights") + # download weights + maybe_download_and_extract(model_saved_name[layer_type], 'model', model_urls[layer_type]) + weights = [] + if layer_type == 'vgg16': + npz = np.load(os.path.join('model', model_saved_name[layer_type]), allow_pickle=True) + # get weight list + for val in sorted(npz.items()): + logging.info(" Loading weights %s in %s" % (str(val[1].shape), val[0])) + weights.append(val[1]) + if len(model.all_weights) == len(weights): + break + elif layer_type == 'vgg19': + npz = np.load(os.path.join('model', model_saved_name[layer_type]), allow_pickle=True, encoding='latin1').item() + # get weight list + for val in sorted(npz.items()): + logging.info(" Loading %s in %s" % (str(val[1][0].shape), val[0])) + logging.info(" Loading %s in %s" % (str(val[1][1].shape), val[0])) + weights.extend(val[1]) + if len(model.all_weights) == len(weights): + break + # assign weight values + assign_weights(weights, model) + del weights + +def vgg16(pretrained=False, end_with='outputs', mode='dynamic', name=None): + """Pre-trained VGG16 model. + + Parameters + ------------ + pretrained : boolean + Whether to load pretrained weights. Default False. + end_with : str + The end point of the model. Default ``fc3_relu`` i.e. the whole model. + mode : str. + Model building mode, 'dynamic' or 'static'. Default 'dynamic'. + name : None or str + A unique layer name. + + Examples + --------- + Classify ImageNet classes with VGG16, see `tutorial_models_vgg.py `__ + With TensorLayer + TODO Modify the usage example according to the model storage location + >>> # get the whole model, without pre-trained VGG parameters + >>> vgg = tl.models.vgg16() + >>> # get the whole model, restore pre-trained VGG parameters + >>> vgg = tl.models.vgg16(pretrained=True) + >>> # use for inferencing + >>> output = vgg(img, is_train=False) + >>> probs = tf.nn.softmax(output)[0].numpy() + + Extract features with VGG16 and Train a classifier with 100 classes + + >>> # get VGG without the last layer + >>> cnn = tl.models.vgg16(end_with='fc2_relu', mode='static').as_layer() + >>> # add one more layer and build a new model + >>> ni = Input([None, 224, 224, 3], name="inputs") + >>> nn = cnn(ni) + >>> nn = tl.layers.Dense(n_units=100, name='out')(nn) + >>> model = tl.models.Model(inputs=ni, outputs=nn) + >>> # train your own classifier (only update the last layer) + >>> train_params = model.get_layer('out').trainable_weights + + Reuse model + + >>> # in dynamic model, we can directly use the same model + >>> # in static model + >>> vgg_layer = tl.models.vgg16().as_layer() + >>> ni_1 = tl.layers.Input([None, 224, 244, 3]) + >>> ni_2 = tl.layers.Input([None, 224, 244, 3]) + >>> a_1 = vgg_layer(ni_1) + >>> a_2 = vgg_layer(ni_2) + >>> M = Model(inputs=[ni_1, ni_2], outputs=[a_1, a_2]) + + """ + if mode == 'dynamic': + model = VGG(layer_type='vgg16', batch_norm=False, end_with=end_with, name=name) + elif mode == 'static': + raise NotImplementedError + else: + raise Exception("No such mode %s" % mode) + if pretrained: + restore_model(model, layer_type='vgg16') + return model + + +def vgg19(pretrained=False, end_with='outputs', mode='dynamic', name=None): + """Pre-trained VGG19 model. + + Parameters + ------------ + pretrained : boolean + Whether to load pretrained weights. Default False. + end_with : str + The end point of the model. Default ``fc3_relu`` i.e. the whole model. + mode : str. + Model building mode, 'dynamic' or 'static'. Default 'dynamic'. + name : None or str + A unique layer name. + + Examples + --------- + Classify ImageNet classes with VGG19, see `tutorial_models_vgg.py `__ + With TensorLayer + + >>> # get the whole model, without pre-trained VGG parameters + >>> vgg = tl.models.vgg19() + >>> # get the whole model, restore pre-trained VGG parameters + >>> vgg = tl.models.vgg19(pretrained=True) + >>> # use for inferencing + >>> output = vgg(img, is_train=False) + >>> probs = tf.nn.softmax(output)[0].numpy() + + Extract features with VGG19 and Train a classifier with 100 classes + + >>> # get VGG without the last layer + >>> cnn = tl.models.vgg19(end_with='fc2_relu', mode='static').as_layer() + >>> # add one more layer and build a new model + >>> ni = Input([None, 224, 224, 3], name="inputs") + >>> nn = cnn(ni) + >>> nn = tl.layers.Dense(n_units=100, name='out')(nn) + >>> model = tl.models.Model(inputs=ni, outputs=nn) + >>> # train your own classifier (only update the last layer) + >>> train_params = model.get_layer('out').trainable_weights + + Reuse model + + >>> # in dynamic model, we can directly use the same model + >>> # in static model + >>> vgg_layer = tl.models.vgg19().as_layer() + >>> ni_1 = tl.layers.Input([None, 224, 244, 3]) + >>> ni_2 = tl.layers.Input([None, 224, 244, 3]) + >>> a_1 = vgg_layer(ni_1) + >>> a_2 = vgg_layer(ni_2) + >>> M = Model(inputs=[ni_1, ni_2], outputs=[a_1, a_2]) + + """ + if mode == 'dynamic': + model = VGG(layer_type='vgg19', batch_norm=False, end_with=end_with, name=name) + elif mode == 'static': + raise NotImplementedError + else: + raise Exception("No such mode %s" % mode) + if pretrained: + restore_model(model, layer_type='vgg19') + return model + + +VGG16 = vgg16 +VGG19 = vgg19 + +# models without pretrained parameters +# def vgg11(pretrained=False, end_with='outputs'): +# model = VGG(layer_type='vgg11', batch_norm=False, end_with=end_with) +# if pretrained: +# model.restore_weights() +# return model +# +# +# def vgg11_bn(pretrained=False, end_with='outputs'): +# model = VGG(layer_type='vgg11_bn', batch_norm=True, end_with=end_with) +# if pretrained: +# model.restore_weights() +# return model +# +# +# def vgg13(pretrained=False, end_with='outputs'): +# model = VGG(layer_type='vgg13', batch_norm=False, end_with=end_with) +# if pretrained: +# model.restore_weights() +# return model +# +# +# def vgg13_bn(pretrained=False, end_with='outputs'): +# model = VGG(layer_type='vgg13_bn', batch_norm=True, end_with=end_with) +# if pretrained: +# model.restore_weights() +# return model +# +# +# def vgg16_bn(pretrained=False, end_with='outputs'): +# model = VGG(layer_type='vgg16_bn', batch_norm=True, end_with=end_with) +# if pretrained: +# model.restore_weights() +# return model +# +# +# def vgg19_bn(pretrained=False, end_with='outputs'): +# model = VGG(layer_type='vgg19_bn', batch_norm=True, end_with=end_with) +# if pretrained: +# model.restore_weights() +# return model diff --git a/examples/model_zoo/yolo.py b/examples/model_zoo/yolo.py new file mode 100644 index 0000000..d3784b2 --- /dev/null +++ b/examples/model_zoo/yolo.py @@ -0,0 +1,376 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +"""YOLOv4 for MS-COCO. + +# Reference: +- [tensorflow-yolov4-tflite]( + https://github.com/hunglc007/tensorflow-yolov4-tflite) + +""" + +import numpy as np +import tensorlayer as tl +from tensorlayer.layers.activation import Mish +from tensorlayer.layers import Conv2d, MaxPool2d, BatchNorm2d, ZeroPad2d, UpSampling2d, Concat, Elementwise +from tensorlayer.layers import Module, SequentialLayer +from tensorlayer import logging + +INPUT_SIZE = 416 +weights_url = {'link': 'https://pan.baidu.com/s/1MC1dmEwpxsdgHO1MZ8fYRQ', 'password': 'idsz'} + + +class Convolutional(Module): + """ + Create Convolution layer + Because it is only a stack of reference layers, there is no build, so self._built=True + """ + def __init__(self, filters_shape, downsample=False, activate=True, bn=True, activate_type='leaky',name=None): + super(Convolutional, self).__init__() + self.act = activate + self.act_type = activate_type + self.downsample = downsample + self.bn = bn + self._built = True + if downsample: + padding = 'VALID' + strides = 2 + else: + strides = 1 + padding = 'SAME' + + if bn: + b_init = None + else: + b_init = tl.initializers.constant(value=0.0) + + self.zeropad = ZeroPad2d(((1, 0), (1, 0))) + self.conv = Conv2d(n_filter=filters_shape[-1], in_channels=filters_shape[2], filter_size=(filters_shape[0], filters_shape[1]), + strides=(strides, strides),padding=padding, b_init=b_init, name=name) + + if bn: + if activate == True: + if activate_type == 'leaky': + self.batchnorm2d = BatchNorm2d(act='leaky_relu0.1', num_features=filters_shape[-1]) + elif activate_type == 'mish': + self.batchnorm2d = BatchNorm2d(act=Mish, num_features=filters_shape[-1]) + else: + self.batchnorm2d = BatchNorm2d(act=None, num_features=filters_shape[-1]) + + def forward(self, input): + if self.downsample: + input = self.zeropad(input) + + output = self.conv(input) + + if self.bn: + output = self.batchnorm2d(output) + return output + +class residual_block(Module): + def __init__(self, input_channel, filter_num1, filter_num2, activate_type='leaky'): + super(residual_block, self).__init__() + self.conv1 = Convolutional(filters_shape=(1, 1, input_channel, filter_num1), activate_type=activate_type) + self.conv2 = Convolutional(filters_shape=(3, 3, filter_num1, filter_num2), activate_type=activate_type) + self.add = Elementwise(tl.add) + + def forward(self, inputs): + output = self.conv1(inputs) + output = self.conv2(output) + output = self.add([inputs, output]) + return output + +def residual_block_num(num, input_channel, filter_num1, filter_num2, activate_type='leaky'): + residual_list = [] + for i in range(num): + residual_list.append(residual_block(input_channel, filter_num1, filter_num2, activate_type=activate_type)) + return SequentialLayer(residual_list) + +class cspdarknet53(Module): + def __init__(self): + super(cspdarknet53, self).__init__() + self._built = True + self.conv1_1 = Convolutional((3, 3, 3, 32), activate_type='mish') + self.conv1_2 = Convolutional((3, 3, 32, 64), downsample=True, activate_type='mish') + self.conv1_3 = Convolutional((1, 1, 64, 64), activate_type='mish', name='conv_rote_block_1') + self.conv1_4 = Convolutional((1, 1, 64, 64), activate_type='mish') + self.residual_1 = residual_block_num(1, 64, 32, 64, activate_type="mish") + + self.conv2_1 = Convolutional((1, 1, 64, 64), activate_type='mish') + self.concat = Concat() + self.conv2_2 = Convolutional((1, 1, 128, 64), activate_type='mish') + self.conv2_3 = Convolutional((3, 3, 64, 128), downsample=True, activate_type='mish') + self.conv2_4 = Convolutional((1, 1, 128, 64), activate_type='mish', name='conv_rote_block_2') + self.conv2_5 = Convolutional((1, 1, 128, 64), activate_type='mish') + self.residual_2 = residual_block_num(2, 64, 64, 64, activate_type='mish') + + self.conv3_1 = Convolutional((1, 1, 64, 64), activate_type='mish') + self.conv3_2 = Convolutional((1, 1, 128, 128), activate_type='mish') + self.conv3_3 = Convolutional((3, 3, 128, 256), downsample=True, activate_type='mish') + self.conv3_4 = Convolutional((1, 1, 256, 128), activate_type='mish', name='conv_rote_block_3') + self.conv3_5 = Convolutional((1, 1, 256, 128), activate_type='mish') + self.residual_3 = residual_block_num(8, 128, 128, 128, activate_type="mish") + + self.conv4_1 = Convolutional((1, 1, 128, 128), activate_type='mish') + self.conv4_2 = Convolutional((1, 1, 256, 256), activate_type='mish') + self.conv4_3 = Convolutional((3, 3, 256, 512), downsample=True, activate_type='mish') + self.conv4_4 = Convolutional((1, 1, 512, 256), activate_type='mish', name='conv_rote_block_4') + self.conv4_5 = Convolutional((1, 1, 512, 256), activate_type='mish') + self.residual_4 = residual_block_num(8, 256, 256, 256, activate_type="mish") + + self.conv5_1 = Convolutional((1, 1, 256, 256), activate_type='mish') + self.conv5_2 = Convolutional((1, 1, 512, 512), activate_type='mish') + self.conv5_3 = Convolutional((3, 3, 512, 1024), downsample=True, activate_type='mish') + self.conv5_4 = Convolutional((1, 1, 1024, 512), activate_type='mish', name='conv_rote_block_5') + self.conv5_5 = Convolutional((1, 1, 1024, 512), activate_type='mish') + self.residual_5 = residual_block_num(4, 512, 512, 512, activate_type="mish") + + + self.conv6_1 = Convolutional((1, 1, 512, 512), activate_type='mish') + self.conv6_2 = Convolutional((1, 1, 1024, 1024), activate_type='mish') + self.conv6_3 = Convolutional((1, 1, 1024, 512)) + self.conv6_4 = Convolutional((3, 3, 512, 1024)) + self.conv6_5 = Convolutional((1, 1, 1024, 512)) + + self.maxpool1 = MaxPool2d(filter_size=(13, 13), strides=(1, 1)) + self.maxpool2 = MaxPool2d(filter_size=(9, 9), strides=(1, 1)) + self.maxpool3 = MaxPool2d(filter_size=(5, 5), strides=(1, 1)) + + self.conv7_1 = Convolutional((1, 1, 2048, 512)) + self.conv7_2 = Convolutional((3, 3, 512, 1024)) + self.conv7_3 = Convolutional((1, 1, 1024, 512)) + + def forward(self, input_data): + input_data = self.conv1_1(input_data) + input_data = self.conv1_2(input_data) + route = input_data + route = self.conv1_3(route) + input_data = self.conv1_4(input_data) + input_data = self.residual_1(input_data) + + input_data = self.conv2_1(input_data) + input_data = self.concat([input_data, route]) + input_data = self.conv2_2(input_data) + input_data = self.conv2_3(input_data) + route = input_data + route = self.conv2_4(route) + input_data = self.conv2_5(input_data) + input_data = self.residual_2(input_data) + + input_data = self.conv3_1(input_data) + input_data = self.concat([input_data, route]) + input_data = self.conv3_2(input_data) + input_data = self.conv3_3(input_data) + route = input_data + route = self.conv3_4(route) + input_data = self.conv3_5(input_data) + input_data = self.residual_3(input_data) + + input_data = self.conv4_1(input_data) + input_data = self.concat([input_data, route]) + input_data = self.conv4_2(input_data) + route_1 = input_data + input_data = self.conv4_3(input_data) + route = input_data + route = self.conv4_4(route) + input_data = self.conv4_5(input_data) + input_data = self.residual_4(input_data) + + input_data = self.conv5_1(input_data) + input_data = self.concat([input_data, route]) + input_data = self.conv5_2(input_data) + route_2 = input_data + input_data = self.conv5_3(input_data) + route = input_data + route = self.conv5_4(route) + input_data = self.conv5_5(input_data) + input_data = self.residual_5(input_data) + + input_data = self.conv6_1(input_data) + input_data = self.concat([input_data, route]) + + input_data = self.conv6_2(input_data) + input_data = self.conv6_3(input_data) + input_data = self.conv6_4(input_data) + input_data = self.conv6_5(input_data) + + maxpool1 = self.maxpool1(input_data) + maxpool2 = self.maxpool2(input_data) + maxpool3 = self.maxpool3(input_data) + input_data = self.concat([maxpool1, maxpool2, maxpool3, input_data]) + + input_data = self.conv7_1(input_data) + input_data = self.conv7_2(input_data) + input_data = self.conv7_3(input_data) + + return route_1, route_2, input_data + + +class YOLOv4_model(Module): + def __init__(self, NUM_CLASS): + super(YOLOv4_model, self).__init__() + self.cspdarnnet = cspdarknet53() + + self.conv1_1 = Convolutional((1, 1, 512, 256)) + self.upsamle = UpSampling2d(scale=2) + self.conv1_2 = Convolutional((1, 1, 512, 256), name='conv_yolo_1') + self.concat = Concat() + + self.conv2_1 = Convolutional((1, 1, 512, 256)) + self.conv2_2 = Convolutional((3, 3, 256, 512)) + self.conv2_3 = Convolutional((1, 1, 512, 256)) + self.conv2_4 = Convolutional((3, 3, 256, 512)) + self.conv2_5 = Convolutional((1, 1, 512, 256)) + + self.conv3_1 = Convolutional((1, 1, 256, 128)) + self.conv3_2 = Convolutional((1, 1, 256, 128), name='conv_yolo_2') + + self.conv4_1 = Convolutional((1, 1, 256, 128)) + self.conv4_2 = Convolutional((3, 3, 128, 256)) + self.conv4_3 = Convolutional((1, 1, 256, 128)) + self.conv4_4 = Convolutional((3, 3, 128, 256)) + self.conv4_5 = Convolutional((1, 1, 256, 128)) + + self.conv5_1 = Convolutional((3, 3, 128, 256), name='conv_route_1') + self.conv5_2 = Convolutional((1, 1, 256, 3 * (NUM_CLASS + 5)), activate=False, bn=False) + + self.conv6_1 = Convolutional((3, 3, 128, 256), downsample=True, name='conv_route_2') + self.conv6_2 = Convolutional((1, 1, 512, 256)) + self.conv6_3 = Convolutional((3, 3, 256, 512)) + self.conv6_4 = Convolutional((1, 1, 512, 256)) + self.conv6_5 = Convolutional((3, 3, 256, 512)) + self.conv6_6 = Convolutional((1, 1, 512, 256)) + + self.conv7_1 = Convolutional((3, 3, 256, 512), name='conv_route_3') + self.conv7_2 = Convolutional((1, 1, 512, 3 * (NUM_CLASS + 5)), activate=False, bn=False) + self.conv7_3 = Convolutional((3, 3, 256, 512), downsample=True, name='conv_route_4') + + self.conv8_1 = Convolutional((1, 1, 1024, 512)) + self.conv8_2 = Convolutional((3, 3, 512, 1024)) + self.conv8_3 = Convolutional((1, 1, 1024, 512)) + self.conv8_4 = Convolutional((3, 3, 512, 1024)) + self.conv8_5 = Convolutional((1, 1, 1024, 512)) + + self.conv9_1 = Convolutional((3, 3, 512, 1024)) + self.conv9_2 = Convolutional((1, 1, 1024, 3 * (NUM_CLASS + 5)), activate=False, bn=False) + + def forward(self, inputs): + route_1, route_2, conv = self.cspdarnnet(inputs) + + route = conv + conv = self.conv1_1(conv) + conv = self.upsamle(conv) + route_2 = self.conv1_2(route_2) + conv = self.concat([route_2, conv]) + + conv = self.conv2_1(conv) + conv = self.conv2_2(conv) + conv = self.conv2_3(conv) + conv = self.conv2_4(conv) + conv = self.conv2_5(conv) + + route_2 = conv + conv = self.conv3_1(conv) + conv = self.upsamle(conv) + route_1 = self.conv3_2(route_1) + conv = self.concat([route_1, conv]) + + conv = self.conv4_1(conv) + conv = self.conv4_2(conv) + conv = self.conv4_3(conv) + conv = self.conv4_4(conv) + conv = self.conv4_5(conv) + + route_1 = conv + conv = self.conv5_1(conv) + conv_sbbox = self.conv5_2(conv) + + conv = self.conv6_1(route_1) + conv = self.concat([conv, route_2]) + + conv = self.conv6_2(conv) + conv = self.conv6_3(conv) + conv = self.conv6_4(conv) + conv = self.conv6_5(conv) + conv = self.conv6_6(conv) + + route_2 = conv + conv = self.conv7_1(conv) + conv_mbbox = self.conv7_2(conv) + conv = self.conv7_3(route_2) + conv = self.concat([conv, route]) + + conv = self.conv8_1(conv) + conv = self.conv8_2(conv) + conv = self.conv8_3(conv) + conv = self.conv8_4(conv) + conv = self.conv8_5(conv) + + conv = self.conv9_1(conv) + conv_lbbox = self.conv9_2(conv) + + return conv_sbbox, conv_mbbox, conv_lbbox + +def YOLOv4(NUM_CLASS, pretrained=False): + """Pre-trained YOLOv4 model. + + Parameters + ------------ + NUM_CLASS : int + Number of classes in final prediction. + pretrained : boolean + Whether to load pretrained weights. Default False. + + Examples + --------- + Object Detection with YOLOv4, see `computer_vision.py + `__ + With TensorLayer + + >>> # get the whole model, without pre-trained YOLOv4 parameters + >>> yolov4 = tl.app.YOLOv4(NUM_CLASS=80, pretrained=False) + >>> # get the whole model, restore pre-trained YOLOv4 parameters + >>> yolov4 = tl.app.YOLOv4(NUM_CLASS=80, pretrained=True) + >>> # use for inferencing + >>> output = yolov4(img, is_train=False) + + """ + + network = YOLOv4_model(NUM_CLASS=NUM_CLASS) + + if pretrained: + restore_params(network, model_path='model/yolov4_model.npz') + + return network + + +def restore_params(network, model_path='models.npz'): + logging.info("Restore pre-trained weights") + + try: + npz = np.load(model_path, allow_pickle=True) + except: + print("Download the model file, placed in the /model ") + print("Weights download: ", weights_url['link'], "password:", weights_url['password']) + + txt_path = 'model/yolov4_weights3_config.txt' + f = open(txt_path, "r") + line = f.readlines() + for i in range(len(line)): + network.all_weights[i].assign(npz[line[i].strip()]) + logging.info(" Loading weights %s in %s" % (network.all_weights[i].shape, network.all_weights[i].name)) + +def tl2_weights_to_tl3_weights(weights_2_path='model/weights_2.txt', weights_3_path='model/weights_3.txt', txt_path='model/yolov4_weights_config.txt'): + weights_2_path = weights_2_path + weights_3_path = weights_3_path + txt_path = txt_path + f1 = open(weights_2_path, "r") + f2 = open(weights_3_path, "r") + f3 = open(txt_path, "r") + line1 = f1.readlines() + line2 = f2.readlines() + line3 = f3.readlines() + _dicts = {} + for i in range(len(line1)): + _dicts[line1[i].strip()] = line3[i].strip() + for j in range(len(line2)): + print(_dicts[line2[j].strip()]) diff --git a/img/TL_gardener.png b/img/TL_gardener.png new file mode 100644 index 0000000..ca60068 Binary files /dev/null and b/img/TL_gardener.png differ diff --git a/img/TL_gardener.psd b/img/TL_gardener.psd new file mode 100644 index 0000000..e45b815 Binary files /dev/null and b/img/TL_gardener.psd differ diff --git a/img/awesome-mentioned.png b/img/awesome-mentioned.png new file mode 100644 index 0000000..1bbc400 Binary files /dev/null and b/img/awesome-mentioned.png differ diff --git a/img/github_mascot.png b/img/github_mascot.png new file mode 100644 index 0000000..a93ba99 Binary files /dev/null and b/img/github_mascot.png differ diff --git a/img/img_tensorflow.png b/img/img_tensorflow.png new file mode 100644 index 0000000..a8190c9 Binary files /dev/null and b/img/img_tensorflow.png differ diff --git a/img/img_tensorlayer.png b/img/img_tensorlayer.png new file mode 100644 index 0000000..c6b1f7c Binary files /dev/null and b/img/img_tensorlayer.png differ diff --git a/img/img_tlayer1.png b/img/img_tlayer1.png new file mode 100644 index 0000000..7b998ea Binary files /dev/null and b/img/img_tlayer1.png differ diff --git a/img/join_slack.png b/img/join_slack.png new file mode 100644 index 0000000..96e5902 Binary files /dev/null and b/img/join_slack.png differ diff --git a/img/join_slack.psd b/img/join_slack.psd new file mode 100644 index 0000000..0716f60 Binary files /dev/null and b/img/join_slack.psd differ diff --git a/img/medium/Readme.md b/img/medium/Readme.md new file mode 100644 index 0000000..35ea25a --- /dev/null +++ b/img/medium/Readme.md @@ -0,0 +1,19 @@ +# Medium GuideLines + +## Publication Avatar + +![Medium Avatar](https://help.medium.com/hc/article_attachments/115008917667/pub1.png) + +## Publication Logo (appears on posts) + +![Medium Publication Logo 1](https://help.medium.com/hc/article_attachments/115009021788/pub2.png) +![Medium Publication Logo 2](https://help.medium.com/hc/article_attachments/115009021788/pub3.png) + +Add a publication logo, which appears at the top of all your publication's stories. + +It is 72px tall and can have a maximum width of 600px. + +## Publication homepage images + +Under Homepage and settings > Layout, you can select a header size, upload a logo and add a background image (large header size only). + diff --git a/img/medium/medium_header.png b/img/medium/medium_header.png new file mode 100644 index 0000000..87bebf3 Binary files /dev/null and b/img/medium/medium_header.png differ diff --git a/img/medium/medium_header.psd b/img/medium/medium_header.psd new file mode 100644 index 0000000..9212a0a Binary files /dev/null and b/img/medium/medium_header.psd differ diff --git a/img/tl_black_logo.png b/img/tl_black_logo.png new file mode 100644 index 0000000..8bf892d Binary files /dev/null and b/img/tl_black_logo.png differ diff --git a/img/tl_transparent_logo.png b/img/tl_transparent_logo.png new file mode 100644 index 0000000..359a232 Binary files /dev/null and b/img/tl_transparent_logo.png differ diff --git a/img/tl_white_logo.png b/img/tl_white_logo.png new file mode 100644 index 0000000..a42806d Binary files /dev/null and b/img/tl_white_logo.png differ diff --git a/requirements/requirements.txt b/requirements/requirements.txt new file mode 100644 index 0000000..f251c65 --- /dev/null +++ b/requirements/requirements.txt @@ -0,0 +1,10 @@ +imageio>=2.5.0 +numpy>=1.16 +progressbar2>=3.39.3 +requests>=2.21.0 +scikit-learn>=0.21.0 +scikit-image>=0.15.0 +scipy>=1.2.1 +wrapt>=1.11.1 +h5py>=2.9 +cloudpickle>=0.8.1 diff --git a/requirements/requirements_contrib_loggers.txt b/requirements/requirements_contrib_loggers.txt new file mode 100644 index 0000000..245d842 --- /dev/null +++ b/requirements/requirements_contrib_loggers.txt @@ -0,0 +1 @@ +hyperdash>=0.15,<0.16 \ No newline at end of file diff --git a/requirements/requirements_db.txt b/requirements/requirements_db.txt new file mode 100644 index 0000000..f9cb647 --- /dev/null +++ b/requirements/requirements_db.txt @@ -0,0 +1 @@ +pymongo>=3.8.0 diff --git a/requirements/requirements_dev.txt b/requirements/requirements_dev.txt new file mode 100644 index 0000000..ef32fc1 --- /dev/null +++ b/requirements/requirements_dev.txt @@ -0,0 +1 @@ +autopep8>=1.3,<1.5 diff --git a/requirements/requirements_doc.txt b/requirements/requirements_doc.txt new file mode 100644 index 0000000..02d813a --- /dev/null +++ b/requirements/requirements_doc.txt @@ -0,0 +1,8 @@ +flake8-docstrings>=1.3,<1.4 +pycodestyle>=2.5.0 +pydocstyle>=2.1,<3.1 +sphinx==2.0.1 +sphinx_rtd_theme>=0.4,<0.5 +wrapt>=1.11.1 +h5py>=2.9 +cloudpickle>=0.8.1 diff --git a/requirements/requirements_extra.txt b/requirements/requirements_extra.txt new file mode 100644 index 0000000..3a818c6 --- /dev/null +++ b/requirements/requirements_extra.txt @@ -0,0 +1,6 @@ +opencv-python>=4.1.0.25 +nltk>=3.3,<3.5 +matplotlib>=2.2,<3.1 +requests>=2.21.0 +tqdm>=4.31.1 +lxml>=4.3.3 diff --git a/requirements/requirements_test.txt b/requirements/requirements_test.txt new file mode 100644 index 0000000..e47c0ed --- /dev/null +++ b/requirements/requirements_test.txt @@ -0,0 +1,9 @@ +keras>=2.2,<2.3 +pycodestyle>=2.5.0 +pydocstyle>=2.1,<3.1 +pytest>=4.5.0 +pytest-cache>=1.0,<1.1 +pytest-cov>=2.7.1 +pytest-xdist>=1.28.0 +sphinx==2.0.1 +yapf>=0.27.0 diff --git a/requirements/requirements_tf_cpu.txt b/requirements/requirements_tf_cpu.txt new file mode 100644 index 0000000..72e758e --- /dev/null +++ b/requirements/requirements_tf_cpu.txt @@ -0,0 +1 @@ +tensorflow>=2.0.0-alpha0 diff --git a/requirements/requirements_tf_gpu.txt b/requirements/requirements_tf_gpu.txt new file mode 100644 index 0000000..25247e0 --- /dev/null +++ b/requirements/requirements_tf_gpu.txt @@ -0,0 +1 @@ +tensorflow-gpu>=2.0.0-alpha0 diff --git a/run_compile.py b/run_compile.py new file mode 100644 index 0000000..495272d --- /dev/null +++ b/run_compile.py @@ -0,0 +1,74 @@ +import tensorlayer as T +from dragon.vm.tensorlayer.layers import Dense +from dragon.vm.tensorlayer.models import Model +import dragon.vm.tensorlayer as tl + +import dragon as dg +import argparse +import numpy as np + +X_train, y_train, X_val, y_val, X_test, y_test = T.files.load_mnist_dataset(shape=(-1, 784)) + + +class MLP(Model): + + def __init__(self): + super(MLP, self).__init__() + self.dense1 = Dense(n_units=800, act=tl.act.relu, in_channels=784) + self.dense2 = Dense(n_units=800, act=tl.act.relu, in_channels=800) + self.dense3 = Dense(n_units=10, act=tl.act.relu, in_channels=800) + + def forward(self, x): + z = self.dense1(x) + z = self.dense2(z) + out = self.dense3(z) + return out + + +class Classifier(object): + """The base classifier class.""" + + # TensorSpec for graph execution + image_spec = dg.Tensor([None, 3, 32, 32], 'float32') + label_spec = dg.Tensor([None], 'int64') + + def __init__(self, optimizer): + super(Classifier, self).__init__() + self.net = MLP() + self.optimizer = optimizer + self.params = self.net.trainable_weights + + def step(self, image, label): + with dg.GradientTape() as tape: + logit = self.net(image) + # logit = dg.cast(logit, 'float64') + logit = dg.cast(dg.math.argmax(logit, -1), 'int32') + # label = dg.cast(label, 'float32') + # print("logit :\n", logit, label) + # loss = dg.losses.smooth_l1_loss([logit, label]) + # loss = tl.losses.sparse_softmax_crossentropy(logit, label) + loss = dg.math.sum( + (logit - label) * (logit - label) + ) # dg.losses.sparse_softmax_cross_entropy([logit, label]) + accuracy = dg.math.mean(dg.math.equal([logit, label]).astype('float32')) + grads = tape.gradient(loss, self.params) + self.optimizer.apply_gradients(zip(self.params, grads)) + return loss, accuracy, self.optimizer + + +if __name__ == '__main__': + dg.autograph.set_execution('EAGER_MODE') + # Define the model + model = Classifier(dg.optimizers.SGD(base_lr=0.001, momentum=0.9, weight_decay=1e-4)) + # Main loop + batch_size = 200 + for i in range(50): + for X_batch, y_batch in T.iterate.minibatches(X_train, y_train, batch_size, shuffle=True): + image = dg.EagerTensor(X_batch, copy=False) + label = dg.EagerTensor(y_batch, copy=False, dtype='float32') + loss, accuracy, _ = model.step(image, label) + if i % 20 == 0: + dg.logging.info( + 'Iteration %d, lr = %s, loss = %.5f, accuracy = %.3f' % + (i, str(model.optimizer.base_lr), loss, accuracy) + ) diff --git a/scripts/download_and_install_openmpi3_ubuntu.sh b/scripts/download_and_install_openmpi3_ubuntu.sh new file mode 100644 index 0000000..8aa233a --- /dev/null +++ b/scripts/download_and_install_openmpi3_ubuntu.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +set -e + +if [ $(uname) == "Darwin" ]; then + NPROC=$(sysctl -n hw.ncpu) +else + NPROC=$(nproc) +fi + +mkdir -p $HOME/openmpi_tmp && cd $HOME/openmpi_tmp + +# TODO: upgrade to latest version once https://github.com/open-mpi/ompi/pull/5296 is in the release +MPI_MAJOR=3 +MPI_MINOR=1 + +VERSION=${MPI_MAJOR}.${MPI_MINOR}.0 +FILENAME=openmpi-${VERSION}.tar.bz2 +FOLDER=openmpi-${VERSION} +URL=https://download.open-mpi.org/release/open-mpi/v${MPI_MAJOR}.${MPI_MINOR}/${FILENAME} + +[ ! -f ${FILENAME} ] && curl -vLOJ $URL +tar -xf ${FILENAME} +cd ${FOLDER} + +# will take about 8 min or longer depends on your machine +./configure --prefix=$HOME/local/openmpi +make -j ${NPROC} all +make install + +rm -rf $HOME/openmpi_tmp + +echo 'Update the PATH with OpenMPI bin by running: PATH=$PATH:$HOME/local/openmpi/bin' +echo 'Update the PATH in ~/.bashrc if you want OpenMPI to be ready once the machine start' diff --git a/scripts/install-horovod-for-doc-test.sh b/scripts/install-horovod-for-doc-test.sh new file mode 100644 index 0000000..22c0558 --- /dev/null +++ b/scripts/install-horovod-for-doc-test.sh @@ -0,0 +1,15 @@ +# This script is for installing horovod on travis-CI only! + +set -e + +[ ! -z "$1" ] && export PATH=$1:$PATH + +mkdir -p /opt +chmod a+rx /opt +cd /opt +wget https://github.com/lgarithm/openmpi-release/raw/master/releases/openmpi-bin-3.1.0.tar.bz2 +tar -xf openmpi-bin-3.1.0.tar.bz2 + +PATH=/opt/openmpi/bin:$PATH pip install horovod + +echo "done $0" diff --git a/scripts/install-requirements-for-rtd.sh b/scripts/install-requirements-for-rtd.sh new file mode 100644 index 0000000..fd0693c --- /dev/null +++ b/scripts/install-requirements-for-rtd.sh @@ -0,0 +1,28 @@ +# This script is for installing horovod on readthedocs only! +set -e + +pwd +SCRIPT_DIR=$(cd $(dirname $0) && pwd) + +[ ! -z "$1" ] && export PATH=$1:$PATH + +LOCATION=/home/docs +URL=https://github.com/lgarithm/openmpi-release/raw/master/releases/openmpi-bin-3.1.0-rtd.tar.bz2 + +mkdir -p ${LOCATION} +chmod a+rx ${LOCATION} +cd ${LOCATION} +curl -vLOJ ${URL} +tar -xf *.tar.bz2 + +pip install tensorflow==1.5.0 # must install tensorflow before horovod +PATH=${LOCATION}/openmpi/bin:$PATH pip install horovod + +# install all requirements except tensorflow +for req in $(find $SCRIPT_DIR/../requirements -type f); do + if [ ! $(grep tensorflow $req) ]; then + pip install -r $req + fi +done + +echo "done $0" diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..67f86fe --- /dev/null +++ b/setup.cfg @@ -0,0 +1,79 @@ +[tool:pytest] +testpaths = tests/ + +[flake8] +max-line-length = 120 +ignore = + D301 + E221 # Space before equal sign + E251 # Space after equal sign +exclude = + .git, + venv, + __pycache__, + .pytest_cache, + tensorlayer.egg-info, + build, + dist, + img + +[yapf] +based_on_style=google + +# The number of columns to use for indentation. +indent_width = 4 + +# The column limit. +column_limit=120 + +# Place each dictionary entry onto its own line. +each_dict_entry_on_separate_line = True + +# Put closing brackets on a separate line, dedented, if the bracketed +# expression can't fit in a single line. Applies to all kinds of brackets, +# including function definitions and calls. For example: +# +# config = { +# 'key1': 'value1', +# 'key2': 'value2', +# } # <--- this bracket is dedented and on a separate line +# +# time_series = self.remote_client.query_entity_counters( +# entity='dev3246.region1', +# key='dns.query_latency_tcp', +# transform=Transformation.AVERAGE(window=timedelta(seconds=60)), +# start_ts=now()-timedelta(days=3), +# end_ts=now(), +# ) # <--- this bracket is dedented and on a separate line +dedent_closing_brackets=True + +# Do not split consecutive brackets. Only relevant when DEDENT_CLOSING_BRACKETS is set +coalesce_brackets = False + +# Align closing bracket with visual indentation. +align_closing_bracket_with_visual_indent = False + +# Split named assignments onto individual lines. +split_before_named_assigns = False + +# If an argument / parameter list is going to be split, then split before the first argument. +split_before_first_argument = True + +# Split before arguments if the argument list is terminated by a comma. +split_arguments_when_comma_terminated = False + +# Insert a space between the ending comma and closing bracket of a list, etc. +space_between_ending_comma_and_closing_bracket = True + +# Join short lines into one line. E.g., single line if statements. +join_multiple_lines = True + +# Do not include spaces around selected binary operators. +# Example: 1 + 2 * 3 - 4 / 5 => 1 + 2*3 - 4/5 +no_spaces_around_selected_binary_operators = True + +# Allow lambdas to be formatted on more than one line. +allow_multiline_lambdas = True + +SPLIT_PENALTY_FOR_ADDED_LINE_SPLIT = 10 +SPLIT_PENALTY_AFTER_OPENING_BRACKET = 500 \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..76ac682 --- /dev/null +++ b/setup.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python +import codecs +import os +import sys + +os.environ['TENSORLAYER_PACKAGE_BUILDING'] = 'True' + + +try: + from setuptools import find_packages, setup, Extension + from setuptools.command.build_ext import build_ext + +except ImportError: + from distutils.core import ( + setup, + find_packages + ) + + +from tensorlayer import ( + __contact_emails__, + __contact_names__, + __description__, + __download_url__, + __homepage__, + __keywords__, + __license__, + __package_name__, + __repository_url__, + __version__ +) + + +# =================== Reading Readme file as TXT files =================== + +if os.path.exists('README.rst'): + # codec is used for consistent encoding + long_description = codecs.open( + os.path.join(os.path.abspath(os.path.dirname(__file__)), 'README.rst'), + 'r', 'utf-8' + ).read() + +else: + long_description = 'See ' + __homepage__ + +# ======================= Reading Requirements files as TXT files ======================= + + +def req_file(filename, folder="requirements"): + with open(os.path.join(folder, filename)) as f: + content = f.readlines() + # you may also want to remove whitespace characters + # Example: `\n` at the end of each line + return [x.strip() for x in content] + +# ======================= Defining the requirements var ======================= + + +install_requires = req_file("requirements.txt") + +extras_require = { + # User packages + 'tf_cpu': req_file("requirements_tf_cpu.txt"), + 'tf_gpu': req_file("requirements_tf_gpu.txt"), + 'extra': req_file("requirements_extra.txt"), + + # Contrib Packages + 'contrib_loggers': req_file("requirements_contrib_loggers.txt"), + + # Dev Packages + 'test': req_file("requirements_test.txt"), + 'dev': req_file("requirements_dev.txt"), + 'doc': req_file("requirements_doc.txt"), + 'db': req_file("requirements_db.txt"), +} + +extras_require['all'] = sum([extras_require.get(key) for key in ['extra', 'contrib_loggers']], list()) + +extras_require['all_cpu'] = sum([extras_require.get(key) for key in ['all', 'tf_cpu']], list()) +extras_require['all_gpu'] = sum([extras_require.get(key) for key in ['all', 'tf_gpu']], list()) + +extras_require['all_dev'] = sum([extras_require.get(key) for key in ['all', 'db', 'dev', 'doc', 'test']], list()) +extras_require['all_cpu_dev'] = sum([extras_require.get(key) for key in ['all_dev', 'tf_cpu']], list()) +extras_require['all_gpu_dev'] = sum([extras_require.get(key) for key in ['all_dev', 'tf_gpu']], list()) + + +cmdclass = dict() +ext_modules = [] + + +# Readthedocs requires TF 1.5.0 to build properly +if 'READTHEDOCS' in os.environ: + ext_modules = [ + Extension('install_requirements_for_rtd', []), + ] + + class custom_build_ext(build_ext): + def build_extensions(self): + os.system('./scripts/install-requirements-for-rtd.sh %s' % + os.path.dirname(sys.executable)) + + cmdclass = {'build_ext': custom_build_ext} + + +# ======================= Define the package setup ======================= + + +setup( + name=__package_name__, + + # Versions should comply with PEP440. For a discussion on single-sourcing + # the version across setup.py and the project code, see + # https://packaging.python.org/en/latest/single_source_version.html + version=__version__, + + description=__description__, + long_description=long_description, + + # The project's main homepage. + url=__repository_url__, + download_url=__download_url__, + + # Author details + author=__contact_names__, + author_email=__contact_emails__, + + # maintainer Details + maintainer=__contact_names__, + maintainer_email=__contact_emails__, + + # The licence under which the project is released + license=__license__, + + classifiers=[ + # How mature is this project? Common values are + # 1 - Planning + # 2 - Pre-Alpha + # 3 - Alpha + # 4 - Beta + # 5 - Production/Stable + # 6 - Mature + # 7 - Inactive + 'Development Status :: 5 - Production/Stable', + + # Indicate who your project is intended for + 'Intended Audience :: Developers', + 'Intended Audience :: Science/Research', + 'Intended Audience :: Information Technology', + + # Indicate what your project relates to + 'Topic :: Scientific/Engineering', + 'Topic :: Scientific/Engineering :: Image Recognition', + 'Topic :: Scientific/Engineering :: Artificial Intelligence', + 'Topic :: Software Development :: Libraries', + 'Topic :: Utilities', + + # Pick your license as you wish (should match "license" above) + 'License :: OSI Approved :: Apache Software License', + + # Specify the Python versions you support here. In particular, ensure + # that you indicate whether you support Python 2, Python 3 or both. + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + + # Additionnal Settings + 'Environment :: Console', + 'Natural Language :: English', + 'Operating System :: OS Independent', + ], + + keywords=__keywords__, + packages=find_packages(), + + # List run-time dependencies here. These will be installed by pip when + # your project is installed. For an analysis of "install_requires" vs pip's + # requirements files see: + # https://packaging.python.org/en/latest/requirements.html + install_requires=install_requires, + + cmdclass=cmdclass, + + # List additional groups of dependencies here (e.g. development + # dependencies). You can install these using the following syntax, + # $ pip install -e .[test] + extras_require=extras_require, + ext_modules=ext_modules, + + scripts=[ + 'tl', + ], +) diff --git a/setup.travis.cfg b/setup.travis.cfg new file mode 100644 index 0000000..b786d1b --- /dev/null +++ b/setup.travis.cfg @@ -0,0 +1,111 @@ +[tool:pytest] +testpaths = tests/ +addopts = --ignore=tests/test_documentation.py + --ignore=tests/test_yapf_format.py + --ignore=tests/pending/test_decorators.py + --ignore=tests/pending/test_documentation.py + --ignore=tests/pending/test_logging.py + --ignore=tests/pending/test_pydocstyle.py + --ignore=tests/pending/test_layers_padding.py + --ignore=tests/pending/test_timeout.py + --ignore=tests/pending/test_layers_super_resolution.py + --ignore=tests/pending/test_reuse_mlp.py + --ignore=tests/pending/test_layers_importer.py + --ignore=tests/pending/test_layers_time_distributed.py + --ignore=tests/pending/test_layers_spatial_transformer.py + --ignore=tests/pending/test_layers_stack.py + --ignore=tests/pending/test_mnist_simple.py + --ignore=tests/pending/test_tf_layers.py + --ignore=tests/pending/test_array_ops.py + --ignore=tests/pending/test_layers_basic.py + --ignore=tests/pending/test_models.py + --ignore=tests/pending/test_optimizer_amsgrad.py + --ignore=tests/pending/test_logging_hyperdash.py + --ignore=tests/pending/test_yapf_format.py + --ignore=tests/pending/test_layers_normalization.py + --ignore=tests/pending/test_utils_predict.py + --ignore=tests/pending/test_layers_flow_control.py + --ignore=tests/performance_test/vgg/tl2-autograph.py + --ignore=tests/performance_test/vgg/tf2-eager.py + --ignore=tests/performance_test/vgg/exp_config.py + --ignore=tests/performance_test/vgg/tl2-eager.py + --ignore=tests/performance_test/vgg/tf2-autograph.py + --ignore=tests/performance_test/vgg/keras_test.py + --ignore=tests/performance_test/vgg/pytorch_test.py + +[flake8] +max-line-length = 120 +ignore = + D301 + E221 # Space before equal sign + E251 # Space after equal sign +exclude = + .git, + venv, + __pycache__, + .pytest_cache, + tensorlayer.egg-info, + build, + dist, + img + +[yapf] +based_on_style=google + +# The number of columns to use for indentation. +indent_width = 4 + +# The column limit. +column_limit=120 + +# Place each dictionary entry onto its own line. +each_dict_entry_on_separate_line = True + +# Put closing brackets on a separate line, dedented, if the bracketed +# expression can't fit in a single line. Applies to all kinds of brackets, +# including function definitions and calls. For example: +# +# config = { +# 'key1': 'value1', +# 'key2': 'value2', +# } # <--- this bracket is dedented and on a separate line +# +# time_series = self.remote_client.query_entity_counters( +# entity='dev3246.region1', +# key='dns.query_latency_tcp', +# transform=Transformation.AVERAGE(window=timedelta(seconds=60)), +# start_ts=now()-timedelta(days=3), +# end_ts=now(), +# ) # <--- this bracket is dedented and on a separate line +dedent_closing_brackets=True + +# Do not split consecutive brackets. Only relevant when DEDENT_CLOSING_BRACKETS is set +coalesce_brackets = False + +# Align closing bracket with visual indentation. +align_closing_bracket_with_visual_indent = False + +# Split named assignments onto individual lines. +split_before_named_assigns = False + +# If an argument / parameter list is going to be split, then split before the first argument. +split_before_first_argument = True + +# Split before arguments if the argument list is terminated by a comma. +split_arguments_when_comma_terminated = False + +# Insert a space between the ending comma and closing bracket of a list, etc. +space_between_ending_comma_and_closing_bracket = True + +# Join short lines into one line. E.g., single line if statements. +join_multiple_lines = True + +# Do not include spaces around selected binary operators. +# Example: 1 + 2 * 3 - 4 / 5 => 1 + 2*3 - 4/5 +no_spaces_around_selected_binary_operators = True + +# Allow lambdas to be formatted on more than one line. +allow_multiline_lambdas = True + +SPLIT_PENALTY_FOR_ADDED_LINE_SPLIT = 10 +SPLIT_PENALTY_AFTER_OPENING_BRACKET = 500 diff --git a/setup.travis_doc.cfg b/setup.travis_doc.cfg new file mode 100644 index 0000000..55267cf --- /dev/null +++ b/setup.travis_doc.cfg @@ -0,0 +1,81 @@ +[tool:pytest] +testpaths = tests/ +python_files=*test_documentation* + *test_yapf_format* + +[flake8] +max-line-length = 120 +ignore = + D301 + E221 # Space before equal sign + E251 # Space after equal sign +exclude = + .git, + venv, + __pycache__, + .pytest_cache, + tensorlayer.egg-info, + build, + dist, + img + +[yapf] +based_on_style=google + +# The number of columns to use for indentation. +indent_width = 4 + +# The column limit. +column_limit=120 + +# Place each dictionary entry onto its own line. +each_dict_entry_on_separate_line = True + +# Put closing brackets on a separate line, dedented, if the bracketed +# expression can't fit in a single line. Applies to all kinds of brackets, +# including function definitions and calls. For example: +# +# config = { +# 'key1': 'value1', +# 'key2': 'value2', +# } # <--- this bracket is dedented and on a separate line +# +# time_series = self.remote_client.query_entity_counters( +# entity='dev3246.region1', +# key='dns.query_latency_tcp', +# transform=Transformation.AVERAGE(window=timedelta(seconds=60)), +# start_ts=now()-timedelta(days=3), +# end_ts=now(), +# ) # <--- this bracket is dedented and on a separate line +dedent_closing_brackets=True + +# Do not split consecutive brackets. Only relevant when DEDENT_CLOSING_BRACKETS is set +coalesce_brackets = False + +# Align closing bracket with visual indentation. +align_closing_bracket_with_visual_indent = False + +# Split named assignments onto individual lines. +split_before_named_assigns = False + +# If an argument / parameter list is going to be split, then split before the first argument. +split_before_first_argument = True + +# Split before arguments if the argument list is terminated by a comma. +split_arguments_when_comma_terminated = False + +# Insert a space between the ending comma and closing bracket of a list, etc. +space_between_ending_comma_and_closing_bracket = True + +# Join short lines into one line. E.g., single line if statements. +join_multiple_lines = True + +# Do not include spaces around selected binary operators. +# Example: 1 + 2 * 3 - 4 / 5 => 1 + 2*3 - 4/5 +no_spaces_around_selected_binary_operators = True + +# Allow lambdas to be formatted on more than one line. +allow_multiline_lambdas = True + +SPLIT_PENALTY_FOR_ADDED_LINE_SPLIT = 10 +SPLIT_PENALTY_AFTER_OPENING_BRACKET = 500 \ No newline at end of file diff --git a/tensorlayer/__init__.py b/tensorlayer/__init__.py new file mode 100644 index 0000000..b111a3e --- /dev/null +++ b/tensorlayer/__init__.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +"""Deep learning and Reinforcement learning library for Researchers and Engineers""" + +# import backend +from .backend import * +# from .backend import ops +# import dataflow +# from .dataflow import * + +import os +from distutils.version import LooseVersion + +from tensorlayer.package_info import ( + VERSION, __contact_emails__, __contact_names__, __description__, __download_url__, __homepage__, __keywords__, + __license__, __package_name__, __repository_url__, __shortversion__, __version__ +) + +if 'TENSORLAYER_PACKAGE_BUILDING' not in os.environ: + + try: + import tensorflow + except Exception as e: + raise ImportError( + "Tensorflow is not installed, please install it with the one of the following commands:\n" + " - `pip install --upgrade tensorflow`\n" + " - `pip install --upgrade tensorflow-gpu`" + ) + + if ("SPHINXBUILD" not in os.environ and "READTHEDOCS" not in os.environ and + LooseVersion(tensorflow.__version__) < LooseVersion("2.0.0")): + raise RuntimeError( + "TensorLayer does not support Tensorflow version older than 2.0.0.\n" + "Please update Tensorflow with:\n" + " - `pip install --upgrade tensorflow`\n" + " - `pip install --upgrade tensorflow-gpu`" + ) + + from tensorlayer import array_ops + from tensorlayer import cost + from tensorlayer import decorators + from tensorlayer import files + from tensorlayer import initializers + from tensorlayer import iterate + from tensorlayer import layers + from tensorlayer import lazy_imports + from tensorlayer import logging + from tensorlayer import models + from tensorlayer import optimizers + from tensorlayer import rein + from tensorlayer import utils + from tensorlayer import dataflow + + from tensorlayer.lazy_imports import LazyImport + + # Lazy Imports + db = LazyImport("tensorlayer.db") + distributed = LazyImport("tensorlayer.distributed") + nlp = LazyImport("tensorlayer.nlp") + prepro = LazyImport("tensorlayer.prepro") + utils = LazyImport("tensorlayer.utils") + visualize = LazyImport("tensorlayer.visualize") + + # alias + vis = visualize + + alphas = array_ops.alphas + alphas_like = array_ops.alphas_like + + # global vars + global_flag = {} + global_dict = {} diff --git a/tensorlayer/array_ops.py b/tensorlayer/array_ops.py new file mode 100644 index 0000000..f09e833 --- /dev/null +++ b/tensorlayer/array_ops.py @@ -0,0 +1,110 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +"""A file containing functions related to array manipulation.""" + +from tensorflow.python.eager import context +from tensorflow.python.framework import constant_op, dtypes, ops, tensor_shape +from tensorflow.python.framework.constant_op import constant +from tensorflow.python.framework.ops import convert_to_tensor +from tensorflow.python.ops.array_ops import shape_internal +from tensorflow.python.ops.gen_array_ops import fill, reshape + +__all__ = ['alphas', 'alphas_like'] + + +def alphas(shape, alpha_value, name=None): + """Creates a tensor with all elements set to `alpha_value`. + This operation returns a tensor of type `dtype` with shape `shape` and all + elements set to alpha. + + Parameters + ---------- + shape: A list of integers, a tuple of integers, or a 1-D `Tensor` of type `int32`. + The shape of the desired tensor + alpha_value: `float32`, `float64`, `int8`, `uint8`, `int16`, `uint16`, int32`, `int64` + The value used to fill the resulting `Tensor`. + name: str + A name for the operation (optional). + + Returns + ------- + A `Tensor` with all elements set to alpha. + + Examples + -------- + >>> tl.alphas([2, 3], tf.int32) # [[alpha, alpha, alpha], [alpha, alpha, alpha]] + """ + with ops.name_scope(name, "alphas", [shape]) as name: + + alpha_tensor = convert_to_tensor(alpha_value) + alpha_dtype = dtypes.as_dtype(alpha_tensor.dtype).base_dtype + + if not isinstance(shape, ops.Tensor): + try: + shape = constant_op._tensor_shape_tensor_conversion_function(tensor_shape.TensorShape(shape)) + except (TypeError, ValueError): + shape = ops.convert_to_tensor(shape, dtype=dtypes.int32) + + if not shape._shape_tuple(): + shape = reshape(shape, [-1]) # Ensure it's a vector + + try: + output = constant(alpha_value, shape=shape, dtype=alpha_dtype, name=name) + + except (TypeError, ValueError): + output = fill(shape, constant(alpha_value, dtype=alpha_dtype), name=name) + + if output.dtype.base_dtype != alpha_dtype: + raise AssertionError("Dtypes do not corresponds: %s and %s" % (output.dtype.base_dtype, alpha_dtype)) + + return output + + +def alphas_like(tensor, alpha_value, name=None, optimize=True): + """Creates a tensor with all elements set to `alpha_value`. + Given a single tensor (`tensor`), this operation returns a tensor of the same + type and shape as `tensor` with all elements set to `alpha_value`. + + Parameters + ---------- + tensor: tf.Tensor + The Tensorflow Tensor that will be used as a template. + alpha_value: `float32`, `float64`, `int8`, `uint8`, `int16`, `uint16`, int32`, `int64` + The value used to fill the resulting `Tensor`. + name: str + A name for the operation (optional). + optimize: bool + if true, attempt to statically determine the shape of 'tensor' and encode it as a constant. + + Returns + ------- + A `Tensor` with all elements set to `alpha_value`. + + Examples + -------- + >>> tensor = tf.constant([[1, 2, 3], [4, 5, 6]]) + >>> tl.alphas_like(tensor, 0.5) # [[0.5, 0.5, 0.5], [0.5, 0.5, 0.5]] + """ + with ops.name_scope(name, "alphas_like", [tensor]) as name: + tensor = ops.convert_to_tensor(tensor, name="tensor") + + if context.in_eager_mode(): # and dtype is not None and dtype != tensor.dtype: + ret = alphas(shape_internal(tensor, optimize=optimize), alpha_value=alpha_value, name=name) + + else: # if context.in_graph_mode(): + + # For now, variant types must be created via zeros_like; as we need to + # pass the input variant object to the proper zeros callback. + + if (optimize and tensor.shape.is_fully_defined()): + # We can produce a zeros tensor independent of the value of 'tensor', + # since the shape is known statically. + ret = alphas(tensor.shape, alpha_value=alpha_value, name=name) + + # elif dtype is not None and dtype != tensor.dtype and dtype != dtypes.variant: + else: + ret = alphas(shape_internal(tensor, optimize=optimize), alpha_value=alpha_value, name=name) + + ret.set_shape(tensor.get_shape()) + + return ret diff --git a/tensorlayer/backend/__init__.py b/tensorlayer/backend/__init__.py new file mode 100644 index 0000000..01e5c83 --- /dev/null +++ b/tensorlayer/backend/__init__.py @@ -0,0 +1,6 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +# load ops +from .ops import * +from tensorlayer.backend import ops \ No newline at end of file diff --git a/tensorlayer/backend/ops/__init__.py b/tensorlayer/backend/ops/__init__.py new file mode 100644 index 0000000..5b8a61b --- /dev/null +++ b/tensorlayer/backend/ops/__init__.py @@ -0,0 +1,126 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +# load nn ops +from .load_backend import padding_format +from .load_backend import preprocess_1d_format +from .load_backend import preprocess_2d_format +from .load_backend import preprocess_3d_format +from .load_backend import nchw_to_nhwc +from .load_backend import nhwc_to_nchw +from .load_backend import relu +from .load_backend import relu6 +from .load_backend import leaky_relu +from .load_backend import softplus +from .load_backend import tanh +from .load_backend import sigmoid +from .load_backend import softmax +from .load_backend import bias_add +from .load_backend import conv1d +from .load_backend import conv2d +from .load_backend import conv3d +from .load_backend import lrn +from .load_backend import moments +from .load_backend import max_pool +from .load_backend import avg_pool +from .load_backend import max_pool3d +from .load_backend import avg_pool3d +from .load_backend import pool +from .load_backend import depthwise_conv2d +from .load_backend import Conv1d_transpose +from .load_backend import Conv2d_transpose +from .load_backend import Conv3d_transpose + +from .load_backend import ReLU +from .load_backend import ReLU6 +from .load_backend import LeakyReLU +from .load_backend import Softplus +from .load_backend import Tanh +from .load_backend import Sigmoid +from .load_backend import Softmax +from .load_backend import Conv1D +from .load_backend import Conv2D +from .load_backend import Conv3D +from .load_backend import BiasAdd +from .load_backend import MaxPool +from .load_backend import AvgPool +from .load_backend import Dropout +from .load_backend import BatchNorm +from .load_backend import DepthwiseConv2d + +# load ops +from .load_backend import Variable +from .load_backend import matmul +from .load_backend import add +from .load_backend import dtypes +from .load_backend import minimum +from .load_backend import reshape +from .load_backend import concat +from .load_backend import convert_to_tensor +from .load_backend import sqrt +from .load_backend import reduce_mean +from .load_backend import reduce_min +from .load_backend import reduce_max +from .load_backend import pad +from .load_backend import stack +from .load_backend import meshgrid +from .load_backend import range +from .load_backend import expand_dims +from .load_backend import tile +from .load_backend import cast +from .load_backend import transpose +from .load_backend import gather_nd +from .load_backend import clip_by_value +from .load_backend import split +from .load_backend import get_tensor_shape +from .load_backend import set_context +from .load_backend import resize +from .load_backend import floor +from .load_backend import gather +from .load_backend import linspace +from .load_backend import slice +from .load_backend import add_n +from .load_backend import ceil +from .load_backend import multiply +from .load_backend import divide +from .load_backend import identity + +# dtype +from .load_backend import (DType, float16, float32, float64, int8, int16, int32, int64, uint8, uint16, uint32, uint64) +# initlizers +from .load_backend import (zeros, ones, constant, random_uniform, random_normal, truncated_normal, he_normal) +# backend +from .load_backend import BACKEND +from .load_backend import BACKEND_VERSION + +from .load_backend import Reshape +from .load_backend import ReduceSum +from .load_backend import ReduceMax +from .load_backend import ReduceMean +from .load_backend import OneHot +from .load_backend import L2Normalize +from .load_backend import EmbeddingLookup +from .load_backend import NCELoss +from .load_backend import Not_equal +from .load_backend import Cast +from .load_backend import ExpandDims +from .load_backend import Count_nonzero +from .load_backend import FlattenReshape +from .load_backend import Transpose +from .load_backend import MatMul +from .load_backend import Tile +from .load_backend import Concat +from .load_backend import ZeroPadding1D +from .load_backend import ZeroPadding2D +from .load_backend import ZeroPadding3D +from .load_backend import Stack +from .load_backend import Unstack +from .load_backend import Sign +from .load_backend import Resize +from .load_backend import Pad +from .load_backend import Minimum +from .load_backend import Maximum +from .load_backend import Meshgrid +from .load_backend import BatchToSpace +from .load_backend import DepthToSpace + diff --git a/tensorlayer/backend/ops/dragon_backend.py b/tensorlayer/backend/ops/dragon_backend.py new file mode 100644 index 0000000..37e6e5a --- /dev/null +++ b/tensorlayer/backend/ops/dragon_backend.py @@ -0,0 +1,1039 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, print_function + +import numpy as np +import dragon as D + +from dragon.core.eager import context +from dragon.core.ops import init_ops +from dragon.core.ops import vision_ops + +_dtypeDict = ['float16', 'float32', 'float64', 'int8', 'int16', 'int32', 'int64', 'uint8', 'uint16', 'uint32', 'uint64'] +# TODO NotImplemented +DType = None +float16 = 'float16' +float32 = 'float32' +float64 = 'float64' +int8 = 'int8' +int16 = 'int16' +int32 = 'int32' +int64 = 'int64' +uint8 = 'uint8' +uint16 = 'uint16' +uint32 = 'uint32' +uint64 = 'uint64' + +# isinstance input output +# TODO NotImplemented +# TensorLike = None + + +def _getter(init_fn, **kwargs): + """Return an named eager tensor.""" + with context.eager_mode(): + value = init_fn(**kwargs) + value._name = kwargs.get('name', value.id) + return value + + +def set_context(**kwargs): + raise Exception("Using Dragon backend,You don't need to set context") + + +def get_tensor_shape(x): + return x.shape + + +# initializers +def zeros(shape, dtype='float32'): + """ + Creates a tensor with all elements set to zero. + + Parameters + ---------- + shape : A list of integers + a tuple of integers, or a 1-D Tensor of type int32. + dtype : tensor + The DType of an element in the resulting Tensor + + Returns + ------- + A Tensor with all elements set to zero. + + """ + return _getter( + init_ops.fill, + value=0, + shape=shape, + dtype=dtype, + ) + + +def ones(shape, dtype='float32'): + """ + Creates a tensor with all elements set to ones. + + Parameters + ---------- + shape : A list of integers + a tuple of integers, or a 1-D Tensor of type int32. + dtype : tensor + The DType of an element in the resulting Tensor + + Returns + ------- + A Tensor with all elements set to zero. + + """ + return _getter( + init_ops.fill, + value=1, + shape=shape, + dtype=dtype, + ) + + +def constant(value, shape, dtype='float32'): + """ + Creates a constant tensor from a tensor-like object. + + Parameters + ---------- + value : list + A constant value (or list) of output type dtype. + dtype : tensor + The type of the elements of the resulting tensor. + shape : tuple + Optional dimensions of resulting tensor. + + Returns + ------- + A Constant Tensor. + + """ + # shape = shape[::-1] + return _getter( + init_ops.fill, + value=value, + shape=shape, + dtype=dtype, + ) + + +def random_uniform(shape, minval=0, maxval=None, dtype='float32', seed=None): + """ + Outputs random values from a uniform distribution. + + Parameters + ---------- + shape : tuple + A 1-D integer Tensor or Python array. The shape of the output tensor. + minval : int + The lower bound on the range of random values to generate (inclusive). Defaults to 0. + maxval : int + The upper bound on the range of random values to generate (exclusive). Defaults to 1 if dtype is floating point. + dtype : tensor + The type of the output: float16, float32, float64, int32, or int64. + seed : int + Used in combination with dragon.random.set_seed to create a reproducible sequence of tensors across multiple calls. + Returns + ------- + A tensor of the specified shape filled with random uniform values. + + """ + return _getter(init_ops.random_uniform, low=minval, high=maxval, shape=shape, dtype=dtype) + + +def random_normal(shape, mean=0.0, stddev=1.0, dtype='float32', seed=None): + """ + Outputs random values from a normal distribution. + + Parameters + ---------- + shape : tuple + A 1-D integer Tensor or Python array. The shape of the output tensor. + mean : float + The mean of the normal distribution + stddev : float + The standard deviation of the normal distribution. + dtype : tensor + The type of the output. + seed : A Python integer + Used to create a random seed for the distribution + + Returns + ------- + A tensor of the specified shape filled with random normal values. + + """ + return _getter( + init_ops.random_normal, + mean=mean, + std=stddev, + shape=shape, + dtype=dtype, + ) + + +def truncated_normal(shape, mean=0.0, stddev=1.0, dtype='float32', seed=None): + """ + Outputs random values from a truncated normal distribution. + + Parameters + ---------- + shape : tuple + A 1-D integer Tensor or Python array. The shape of the output tensor. + mean : float + The mean of the normal distribution + stddev : float + The standard deviation of the normal distribution. + dtype : tensor + The type of the output. + seed : A Python integer + Used to create a random seed for the distribution + + Returns + ------- + A tensor of the specified shape filled with random truncated normal values. + + """ + return _getter( + init_ops.truncated_normal, + mean=mean, + std=stddev, + shape=shape, + dtype=dtype, + ) + + +def he_normal(shape, dtype, seed=None): + """ + He normal initializer. + + Parameters + ---------- + seed : A Python integer. + Used to seed the random generator. + shape : tuple + A 1-D integer Tensor or Python array. The shape of the output tensor. + dtype : tensor + The type of the output. + + Returns + ------- + A tensor of the specified shape filled with he normal values. + """ + # shape = shape[::-1] + raise NotImplementedError("He_Normal is not implemented") + + +def Variable(initial_value, name, trainable=None): + """ + Creates a new variable with value initial_value. + + Parameters + ---------- + initial_value : tensor + A Tensor, or Python object convertible to a Tensor + name : str + Optional name for the variable. Defaults to 'Variable' and gets uniquified automatically. + Returns + ------- + Variable + """ + return D.Tensor(name=name, shape=initial_value) + + +class MatMul(object): + + def __init__(self): + pass + + def __call__(self, a, b): + inputs = [a, b] + return D.math.matmul(inputs) + + +def matmul(a, b): + """ + Multiplies matrix a by matrix b, producing a * b. + + Parameters + ---------- + a : tensor + type float16, float32, float64, int32, complex64, complex128 and rank > 1. + b : tensor + with same type and rank as a. + + Returns + ------- + A Tensor of the same type as a and b + """ + inputs = [a, b] + return D.math.matmul(inputs) + + +def add(value, bias): + """ + Returns x + y element-wise. + + Parameters + ---------- + value : tensor. + Must be one of the following types: bfloat16, half, float32, float64, + uint8, int8, int16, int32, int64, complex64, complex128, string. + bias : tensor + Must have the same type as a + name : str + A name for the operation + + Returns + ------- + A Tensor. Has the same type as a. + """ + + inputs = [value, bias] + return D.math.add(inputs) + + +def dtypes(dt): + """ + Data dtypes. + + Parameters + ---------- + dt : string + It could be 'uint8', 'uint16', 'uint32', 'uint64', 'int8', 'int16', + 'int32', 'int64', 'float16', 'float32', 'float64', 'DType'. + + Returns + ------- + Data dtypes + """ + if dt not in _dtypeDict: + raise Exception("Unsupported dtype: {}".format(dt)) + return dt + + +def minimum(x, y): + """ + Returns the min of x and y (i.e. x < y ? x : y) element-wise. + + Parameters + ---------- + x : tensor. + Must be one of the following types: bfloat16, half, float32, float64, int32, int64. + y : A Tensor. + Must have the same type as x. + name : str + A name for the operation (optional). + + Returns + ------- + A Tensor. Has the same type as x + """ + inputs = [x, y] + return D.math.minimum(inputs) + + +class FlattenReshape(object): + + def __init__(self): + pass + + def __call__(self, inputs): + dim = 1 + for d in get_tensor_shape(inputs)[1:]: + dim *= d + return D.reshape(inputs, [-1, dim]) + + +class Reshape(object): + + def __init__(self, shape): + self.shape = shape + + def __call__(self, tensor): + return D.reshape(tensor, shape=self.shape) + + +def reshape(tensor, shape): + """ + Reshapes a tensor. + + Parameters + ---------- + tensor : tensor + A Tensor. + shape : tensor + Defines the shape of the output tensor. + Returns + ------- + A Tensor. Has the same type as tensor + """ + return D.reshape(tensor, shape=shape) + + +class Concat(object): + + def __init__(self, axis): + super(Concat, self).__init__() + self.axis = axis + + def __call__(self, values): + return D.concat(values=values, axis=self.axis) + + +def concat(values, axis): + """ + Concatenates tensors along one dimension. + + Parameters + ---------- + values : list + A list of Tensor objects or a single Tensor + axis : int + 0-D int32 Tensor. Dimension along which to concatenate + Returns + ------- + A Tensor resulting from concatenation of the input tensors. + """ + return D.concat(values, axis=axis) + + +def convert_to_tensor(value, dtype=None): + """ + Converts the given value to a Tensor. + + Parameters + ---------- + value : object + An object whose type has a registered Tensor conversion function. + dtype : optional + Optional element type for the returned tensor. If missing, the type is inferred from the type of value. + + Returns + ------- + A Tensor based on value. + """ + return D.Tensor.convert_to(value, dtype) + + +def sqrt(x): + """ + Computes square root of x element-wise. + + Parameters + ---------- + x : tensor + Must be one of the following types: bfloat16, half, float32, float64, complex64, complex128. + + Returns + ------- + A Tensor. Has the same type as x. + """ + return D.math.sqrt(x) + + +class ReduceSum(object): + + def __init__(self, axis): + pass + + def construct(self, input): + pass + + +class ReduceMean(object): + + def __init__(self, axis): + if axis == [1, 2]: + self.data_format = 'NHWC' + elif axis == [2, 3]: + self.data_format = 'NCHW' + else: + raise ("`data_format` should have one of the following values: [`channels_last`, `channels_first`]") + + def __call__(self, inputs): + return vision_ops.pool2d( + inputs, + kernel_shape=1, + strides=1, + pads=0, + mode='AVG', + global_pooling=True, + data_format=self.data_format, + ) + + +def reduce_mean(input_tensor, axis=None): + """ + Computes the mean of elements across dimensions of a tensor. + + Parameters + ---------- + input_tensor : tensor + The tensor to reduce. Should have numeric type. + axis : int + The dimensions to reduce. If None (the default), reduces all dimensions. + Must be in the range [-rank(input_tensor), rank(input_tensor)). + name : str + A name for the operation (optional). + + Returns + ------- + The reduced tensor. + """ + + return D.mean(input_tensor, axes=axis) + + +class ReduceMax(object): + + def __init__(self, axis): + if axis == [1, 2]: + self.data_format = 'NHWC' + elif axis == [2, 3]: + self.data_format = 'NCHW' + else: + raise ("`data_format` should have one of the following values: [`channels_last`, `channels_first`]") + + def __call__(self, inputs): + return vision_ops.pool2d( + inputs, kernel_shape=1, strides=1, pads=0, mode='MAX', global_pooling=True, data_format=self.data_format + ) + + +def reduce_max(input_tensor, axis=None): + """ + Computes the maximum of elements across dimensions of a tensor. + + Parameters + ---------- + input_tensor : tensor + The tensor to reduce. Should have real numeric type. + axis : int + The dimensions to reduce. If None (the default), reduces all dimensions. + Must be in the range [-rank(input_tensor), rank(input_tensor)). + name : str + A name for the operation (optional). + + Returns + ------- + The reduced tensor. + """ + + return D.max(input_tensor, axis) + + +def reduce_min(input_tensor, axis=None): + """ + Computes the minimum of elements across dimensions of a tensor. + + Parameters + ---------- + input_tensor : tensor + The tensor to reduce. Should have real numeric type. + axis : int + The dimensions to reduce. If None (the default), reduces all dimensions. + Must be in the range [-rank(input_tensor), rank(input_tensor)). + name : str + A name for the operation (optional). + + Returns + ------- + The reduced tensor. + """ + return D.min(input_tensor, axis) + +class Pad(object): + def __init__(self, paddings, mode="REFLECT"): + if mode not in ['CONSTANT', 'REFLECT', 'SYMMETRIC']: + raise Exception("Unsupported mode: {}".format(mode)) + if mode == 'SYMMETRIC': + mode = 'EDGE' + self.paddings = paddings + self.mode = mode + + def __call__(self, x): + outputs = D.pad(x, pads=self.paddings, mode=self.mode, value=0) + return outputs + +def pad(tensor, paddings, mode='CONSTANT', constant_values=0): + """ + Pads a tensor. + + Parameters + ---------- + tensor : tensor + A Tensor. + paddings : tuple + A tuple of type int32. + mode : str + One of "CONSTANT", "REFLECT", or "SYMMETRIC" (case-insensitive) + constant_values : int + In "CONSTANT" mode, the scalar pad value to use. Must be same type as tensor. + + Returns + ------- + A Tensor. Has the same type as tensor. + """ + if mode not in ['CONSTANT', 'REFLECT', 'SYMMETRIC']: + raise Exception("Unsupported mode: {}".format(mode)) + if mode == 'SYMMETRIC': + mode = 'EDGE' + outputs = D.pad(tensor, pads=paddings, mode=mode, value=constant_values) + return outputs + + +class Unstack(object): + + def __init__(self, axis, num=None): + self.axis = axis + self.num = num + + def __call__(self, values): + raise NotImplementedError + + +class Stack(object): + + def __init__(self, axis): + self.axis = axis + + def __call__(self, values): + return D.stack(values, axis=self.axis) + + +def stack(values, axis=0): + """ + Stacks a list of rank-R tensors into one rank-(R+1) tensor. + + Parameters + ---------- + values : list + A list of Tensor objects with the same shape and type. + axis : int + An int. The axis to stack along. Defaults to the first dimension. + Negative values wrap around, so the valid range is [-(R+1), R+1). + + Returns + ------- + A stacked Tensor with the same type as values. + """ + return D.stack(values, axis=axis) + + +class Meshgrid(object): + def __init__(self, indexing='xy'): + super(Meshgrid, self).__init__() + self.index = indexing + + def __call__(self, inputs): + pass + + +def meshgrid(x, y): + """ + Broadcasts parameters for evaluation on an N-D grid. + + Parameters + ---------- + x : tensor + Tensors with rank 1. + y : tensor + Tensors with rank 1. + + Returns + ------- + A list of N Tensors with rank N. + """ + + pass + + +def range(start, limit=None, delta=1, dtype=None): + """ + Creates a sequence of numbers. + + Parameters + ---------- + start : tensor + A 0-D Tensor (scalar). Acts as first entry in the range if limit is not None; + otherwise, acts as range limit and first entry defaults to 0. + limit : tensor + A 0-D Tensor (scalar). Upper limit of sequence, exclusive. If None, + defaults to the value of start while the first entry of the range defaults to 0. + delta : tensor + A 0-D Tensor (scalar). Number that increments start. Defaults to 1. + dtype : type + The type of the elements of the resulting tensor. + + Returns + ------- + An 1-D Tensor of type dtype. + """ + if dtype is None: + dtype = 'int32' + if limit is None: + outputs = D.arange(start=0, stop=start, step=delta, dtype=dtype) + else: + outputs = D.arange(start, stop=limit, step=delta, dtype=dtype) + return outputs + + +class ExpandDims(object): + + def __init__(self, axis): + pass + + def construct(self, input): + pass + + +def expand_dims(input, axis): + """ + Inserts a dimension of 1 into a tensor's shape. + + Parameters + ---------- + input : tensor + A Tensor. + axis : int + 0-D (scalar). Specifies the dimension index at which to expand the shape of input. + Must be in the range [-rank(input) - 1, rank(input)]. + + Returns + ------- + A Tensor with the same data as input, but its shape has an additional dimension of size 1 added. + """ + + return D.expand_dims(input, axis=axis) + + +class Tile(object): + + def __init__(self): + pass + + def __call__(self, input, multiples): + return D.tile(input, multiples) + + +def tile(input, multiples): + """ + Constructs a tensor by tiling a given tensor. + + Parameters + ---------- + input : tensor + A Tensor. 1-D or higher. + multiples : tensor + Must be one of the following types: int32, int64. 1-D. + Length must be the same as the number of dimensions in input + + Returns + ------- + A Tensor. Has the same type as input. + """ + return D.tile(input, multiples) + + +class Cast(object): + + def __init__(self, dtype): + pass + + def __call__(self, input): + pass + + +def cast(x, dtype): + """ + Casts a tensor to a new type. + + Parameters + ---------- + x : tensor + A Tensor or SparseTensor or IndexedSlices of numeric type. + It could be uint8, uint16, uint32, uint64, int8, int16, int32, int64, float16, float32, float64. + dtype : dtpye + The destination type. The list of supported dtypes is the same as x + + Returns + ------- + A Tensor or SparseTensor or IndexedSlices with same shape as x and same type as dtype. + """ + return D.cast(x, dtype=dtype) + + +class Transpose(object): + + def __init__(self, perm, conjugate=False): + self.perm = perm + if conjugate: + raise ("The conjugate Parameters not supported") + + def __call__(self, a): + return D.transpose(a, self.perm) + + +def transpose(a, perm=None, conjugate=False): + """ + Transposes a. + + Parameters + ---------- + a : tensor + A Tensor. + perm : int + A permutation of the dimensions of a. + conjugate : bool + Setting it to True is mathematically equivalent to ms.math.conj(ms.transpose(input)). + + Returns + ------- + A transposed Tensor. + """ + + conjugate = conjugate + return D.transpose(a, perm=perm) + + +def gather_nd(params, indices, batch_dims=0): + """ + Gather slices from params into a Tensor with shape specified by indices. + + Parameters + ---------- + params : tensor + The tensor from which to gather values. + indices : tensor + Must be one of the following types: int32, int64. Index tensor. + batch_dims : int + An integer or a scalar 'Tensor'. The number of batch dimensions. + + Returns + ------- + A Tensor. Has the same type as params. + """ + + pass + + +def clip_by_value(t, clip_value_min, clip_value_max): + """ + Clips tensor values to a specified min and max. + + Parameters + ---------- + t : tensor + A Tensor or IndexedSlices + clip_value_min : tensor + A 0-D (scalar) Tensor, or a Tensor with the same shape as t. The minimum value to clip by + clip_value_max : tensor + A 0-D (scalar) Tensor, or a Tensor with the same shape as t. The minimum value to clip by + + Returns + ------- + A clipped Tensor or IndexedSlices. + """ + + pass + + +def split(value, num_or_size_splits, axis=0, num=None): + """ + Splits a tensor into sub tensors. + + Parameters + ---------- + value : tensor + The Tensor to split. + num_or_size_splits : list + Either an integer indicating the number of splits along split_dim or a 1-D integer Tensor or + Python list containing the sizes of each output tensor along split_dim. + axis : int + The dimension along which to split. Must be in the range [-rank(value), rank(value)). Defaults to 0. + num : int + used to specify the number of outputs when it cannot be inferred from the shape of size_splits. + + Returns + ------- + Tensor objects resulting from splitting value. + """ + pass + + +def floor(x): + return D.math.floor(x) + + +def gather(params, indices): + return NotImplementedError + + +def linspace(start, stop, num): + return D.linspace(start, stop, num) + + +def slice(inputs, starts, sizes): + return D.slice(inputs, starts, sizes) + + +def add_n(inputs): + return NotImplementedError + + +class OneHot(object): + + def __init__(self, axis=-1, depth=1, on_value=1.0, off_value=0.0, dtype='float32'): + self.depth = depth + self.dtype = dtype + + def __call__(self, indices): + outputs = np.zeros(shape=(indices.shape[0], self.depth)) + for i in np.arange(indices.shape[0]): + outputs[int(i)][int(indices[int(i)].get_value())] = 1 + outputs = D.constant(outputs, dtype=self.dtype) + return outputs + + +class L2Normalize(object): + + def __init__(self, axis=None, epsilon=1e-12): + super(L2Normalize, self).__init__() + pass + + def __call__(self, input, *args, **kwargs): + pass + + +class EmbeddingLookup(object): + + def __init__(self, max_norm=None): + self.max_norm = max_norm + + def __call__(self, params, ids, *args, **kwargs): + pass + + +class NCELoss(object): + + def __init__(self, num_true=1, sampled_values=None, remove_accidental_hits=False): + super(NCELoss, self).__init__() + + def __call__(self, weights, biases, labels, inputs, num_sampled, num_classes): + pass + + +class Not_equal(object): + + def __init__(self): + pass + + def __call__(self, x, y): + pass + + +class Count_nonzero(object): + + def __init__(self, keepdims=None, dtype='int64'): + pass + + def __call__(self, *args, **kwargs): + pass + + + +class Resize: + + def __init__(self, scale, method, antialias=False, data_format='channels_last', ksize=None): + if method not in ['nearest', 'linear', 'bilinear']: + raise ('Current resize does not support this method.') + if method == 'bilinear': + method = 'linear' + self.method = method + self.antialias = antialias + self.scale = scale + if data_format != 'channel_last': + raise Exception("UpSampling2d resize_images only support channel_last") + + def __call__(self, inputs): + output_size = (int(inputs.shape[1] * self.scale[0]), int(inputs.shape[2] * self.scale[1])) + outputs = D.vision.resize(inputs, sizes=output_size, mode=self.method, align_corners=self.antialias) + return outputs + + +def resize(inputs, output_size, method, antialias): + if method not in ['nearest', 'linear', 'bilinear']: + raise ('Current resize does not support this method.') + if method == 'bilinear': + method = 'linear' + return D.vision.resize(inputs, sizes=output_size, mode=method, align_corners=antialias) + + +class ZeroPadding1D(object): + + def __init__(self): + pass + + def __call__(self, padding): + raise NotImplementedError + + +class ZeroPadding2D(object): + + def __init__(self): + pass + + def __call__(self, padding): + raise NotImplementedError + + +class ZeroPadding3D(object): + + def __init__(self): + pass + + def __call__(self, padding): + raise NotImplementedError + + +class Sign(object): + + def __init__(self): + pass + + def __call__(self, x): + return D.math.sign(x) + +def ceil(x): + raise NotImplementedError + +def multiply(x, y): + raise NotImplementedError + +def divide(x, y): + raise NotImplementedError + +def identity(x): + raise NotImplementedError + +class BatchToSpace(object): + def __init__(self, block_size, crops): + super(BatchToSpace, self).__init__() + pass + + def __call__(self, input_x): + raise NotImplementedError + + +class DepthToSpace(object): + def __init__(self, block_size, data_format='NHWC'): + pass + + def __call__(self, input): + raise NotImplementedError \ No newline at end of file diff --git a/tensorlayer/backend/ops/dragon_nn.py b/tensorlayer/backend/ops/dragon_nn.py new file mode 100644 index 0000000..e6b5105 --- /dev/null +++ b/tensorlayer/backend/ops/dragon_nn.py @@ -0,0 +1,910 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +import dragon as D +from dragon.core.ops import vision_ops +from dragon.core.ops import activation_ops + + +def padding_format(padding): + """ + Checks that the padding format correspond format. + + Parameters + ---------- + padding : str + Must be one of the following:"same", "SAME", "VALID", "valid" + + Returns + ------- + str "SAME" or "VALID" + """ + + if padding in ["SAME", "same"]: + padding = "SAME" + elif padding in ["VALID", "valid"]: + padding = "VALID" + elif padding == None: + padding = None + else: + raise Exception("Unsupported padding: " + str(padding)) + return padding + + +def preprocess_1d_format(data_format, padding): + """ + Checks that the 1-D dataformat format correspond format. + + Parameters + ---------- + data_format : str + Must be one of the following:"channels_last","NWC","NCW","channels_first" + padding : str + Must be one of the following:"same","valid","SAME","VALID" + + Returns + ------- + str "NWC" or "NCW" and "SAME" or "VALID" + """ + + if data_format in ["channels_last", "NWC"]: + data_format = "NWC" + elif data_format in ["channels_first", "NCW"]: + data_format = "NCW" + elif data_format == None: + data_format = None + else: + raise Exception("Unsupported data format: " + str(data_format)) + padding = padding_format(padding) + return data_format, padding + + +def preprocess_2d_format(data_format, padding): + """ + Checks that the 2-D dataformat format correspond format. + + Parameters + ---------- + data_format : str + Must be one of the following:"channels_last","NHWC","NCHW","channels_first" + padding : str + Must be one of the following:"same","valid","SAME","VALID" + + Returns + ------- + str "NHWC" or "NCHW" and "SAME" or "VALID" + """ + + if data_format in ["channels_last", "NHWC", "nhwc"]: + data_format = "NHWC" + elif data_format in ["channels_first", "NCHW", "nchw"]: + data_format = "NCHW" + elif data_format == None: + data_format = None + else: + raise Exception("Unsupported data format: " + str(data_format)) + padding = padding_format(padding) + return data_format, padding + + +def preprocess_3d_format(data_format, padding): + """ + Checks that the 3-D dataformat format correspond format. + + Parameters + ---------- + data_format : str + Must be one of the following:"channels_last","NDHWC","NCDHW","channels_first" + padding : str + Must be one of the following:"same","valid","SAME","VALID" + + Returns + ------- + str "NDHWC" or "NCDHW" and "SAME" or "VALID" + """ + + if data_format in ['channels_last', 'NDHWC']: + data_format = 'NDHWC' + elif data_format in ['channels_first', 'NCDHW']: + data_format = 'NCDHW' + elif data_format == None: + data_format = None + else: + raise Exception("Unsupported data format: " + str(data_format)) + padding = padding_format(padding) + return data_format, padding + + +def nchw_to_nhwc(x): + """ + Channels first to channels last + + Parameters + ---------- + x : tensor + channels first tensor data + + Returns + ------- + channels last tensor data + """ + + pass + + +def nhwc_to_nchw(x): + """ + Channles last to channels first + + Parameters + ---------- + x : tensor + channels last tensor data + + Returns + ------- + channels first tensor data + """ + + pass + + +class ReLU(object): + + def __init__(self): + pass + + def __call__(self, x): + return D.nn.relu(x) + + +def relu(x): + """ + Computes rectified linear: max(features, 0). + + Parameters + ---------- + x : tensor + Must be one of the following types: float32, float64, int32, uint8, int16, + int8, int64, bfloat16, uint16, half, uint32, uint64, qint8. + + Returns + ------- + A Tensor. Has the same type as features. + """ + return D.nn.relu(x) + + +class ReLU6(object): + + def __init__(self): + pass + + def __call__(self, x): + return D.nn.relu6(x) + + +def relu6(x): + """ + Computes Rectified Linear 6: min(max(features, 0), 6). + + Parameters + ---------- + x : tensor + Must be one of the following types: float32, float64, int32, uint8, int16, + int8, int64, bfloat16, uint16, half, uint32, uint64, qint8. + + Returns + ------- + A Tensor with the same type as features. + """ + return D.nn.relu6(x) + + +class LeakyReLU(object): + + def __init__(self, alpha=0.2): + self.alpha = alpha + + def __call__(self, x): + return D.nn.leaky_relu(x, alpha=self.alpha) + + +def leaky_relu(x): + """ + Compute the Leaky ReLU activation function. + + Parameters + ---------- + x : tensor + representing preactivation values. Must be one of the following types: + float16, float32, float64, int32, int64. + + Returns + ------- + The activation value. + """ + + return D.nn.leaky_relu(x) + + +class Softplus(object): + + def __init__(self): + pass + + def __call__(self, x): + raise NotImplementedError + + +def softplus(x): + """ + Computes softplus: log(exp(features) + 1). + + Parameters + ---------- + x : tensor + Must be one of the following types: half, bfloat16, float32, float64. + + Returns + ------- + A Tensor. Has the same type as features. + """ + + raise NotImplementedError + + +class Tanh(object): + + def __init__(self): + pass + + def __call__(self, x): + return activation_ops.tanh(x) + + +def tanh(x): + """ + Computes hyperbolic tangent of x element-wise. + + Parameters + ---------- + x : tensor + Must be one of the following types: bfloat16, half, float32, float64, complex64, complex128. + + Returns + ------- + A Tensor. Has the same type as x. + """ + + return activation_ops.tanh(x) + + +class Sigmoid(object): + + def __init__(self): + pass + + def __call__(self, x): + return activation_ops.sigmoid(x) + + +def sigmoid(x): + """ + Computes sigmoid of x element-wise. + + Parameters + ---------- + x : tensor + A Tensor with type float16, float32, float64, complex64, or complex128. + + Returns + ------- + A Tensor with the same type as x. + """ + return activation_ops.sigmoid(x) + + +class Softmax(object): + + def __init__(self): + pass + + def __call__(self, x): + return D.nn.softmax(x) + + +def softmax(logits, axis=None): + """ + Computes softmax activations. + + Parameters + ---------- + logits : tensor + Must be one of the following types: half, float32, float64. + axis : int + The dimension softmax would be performed on. The default is -1 which indicates the last dimension. + + Returns + ------- + A Tensor. Has the same type and shape as logits. + """ + return D.nn.softmax(logits) + + +class Dropout(object): + + def __init__(self, keep, seed=1): + self.keep = 1 - keep + self.seed = seed + + def __call__(self, inputs): + return D.nn.dropout(inputs, prob=self.keep) + + +class BiasAdd(object): + """ + Adds bias to value. + + Parameters + ---------- + x : tensor + A Tensor with type float, double, int64, int32, uint8, int16, int8, complex64, or complex128. + bias : tensor + Must be the same type as value unless value is a quantized type, + in which case a different quantized type may be used. + Returns + ------- + A Tensor with the same type as value. + """ + + def __init__(self, data_format='NHWC'): + self.data_format = data_format + + def __call__(self, x, bias): + inputs = [x, bias] + return vision_ops.bias_add(inputs, data_format=self.data_format) + + +def bias_add(x, bias): + """ + Adds bias to value. + + Parameters + ---------- + x : tensor + A Tensor with type float, double, int64, int32, uint8, int16, int8, complex64, or complex128. + bias : tensor + Must be the same type as value unless value is a quantized type, + in which case a different quantized type may be used. + data_format : A string. + 'N...C' and 'NC...' are supported. + name : str + A name for the operation (optional). + Returns + ------- + A Tensor with the same type as value. + """ + inputs = [x, bias] + return vision_ops.bias_add(inputs, data_format='NHWC') + + +class Conv1D(object): + pass + # raise NotImplementedError + + +def conv1d(input, filters, stride, padding, data_format='NWC', dilations=None, name=None): + """ + Computes a 1-D convolution given 3-D input and filter tensors. + + Parameters + ---------- + input : tensor + A 3D Tensor. Must be of type float16, float32, or float64 + filters : tensor + A 3D Tensor. Must have the same type as input. + stride : int of list + An int or list of ints that has length 1 or 3. The number of entries by which the filter is moved right at each step. + padding : string + 'SAME' or 'VALID' + data_format : string + An optional string from "NWC", "NCW". Defaults to "NWC", the data is stored in the order of + [batch, in_width, in_channels]. The "NCW" format stores data as [batch, in_channels, in_width]. + dilations : int or list + An int or list of ints that has length 1 or 3 which defaults to 1. + The dilation factor for each dimension of input. If set to k > 1, + there will be k-1 skipped cells between each filter element on that dimension. + Dilations in the batch and depth dimensions must be 1. + name : string + A name for the operation (optional). + Returns + ------- + A Tensor. Has the same type as input. + """ + + pass + + +class Conv2D(object): + + def __init__(self, strides, padding, data_format='NHWC', dilations=None, out_channel=None, k_size=None): + self.data_format, self.padding = preprocess_2d_format(data_format, padding) + self.ksize = k_size[0] + if self.data_format is 'NHWC': + self.dg_stride = strides[1] + self.dg_dilation = dilations[1] + elif self.data_format is 'NCHW': + self.dg_stride = strides[2] + self.dg_dilation = dilations[2] + + def __call__(self, inputs, filters): + outputs = vision_ops.conv2d( + [inputs, filters], + kernel_shape=self.ksize, + strides=self.dg_stride, + padding=self.padding, + dilations=self.dg_dilation, + data_format=self.data_format, + ) + return outputs + + +def conv2d(input, filters, strides, padding, data_format='NCHW', dilations=None): + """ + Computes a 2-D convolution given 4-D input and filters tensors. + + Parameters + ---------- + input : tensor + Must be one of the following types: half, bfloat16, float32, float64. A 4-D tensor. + The dimension order is interpreted according to the value of data_format, see below for details. + filters : tensor + Must have the same type as input. A 4-D tensor of shape [filter_height, filter_width, in_channels, out_channels] + strides : int of list + The stride of the sliding window for each dimension of input. If a single value is given it is replicated in the H and W dimension. + By default the N and C dimensions are set to 1. The dimension order is determined by the value of data_format, see below for details. + padding : string + "SAME" or "VALID" + data_format : string + "NHWC", "NCHW". Defaults to "NCHW". + dilations : list or ints + list of ints that has length 1, 2 or 4, defaults to 1. The dilation factor for each dimension ofinput. + + Returns + ------- + A Tensor. Has the same type as input. + """ + raise NotImplementedError + + +class Conv3D(object): + pass + # raise NotImplementedError + + +def conv3d(input, filters, strides, padding, data_format='NDHWC', dilations=None, name=None): + """ + Computes a 3-D convolution given 5-D input and filters tensors. + + Parameters + ---------- + input : tensor + Must be one of the following types: half, bfloat16, float32, float64. + Shape [batch, in_depth, in_height, in_width, in_channels]. + filters : tensor + Must have the same type as input. Shape [filter_depth, filter_height, filter_width, in_channels, out_channels]. + in_channels must match between input and filters. + strides : list of ints + A list of ints that has length >= 5. 1-D tensor of length 5. + The stride of the sliding window for each dimension of input. + Must have strides[0] = strides[4] = 1. + padding : string + A string from: "SAME", "VALID". The type of padding algorithm to use. + data_format : string + An optional string from: "NDHWC", "NCDHW". Defaults to "NDHWC". The data format of the input and output data. + With the default format "NDHWC", the data is stored in the order of: [batch, in_depth, in_height, in_width, in_channels]. + Alternatively, the format could be "NCDHW", the data storage order is: [batch, in_channels, in_depth, in_height, in_width]. + dilations : list of ints + Defaults to [1, 1, 1, 1, 1]. 1-D tensor of length 5. The dilation factor for each dimension of input. + If set to k > 1, there will be k-1 skipped cells between each filter element on that dimension. + The dimension order is determined by the value of data_format, see above for details. + Dilations in the batch and depth dimensions must be 1. + name : string + A name for the operation (optional). + + Returns + ------- + A Tensor. Has the same type as input. + """ + + raise NotImplementedError + + +def lrn(inputs, depth_radius, bias, alpha, beta): + """ + Local Response Normalization. + + Parameters + ---------- + inputs : tensor + Must be one of the following types: half, bfloat16, float32. 4-D. + depth_radius : int + Defaults to 5. 0-D. Half-width of the 1-D normalization window. + bias : float + Defaults to 1. An offset (usually positive to avoid dividing by 0). + alpha : float + Defaults to 1. A scale factor, usually positive. + beta : float + Defaults to 0.5. An exponent. + + Returns + ------- + A Tensor. Has the same type as input. + """ + pass + + +def moments(x, axes, shift=None, keepdims=False): + """ + Calculates the mean and variance of x. + + Parameters + ---------- + x : tensor + A Tensor + axes : ints + Axes along which to compute mean and variance. + shift : int + Not used in the current implementation. + keepdims : bool + produce moments with the same dimensionality as the input. + + Returns + ------- + Two Tensor objects: mean and variance. + """ + + pass + + +class MaxPool(object): + + def __init__(self, ksize, strides, padding, data_format=None): + self.data_format, self.padding = preprocess_2d_format(data_format, padding) + self.ksize = ksize + self.strides = strides + + def __call__(self, inputs): + return vision_ops.pool2d( + inputs, + kernel_shape=self.ksize, + strides=self.strides, + padding=self.padding, + mode='MAX', + global_pooling=False, + data_format=self.data_format, + ) + + +def max_pool(input, ksize, strides, padding, data_format=None): + """ + Performs the max pooling on the input. + + Parameters + ---------- + input : tensor + Tensor of rank N+2, of shape [batch_size] + input_spatial_shape + [num_channels] if data_format does not start + with "NC" (default), or [batch_size, num_channels] + input_spatial_shape if data_format starts with "NC". + Pooling happens over the spatial dimensions only. + ksize : int or list of ints + An int or list of ints that has length 1, N or N+2. + The size of the window for each dimension of the input tensor. + strides : int or list of ints + An int or list of ints that has length 1, N or N+2. + The stride of the sliding window for each dimension of the input tensor. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + + Returns + ------- + A Tensor of format specified by data_format. The max pooled output tensor. + """ + pass + + +class AvgPool(object): + + def __init__(self, ksize, strides, padding, data_format=None): + self.data_format, self.padding = preprocess_2d_format(data_format, padding) + self.filter_size = ksize + self.strides = strides + + def __call__(self, inputs): + return vision_ops.pool2d( + inputs, + kernel_shape=self.filter_size, + strides=self.strides, + padding=self.padding, + mode='AVG', + global_pooling=False, + data_format=self.data_format, + ) + + +def avg_pool(input, ksize, strides, padding): + """ + Performs the avg pooling on the input. + + Parameters + ---------- + input : tensor + Tensor of rank N+2, of shape [batch_size] + input_spatial_shape + [num_channels] + if data_format does not start with "NC" (default), or [batch_size, num_channels] + input_spatial_shape + if data_format starts with "NC". Pooling happens over the spatial dimensions only. + ksize : int or list of ints + An int or list of ints that has length 1, N or N+2. + The size of the window for each dimension of the input tensor. + strides : int or list of ints + An int or list of ints that has length 1, N or N+2. + The stride of the sliding window for each dimension of the input tensor. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + + Returns + ------- + A Tensor of format specified by data_format. The average pooled output tensor. + """ + pass + + +def max_pool3d(input, ksize, strides, padding, data_format=None, name=None): + """ + Performs the max pooling on the input. + + Parameters + ---------- + input : tensor + A 5-D Tensor of the format specified by data_format. + ksize : int or list of ints + An int or list of ints that has length 1, 3 or 5. + The size of the window for each dimension of the input tensor. + strides : int or list of ints + An int or list of ints that has length 1, 3 or 5. + The stride of the sliding window for each dimension of the input tensor. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + "NDHWC", "NCDHW". Defaults to "NDHWC". The data format of the input and output data. + With the default format "NDHWC", the data is stored in the order of: [batch, in_depth, in_height, in_width, in_channels]. + Alternatively, the format could be "NCDHW", the data storage order is: [batch, in_channels, in_depth, in_height, in_width]. + name : string + A name for the operation (optional). + + Returns + ------- + A Tensor of format specified by data_format. The max pooled output tensor. + """ + pass + + +def avg_pool3d(input, ksize, strides, padding, data_format=None, name=None): + """ + Performs the average pooling on the input. + + Parameters + ---------- + input : tensor + A 5-D Tensor of shape [batch, height, width, channels] and type float32, float64, qint8, quint8, or qint32. + ksize : int or list of ints + An int or list of ints that has length 1, 3 or 5. The size of the window for each dimension of the input tensor. + strides : int or list of ints + An int or list of ints that has length 1, 3 or 5. + The stride of the sliding window for each dimension of the input tensor. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + 'NDHWC' and 'NCDHW' are supported. + name : string + Optional name for the operation. + + Returns + ------- + A Tensor with the same type as value. The average pooled output tensor. + """ + pass + + +def pool(input, window_shape, pooling_type, strides=None, padding='VALID', data_format=None, dilations=None, name=None): + """ + Performs an N-D pooling operation. + + Parameters + ---------- + input : tensor + Tensor of rank N+2, of shape [batch_size] + input_spatial_shape + [num_channels] + if data_format does not start with "NC" (default), or [batch_size, num_channels] + input_spatial_shape + if data_format starts with "NC". Pooling happens over the spatial dimensions only. + window_shape : int + Sequence of N ints >= 1. + pooling_type : string + Specifies pooling operation, must be "AVG" or "MAX". + strides : ints + Sequence of N ints >= 1. Defaults to [1]*N. If any value of strides is > 1, then all values of dilation_rate must be 1. + padding : string + The padding algorithm, must be "SAME" or "VALID". Defaults to "SAME". + See the "returns" section of tf.ops.convolution for details. + data_format : string + Specifies whether the channel dimension of the input and output is the last dimension (default, or if data_format does not start with "NC"), + or the second dimension (if data_format starts with "NC"). + For N=1, the valid values are "NWC" (default) and "NCW". For N=2, the valid values are "NHWC" (default) and "NCHW". + For N=3, the valid values are "NDHWC" (default) and "NCDHW". + dilations : list of ints + Dilation rate. List of N ints >= 1. Defaults to [1]*N. If any value of dilation_rate is > 1, then all values of strides must be 1. + name : string + Optional. Name of the op. + + Returns + ------- + Tensor of rank N+2, of shape [batch_size] + output_spatial_shape + [num_channels] + """ + pass + + +class DepthwiseConv2d(object): + + def __init__(self, strides, padding, data_format=None, dilations=None, ksize=None, channel_multiplier=1): + self.data_format, self.padding = preprocess_2d_format(data_format, padding) + self.stride = strides + self.dilations = dilations + + def __call__(self, input, filter): + raise NotImplementedError("Not implemented depthwiseconv2d") + + +def depthwise_conv2d(input, filter, strides, padding, data_format=None, dilations=None, name=None): + """ + Depthwise 2-D convolution. + + Parameters + ---------- + input : tensor + 4-D with shape according to data_format. + filter : tensor + 4-D with shape [filter_height, filter_width, in_channels, channel_multiplier]. + strides : list + 1-D of size 4. The stride of the sliding window for each dimension of input. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + The data format for input. Either "NHWC" (default) or "NCHW". + dilations : list + 1-D of size 2. The dilation rate in which we sample input values across the height and width dimensions in atrous convolution. + If it is greater than 1, then all values of strides must be 1. + name : string + A name for this operation (optional). + + Returns + ------- + A 4-D Tensor with shape according to data_format. + E.g., for "NHWC" format, shape is [batch, out_height, out_width, in_channels * channel_multiplier]. + """ + + pass + + +def conv1d_transpose( + input, filters, output_shape, strides, padding='SAME', data_format='NWC', dilations=None, name=None +): + """ + The transpose of conv1d. + + Parameters + ---------- + input : tensor + A 3-D Tensor of type float and shape [batch, in_width, in_channels] + for NWC data format or [batch, in_channels, in_width] for NCW data format. + filters : tensor + A 3-D Tensor with the same type as value and shape [filter_width, output_channels, in_channels]. + filter's in_channels dimension must match that of value. + output_shape : tensor + A 1-D Tensor, containing three elements, representing the output shape of the deconvolution op. + strides : list + An int or list of ints that has length 1 or 3. The number of entries by which the filter is moved right at each step. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + 'NWC' and 'NCW' are supported. + dilations : list + An int or list of ints that has length 1 or 3 which defaults to 1. + The dilation factor for each dimension of input. If set to k > 1, + there will be k-1 skipped cells between each filter element on that dimension. + Dilations in the batch and depth dimensions must be 1. + name : string + Optional name for the returned tensor. + + Returns + ------- + A Tensor with the same type as value. + """ + pass + + +def conv2d_transpose( + input, filters, output_shape, strides, padding='SAME', data_format='NHWC', dilations=None, name=None +): + """ + The transpose of conv2d. + + Parameters + ---------- + input : tensor + A 4-D Tensor of type float and shape [batch, height, width, in_channels] + for NHWC data format or [batch, in_channels, height, width] for NCHW data format. + filters : tensor + A 4-D Tensor with the same type as input and shape [height, width, + output_channels, in_channels]. filter's in_channels dimension must match that of input. + output_shape : tensor + A 1-D Tensor representing the output shape of the deconvolution op. + strides : list + An int or list of ints that has length 1, 2 or 4. The stride of the sliding window for each dimension of input. + If a single value is given it is replicated in the H and W dimension. + By default the N and C dimensions are set to 0. + The dimension order is determined by the value of data_format, see below for details. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + 'NHWC' and 'NCHW' are supported. + dilations : list + An int or list of ints that has length 1, 2 or 4, defaults to 1. + name : string + Optional name for the returned tensor. + + Returns + ------- + A Tensor with the same type as input. + """ + pass + + +def conv3d_transpose( + input, filters, output_shape, strides, padding='SAME', data_format='NDHWC', dilations=None, name=None +): + """ + The transpose of conv3d. + + Parameters + ---------- + input : tensor + A 5-D Tensor of type float and shape [batch, height, width, in_channels] for + NHWC data format or [batch, in_channels, height, width] for NCHW data format. + filters : tensor + A 5-D Tensor with the same type as value and shape [height, width, output_channels, in_channels]. + filter's in_channels dimension must match that of value. + output_shape : tensor + A 1-D Tensor representing the output shape of the deconvolution op. + strides : list + An int or list of ints that has length 1, 3 or 5. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + 'NDHWC' and 'NCDHW' are supported. + dilations : list of ints + An int or list of ints that has length 1, 3 or 5, defaults to 1. + name : string + Optional name for the returned tensor. + + Returns + ------- + A Tensor with the same type as value. + """ + + pass + + +class BatchNorm(object): + + def __init__(self): + pass + + def __call__(self, *args, **kwargs): + pass diff --git a/tensorlayer/backend/ops/load_backend.py b/tensorlayer/backend/ops/load_backend.py new file mode 100644 index 0000000..4343507 --- /dev/null +++ b/tensorlayer/backend/ops/load_backend.py @@ -0,0 +1,81 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import json +import os +import sys + +BACKEND = 'tensorflow' +# BACKEND = 'mindspore' +# BACKEND = 'dragon' + +# Check for backend.json files +tl_backend_dir = os.path.expanduser('~') +if not os.access(tl_backend_dir, os.W_OK): + tl_backend_dir = '/tmp' +tl_dir = os.path.join(tl_backend_dir, '.tl') + +config = { + 'backend': BACKEND, +} +if not os.path.exists(tl_dir): + path = os.path.join(tl_dir, 'tl_backend.json') + os.makedirs(tl_dir) + with open(path, "w") as f: + json.dump(config, f) + BACKEND = config['backend'] + sys.stderr.write("Create the backend configuration file :" + path + '\n') +else: + path = os.path.join(tl_dir, 'tl_backend.json') + with open(path, 'r') as load_f: + load_dict = json.load(load_f) + if load_dict['backend'] is not config['backend']: + BACKEND = config['backend'] + else: + BACKEND = load_dict['backend'] + +# Set backend based on TL_BACKEND flag. +if 'TL_BACKEND' in os.environ: + backend = os.environ['TL_BACKEND'] + if backend: + BACKEND = backend + +# import backend functions +if BACKEND == 'tensorflow': + from .tensorflow_backend import * + from .tensorflow_nn import * + import tensorflow as tf + BACKEND_VERSION = tf.__version__ + sys.stderr.write('Using TensorFlow backend.\n') + +elif BACKEND == 'mindspore': + from .mindspore_backend import * + from .mindspore_nn import * + import mindspore as ms + BACKEND_VERSION = ms.__version__ + # set context + import mindspore.context as context + import os + os.environ['DEVICE_ID'] = '0' + #context.set_context(mode=context.PYNATIVE_MODE,device_target='GPU'), + context.set_context(mode=context.GRAPH_MODE, device_target='CPU'), + # enable_task_sink=True, enable_loop_sink=True) + # context.set_context(mode=context.GRAPH_MODE, backend_policy='ms', + # device_target='Ascend', enable_task_sink=True, enable_loop_sink=True) + sys.stderr.write('Using MindSpore backend.\n') + +elif BACKEND == 'dragon': + from .dragon_backend import * + from .dragon_nn import * + import dragon as dg + BACKEND_VERSION = dg.__version__ + sys.stderr.write('Using Dragon backend.\n') + +elif BACKEND == 'paddle': + from .paddle_backend import * + from .paddle_nn import * + import paddle as pd + BACKEND_VERSION = pd.__version__ + sys.stderr.write('Using Paddle backend.\n') +else: + raise NotImplementedError("This backend is not supported") diff --git a/tensorlayer/backend/ops/mindspore_backend.py b/tensorlayer/backend/ops/mindspore_backend.py new file mode 100644 index 0000000..d067be2 --- /dev/null +++ b/tensorlayer/backend/ops/mindspore_backend.py @@ -0,0 +1,1239 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, print_function +from .mindspore_nn import nchw_to_nhwc, nhwc_to_nchw +from mindspore._c_expression.typing import Type +from mindspore.common import dtype as mstype + +from mindspore.common.parameter import Parameter +from mindspore.common.initializer import ( + initializer, Constant, Normal, TruncatedNormal, Initializer, _assignment, _calculate_in_and_out, One, Zero +) +from mindspore.common.tensor import Tensor +from mindspore._c_expression import Tensor as Tensor_ +from mindspore.ops import operations as P +from mindspore.ops import functional as F +from mindspore.ops import composite as C +import mindspore.context as context +from mindspore.nn import Cell + +import numpy as np +from scipy.stats import truncnorm +import random + +_dtypeDict = { + 'DType': Type, + 'float16': mstype.float16, + 'float32': mstype.float32, + 'float64': mstype.float64, + 'int8': mstype.int8, + 'int16': mstype.int16, + 'int32': mstype.int32, + 'int64': mstype.int64, + 'uint8': mstype.uint8, + 'uint16': mstype.uint16, + 'uint32': mstype.uint32, + 'uint64': mstype.uint64 +} + +DType = Type +float16 = mstype.float16 +float32 = mstype.float32 +float64 = mstype.float64 +int8 = mstype.int8 +int16 = mstype.int16 +int32 = mstype.int32 +int64 = mstype.int64 +uint8 = mstype.uint8 +uint16 = mstype.uint16 +uint32 = mstype.uint32 +uint64 = mstype.uint64 + +# isinstance input output +# TensorLike = Tensor_ + + +def set_context(**kwargs): + return context.set_context(**kwargs) + + +def get_tensor_shape(x): + return list(P.Shape()(x)) + + +# initializers +def zeros(shape, dtype=mstype.float32): + """ + Creates a tensor with all elements set to zero. + + Parameters + ---------- + shape : A list of integers + a tuple of integers, or a 1-D Tensor of type int32. + dtype : tensor + The DType of an element in the resulting Tensor + + Returns + ------- + A Tensor with all elements set to zero. + + """ + # shape = shape[::-1] + arr = np.ndarray(shape) + init_obj = Zero() + init_obj(arr) + return Tensor(arr, dtype=dtype) + + +def ones(shape, dtype=mstype.float32): + """ + Creates a tensor with all elements set to ones. + + Parameters + ---------- + shape : A list of integers + a tuple of integers, or a 1-D Tensor of type int32. + dtype : tensor + The DType of an element in the resulting Tensor + + Returns + ------- + A Tensor with all elements set to zero. + + """ + # shape = shape[::-1] + arr = np.ndarray(shape) + init_obj = One() + init_obj(arr) + return Tensor(arr, dtype=dtype) + + +def constant(value, dtype=mstype.float32, shape=None): + """ + Creates a constant tensor from a tensor-like object. + + Parameters + ---------- + value : list + A constant value (or list) of output type dtype. + dtype : tensor + The type of the elements of the resulting tensor. + shape : tuple + Optional dimensions of resulting tensor. + + Returns + ------- + A Constant Tensor. + + """ + # shape = shape[::-1] + arr = np.ndarray(shape) + Constant(value)(arr=arr) + return Tensor(arr, dtype=dtype) + + +class Uniform(Initializer): + """ + Initialize a uniform array, and obtain values U(-scale, scale) from the uniform distribution + to fill the input tensor. + + Args: + minval : int + The lower bound on the range of random values to generate (inclusive). Defaults to 0. + maxval : int + The upper bound on the range of random values to generate (exclusive). Defaults to 1 if dtype is floating point. + seed : int + Used in combination with tf.random.set_seed to create a reproducible sequence of tensors across multiple calls. + + Returns: + Array, uniform array. + """ + + def __init__(self, minval=0, maxval=None, seed=None): + super(Uniform, self).__init__(minval=minval, maxval=maxval, seed=seed) + self.minval = minval + self.maxval = maxval + self.seed = seed + + def _initialize(self, arr): + random.seed(self.seed) + tmp = np.random.uniform(self.minval, self.maxval, arr.shape) + _assignment(arr, tmp) + + +def random_uniform(shape, minval=0, maxval=None, dtype=mstype.float32, seed=None): + """ + Outputs random values from a uniform distribution. + + Parameters + ---------- + shape : tuple + A 1-D integer Tensor or Python array. The shape of the output tensor. + minval : int + The lower bound on the range of random values to generate (inclusive). Defaults to 0. + maxval : int + The upper bound on the range of random values to generate (exclusive). Defaults to 1 if dtype is floating point. + dtype : tensor + The type of the output: float16, float32, float64, int32, or int64. + seed : int + Used in combination with tf.random.set_seed to create a reproducible sequence of tensors across multiple calls. + Returns + ------- + A tensor of the specified shape filled with random uniform values. + + """ + # shape = shape[::-1] + arr = np.ndarray(shape) + init_obj = Uniform(minval=minval, maxval=maxval, seed=seed) + init_obj(arr) + return Tensor(arr, dtype=dtype) + + +class Normal(Initializer): + """ + Initialize a normal array, and obtain values N(0, sigma) from the uniform distribution + to fill the input tensor. + + Parameters + ---------- + mean : float + The mean of the normal distribution + stddev : float + The standard deviation of the normal distribution. + seed : A Python integer + Used to create a random seed for the distribution + + Returns: + Array, normal array. + """ + + def __init__(self, mean=0.0, stddev=0.01, seed=None): + super(Normal, self).__init__(mean=mean, stddev=stddev) + self.mean = mean + self.stddev = stddev + self.seed = seed + + def _initialize(self, arr): + random.seed(self.seed) + tmp = np.random.normal(self.mean, self.stddev, arr.shape) + _assignment(arr, tmp) + + +class RandomNormal(Cell): + def __init__(self, mean=0.0, stddev=0.01, seed=None): + super(RandomNormal, self).__init__() + self.normal = Normal(mean=mean, stddev=stddev, seed=seed) + + def construct(self, shape): + arr = np.ndarray(shape) + outputs = self.normal(arr) + return outputs + + +def random_normal(shape, mean=0.0, stddev=1.0, dtype=mstype.float32, seed=None): + """ + Outputs random values from a normal distribution. + + Parameters + ---------- + shape : tuple + A 1-D integer Tensor or Python array. The shape of the output tensor. + mean : float + The mean of the normal distribution + stddev : float + The standard deviation of the normal distribution. + dtype : tensor + The type of the output. + seed : A Python integer + Used to create a random seed for the distribution + + Returns + ------- + A tensor of the specified shape filled with random normal values. + + """ + # shape = shape[::-1] + arr = np.ndarray(shape) + init_obj = Normal(mean=mean, stddev=stddev, seed=seed) + init_obj(arr) + return Tensor(arr, dtype=dtype) + + +class TruncatedNormal(Initializer): + """ + Initialize a truncated normal distribution which is a bounded normal distribution within N(low, high). + + Args: + sigma (float): The sigma of the array. Default: 0.01. + + Returns: + Array, truncated normal array. + """ + + def __init__(self, mean=0.0, stddev=0.01, seed=None): + super(TruncatedNormal, self).__init__(mean=mean, stddev=stddev, seed=seed) + self.mean = mean + self.stddev = stddev + self.seed = seed + + def _initialize(self, arr): + tmp = truncnorm.rvs(-2, 2, loc=self.mean, scale=self.stddev, size=arr.shape, random_state=None) + _assignment(arr, tmp) + + +def truncated_normal(shape, mean=0.0, stddev=1.0, dtype=mstype.float32, seed=None): + """ + Outputs random values from a truncated normal distribution. + + Parameters + ---------- + shape : tuple + A 1-D integer Tensor or Python array. The shape of the output tensor. + mean : float + The mean of the normal distribution + stddev : float + The standard deviation of the normal distribution. + dtype : tensor + The type of the output. + seed : A Python integer + Used to create a random seed for the distribution + + Returns + ------- + A tensor of the specified shape filled with random truncated normal values. + + """ + # shape = shape[::-1] + arr = np.ndarray(shape) + init_obj = TruncatedNormal(mean=mean, stddev=stddev, seed=seed) + init_obj(arr) + return Tensor(arr, dtype=dtype) + + +class HeNormal(Initializer): + r""" + he_normal: It draws samples from a truncated normal distribution centered on 0 with + stddev = sqrt(2 / fan_in) where fan_in is the number of input units in the weight tensor. + + Args: + arr (Array): The array to be assigned. + + Returns: + Array, assigned array. + """ + + def __init__(self, seed=None): + super(HeNormal, self).__init__(seed=seed) + self.seed = seed + + def _initialize(self, arr): + n_in, _ = _calculate_in_and_out(arr) + boundary = np.sqrt(2.0 / n_in) + random.seed(self.seed) + data = np.random.normal(-boundary, boundary, arr.shape) + _assignment(arr, data) + + +def he_normal(shape, dtype, seed=None): + """ + He normal initializer. + + Parameters + ---------- + seed : A Python integer. + Used to seed the random generator. + shape : tuple + A 1-D integer Tensor or Python array. The shape of the output tensor. + dtype : tensor + The type of the output. + + Returns + ------- + A tensor of the specified shape filled with he normal values. + """ + # shape = shape[::-1] + arr = np.ndarray(shape) + init_obj = HeNormal(seed) + init_obj(arr) + return Tensor(arr, dtype=dtype) + + +def Variable(initial_value, name, trainable=True): + """ + Creates a new variable with value initial_value. + + Parameters + ---------- + initial_value : tensor + A Tensor, or Python object convertible to a Tensor + name : str + Optional name for the variable. Defaults to 'Variable' and gets uniquified automatically. + Returns + ------- + Variable + """ + + var = Parameter(initial_value, name=name, requires_grad=trainable) + return var + + +class MatMul(Cell): + + def __init__(self): + super(MatMul, self).__init__() + self.matmul = P.MatMul() + + def construct(self, a, b): + return self.matmul(a, b) + + +def matmul(a, b): + """ + Multiplies matrix a by matrix b, producing a * b. + + Parameters + ---------- + a : tensor + type float16, float32, float64, int32, complex64, complex128 and rank > 1. + b : tensor + with same type and rank as a. + + Returns + ------- + A Tensor of the same type as a and b + """ + matmul_obj = P.MatMul() + outputs = matmul_obj(a, b) + return outputs + + +def add(value, bias): + """ + Returns x + y element-wise. + + Parameters + ---------- + value : tensor. + Must be one of the following types: bfloat16, half, float32, float64, + uint8, int8, int16, int32, int64, complex64, complex128, string. + bias : tensor + Must have the same type as a + name : str + A name for the operation + + Returns + ------- + A Tensor. Has the same type as a. + """ + + add_obj = P.TensorAdd() + outputs = add_obj(value, bias) + return outputs + + +def dtypes(dt): + """ + Data dtypes. + + Parameters + ---------- + dt : string + It could be 'uint8', 'uint16', 'uint32', 'uint64', 'int8', 'int16', + 'int32', 'int64', 'float16', 'float32', 'float64', 'DType'. + + Returns + ------- + Data dtypes + """ + + if dt not in _dtypeDict.keys(): + raise Exception("Unsupported dtype: {}".format(dt)) + return _dtypeDict[dt] + + +class Maximum(Cell): + + def __init__(self): + super(Maximum, self).__init__() + self.maximum = P.Maximum() + + def construct(self, x, y): + return self.maximum(x, y) + + +class Minimum(Cell): + + def __init__(self): + super(Minimum, self).__init__() + self.minimum = P.Minimum() + + def construct(self, x, y): + return self.minimum(x, y) + + +def minimum(x, y): + """ + Returns the min of x and y (i.e. x < y ? x : y) element-wise. + + Parameters + ---------- + x : tensor. + Must be one of the following types: bfloat16, half, float32, float64, int32, int64. + y : A Tensor. + Must have the same type as x. + name : str + A name for the operation (optional). + + Returns + ------- + A Tensor. Has the same type as x + """ + minimum_obj = P.Minimum() + outputs = minimum_obj(x, y) + return outputs + + +class FlattenReshape(Cell): + + def __init__(self): + super(FlattenReshape, self).__init__() + self.shape = P.Shape() + self.reshape = P.Reshape() + + def construct(self, inputs): + dim = 1 + for d in self.shape(inputs)[1:]: + dim *= d + return self.reshape(inputs, (-1, dim)) + + +class Reshape(Cell): + + def __init__(self, shape): + super(Reshape, self).__init__() + self.reshape = P.Reshape() + self.shape = tuple(shape) + + def construct(self, tensor): + return self.reshape(tensor, self.shape) + + +def reshape(tensor, shape): + """ + Reshapes a tensor. + + Parameters + ---------- + tensor : tensor + A Tensor. + shape : tensor + Defines the shape of the output tensor. + Returns + ------- + A Tensor. Has the same type as tensor + """ + reshape_obj = P.Reshape() + outputs = reshape_obj(tensor, tuple(shape)) + return outputs + + +class Concat(Cell): + + def __init__(self, axis): + super(Concat, self).__init__() + self.concat = P.Concat(axis) + + def construct(self, values): + return self.concat(values) + + +def concat(values, axis): + """ + Concatenates tensors along one dimension. + + Parameters + ---------- + values : list + A list of Tensor objects or a single Tensor + axis : int + 0-D int32 Tensor. Dimension along which to concatenate + Returns + ------- + A Tensor resulting from concatenation of the input tensors. + """ + # TODO testing axis + concat_obj = P.Concat(axis) + outputs = concat_obj(values) + return outputs + + +def convert_to_tensor(value, dtype=None): + """ + Converts the given value to a Tensor. + + Parameters + ---------- + value : object + An object whose type has a registered Tensor conversion function. + dtype : optional + Optional element type for the returned tensor. If missing, the type is inferred from the type of value. + + Returns + ------- + A Tensor based on value. + """ + #todo testing value + return Tensor(value, dtype=dtype) + + +def sqrt(x): + """ + Computes square root of x element-wise. + + Parameters + ---------- + x : tensor + Must be one of the following types: bfloat16, half, float32, float64, complex64, complex128. + + Returns + ------- + A Tensor. Has the same type as x. + """ + sqrt_obj = P.Sqrt() + outputs = sqrt_obj(x) + return outputs + + +class ReduceSum(Cell): + + def __init__(self, axis): + super(ReduceSum, self).__init__() + self.axis = axis + self.reduce_sum = P.ReduceSum(keep_dims=True) + + def construct(self, input): + return self.reduce_sum(input, self.axis) + + +class ReduceMean(Cell): + + def __init__(self, axis): + super(ReduceMean, self).__init__() + self.axis = axis + self.reducemean = P.ReduceMean(keep_dims=False) + + def construct(self, inputs): + output = self.reducemean(inputs, self.axis) + return output + + +def reduce_mean(input_tensor, axis=None): + """ + Computes the mean of elements across dimensions of a tensor. + + Parameters + ---------- + input_tensor : tensor + The tensor to reduce. Should have numeric type. + axis : int + The dimensions to reduce. If None (the default), reduces all dimensions. + Must be in the range [-rank(input_tensor), rank(input_tensor)). + name : str + A name for the operation (optional). + + Returns + ------- + The reduced tensor. + """ + + Rmean_obj = P.ReduceMean(keep_dims=False) + outputs = Rmean_obj(input_tensor, axis) + return outputs + + +class ReduceMax(Cell): + + def __init__(self, axis): + super(ReduceMax, self).__init__() + self.axis = axis + self.reducemax = P.ReduceMax(keep_dims=False) + + def construct(self, inputs): + output = self.reducemax(inputs, self.axis) + return output + + +def reduce_max(input_tensor, axis=None): + """ + Computes the maximum of elements across dimensions of a tensor. + + Parameters + ---------- + input_tensor : tensor + The tensor to reduce. Should have real numeric type. + axis : int + The dimensions to reduce. If None (the default), reduces all dimensions. + Must be in the range [-rank(input_tensor), rank(input_tensor)). + name : str + A name for the operation (optional). + + Returns + ------- + The reduced tensor. + """ + + Rmax_obj = P.ReduceMax(keep_dims=False) + outputs = Rmax_obj(input_tensor, axis) + return outputs + + +def reduce_min(input_tensor, axis=None): + """ + Computes the minimum of elements across dimensions of a tensor. + + Parameters + ---------- + input_tensor : tensor + The tensor to reduce. Should have real numeric type. + axis : int + The dimensions to reduce. If None (the default), reduces all dimensions. + Must be in the range [-rank(input_tensor), rank(input_tensor)). + name : str + A name for the operation (optional). + + Returns + ------- + The reduced tensor. + """ + + Rmin_obj = P.ReduceMin(keep_dims=False) + outputs = Rmin_obj(input_tensor, axis) + return outputs + +class Pad(Cell): + def __init__(self, paddings, mode="REFLECT"): + super(Pad, self).__init__() + if mode not in ["REFLECT", "SYMMETRIC"]: + raise Exception("Unsupported mode: {}".format(mode)) + self.pad = P.MirrorPad(mode=mode) + self.paddings = Tensor(paddings) + + def construct(self, x): + return self.pad(x, self.paddings) + +def pad(tensor, paddings, mode='CONSTANT', constant_values=0): + """ + Pads a tensor. + + Parameters + ---------- + tensor : tensor + A Tensor. + paddings : tuple + A tuple of type int32. + mode : str + One of "CONSTANT", "REFLECT", or "SYMMETRIC" (case-insensitive) + constant_values : int + In "CONSTANT" mode, the scalar pad value to use. Must be same type as tensor. + + Returns + ------- + A Tensor. Has the same type as tensor. + """ + raise NotImplementedError + + +class Unstack(Cell): + def __init__(self, axis, num=None): + super(Unstack, self).__init__() + if num is not None: + raise ("The num Parameters do not need to be set.") + self.unstack = P.Unpack(axis=axis) + + def construct(self, values): + return self.unstack(values) + + +class Stack(Cell): + def __init__(self, axis=0): + super(Stack, self).__init__() + self.stack = P.Pack(axis=axis) + + def construct(self, values): + return self.stack(values) + + +def stack(values, axis=0): + """ + Stacks a list of rank-R tensors into one rank-(R+1) tensor. + + Parameters + ---------- + values : list + A list of Tensor objects with the same shape and type. + axis : int + An int. The axis to stack along. Defaults to the first dimension. + Negative values wrap around, so the valid range is [-(R+1), R+1). + + Returns + ------- + A stacked Tensor with the same type as values. + """ + _stack = P.Pack(axis=axis) + return _stack(values) + + +class Meshgrid(Cell): + def __init__(self, indexing='xy'): + super(Meshgrid, self).__init__() + self._meshgrid = P.Meshgrid(indexing=indexing) + + def construct(self, *args): + inputs = tuple(*args) + return self._meshgrid(inputs) + + + +def meshgrid(*args, **kwargs): + """ + Broadcasts parameters for evaluation on an N-D grid. + + Parameters + ---------- + x : tensor + Tensors with rank 1. + y : tensor + Tensors with rank 1. + + Returns + ------- + A list of N Tensors with rank N. + """ + + _meshgrid = P.Meshgrid(**kwargs) + return _meshgrid(*args) + + + +def range(start, limit=None, delta=1, dtype=None): + """ + Creates a sequence of numbers. + + Parameters + ---------- + start : tensor + A 0-D Tensor (scalar). Acts as first entry in the range if limit is not None; + otherwise, acts as range limit and first entry defaults to 0. + limit : tensor + A 0-D Tensor (scalar). Upper limit of sequence, exclusive. If None, + defaults to the value of start while the first entry of the range defaults to 0. + delta : tensor + A 0-D Tensor (scalar). Number that increments start. Defaults to 1. + dtype : type + The type of the elements of the resulting tensor. + + Returns + ------- + An 1-D Tensor of type dtype. + """ + + pass + + +class ExpandDims(Cell): + + def __init__(self, axis): + super(ExpandDims, self).__init__() + self.axis = axis + self.expand_dims = P.ExpandDims() + + def construct(self, input): + output = self.expand_dims(input, self.axis) + return output + + +def expand_dims(input, axis): + """ + Inserts a dimension of 1 into a tensor's shape. + + Parameters + ---------- + input : tensor + A Tensor. + axis : int + 0-D (scalar). Specifies the dimension index at which to expand the shape of input. + Must be in the range [-rank(input) - 1, rank(input)]. + + Returns + ------- + A Tensor with the same data as input, but its shape has an additional dimension of size 1 added. + """ + + expand_obj = P.ExpandDims() + outputs = expand_obj(input, axis) + return outputs + + +class Tile(Cell): + + def __init__(self): + super(Tile, self).__init__() + self.tile = P.Tile() + + def construct(self, input, multiples): + return self.tile(input, tuple(multiples)) + + + +def tile(input, multiples): + """ + Constructs a tensor by tiling a given tensor. + + Parameters + ---------- + input : tensor + A Tensor. 1-D or higher. + multiples : tensor + Must be one of the following types: int32, int64. 1-D. + Length must be the same as the number of dimensions in input + + Returns + ------- + A Tensor. Has the same type as input. + """ + tile_obj = P.Tile() + outputs = tile_obj(input, multiples) + return outputs + + +class Cast(Cell): + + def __init__(self, dtype): + super(Cast, self).__init__() + self.dtype = dtype + self.cast = P.Cast() + + def construct(self, input): + return self.cast(input, dtype=self.dtype) + + +def cast(x, dtype): + """ + Casts a tensor to a new type. + + Parameters + ---------- + x : tensor + A Tensor or SparseTensor or IndexedSlices of numeric type. + It could be uint8, uint16, uint32, uint64, int8, int16, int32, int64, float16, float32, float64. + dtype : dtpye + The destination type. The list of supported dtypes is the same as x + + Returns + ------- + A Tensor or SparseTensor or IndexedSlices with same shape as x and same type as dtype. + """ + cast_obj = P.Cast() + outputs = cast_obj(x, dtype) + return outputs + + +class Transpose(Cell): + + def __init__(self, perm, conjugate=False): + super(Transpose, self).__init__() + self.perm = tuple(perm) + self.conjugate = conjugate + self.transpose = P.Transpose() + if self.conjugate: + raise NotImplementedError("conjugate not implemented") + + def construct(self, a): + return self.transpose(a, self.perm) + + +def transpose(a, perm=None, conjugate=False): + """ + Transposes a. + + Parameters + ---------- + a : tensor + A Tensor. + perm : int + A permutation of the dimensions of a. + conjugate : bool + Setting it to True is mathematically equivalent to ms.math.conj(ms.transpose(input)). + + Returns + ------- + A transposed Tensor. + """ + # TODO conjugate + trans_obj = P.Transpose() + outputs = trans_obj(a, perm) + print(outputs) + + +def gather_nd(params, indices, batch_dims=0): + """ + Gather slices from params into a Tensor with shape specified by indices. + + Parameters + ---------- + params : tensor + The tensor from which to gather values. + indices : tensor + Must be one of the following types: int32, int64. Index tensor. + batch_dims : int + An integer or a scalar 'Tensor'. The number of batch dimensions. + + Returns + ------- + A Tensor. Has the same type as params. + """ + + pass + + +def clip_by_value(t, clip_value_min, clip_value_max): + """ + Clips tensor values to a specified min and max. + + Parameters + ---------- + t : tensor + A Tensor or IndexedSlices + clip_value_min : tensor + A 0-D (scalar) Tensor, or a Tensor with the same shape as t. The minimum value to clip by + clip_value_max : tensor + A 0-D (scalar) Tensor, or a Tensor with the same shape as t. The minimum value to clip by + + Returns + ------- + A clipped Tensor or IndexedSlices. + """ + min_value = Tensor(clip_value_min, mstype.float32) + max_value = Tensor(clip_value_max, mstype.float32) + output = C.clip_by_value(t, min_value, max_value) + return output + + +def split(value, num_or_size_splits, axis=0, num=None): + """ + Splits a tensor into sub tensors. + + Parameters + ---------- + value : tensor + The Tensor to split. + num_or_size_splits : list + Either an integer indicating the number of splits along split_dim or a 1-D integer Tensor or + Python list containing the sizes of each output tensor along split_dim. + axis : int + The dimension along which to split. Must be in the range [-rank(value), rank(value)). Defaults to 0. + num : int + used to specify the number of outputs when it cannot be inferred from the shape of size_splits. + + Returns + ------- + Tensor objects resulting from splitting value. + """ + pass + + +def floor(x): + return NotImplementedError + + +def gather(params, indices): + return NotImplementedError + + +def linspace(start, stop, num): + return NotImplementedError + + +def slice(inputs, starts, sizes): + return NotImplementedError + + +def add_n(inputs): + return NotImplementedError + + +class OneHot(Cell): + + def __init__(self, axis=-1, depth=1, on_value=1.0, off_value=0.0, dtype=mstype.float32): + super(OneHot, self).__init__() + self.onehot = P.OneHot(axis) + self.depth = depth + self.dtype = dtype + self.on_value = F.cast(on_value, self.dtype) + self.off_value = F.cast(off_value, self.dtype) + + def construct(self, indices): + return self.onehot(indices, self.depth, self.on_value, self.off_value) + + +class L2Normalize(Cell): + + def __init__(self, axis=None, epsilon=1e-12): + super(L2Normalize, self).__init__() + pass + + def __call__(self, input, *args, **kwargs): + pass + + +class EmbeddingLookup(Cell): + + def __init__(self, max_norm=None): + self.max_norm = max_norm + + def __call__(self, params, ids, *args, **kwargs): + pass + + +class NCELoss(object): + + def __init__(self, num_true=1, sampled_values=None, remove_accidental_hits=False): + super(NCELoss, self).__init__() + + def __call__(self, weights, biases, labels, inputs, num_sampled, num_classes): + pass + + +class Not_equal(object): + + def __init__(self): + pass + + def __call__(self, x, y): + pass + + +class Count_nonzero(object): + + def __init__(self, keepdims=None, dtype=int64): + pass + + def __call__(self, *args, **kwargs): + pass + + +class Resize(Cell): + + def __init__(self, scale, method, antialias=False, data_format='channels_last', ksize=None): + super(Resize, self).__init__() + self.data_format = data_format + if method not in ['nearest', 'bilinear']: + raise ('The method must be "nearest" or "bilinear".') + self.method = method + + if ksize is None: + raise ('The "bilinear" and "nearest" method must enter ksize. The dimension of size must be 2 (H, W).') + + out_seize = (int(ksize[0] * scale[0]), int(ksize[1] * scale[1])) + if self.method == 'nearest': + self.resize = P.ResizeNearestNeighbor(size=out_seize, align_corners=antialias) + elif self.method == 'bilinear': + + self.resize = P.ResizeBilinear(size=out_seize) + + def construct(self, inputs): + if self.data_format == 'channels_last': + inputs = nhwc_to_nchw(inputs) + outputs = self.resize(inputs) + if self.data_format == 'channels_last': + outputs = nchw_to_nhwc(outputs) + return outputs + + +def resize(inputs, output_size, method, antialias): + raise NotImplementedError + + +class ZeroPadding1D(Cell): + def __init__(self, padding): + super(ZeroPadding1D, self).__init__() + if np.size(padding) == 2: + self.pad = P.Pad(paddings=padding) + else: + raise ("The shape of parameter paddings is (N, 2). N is the rank of input data.") + + def construct(self, inputs): + return self.pad(inputs) + + +class ZeroPadding2D(Cell): + def __init__(self, padding): + super(ZeroPadding2D, self).__init__() + if np.size(padding) == 4: + self.pad = P.Pad(paddings=padding) + else: + raise ("The shape of parameter paddings is (N, 2). N is the rank of input data.") + + def construct(self, inputs): + return self.pad(inputs) + + +class ZeroPadding3D(Cell): + def __init__(self, padding): + super(ZeroPadding3D, self).__init__() + if np.size(padding) == 6: + self.pad = P.Pad(paddings=padding) + else: + raise ("The shape of parameter paddings is (N, 2). N is the rank of input data.") + + def construct(self, inputs): + return self.pad(inputs) + + +class Sign(Cell): + + def __init__(self): + super(Sign, self).__init__() + self.sign = P.Sign() + + def construct(self, x): + return self.sign(x) + +def ceil(x): + _ceil = P.Ceil() + return _ceil(x) + +def multiply(x, y): + raise NotImplementedError + +def divide(x, y): + raise NotImplementedError + +def identity(x): + raise NotImplementedError + +class BatchToSpace(Cell): + def __init__(self, block_size, crops): + super(BatchToSpace, self).__init__() + self.batch_to_space = P.BatchToSpace(block_size=block_size, crops=crops) + + def __call__(self, input_x): + return self.batch_to_space(input_x) + +class DepthToSpace(Cell): + def __init__(self, block_size, data_format='NHWC'): + super(DepthToSpace, self).__init__() + self.data_format = data_format + self.depth_to_space = P.DepthToSpace(block_size=block_size) + + def __call__(self, input): + if self.data_format == 'NHWC': + input = nhwc_to_nchw(input) + + output = self.depth_to_space(input) + + if self.data_format == 'NHWC': + output = nchw_to_nhwc(output) + + return output \ No newline at end of file diff --git a/tensorlayer/backend/ops/mindspore_nn.py b/tensorlayer/backend/ops/mindspore_nn.py new file mode 100644 index 0000000..3af4430 --- /dev/null +++ b/tensorlayer/backend/ops/mindspore_nn.py @@ -0,0 +1,1323 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, print_function + +from mindspore.nn.cell import Cell +from mindspore import context +import mindspore as ms +from mindspore.ops import operations as P +from mindspore.ops import functional as F +from mindspore.communication.management import get_group_size, get_rank +from mindspore.communication import management +from mindspore._checkparam import check_int_positive +from mindspore._extends import cell_attr_register + + + +def padding_format(padding): + """ + Checks that the padding format correspond format. + + Parameters + ---------- + padding : str + Must be one of the following:"same", "SAME", "VALID", "valid" + + Returns + ------- + str "SAME" or "VALID" + """ + + if padding in ["SAME", "same"]: + padding = "same" + elif padding in ["VALID", "valid"]: + padding = "valid" + elif padding == None: + padding = None + else: + raise Exception("Unsupported padding: " + str(padding)) + return padding + + +def preprocess_1d_format(data_format, padding): + """ + Checks that the 1-D dataformat format correspond format. + + Parameters + ---------- + data_format : str + Must be one of the following:"channels_last","NWC","NCW","channels_first" + padding : str + Must be one of the following:"same","valid","SAME","VALID" + + Returns + ------- + str "NWC" or "NCW" and "SAME" or "VALID" + """ + + if data_format in ["channels_last", "NWC"]: + data_format = "NWC" + elif data_format in ["channels_first", "NCW"]: + data_format = "NCW" + elif data_format == None: + data_format = None + else: + raise Exception("Unsupported data format: " + str(data_format)) + padding = padding_format(padding) + return data_format, padding + + +def preprocess_2d_format(data_format, padding): + """ + Checks that the 2-D dataformat format correspond format. + + Parameters + ---------- + data_format : str + Must be one of the following:"channels_last","NHWC","NCHW","channels_first" + padding : str + Must be one of the following:"same","valid","SAME","VALID" + + Returns + ------- + str "NHWC" or "NCHW" and "SAME" or "VALID" + """ + + if data_format in ["channels_last", "NHWC", "nhwc"]: + data_format = "NHWC" + elif data_format in ["channels_first", "NCHW", "nchw"]: + data_format = "NCHW" + elif data_format == None: + data_format = None + else: + raise Exception("Unsupported data format: " + str(data_format)) + padding = padding_format(padding) + return data_format, padding + + +def preprocess_3d_format(data_format, padding): + """ + Checks that the 3-D dataformat format correspond format. + + Parameters + ---------- + data_format : str + Must be one of the following:"channels_last","NDHWC","NCDHW","channels_first" + padding : str + Must be one of the following:"same","valid","SAME","VALID" + + Returns + ------- + str "NDHWC" or "NCDHW" and "SAME" or "VALID" + """ + + if data_format in ['channels_last', 'NDHWC']: + data_format = 'NDHWC' + elif data_format in ['channels_first', 'NCDHW']: + data_format = 'NCDHW' + elif data_format == None: + data_format = None + else: + raise Exception("Unsupported data format: " + str(data_format)) + padding = padding_format(padding) + return data_format, padding + + +def nchw_to_nhwc(x): + """ + Channels first to channels last + + Parameters + ---------- + x : tensor + channels first tensor data + + Returns + ------- + channels last tensor data + """ + + if len(P.Shape()(x)) == 3: + x = P.Transpose()(x, (0, 2, 1)) + elif len(P.Shape()(x)) == 4: + x = P.Transpose()(x, (0, 2, 3, 1)) + elif len(P.Shape()(x)) == 5: + x = P.Transpose()(x, (0, 2, 3, 4, 1)) + # else: + # raise Exception("Unsupported dimensions") + return x + + +def nhwc_to_nchw(x): + """ + Channles last to channels first + + Parameters + ---------- + x : tensor + channels last tensor data + + Returns + ------- + channels first tensor data + """ + + if len(P.Shape()(x)) == 3: + x = P.Transpose()(x, (0, 2, 1)) + elif len(P.Shape()(x)) == 4: + x = P.Transpose()(x, (0, 3, 1, 2)) + elif len(P.Shape()(x)) == 5: + x = P.Transpose()(x, (0, 4, 1, 2, 3)) + # else: + # raise Exception("Unsupported dimensions") + return x + + +class ReLU(Cell): + + def __init__(self): + super(ReLU, self).__init__() + self.relu = P.ReLU() + + def construct(self, x): + return self.relu(x) + + +def relu(x): + """ + Computes rectified linear: max(features, 0). + + Parameters + ---------- + x : tensor + Must be one of the following types: float32, float64, int32, uint8, int16, + int8, int64, bfloat16, uint16, half, uint32, uint64, qint8. + + Returns + ------- + A Tensor. Has the same type as features. + """ + outputs = P.ReLU() + return outputs(x) + + +class ReLU6(Cell): + + def __init__(self): + super(ReLU6, self).__init__() + self.relu6 = P.ReLU6() + + def construct(self, x): + return self.relu6(x) + + +def relu6(x): + """ + Computes Rectified Linear 6: min(max(features, 0), 6). + + Parameters + ---------- + x : tensor + Must be one of the following types: float32, float64, int32, uint8, int16, + int8, int64, bfloat16, uint16, half, uint32, uint64, qint8. + + Returns + ------- + A Tensor with the same type as features. + """ + outputs = P.ReLU6() + return outputs(x) + + +class LeakyReLU(Cell): + + def __init__(self, alpha=0.2): + super(LeakyReLU, self).__init__() + self.leakyrelu = ms.nn.LeakyReLU(alpha=alpha) + + def construct(self, x): + return self.leakyrelu(x) + + +def leaky_relu(x, alpha=0.2): + """ + Compute the Leaky ReLU activation function. + + Parameters + ---------- + x : tensor + representing preactivation values. Must be one of the following types: + float16, float32, float64, int32, int64. + + Returns + ------- + The activation value. + """ + + leaky_relu = LeakyReLU(alpha=alpha) + output = leaky_relu(x) + return leaky_relu + + +class Softplus(Cell): + + def __init__(self): + super(Softplus, self).__init__() + self.softplus = P.Softplus() + + def construct(self, x): + return self.softplus(x) + + +def softplus(x): + """ + Computes softplus: log(exp(features) + 1). + + Parameters + ---------- + x : tensor + Must be one of the following types: half, bfloat16, float32, float64. + + Returns + ------- + A Tensor. Has the same type as features. + """ + + obj = Softplus() + return obj(x) + + +class Tanh(Cell): + + def __init__(self): + super(Tanh, self).__init__() + self.tanh = P.Tanh() + + def construct(self, x): + return self.tanh(x) + + +def tanh(x): + """ + Computes hyperbolic tangent of x element-wise. + + Parameters + ---------- + x : tensor + Must be one of the following types: bfloat16, half, float32, float64, complex64, complex128. + + Returns + ------- + A Tensor. Has the same type as x. + """ + + _tanh = Tanh() + return _tanh(x) + + +class Sigmoid(Cell): + + def __init__(self): + super(Sigmoid, self).__init__() + self.sigmoid = P.Sigmoid() + + def construct(self, x): + return self.sigmoid(x) + + +def sigmoid(x): + """ + Computes sigmoid of x element-wise. + + Parameters + ---------- + x : tensor + A Tensor with type float16, float32, float64, complex64, or complex128. + + Returns + ------- + A Tensor with the same type as x. + """ + outputs = P.Sigmoid() + return outputs(x) + + +class Softmax(Cell): + + def __init__(self): + super(Softmax, self).__init__() + self.softmax = P.Softmax() + + def construct(self, x): + return self.softmax(x) + + +def softmax(logits, axis=None): + """ + Computes softmax activations. + + Parameters + ---------- + logits : tensor + Must be one of the following types: half, float32, float64. + axis : int + The dimension softmax would be performed on. The default is -1 which indicates the last dimension. + + Returns + ------- + A Tensor. Has the same type and shape as logits. + """ + outputs = P.Softmax(axis) + return outputs(logits) + + +class Dropout(Cell): + + def __init__(self, keep, seed=0): + super(Dropout, self).__init__() + self.dropout = P.Dropout(keep_prob=keep) + self.is_gpu = context.get_context('device_target') in ["GPU"] + self.get_shape = P.Shape() + self.dropout_gen_mask = P.DropoutGenMask(Seed0=seed, Seed1=0) + self.dropout_do_mask = P.DropoutDoMask() + self.cast = P.Cast() + self.keep_prob = keep # ms.Tensor(keep, dtype=ms.float32) + # print(self.keep_prob, type(self.keep_prob)) + + def construct(self, inputs): + if self.is_gpu: + outputs, _ = self.dropout(inputs) + return outputs + if self.keep_prob == 1: + return inputs + shape = self.get_shape(inputs) + dtype = P.DType()(inputs) + if self._is_float_dtype(dtype): + keep_prob = self.cast(self.keep_prob, dtype=dtype) + else: + keep_prob = self.cast(self.keep_prob, ms.float16) + output = self.dropout_gen_mask(shape, keep_prob) + return self.dropout_do_mask(inputs, output, keep_prob) + + def _is_float_dtype(dtype): + if dtype in [ms.float32, ms.float16]: + return True + return False + + +class BiasAdd(Cell): + """ + Adds bias to value. + + Parameters + ---------- + x : tensor + A Tensor with type float, double, int64, int32, uint8, int16, int8, complex64, or complex128. + bias : tensor + Must be the same type as value unless value is a quantized type, + in which case a different quantized type may be used. + Returns + ------- + A Tensor with the same type as value. + """ + + def __init__(self, data_format='channels_first'): + super(BiasAdd, self).__init__() + self.bias_add = P.BiasAdd() + if data_format in ['channels_first', 'NCW', 'NCHW', 'NCDHW']: + self.data_format = 'channels_first' + elif data_format in ['channels_last', 'NWC', 'NHWC', 'NDHWC']: + self.data_format = 'channels_last' + else: + raise ("Unsupported data format: " + str(data_format)) + + def construct(self, x, bias): + if self.data_format == 'channels_last': + x = nhwc_to_nchw(x) + outputs = self.bias_add(x, bias) + if self.data_format == 'channels_last': + outputs = nchw_to_nhwc(outputs) + return outputs + + +def bias_add(x, bias): + """ + Adds bias to value. + + Parameters + ---------- + x : tensor + A Tensor with type float, double, int64, int32, uint8, int16, int8, complex64, or complex128. + bias : tensor + Must be the same type as value unless value is a quantized type, + in which case a different quantized type may be used. + data_format : A string. + 'N...C' and 'NC...' are supported. + name : str + A name for the operation (optional). + Returns + ------- + A Tensor with the same type as value. + """ + raise NotImplementedError + + +class Conv1D(Cell): + + def __init__(self, stride, padding, data_format='NWC', dilations=None, out_channel=None, k_size=None): + super(Conv1D, self).__init__() + self.data_format, self.padding = preprocess_1d_format(data_format, padding) + self.stride = (1, stride) + self.dilations = (1, dilations) + self.k_size = (1, k_size) + self.out_channel = out_channel + + self.conv2d = P.Conv2D( + out_channel=self.out_channel, kernel_size=self.k_size, pad_mode=self.padding, stride=self.stride, + dilation=self.dilations, mode=1, group=1 + ) + + self.expand_dims = P.ExpandDims() + self.squeeze = P.Squeeze(2) + + def construct(self, x, filters): + if self.data_format == 'NWC': + x = nhwc_to_nchw(x) + + x = self.expand_dims(x, 2) + filters = self.expand_dims(filters, 2) + + output = self.conv2d(x, filters) + output = self.squeeze(output) + + if self.data_format == 'NWC': + output = nchw_to_nhwc(output) + return output + + +def conv1d(input, filters, stride, padding, data_format='NWC', dilations=None, name=None): + """ + Computes a 1-D convolution given 3-D input and filter tensors. + + Parameters + ---------- + input : tensor + A 3D Tensor. Must be of type float16, float32, or float64 + filters : tensor + A 3D Tensor. Must have the same type as input. + stride : int of list + An int or list of ints that has length 1 or 3. The number of entries by which the filter is moved right at each step. + padding : string + 'SAME' or 'VALID' + data_format : string + An optional string from "NWC", "NCW". Defaults to "NWC", the data is stored in the order of + [batch, in_width, in_channels]. The "NCW" format stores data as [batch, in_channels, in_width]. + dilations : int or list + An int or list of ints that has length 1 or 3 which defaults to 1. + The dilation factor for each dimension of input. If set to k > 1, + there will be k-1 skipped cells between each filter element on that dimension. + Dilations in the batch and depth dimensions must be 1. + name : string + A name for the operation (optional). + Returns + ------- + A Tensor. Has the same type as input. + """ + + pass + + +class Conv2D(Cell): + + def __init__(self, strides, padding, data_format='NHWC', dilations=None, out_channel=None, k_size=None): + super(Conv2D, self).__init__() + self.data_format, self.padding = preprocess_2d_format(data_format, padding) + + if self.data_format is 'NHWC': + self.ms_stride = strides[1] + self.ms_dilation = dilations[1] + # self.transpose = P.Transpose() + elif self.data_format is 'NCHW': + self.ms_stride = strides[2] + self.ms_dilation = dilations[2] + + # print(out_channel, k_size, self.padding, self.ms_stride, self.ms_dilation) + self.conv2d = P.Conv2D( + out_channel=out_channel, kernel_size=k_size, pad_mode=self.padding, stride=self.ms_stride, + dilation=self.ms_dilation, mode=1, group=1 + ) + + def construct(self, inputs, filters): + if self.data_format == 'NHWC': + inputs = nhwc_to_nchw(inputs) + + outputs = self.conv2d(inputs, filters) + + if self.data_format == 'NHWC': + outputs = nchw_to_nhwc(outputs) + return outputs + + +def conv2d(input, filters, strides, padding, data_format='NCHW', dilations=None): + """ + Computes a 2-D convolution given 4-D input and filters tensors. + + Parameters + ---------- + input : tensor + Must be one of the following types: half, bfloat16, float32, float64. A 4-D tensor. + The dimension order is interpreted according to the value of data_format, see below for details. + filters : tensor + Must have the same type as input. A 4-D tensor of shape [filter_height, filter_width, in_channels, out_channels] + strides : int of list + The stride of the sliding window for each dimension of input. If a single value is given it is replicated in the H and W dimension. + By default the N and C dimensions are set to 1. The dimension order is determined by the value of data_format, see below for details. + padding : string + "SAME" or "VALID" + data_format : string + "NHWC", "NCHW". Defaults to "NCHW". + dilations : list or ints + list of ints that has length 1, 2 or 4, defaults to 1. The dilation factor for each dimension ofinput. + + Returns + ------- + A Tensor. Has the same type as input. + """ + raise NotImplementedError + + +class Conv3D(Cell): + pass + # raise NotImplementedError + + +def conv3d(input, filters, strides, padding, data_format='NDHWC', dilations=None, name=None): + """ + Computes a 3-D convolution given 5-D input and filters tensors. + + Parameters + ---------- + input : tensor + Must be one of the following types: half, bfloat16, float32, float64. + Shape [batch, in_depth, in_height, in_width, in_channels]. + filters : tensor + Must have the same type as input. Shape [filter_depth, filter_height, filter_width, in_channels, out_channels]. + in_channels must match between input and filters. + strides : list of ints + A list of ints that has length >= 5. 1-D tensor of length 5. + The stride of the sliding window for each dimension of input. + Must have strides[0] = strides[4] = 1. + padding : string + A string from: "SAME", "VALID". The type of padding algorithm to use. + data_format : string + An optional string from: "NDHWC", "NCDHW". Defaults to "NDHWC". The data format of the input and output data. + With the default format "NDHWC", the data is stored in the order of: [batch, in_depth, in_height, in_width, in_channels]. + Alternatively, the format could be "NCDHW", the data storage order is: [batch, in_channels, in_depth, in_height, in_width]. + dilations : list of ints + Defaults to [1, 1, 1, 1, 1]. 1-D tensor of length 5. The dilation factor for each dimension of input. + If set to k > 1, there will be k-1 skipped cells between each filter element on that dimension. + The dimension order is determined by the value of data_format, see above for details. + Dilations in the batch and depth dimensions must be 1. + name : string + A name for the operation (optional). + + Returns + ------- + A Tensor. Has the same type as input. + """ + + raise NotImplementedError + + +def lrn(inputs, depth_radius, bias, alpha, beta): + """ + Local Response Normalization. + + Parameters + ---------- + inputs : tensor + Must be one of the following types: half, bfloat16, float32. 4-D. + depth_radius : int + Defaults to 5. 0-D. Half-width of the 1-D normalization window. + bias : float + Defaults to 1. An offset (usually positive to avoid dividing by 0). + alpha : float + Defaults to 1. A scale factor, usually positive. + beta : float + Defaults to 0.5. An exponent. + + Returns + ------- + A Tensor. Has the same type as input. + """ + pass + + +def moments(x, axes, shift=None, keepdims=False): + """ + Calculates the mean and variance of x. + + Parameters + ---------- + x : tensor + A Tensor + axes : ints + Axes along which to compute mean and variance. + shift : int + Not used in the current implementation. + keepdims : bool + produce moments with the same dimensionality as the input. + + Returns + ------- + Two Tensor objects: mean and variance. + """ + + pass + + +class MaxPool(Cell): + + def __init__(self, ksize, strides, padding, data_format=None): + super(MaxPool, self).__init__() + self.data_format, self.padding = preprocess_2d_format(data_format=data_format, padding=padding) + ms_ksize = ksize[1] + ms_strides = strides[1] + self.maxpool = P.MaxPool(ksize=ms_ksize, strides=ms_strides, padding=self.padding) + + def construct(self, inputs): + if self.data_format == 'NHWC': + inputs = nhwc_to_nchw(inputs) + + outputs = self.maxpool(inputs) + + if self.data_format == 'NHWC': + outputs = nchw_to_nhwc(outputs) + return outputs + + +def max_pool(input, ksize, strides, padding, data_format=None): + """ + Performs the max pooling on the input. + + Parameters + ---------- + input : tensor + Tensor of rank N+2, of shape [batch_size] + input_spatial_shape + [num_channels] if data_format does not start + with "NC" (default), or [batch_size, num_channels] + input_spatial_shape if data_format starts with "NC". + Pooling happens over the spatial dimensions only. + ksize : int or list of ints + An int or list of ints that has length 1, N or N+2. + The size of the window for each dimension of the input tensor. + strides : int or list of ints + An int or list of ints that has length 1, N or N+2. + The stride of the sliding window for each dimension of the input tensor. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + + Returns + ------- + A Tensor of format specified by data_format. The max pooled output tensor. + """ + data_format, padding = preprocess_2d_format(data_format=data_format, padding=padding) + if data_format == 'NHWC': + input = nhwc_to_nchw(input) + + ms_ksize = ksize[1] + ms_strides = strides[2] + outputs = P.MaxPool(ksize=ms_ksize, strides=ms_strides, padding=padding)(input) + # channel first to channel last + if data_format == 'NHWC': + outputs = nchw_to_nhwc(outputs) + return outputs + + +class AvgPool(Cell): + + def __init__(self, ksize, strides, padding, data_format=None): + super(AvgPool, self).__init__() + self.data_format, self.padding = preprocess_2d_format(data_format=data_format, padding=padding) + ms_ksize = ksize[1] + ms_strides = strides[1] + self.avgpool = P.AvgPool(ksize=ms_ksize, strides=ms_strides, padding=padding) + + def construct(self, inputs): + if self.data_format == 'NHWC': + inputs = nhwc_to_nchw(inputs) + + outputs = self.avgpool(inputs) + + if self.data_format == 'NHWC': + outputs = nchw_to_nhwc(outputs) + return outputs + + +def avg_pool(input, ksize, strides, padding): + """ + Performs the avg pooling on the input. + + Parameters + ---------- + input : tensor + Tensor of rank N+2, of shape [batch_size] + input_spatial_shape + [num_channels] + if data_format does not start with "NC" (default), or [batch_size, num_channels] + input_spatial_shape + if data_format starts with "NC". Pooling happens over the spatial dimensions only. + ksize : int or list of ints + An int or list of ints that has length 1, N or N+2. + The size of the window for each dimension of the input tensor. + strides : int or list of ints + An int or list of ints that has length 1, N or N+2. + The stride of the sliding window for each dimension of the input tensor. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + + Returns + ------- + A Tensor of format specified by data_format. The average pooled output tensor. + """ + padding = padding_format(padding) + ms_ksize = ksize[0] + ms_strides = strides[1] + outputs = P.AvgPool(ksize=ms_ksize, strides=ms_strides, padding=padding) + return outputs(input) + + +def max_pool3d(input, ksize, strides, padding, data_format=None, name=None): + """ + Performs the max pooling on the input. + + Parameters + ---------- + input : tensor + A 5-D Tensor of the format specified by data_format. + ksize : int or list of ints + An int or list of ints that has length 1, 3 or 5. + The size of the window for each dimension of the input tensor. + strides : int or list of ints + An int or list of ints that has length 1, 3 or 5. + The stride of the sliding window for each dimension of the input tensor. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + "NDHWC", "NCDHW". Defaults to "NDHWC". The data format of the input and output data. + With the default format "NDHWC", the data is stored in the order of: [batch, in_depth, in_height, in_width, in_channels]. + Alternatively, the format could be "NCDHW", the data storage order is: [batch, in_channels, in_depth, in_height, in_width]. + name : string + A name for the operation (optional). + + Returns + ------- + A Tensor of format specified by data_format. The max pooled output tensor. + """ + pass + + +def avg_pool3d(input, ksize, strides, padding, data_format=None, name=None): + """ + Performs the average pooling on the input. + + Parameters + ---------- + input : tensor + A 5-D Tensor of shape [batch, height, width, channels] and type float32, float64, qint8, quint8, or qint32. + ksize : int or list of ints + An int or list of ints that has length 1, 3 or 5. The size of the window for each dimension of the input tensor. + strides : int or list of ints + An int or list of ints that has length 1, 3 or 5. + The stride of the sliding window for each dimension of the input tensor. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + 'NDHWC' and 'NCDHW' are supported. + name : string + Optional name for the operation. + + Returns + ------- + A Tensor with the same type as value. The average pooled output tensor. + """ + pass + + +def pool(input, window_shape, pooling_type, strides=None, padding='VALID', data_format=None, dilations=None, name=None): + """ + Performs an N-D pooling operation. + + Parameters + ---------- + input : tensor + Tensor of rank N+2, of shape [batch_size] + input_spatial_shape + [num_channels] + if data_format does not start with "NC" (default), or [batch_size, num_channels] + input_spatial_shape + if data_format starts with "NC". Pooling happens over the spatial dimensions only. + window_shape : int + Sequence of N ints >= 1. + pooling_type : string + Specifies pooling operation, must be "AVG" or "MAX". + strides : ints + Sequence of N ints >= 1. Defaults to [1]*N. If any value of strides is > 1, then all values of dilation_rate must be 1. + padding : string + The padding algorithm, must be "SAME" or "VALID". Defaults to "SAME". + See the "returns" section of tf.ops.convolution for details. + data_format : string + Specifies whether the channel dimension of the input and output is the last dimension (default, or if data_format does not start with "NC"), + or the second dimension (if data_format starts with "NC"). + For N=1, the valid values are "NWC" (default) and "NCW". For N=2, the valid values are "NHWC" (default) and "NCHW". + For N=3, the valid values are "NDHWC" (default) and "NCDHW". + dilations : list of ints + Dilation rate. List of N ints >= 1. Defaults to [1]*N. If any value of dilation_rate is > 1, then all values of strides must be 1. + name : string + Optional. Name of the op. + + Returns + ------- + Tensor of rank N+2, of shape [batch_size] + output_spatial_shape + [num_channels] + """ + pass + + + +class DepthwiseConv2d(Cell): + + def __init__(self, strides, padding, data_format=None, dilations=None, ksize=None, channel_multiplier=1): + super(DepthwiseConv2d, self).__init__() + self.data_format, self.padding = preprocess_2d_format(data_format, padding) + self.ms_stride = strides[1] + self.ms_dilation = dilations[1] + self.depthwise_conv2d = P.DepthwiseConv2dNative( + channel_multiplier=channel_multiplier, kernel_size=ksize, stride=self.ms_stride, dilation=self.ms_dilation + ) + + def construct(self, input, filter): + if self.data_format == 'NHWC': + input = nhwc_to_nchw(input) + outputs = self.depthwise_conv2d(input, filter) + if self.data_format == 'NHWC': + outputs = nchw_to_nhwc(outputs) + return outputs + + +def depthwise_conv2d(input, filter, strides, padding, data_format=None, dilations=None, name=None): + """ + Depthwise 2-D convolution. + + Parameters + ---------- + input : tensor + 4-D with shape according to data_format. + filter : tensor + 4-D with shape [filter_height, filter_width, in_channels, channel_multiplier]. + strides : list + 1-D of size 4. The stride of the sliding window for each dimension of input. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + The data format for input. Either "NHWC" (default) or "NCHW". + dilations : list + 1-D of size 2. The dilation rate in which we sample input values across the height and width dimensions in atrous convolution. + If it is greater than 1, then all values of strides must be 1. + name : string + A name for this operation (optional). + + Returns + ------- + A 4-D Tensor with shape according to data_format. + E.g., for "NHWC" format, shape is [batch, out_height, out_width, in_channels * channel_multiplier]. + """ + + pass + + +class Conv1d_transpose(Cell): + + def __init__(self, strides, padding, data_format, dilations=None, out_channel=None, k_size=None, in_channels=None): + super(Conv1d_transpose, self).__init__() + self.data_format, self.padding = preprocess_1d_format(data_format, padding) + self.in_channels = in_channels + self.out_channel = out_channel + self.strides = (1, strides) + self.dilations = (1, dilations) + self.k_size = (1, k_size) + + self.conv2d_transpose = P.Conv2DBackpropInput( + out_channel=self.in_channels, kernel_size=self.k_size, pad_mode=self.padding, stride=self.strides, + dilation=self.dilations, mode=1, group=1 + ) + self.shape = P.Shape() + self.expand_dims = P.ExpandDims() + self.squeeze = P.Squeeze(2) + + def _deconv_output_length(self, input_length, filter_size, stride_size, dilation_size): + length = 0 + filter_size = filter_size + (filter_size - 1) * (dilation_size - 1) + + if self.padding == 'same': + length = input_length * stride_size + elif self.padding == 'valid': + length = input_length * stride_size + max(filter_size - stride_size, 0) + + return length + + def construct(self, x, filters): + if self.data_format == 'NWC': + x = nhwc_to_nchw(x) + x = self.expand_dims(x, 2) + filters = self.expand_dims(filters, 2) + n, _, h, w = self.shape(x) + + h_out = self._deconv_output_length(h, self.k_size[0], self.strides[0], self.dilations[0]) + w_out = self._deconv_output_length(w, self.k_size[1], self.strides[1], self.dilations[1]) + output = self.conv2d_transpose(x, filters, (n, self.out_channel, h_out, w_out)) + output = self.squeeze(output) + + if self.data_format == 'NWC': + output = nchw_to_nhwc(output) + return output + + +def conv1d_transpose( + input, filters, output_shape, strides, padding='SAME', data_format='NWC', dilations=None, name=None +): + """ + The transpose of conv1d. + + Parameters + ---------- + input : tensor + A 3-D Tensor of type float and shape [batch, in_width, in_channels] + for NWC data format or [batch, in_channels, in_width] for NCW data format. + filters : tensor + A 3-D Tensor with the same type as value and shape [filter_width, output_channels, in_channels]. + filter's in_channels dimension must match that of value. + output_shape : tensor + A 1-D Tensor, containing three elements, representing the output shape of the deconvolution op. + strides : list + An int or list of ints that has length 1 or 3. The number of entries by which the filter is moved right at each step. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + 'NWC' and 'NCW' are supported. + dilations : list + An int or list of ints that has length 1 or 3 which defaults to 1. + The dilation factor for each dimension of input. If set to k > 1, + there will be k-1 skipped cells between each filter element on that dimension. + Dilations in the batch and depth dimensions must be 1. + name : string + Optional name for the returned tensor. + + Returns + ------- + A Tensor with the same type as value. + """ + pass + + +class Conv2d_transpose(Cell): + + def __init__(self, strides, padding, data_format, dilations=None, out_channel=None, k_size=None, in_channels=None): + super(Conv2d_transpose, self).__init__() + self.data_format, self.padding = preprocess_2d_format(data_format, padding) + self.in_channels = in_channels + self.out_channel = out_channel + + self.k_size = k_size + if self.data_format == 'NHWC': + self.strides = (strides[1], strides[2]) + self.dilations = (dilations[1], dilations[2]) + elif self.data_format == 'NCHW': + self.strides = (strides[2], strides[3]) + self.dilations = (dilations[2], dilations[3]) + + self.conv2d_transpose = P.Conv2DBackpropInput( + out_channel=self.in_channels, kernel_size=self.k_size, pad_mode=self.padding, stride=self.strides, + dilation=self.dilations, mode=1, group=1 + ) + self.shape = P.Shape() + + def _deconv_output_length(self, input_length, filter_size, stride_size, dilation_size): + length = 0 + filter_size = filter_size + (filter_size - 1) * (dilation_size - 1) + + if self.padding == 'same': + length = input_length * stride_size + elif self.padding == 'valid': + length = input_length * stride_size + max(filter_size - stride_size, 0) + + return length + + def construct(self, x, filters): + if self.data_format == 'NHWC': + x = nhwc_to_nchw(x) + + n, _, h, w = self.shape(x) + + h_out = self._deconv_output_length(h, self.k_size[0], self.strides[0], self.dilations[0]) + w_out = self._deconv_output_length(w, self.k_size[1], self.strides[1], self.dilations[1]) + + output = self.conv2d_transpose(x, filters, (n, self.out_channel, h_out, w_out)) + + if self.data_format == 'NHWC': + output = nchw_to_nhwc(x) + + return output + + +def conv2d_transpose( + input, filters, output_shape, strides, padding='SAME', data_format='NHWC', dilations=None, name=None +): + """ + The transpose of conv2d. + + Parameters + ---------- + input : tensor + A 4-D Tensor of type float and shape [batch, height, width, in_channels] + for NHWC data format or [batch, in_channels, height, width] for NCHW data format. + filters : tensor + A 4-D Tensor with the same type as input and shape [height, width, + output_channels, in_channels]. filter's in_channels dimension must match that of input. + output_shape : tensor + A 1-D Tensor representing the output shape of the deconvolution op. + strides : list + An int or list of ints that has length 1, 2 or 4. The stride of the sliding window for each dimension of input. + If a single value is given it is replicated in the H and W dimension. + By default the N and C dimensions are set to 0. + The dimension order is determined by the value of data_format, see below for details. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + 'NHWC' and 'NCHW' are supported. + dilations : list + An int or list of ints that has length 1, 2 or 4, defaults to 1. + name : string + Optional name for the returned tensor. + + Returns + ------- + A Tensor with the same type as input. + """ + pass + + +class Conv3d_transpose(Cell): + pass + + +def conv3d_transpose( + input, filters, output_shape, strides, padding='SAME', data_format='NDHWC', dilations=None, name=None +): + """ + The transpose of conv3d. + + Parameters + ---------- + input : tensor + A 5-D Tensor of type float and shape [batch, height, width, in_channels] for + NHWC data format or [batch, in_channels, height, width] for NCHW data format. + filters : tensor + A 5-D Tensor with the same type as value and shape [height, width, output_channels, in_channels]. + filter's in_channels dimension must match that of value. + output_shape : tensor + A 1-D Tensor representing the output shape of the deconvolution op. + strides : list + An int or list of ints that has length 1, 3 or 5. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + 'NDHWC' and 'NCDHW' are supported. + dilations : list of ints + An int or list of ints that has length 1, 3 or 5, defaults to 1. + name : string + Optional name for the returned tensor. + + Returns + ------- + A Tensor with the same type as value. + """ + + pass + + + +class BatchNorm(Cell): + """Batch Normalization base class.""" + + @cell_attr_register + def __init__( + self, num_features, epsilon=1e-5, decay=0.9, gamma=None, beta=None, moving_mean=None, moving_var=None, + is_train=None, device_num_each_group=1, data_format='channels_last' + ): + super(BatchNorm, self).__init__() + if num_features < 1: + raise ValueError("num_features must be at least 1") + + if decay < 0 or decay > 1: + raise ValueError("momentum should be a number in range [0, 1], but got {}".format(decay)) + + self.data_format = data_format + self.use_batch_statistics = is_train + self.num_features = num_features + self.eps = epsilon + self.moving_mean = moving_mean + self.moving_variance = moving_var + self.gamma = gamma + self.beta = beta + self.group = check_int_positive(device_num_each_group) + self.is_global = False + if self.group != 1: + self.rank_id = get_rank() + self.rank_size = get_group_size() + self.device_list = [i for i in range(0, self.rank_size)] + self.rank_list = self.list_group(self.device_list, self.group) + self.rank_list_idx = len(self.rank_list) + for i in range(self.rank_list_idx): + if self.rank_id in self.rank_list[i] and self.group != 1: + self.is_global = True + management.create_group('group' + str(i), self.rank_list[i]) + self.all_reduce = P.AllReduce(P.ReduceOp.SUM, 'group' + str(i)).add_prim_attr('fusion', 1) + self.shape = P.Shape() + self.reduce_mean = P.ReduceMean(keep_dims=True) + self.square = P.Square() + self.sqrt = P.Sqrt() + self.cast = P.Cast() + self.dtype = P.DType() + self.reshape = P.Reshape() + self.is_ascend = context.get_context("device_target") == "Ascend" + self.is_gpu = context.get_context("device_target") == "GPU" + self.is_graph_mode = context.get_context("mode") == context.GRAPH_MODE + self.momentum = 1.0 - decay + if context.get_context("enable_ge"): + self.is_ge_backend = True + else: + self.is_ge_backend = False + + if self.is_graph_mode and (self.is_ge_backend or self.is_ascend): + self.bn_train = P.BatchNorm(is_training=True, epsilon=self.eps) + elif self.is_gpu: + self.bn_train = P.FusedBatchNormEx(mode=1, epsilon=self.eps, momentum=self.momentum) + else: + self.bn_train = P.FusedBatchNorm(mode=1, epsilon=self.eps, momentum=self.momentum) + self.bn_infer = P.BatchNorm(is_training=False, epsilon=self.eps) + self.enable_global_sync = self.is_global and (self.is_ge_backend or (self.is_graph_mode and self.is_ascend)) + self.enable_default_train = self.is_graph_mode and not self.is_global and \ + (self.is_ge_backend or self.is_ascend) + + data_parallel_strategy = ((1, ), (1, )) + data_parallel_strategy_one = ((1, ), ()) + self.sub_mean = P.Sub().shard(data_parallel_strategy) + self.sub_var = P.Sub().shard(data_parallel_strategy) + self.mul_mean = P.Mul().shard(data_parallel_strategy_one) + self.mul_var = P.Mul().shard(data_parallel_strategy_one) + self.assign_sub_mean = P.AssignSub().shard(data_parallel_strategy) + self.assign_sub_var = P.AssignSub().shard(data_parallel_strategy) + + def _check_data_dim(self, x): + raise NotImplementedError + + def list_group(self, world_rank, group_size): + if group_size > get_group_size(): + raise ValueError( + "group size can not be greater than local rank size, group size is {}, " + "local_rank_size is {}".format(group_size, get_group_size()) + ) + if len(world_rank) % group_size != 0: + raise ValueError("please make your group size correct.") + world_rank_list = zip(*(iter(world_rank), ) * group_size) + group_list = [list(i) for i in world_rank_list] + return group_list + + def _global_sync(self, x, axes, re_shape): + """calculate global batch normalization output""" + x_mean = self.reduce_mean(x, axes) + x_mean_square = self.reduce_mean(self.square(x), axes) + global_batch_mean = self.all_reduce(x_mean) / self.group + global_batch_mean_square = self.all_reduce(x_mean_square) / self.group + global_mean = global_batch_mean + global_var = global_batch_mean_square - self.square(global_mean) + var_sqrt = self.sqrt(global_var + self.eps) + mean_first = (x - global_mean) / var_sqrt + y = mean_first * self.reshape(self.gamma, re_shape) + self.reshape(self.beta, re_shape) + + mean_sub = self.sub_mean(self.reshape(self.moving_mean, re_shape), global_mean) + tmp_mean = self.mul_mean(mean_sub, self.cast(self.momentum, self.dtype(mean_sub))) + mean_sub2 = self.sub_var(self.reshape(self.moving_mean, re_shape), global_var) + tmp_variance = self.mul_var(mean_sub2, self.cast(self.momentum, self.dtype(mean_sub2))) + y = F.depend(y, self.assign_sub_mean(self.moving_mean, self.reshape(tmp_mean, self.shape(self.moving_mean)))) + y = F.depend( + y, self.assign_sub_var(self.moving_variance, self.reshape(tmp_variance, self.shape(self.moving_variance))) + ) + return y + + def get_dim(self, input): + dim = len(self.shape(input)) + if dim == 2: + return '1d' + elif dim == 4: + return '2d' + else: + raise ValueError("The input must has 2 dims or 4 dims.") + + def _shape_check_bn(self, in_shape, in_dims): + dim = len(in_shape) + if in_dims == '1d' and dim != 2: + raise ValueError("The input must has 2 dims.") + if in_dims == '2d' and dim != 4: + raise ValueError("The input must has 4 dims.") + if in_dims == 'both' and dim != 2 and dim != 4: + raise ValueError("The input must has 2 dims or 4 dims.") + + def _shape_infer(self, x_shape, num_feature): + """global batch normalization shape and axes infer""" + if len(x_shape) == 4: + axes = (0, 2, 3) + re_shape = (1, num_feature, 1, 1) + else: + axes = (0, ) + re_shape = (1, num_feature) + return axes, re_shape + + def construct(self, inputs): + x = inputs + self._shape_check_bn(self.shape(x), self.get_dim(x)) + if self.use_batch_statistics is None: + flag = self.training + else: + flag = self.use_batch_statistics + + if flag: + if self.enable_global_sync: + if self.data_format == 'channels_last' and self.get_dim(x) == '2d': + x = nhwc_to_nchw(x) + axes, re_shape = self._shape_infer(F.shape(x), self.num_features) + y = self._global_sync(x, axes, re_shape) + if self.data_format == 'channels_last' and self.get_dim(x) == '2d': + y = nchw_to_nhwc(y) + return y + + if self.enable_default_train: + if self.data_format == 'channels_last' and self.get_dim(x) == '2d': + x = nhwc_to_nchw(x) + y, batch_mean, batch_var, _, _ = self.bn_train(x, self.gamma, self.beta, None, None) + + mean_sub = self.sub_mean(self.moving_mean, batch_mean) + temp_mean = self.mul_mean(mean_sub, self.momentum) + mean_sub2 = self.sub_var(self.moving_variance, batch_var) + temp_variance = self.mul_var(mean_sub2, self.momentum) + y = F.depend(y, self.assign_sub_mean(self.moving_mean, temp_mean)) + y = F.depend(y, self.assign_sub_var(self.moving_variance, temp_variance)) + if self.data_format == 'channels_last' and self.get_dim(x) == '2d': + y = nchw_to_nhwc(y) + return y + + if self.data_format == 'channels_last' and self.get_dim(x) == '2d': + x = nhwc_to_nchw(x) + y = self.bn_train(x, self.gamma, self.beta, self.moving_mean, self.moving_variance)[0] + if self.data_format == 'channels_last' and self.get_dim(x) == '2d': + y = nchw_to_nhwc(y) + return y + if self.data_format == 'channels_last' and self.get_dim(x) == '2d': + x = nhwc_to_nchw(x) + y = self.bn_infer(x, self.gamma, self.beta, self.moving_mean, self.moving_variance)[0] + if self.data_format == 'channels_last' and self.get_dim(x) == '2d': + y = nchw_to_nhwc(y) + return y diff --git a/tensorlayer/backend/ops/paddle_backend.py b/tensorlayer/backend/ops/paddle_backend.py new file mode 100644 index 0000000..e9b37c5 --- /dev/null +++ b/tensorlayer/backend/ops/paddle_backend.py @@ -0,0 +1,972 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, print_function +import paddle as pd +import paddle.nn as nn + +_dtypeDict = ["float16", "float32", "float64", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64"] +# TODO NotImplemented +DType = None +float16 = "float16" +float32 = "float32" +float64 = "float64" +int8 = "int8" +int16 = "int16" +int32 = "int32" +int64 = "int64" +uint8 = "uint8" +uint16 = "uint16" +uint32 = "uint32" +uint64 = "uint64" + +def _getter(init_fn, **kwargs): + """Return an named eager tensor.""" + raise NotImplementedError + + +def set_context(**kwargs): + raise Exception("Using Paddle backend,You don't need to set context") + + +def get_tensor_shape(x): + return pd.shape(x) + + +# initializers +def zeros(shape, dtype="float32"): + """ + Creates a tensor with all elements set to zero. + + Parameters + ---------- + shape : A list of integers + a tuple of integers, or a 1-D Tensor of type int32. + dtype : tensor + The DType of an element in the resulting Tensor + + Returns + ------- + A Tensor with all elements set to zero. + + """ + return pd.zeros(shape=shape, dtype=dtype) + + +def ones(shape, dtype="float32"): + """ + Creates a tensor with all elements set to ones. + + Parameters + ---------- + shape : A list of integers + a tuple of integers, or a 1-D Tensor of type int32. + dtype : tensor + The DType of an element in the resulting Tensor + + Returns + ------- + A Tensor with all elements set to zero. + + """ + return pd.ones(shape=shape, dtype=dtype) + + +def constant(value, shape, dtype="float32"): + """ + Creates a constant tensor from a tensor-like object. + + Parameters + ---------- + value : list + A constant value (or list) of output type dtype. + dtype : tensor + The type of the elements of the resulting tensor. + shape : tuple + Optional dimensions of resulting tensor. + + Returns + ------- + A Constant Tensor. + + """ + return nn.initializer.constant(value=value) + + +def random_uniform(shape, minval=0, maxval=None, dtype="float32", seed=None): + """ + Outputs random values from a uniform distribution. + + Parameters + ---------- + shape : tuple + A 1-D integer Tensor or Python array. The shape of the output tensor. + minval : int + The lower bound on the range of random values to generate (inclusive). Defaults to 0. + maxval : int + The upper bound on the range of random values to generate (exclusive). Defaults to 1 if dtype is floating point. + dtype : tensor + The type of the output: float16, float32, float64, int32, or int64. + seed : int + Used in combination with dragon.random.set_seed to create a reproducible sequence of tensors across multiple calls. + Returns + ------- + A tensor of the specified shape filled with random uniform values. + + """ + raise NotImplementedError + + +def random_normal(shape, mean=0.0, stddev=1.0, dtype="float32", seed=None): + """ + Outputs random values from a normal distribution. + + Parameters + ---------- + shape : tuple + A 1-D integer Tensor or Python array. The shape of the output tensor. + mean : float + The mean of the normal distribution + stddev : float + The standard deviation of the normal distribution. + dtype : tensor + The type of the output. + seed : A Python integer + Used to create a random seed for the distribution + + Returns + ------- + A tensor of the specified shape filled with random normal values. + + """ + raise NotImplementedError + + +def truncated_normal(shape, mean=0.0, stddev=1.0, dtype="float32", seed=None): + """ + Outputs random values from a truncated normal distribution. + + Parameters + ---------- + shape : tuple + A 1-D integer Tensor or Python array. The shape of the output tensor. + mean : float + The mean of the normal distribution + stddev : float + The standard deviation of the normal distribution. + dtype : tensor + The type of the output. + seed : A Python integer + Used to create a random seed for the distribution + + Returns + ------- + A tensor of the specified shape filled with random truncated normal values. + + """ + raise NotImplementedError + + +def he_normal(shape, dtype, seed=None): + """ + He normal initializer. + + Parameters + ---------- + seed : A Python integer. + Used to seed the random generator. + shape : tuple + A 1-D integer Tensor or Python array. The shape of the output tensor. + dtype : tensor + The type of the output. + + Returns + ------- + A tensor of the specified shape filled with he normal values. + """ + # shape = shape[::-1] + raise NotImplementedError + + +def Variable(initial_value, name, trainable=None): + """ + Creates a new variable with value initial_value. + + Parameters + ---------- + initial_value : tensor + A Tensor, or Python object convertible to a Tensor + name : str + Optional name for the variable. Defaults to 'Variable' and gets uniquified automatically. + Returns + ------- + Variable + """ + raise NotImplementedError + + +class MatMul(object): + + def __init__(self): + pass + + def __call__(self, a, b): + return pd.matmul(x=a, y=b) + + +def matmul(a, b): + """ + Multiplies matrix a by matrix b, producing a * b. + + Parameters + ---------- + a : tensor + type float16, float32, float64, int32, complex64, complex128 and rank > 1. + b : tensor + with same type and rank as a. + + Returns + ------- + A Tensor of the same type as a and b + """ + raise NotImplementedError + + +def add(value, bias): + """ + Returns x + y element-wise. + + Parameters + ---------- + value : tensor. + Must be one of the following types: bfloat16, half, float32, float64, + uint8, int8, int16, int32, int64, complex64, complex128, string. + bias : tensor + Must have the same type as a + name : str + A name for the operation + + Returns + ------- + A Tensor. Has the same type as a. + """ + + raise NotImplementedError + + +def dtypes(dt): + """ + Data dtypes. + + Parameters + ---------- + dt : string + It could be 'uint8', 'uint16', 'uint32', 'uint64', 'int8', 'int16', + 'int32', 'int64', 'float16', 'float32', 'float64', 'DType'. + + Returns + ------- + Data dtypes + """ + raise NotImplementedError + + +class Maximum(object): + def __init__(self): + pass + + def __call__(self, x, y): + raise NotImplementedError + + +class Minimum(object): + def __init__(self): + pass + + def __call__(self, x, y): + raise NotImplementedError + + +def minimum(x, y): + """ + Returns the min of x and y (i.e. x < y ? x : y) element-wise. + + Parameters + ---------- + x : tensor. + Must be one of the following types: bfloat16, half, float32, float64, int32, int64. + y : A Tensor. + Must have the same type as x. + name : str + A name for the operation (optional). + + Returns + ------- + A Tensor. Has the same type as x + """ + raise NotImplementedError + + +class FlattenReshape(object): + + def __init__(self): + pass + + def __call__(self, inputs): + return pd.flatten(x=inputs, start_axis=1,stop_axis=-1) + + +class Reshape(object): + + def __init__(self, shape): + self.shape = shape + + def __call__(self, tensor): + raise NotImplementedError + + +def reshape(tensor, shape): + """ + Reshapes a tensor. + + Parameters + ---------- + tensor : tensor + A Tensor. + shape : tensor + Defines the shape of the output tensor. + Returns + ------- + A Tensor. Has the same type as tensor + """ + raise NotImplementedError + + +class Concat(object): + + def __init__(self, axis): + super(Concat, self).__init__() + self.axis = axis + + def __call__(self, values): + raise NotImplementedError + + +def concat(values, axis): + """ + Concatenates tensors along one dimension. + + Parameters + ---------- + values : list + A list of Tensor objects or a single Tensor + axis : int + 0-D int32 Tensor. Dimension along which to concatenate + Returns + ------- + A Tensor resulting from concatenation of the input tensors. + """ + raise NotImplementedError + + +def convert_to_tensor(value, dtype=None): + """ + Converts the given value to a Tensor. + + Parameters + ---------- + value : object + An object whose type has a registered Tensor conversion function. + dtype : optional + Optional element type for the returned tensor. If missing, the type is inferred from the type of value. + + Returns + ------- + A Tensor based on value. + """ + raise NotImplementedError + + +def sqrt(x): + """ + Computes square root of x element-wise. + + Parameters + ---------- + x : tensor + Must be one of the following types: bfloat16, half, float32, float64, complex64, complex128. + + Returns + ------- + A Tensor. Has the same type as x. + """ + raise NotImplementedError + + +class ReduceSum(object): + + def __init__(self, axis): + pass + + def construct(self, input): + pass + + +class ReduceMean(object): + + def __init__(self, axis): + if axis == [1, 2]: + self.data_format = 'NHWC' + elif axis == [2, 3]: + self.data_format = 'NCHW' + else: + raise ("`data_format` should have one of the following values: [`channels_last`, `channels_first`]") + + def __call__(self, inputs): + raise NotImplementedError + + +def reduce_mean(input_tensor, axis=None): + """ + Computes the mean of elements across dimensions of a tensor. + + Parameters + ---------- + input_tensor : tensor + The tensor to reduce. Should have numeric type. + axis : int + The dimensions to reduce. If None (the default), reduces all dimensions. + Must be in the range [-rank(input_tensor), rank(input_tensor)). + name : str + A name for the operation (optional). + + Returns + ------- + The reduced tensor. + """ + + raise NotImplementedError + + +class ReduceMax(object): + + def __init__(self, axis): + if axis == [1, 2]: + self.data_format = 'NHWC' + elif axis == [2, 3]: + self.data_format = 'NCHW' + else: + raise ("`data_format` should have one of the following values: [`channels_last`, `channels_first`]") + + def __call__(self, inputs): + raise NotImplementedError + + +def reduce_max(input_tensor, axis=None): + """ + Computes the maximum of elements across dimensions of a tensor. + + Parameters + ---------- + input_tensor : tensor + The tensor to reduce. Should have real numeric type. + axis : int + The dimensions to reduce. If None (the default), reduces all dimensions. + Must be in the range [-rank(input_tensor), rank(input_tensor)). + name : str + A name for the operation (optional). + + Returns + ------- + The reduced tensor. + """ + + raise NotImplementedError + + +def reduce_min(input_tensor, axis=None): + """ + Computes the minimum of elements across dimensions of a tensor. + + Parameters + ---------- + input_tensor : tensor + The tensor to reduce. Should have real numeric type. + axis : int + The dimensions to reduce. If None (the default), reduces all dimensions. + Must be in the range [-rank(input_tensor), rank(input_tensor)). + name : str + A name for the operation (optional). + + Returns + ------- + The reduced tensor. + """ + raise NotImplementedError + +class Pad(object): + def __init__(self, paddings, mode="REFLECT"): + if mode not in ['CONSTANT', 'REFLECT', 'SYMMETRIC']: + raise Exception("Unsupported mode: {}".format(mode)) + if mode == 'SYMMETRIC': + mode = 'EDGE' + self.paddings = paddings + self.mode = mode + + def __call__(self, x): + raise NotImplementedError + +def pad(tensor, paddings, mode='CONSTANT', constant_values=0): + """ + Pads a tensor. + + Parameters + ---------- + tensor : tensor + A Tensor. + paddings : tuple + A tuple of type int32. + mode : str + One of "CONSTANT", "REFLECT", or "SYMMETRIC" (case-insensitive) + constant_values : int + In "CONSTANT" mode, the scalar pad value to use. Must be same type as tensor. + + Returns + ------- + A Tensor. Has the same type as tensor. + """ + raise NotImplementedError + + +class Unstack(object): + + def __init__(self, axis, num=None): + self.axis = axis + self.num = num + + def __call__(self, values): + raise NotImplementedError + + +class Stack(object): + + def __init__(self, axis): + self.axis = axis + + def __call__(self, values): + raise NotImplementedError + + +def stack(values, axis=0): + """ + Stacks a list of rank-R tensors into one rank-(R+1) tensor. + + Parameters + ---------- + values : list + A list of Tensor objects with the same shape and type. + axis : int + An int. The axis to stack along. Defaults to the first dimension. + Negative values wrap around, so the valid range is [-(R+1), R+1). + + Returns + ------- + A stacked Tensor with the same type as values. + """ + raise NotImplementedError + + +class Meshgrid(object): + def __init__(self, indexing='xy'): + super(Meshgrid, self).__init__() + self.index = indexing + + def __call__(self, inputs): + pass + + +def meshgrid(x, y): + """ + Broadcasts parameters for evaluation on an N-D grid. + + Parameters + ---------- + x : tensor + Tensors with rank 1. + y : tensor + Tensors with rank 1. + + Returns + ------- + A list of N Tensors with rank N. + """ + + pass + + +def range(start, limit=None, delta=1, dtype=None): + """ + Creates a sequence of numbers. + + Parameters + ---------- + start : tensor + A 0-D Tensor (scalar). Acts as first entry in the range if limit is not None; + otherwise, acts as range limit and first entry defaults to 0. + limit : tensor + A 0-D Tensor (scalar). Upper limit of sequence, exclusive. If None, + defaults to the value of start while the first entry of the range defaults to 0. + delta : tensor + A 0-D Tensor (scalar). Number that increments start. Defaults to 1. + dtype : type + The type of the elements of the resulting tensor. + + Returns + ------- + An 1-D Tensor of type dtype. + """ + raise NotImplementedError + + +class ExpandDims(object): + + def __init__(self, axis): + pass + + def construct(self, input): + pass + + +def expand_dims(input, axis): + """ + Inserts a dimension of 1 into a tensor's shape. + + Parameters + ---------- + input : tensor + A Tensor. + axis : int + 0-D (scalar). Specifies the dimension index at which to expand the shape of input. + Must be in the range [-rank(input) - 1, rank(input)]. + + Returns + ------- + A Tensor with the same data as input, but its shape has an additional dimension of size 1 added. + """ + + raise NotImplementedError + + +class Tile(object): + + def __init__(self): + pass + + def __call__(self, input, multiples): + raise NotImplementedError + + +def tile(input, multiples): + """ + Constructs a tensor by tiling a given tensor. + + Parameters + ---------- + input : tensor + A Tensor. 1-D or higher. + multiples : tensor + Must be one of the following types: int32, int64. 1-D. + Length must be the same as the number of dimensions in input + + Returns + ------- + A Tensor. Has the same type as input. + """ + raise NotImplementedError + + +class Cast(object): + + def __init__(self, dtype): + pass + + def __call__(self, input): + pass + + +def cast(x, dtype): + """ + Casts a tensor to a new type. + + Parameters + ---------- + x : tensor + A Tensor or SparseTensor or IndexedSlices of numeric type. + It could be uint8, uint16, uint32, uint64, int8, int16, int32, int64, float16, float32, float64. + dtype : dtpye + The destination type. The list of supported dtypes is the same as x + + Returns + ------- + A Tensor or SparseTensor or IndexedSlices with same shape as x and same type as dtype. + """ + raise NotImplementedError + + +class Transpose(object): + + def __init__(self, perm, conjugate=False): + self.perm = perm + if conjugate: + raise ("The conjugate Parameters not supported") + + def __call__(self, a): + raise NotImplementedError + + +def transpose(a, perm=None, conjugate=False): + """ + Transposes a. + + Parameters + ---------- + a : tensor + A Tensor. + perm : int + A permutation of the dimensions of a. + conjugate : bool + Setting it to True is mathematically equivalent to ms.math.conj(ms.transpose(input)). + + Returns + ------- + A transposed Tensor. + """ + + raise NotImplementedError + + +def gather_nd(params, indices, batch_dims=0): + """ + Gather slices from params into a Tensor with shape specified by indices. + + Parameters + ---------- + params : tensor + The tensor from which to gather values. + indices : tensor + Must be one of the following types: int32, int64. Index tensor. + batch_dims : int + An integer or a scalar 'Tensor'. The number of batch dimensions. + + Returns + ------- + A Tensor. Has the same type as params. + """ + + pass + + +def clip_by_value(t, clip_value_min, clip_value_max): + """ + Clips tensor values to a specified min and max. + + Parameters + ---------- + t : tensor + A Tensor or IndexedSlices + clip_value_min : tensor + A 0-D (scalar) Tensor, or a Tensor with the same shape as t. The minimum value to clip by + clip_value_max : tensor + A 0-D (scalar) Tensor, or a Tensor with the same shape as t. The minimum value to clip by + + Returns + ------- + A clipped Tensor or IndexedSlices. + """ + + pass + + +def split(value, num_or_size_splits, axis=0, num=None): + """ + Splits a tensor into sub tensors. + + Parameters + ---------- + value : tensor + The Tensor to split. + num_or_size_splits : list + Either an integer indicating the number of splits along split_dim or a 1-D integer Tensor or + Python list containing the sizes of each output tensor along split_dim. + axis : int + The dimension along which to split. Must be in the range [-rank(value), rank(value)). Defaults to 0. + num : int + used to specify the number of outputs when it cannot be inferred from the shape of size_splits. + + Returns + ------- + Tensor objects resulting from splitting value. + """ + pass + + +def floor(x): + raise NotImplementedError + + +def gather(params, indices): + raise NotImplementedError + + +def linspace(start, stop, num): + raise NotImplementedError + + +def slice(inputs, starts, sizes): + raise NotImplementedError + + +def add_n(inputs): + raise NotImplementedError + + +class OneHot(object): + + def __init__(self, axis=-1, depth=1, on_value=1.0, off_value=0.0, dtype="float32"): + self.depth = depth + self.dtype = dtype + + def __call__(self, indices): + raise NotImplementedError + + +class L2Normalize(object): + + def __init__(self, axis=None, epsilon=1e-12): + super(L2Normalize, self).__init__() + pass + + def __call__(self, input, *args, **kwargs): + pass + + +class EmbeddingLookup(object): + + def __init__(self, max_norm=None): + self.max_norm = max_norm + + def __call__(self, params, ids, *args, **kwargs): + pass + + +class NCELoss(object): + + def __init__(self, num_true=1, sampled_values=None, remove_accidental_hits=False): + super(NCELoss, self).__init__() + + def __call__(self, weights, biases, labels, inputs, num_sampled, num_classes): + pass + + +class Not_equal(object): + + def __init__(self): + pass + + def __call__(self, x, y): + pass + + +class Count_nonzero(object): + + def __init__(self, keepdims=None, dtype="int64"): + pass + + def __call__(self, *args, **kwargs): + pass + + + +class Resize: + + def __init__(self, scale, method, antialias=False, data_format='channels_last', ksize=None): + if method not in ['nearest', 'linear', 'bilinear']: + raise ('Current resize does not support this method.') + if method == 'bilinear': + method = 'linear' + self.method = method + self.antialias = antialias + self.scale = scale + if data_format != 'channel_last': + raise Exception("UpSampling2d resize_images only support channel_last") + + def __call__(self, inputs): + raise NotImplementedError + + +def resize(inputs, output_size, method, antialias): + raise NotImplementedError + + +class ZeroPadding1D(object): + + def __init__(self): + pass + + def __call__(self, padding): + raise NotImplementedError + + +class ZeroPadding2D(object): + + def __init__(self): + pass + + def __call__(self, padding): + raise NotImplementedError + + +class ZeroPadding3D(object): + + def __init__(self): + pass + + def __call__(self, padding): + raise NotImplementedError + + +class Sign(object): + + def __init__(self): + pass + + def __call__(self, x): + raise NotImplementedError + +def ceil(x): + raise NotImplementedError + +def multiply(x, y): + raise NotImplementedError + +def divide(x, y): + raise NotImplementedError + +def identity(x): + raise NotImplementedError + +class BatchToSpace(object): + def __init__(self, block_size, crops): + super(BatchToSpace, self).__init__() + pass + + def __call__(self, input_x): + raise NotImplementedError + + +class DepthToSpace(object): + def __init__(self, block_size, data_format='NHWC'): + pass + + def __call__(self, input): + raise NotImplementedError \ No newline at end of file diff --git a/tensorlayer/backend/ops/paddle_nn.py b/tensorlayer/backend/ops/paddle_nn.py new file mode 100644 index 0000000..47d9dd0 --- /dev/null +++ b/tensorlayer/backend/ops/paddle_nn.py @@ -0,0 +1,926 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import paddle as pd +import paddle.nn.functional as F + +def padding_format(padding): + """ + Checks that the padding format correspond format. + + Parameters + ---------- + padding : str + Must be one of the following:"same", "SAME", "VALID", "valid" + + Returns + ------- + str "SAME" or "VALID" + """ + + if padding in ["SAME", "same"]: + padding = "SAME" + elif padding in ["VALID", "valid"]: + padding = "VALID" + elif padding == None: + padding = None + else: + raise Exception("Unsupported padding: " + str(padding)) + return padding + + +def preprocess_1d_format(data_format, padding): + """ + Checks that the 1-D dataformat format correspond format. + + Parameters + ---------- + data_format : str + Must be one of the following:"channels_last","NWC","NCW","channels_first" + padding : str + Must be one of the following:"same","valid","SAME","VALID" + + Returns + ------- + str "NWC" or "NCW" and "SAME" or "VALID" + """ + + if data_format in ["channels_last", "NWC"]: + data_format = "NWC" + elif data_format in ["channels_first", "NCW"]: + data_format = "NCW" + elif data_format == None: + data_format = None + else: + raise Exception("Unsupported data format: " + str(data_format)) + padding = padding_format(padding) + return data_format, padding + + +def preprocess_2d_format(data_format, padding): + """ + Checks that the 2-D dataformat format correspond format. + + Parameters + ---------- + data_format : str + Must be one of the following:"channels_last","NHWC","NCHW","channels_first" + padding : str + Must be one of the following:"same","valid","SAME","VALID" + + Returns + ------- + str "NHWC" or "NCHW" and "SAME" or "VALID" + """ + + if data_format in ["channels_last", "NHWC", "nhwc"]: + data_format = "NHWC" + elif data_format in ["channels_first", "NCHW", "nchw"]: + data_format = "NCHW" + elif data_format == None: + data_format = None + else: + raise Exception("Unsupported data format: " + str(data_format)) + padding = padding_format(padding) + return data_format, padding + + +def preprocess_3d_format(data_format, padding): + """ + Checks that the 3-D dataformat format correspond format. + + Parameters + ---------- + data_format : str + Must be one of the following:"channels_last","NDHWC","NCDHW","channels_first" + padding : str + Must be one of the following:"same","valid","SAME","VALID" + + Returns + ------- + str "NDHWC" or "NCDHW" and "SAME" or "VALID" + """ + + if data_format in ['channels_last', 'NDHWC']: + data_format = 'NDHWC' + elif data_format in ['channels_first', 'NCDHW']: + data_format = 'NCDHW' + elif data_format == None: + data_format = None + else: + raise Exception("Unsupported data format: " + str(data_format)) + padding = padding_format(padding) + return data_format, padding + + +def nchw_to_nhwc(x): + """ + Channels first to channels last + + Parameters + ---------- + x : tensor + channels first tensor data + + Returns + ------- + channels last tensor data + """ + + pass + + +def nhwc_to_nchw(x): + """ + Channles last to channels first + + Parameters + ---------- + x : tensor + channels last tensor data + + Returns + ------- + channels first tensor data + """ + + pass + + +class ReLU(object): + + def __init__(self): + pass + + def __call__(self, x): + return F.relu(x) + + +def relu(x): + """ + Computes rectified linear: max(features, 0). + + Parameters + ---------- + x : tensor + Must be one of the following types: float32, float64, int32, uint8, int16, + int8, int64, bfloat16, uint16, half, uint32, uint64, qint8. + + Returns + ------- + A Tensor. Has the same type as features. + """ + return F.relu(x) + + +class ReLU6(object): + + def __init__(self): + pass + + def __call__(self, x): + return F.relu6(x) + + +def relu6(x): + """ + Computes Rectified Linear 6: min(max(features, 0), 6). + + Parameters + ---------- + x : tensor + Must be one of the following types: float32, float64, int32, uint8, int16, + int8, int64, bfloat16, uint16, half, uint32, uint64, qint8. + + Returns + ------- + A Tensor with the same type as features. + """ + return F.relu6(x) + + +class LeakyReLU(object): + + def __init__(self, alpha=0.2): + self.alpha = alpha + + def __call__(self, x): + return F.leaky_relu(x, negative_slope=self.alpha) + + +def leaky_relu(x): + """ + Compute the Leaky ReLU activation function. + + Parameters + ---------- + x : tensor + representing preactivation values. Must be one of the following types: + float16, float32, float64, int32, int64. + + Returns + ------- + The activation value. + """ + + return F.leaky_relu(x) + + +class Softplus(object): + + def __init__(self): + pass + + def __call__(self, x): + return F.softplus(x) + + +def softplus(x): + """ + Computes softplus: log(exp(features) + 1). + + Parameters + ---------- + x : tensor + Must be one of the following types: half, bfloat16, float32, float64. + + Returns + ------- + A Tensor. Has the same type as features. + """ + + return F.softplus(x) + + +class Tanh(object): + + def __init__(self): + pass + + def __call__(self, x): + return F.tanh(x) + + +def tanh(x): + """ + Computes hyperbolic tangent of x element-wise. + + Parameters + ---------- + x : tensor + Must be one of the following types: bfloat16, half, float32, float64, complex64, complex128. + + Returns + ------- + A Tensor. Has the same type as x. + """ + + return F.tanh(x) + + +class Sigmoid(object): + + def __init__(self): + pass + + def __call__(self, x): + return F.sigmoid(x) + + +def sigmoid(x): + """ + Computes sigmoid of x element-wise. + + Parameters + ---------- + x : tensor + A Tensor with type float16, float32, float64, complex64, or complex128. + + Returns + ------- + A Tensor with the same type as x. + """ + return F.sigmoid(x) + + +class Softmax(object): + + def __init__(self): + pass + + def __call__(self, x): + return F.softmax(x) + + +def softmax(logits, axis=-1): + """ + Computes softmax activations. + + Parameters + ---------- + logits : tensor + Must be one of the following types: half, float32, float64. + axis : int + The dimension softmax would be performed on. The default is -1 which indicates the last dimension. + + Returns + ------- + A Tensor. Has the same type and shape as logits. + """ + return F.softmax(logits, axis=axis) + + +class Dropout(object): + + def __init__(self, keep, seed=1): + self.keep = 1 - keep + self.seed = seed + + def __call__(self, inputs): + raise NotImplementedError + + +class BiasAdd(object): + """ + Adds bias to value. + + Parameters + ---------- + x : tensor + A Tensor with type float, double, int64, int32, uint8, int16, int8, complex64, or complex128. + bias : tensor + Must be the same type as value unless value is a quantized type, + in which case a different quantized type may be used. + Returns + ------- + A Tensor with the same type as value. + """ + + def __init__(self, data_format='NHWC'): + self.data_format = data_format + + def __call__(self, x, bias): + return pd.add(x, bias) + + +def bias_add(x, bias): + """ + Adds bias to value. + + Parameters + ---------- + x : tensor + A Tensor with type float, double, int64, int32, uint8, int16, int8, complex64, or complex128. + bias : tensor + Must be the same type as value unless value is a quantized type, + in which case a different quantized type may be used. + data_format : A string. + 'N...C' and 'NC...' are supported. + name : str + A name for the operation (optional). + Returns + ------- + A Tensor with the same type as value. + """ + raise NotImplementedError + + +class Conv1D(object): + pass + # raise NotImplementedError + + +def conv1d(input, filters, stride, padding, data_format='NWC', dilations=None, name=None): + """ + Computes a 1-D convolution given 3-D input and filter tensors. + + Parameters + ---------- + input : tensor + A 3D Tensor. Must be of type float16, float32, or float64 + filters : tensor + A 3D Tensor. Must have the same type as input. + stride : int of list + An int or list of ints that has length 1 or 3. The number of entries by which the filter is moved right at each step. + padding : string + 'SAME' or 'VALID' + data_format : string + An optional string from "NWC", "NCW". Defaults to "NWC", the data is stored in the order of + [batch, in_width, in_channels]. The "NCW" format stores data as [batch, in_channels, in_width]. + dilations : int or list + An int or list of ints that has length 1 or 3 which defaults to 1. + The dilation factor for each dimension of input. If set to k > 1, + there will be k-1 skipped cells between each filter element on that dimension. + Dilations in the batch and depth dimensions must be 1. + name : string + A name for the operation (optional). + Returns + ------- + A Tensor. Has the same type as input. + """ + + pass + + +class Conv2D(object): + + def __init__(self, strides, padding, data_format='NHWC', dilations=None, out_channel=None, k_size=None): + self.data_format, self.padding = preprocess_2d_format(data_format, padding) + self.ksize = k_size[0] + if self.data_format is 'NHWC': + self.dg_stride = strides[1] + self.dg_dilation = dilations[1] + elif self.data_format is 'NCHW': + self.dg_stride = strides[2] + self.dg_dilation = dilations[2] + + def __call__(self, inputs, filters): + raise NotImplementedError + + +def conv2d(input, filters, strides, padding, data_format='NCHW', dilations=None): + """ + Computes a 2-D convolution given 4-D input and filters tensors. + + Parameters + ---------- + input : tensor + Must be one of the following types: half, bfloat16, float32, float64. A 4-D tensor. + The dimension order is interpreted according to the value of data_format, see below for details. + filters : tensor + Must have the same type as input. A 4-D tensor of shape [filter_height, filter_width, in_channels, out_channels] + strides : int of list + The stride of the sliding window for each dimension of input. If a single value is given it is replicated in the H and W dimension. + By default the N and C dimensions are set to 1. The dimension order is determined by the value of data_format, see below for details. + padding : string + "SAME" or "VALID" + data_format : string + "NHWC", "NCHW". Defaults to "NCHW". + dilations : list or ints + list of ints that has length 1, 2 or 4, defaults to 1. The dilation factor for each dimension ofinput. + + Returns + ------- + A Tensor. Has the same type as input. + """ + raise NotImplementedError + + +class Conv3D(object): + pass + # raise NotImplementedError + + +def conv3d(input, filters, strides, padding, data_format='NDHWC', dilations=None, name=None): + """ + Computes a 3-D convolution given 5-D input and filters tensors. + + Parameters + ---------- + input : tensor + Must be one of the following types: half, bfloat16, float32, float64. + Shape [batch, in_depth, in_height, in_width, in_channels]. + filters : tensor + Must have the same type as input. Shape [filter_depth, filter_height, filter_width, in_channels, out_channels]. + in_channels must match between input and filters. + strides : list of ints + A list of ints that has length >= 5. 1-D tensor of length 5. + The stride of the sliding window for each dimension of input. + Must have strides[0] = strides[4] = 1. + padding : string + A string from: "SAME", "VALID". The type of padding algorithm to use. + data_format : string + An optional string from: "NDHWC", "NCDHW". Defaults to "NDHWC". The data format of the input and output data. + With the default format "NDHWC", the data is stored in the order of: [batch, in_depth, in_height, in_width, in_channels]. + Alternatively, the format could be "NCDHW", the data storage order is: [batch, in_channels, in_depth, in_height, in_width]. + dilations : list of ints + Defaults to [1, 1, 1, 1, 1]. 1-D tensor of length 5. The dilation factor for each dimension of input. + If set to k > 1, there will be k-1 skipped cells between each filter element on that dimension. + The dimension order is determined by the value of data_format, see above for details. + Dilations in the batch and depth dimensions must be 1. + name : string + A name for the operation (optional). + + Returns + ------- + A Tensor. Has the same type as input. + """ + + raise NotImplementedError + + +def lrn(inputs, depth_radius, bias, alpha, beta): + """ + Local Response Normalization. + + Parameters + ---------- + inputs : tensor + Must be one of the following types: half, bfloat16, float32. 4-D. + depth_radius : int + Defaults to 5. 0-D. Half-width of the 1-D normalization window. + bias : float + Defaults to 1. An offset (usually positive to avoid dividing by 0). + alpha : float + Defaults to 1. A scale factor, usually positive. + beta : float + Defaults to 0.5. An exponent. + + Returns + ------- + A Tensor. Has the same type as input. + """ + pass + + +def moments(x, axes, shift=None, keepdims=False): + """ + Calculates the mean and variance of x. + + Parameters + ---------- + x : tensor + A Tensor + axes : ints + Axes along which to compute mean and variance. + shift : int + Not used in the current implementation. + keepdims : bool + produce moments with the same dimensionality as the input. + + Returns + ------- + Two Tensor objects: mean and variance. + """ + + pass + + +class MaxPool(object): + + def __init__(self, ksize, strides, padding, data_format=None): + self.data_format, self.padding = preprocess_2d_format(data_format, padding) + self.ksize = ksize + self.strides = strides + + def __call__(self, inputs): + raise NotImplementedError + + +def max_pool(input, ksize, strides, padding, data_format=None): + """ + Performs the max pooling on the input. + + Parameters + ---------- + input : tensor + Tensor of rank N+2, of shape [batch_size] + input_spatial_shape + [num_channels] if data_format does not start + with "NC" (default), or [batch_size, num_channels] + input_spatial_shape if data_format starts with "NC". + Pooling happens over the spatial dimensions only. + ksize : int or list of ints + An int or list of ints that has length 1, N or N+2. + The size of the window for each dimension of the input tensor. + strides : int or list of ints + An int or list of ints that has length 1, N or N+2. + The stride of the sliding window for each dimension of the input tensor. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + + Returns + ------- + A Tensor of format specified by data_format. The max pooled output tensor. + """ + pass + + +class AvgPool(object): + + def __init__(self, ksize, strides, padding, data_format=None): + self.data_format, self.padding = preprocess_2d_format(data_format, padding) + self.filter_size = ksize + self.strides = strides + + def __call__(self, inputs): + raise NotImplementedError + + +def avg_pool(input, ksize, strides, padding): + """ + Performs the avg pooling on the input. + + Parameters + ---------- + input : tensor + Tensor of rank N+2, of shape [batch_size] + input_spatial_shape + [num_channels] + if data_format does not start with "NC" (default), or [batch_size, num_channels] + input_spatial_shape + if data_format starts with "NC". Pooling happens over the spatial dimensions only. + ksize : int or list of ints + An int or list of ints that has length 1, N or N+2. + The size of the window for each dimension of the input tensor. + strides : int or list of ints + An int or list of ints that has length 1, N or N+2. + The stride of the sliding window for each dimension of the input tensor. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + + Returns + ------- + A Tensor of format specified by data_format. The average pooled output tensor. + """ + pass + + +def max_pool3d(input, ksize, strides, padding, data_format=None, name=None): + """ + Performs the max pooling on the input. + + Parameters + ---------- + input : tensor + A 5-D Tensor of the format specified by data_format. + ksize : int or list of ints + An int or list of ints that has length 1, 3 or 5. + The size of the window for each dimension of the input tensor. + strides : int or list of ints + An int or list of ints that has length 1, 3 or 5. + The stride of the sliding window for each dimension of the input tensor. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + "NDHWC", "NCDHW". Defaults to "NDHWC". The data format of the input and output data. + With the default format "NDHWC", the data is stored in the order of: [batch, in_depth, in_height, in_width, in_channels]. + Alternatively, the format could be "NCDHW", the data storage order is: [batch, in_channels, in_depth, in_height, in_width]. + name : string + A name for the operation (optional). + + Returns + ------- + A Tensor of format specified by data_format. The max pooled output tensor. + """ + pass + + +def avg_pool3d(input, ksize, strides, padding, data_format=None, name=None): + """ + Performs the average pooling on the input. + + Parameters + ---------- + input : tensor + A 5-D Tensor of shape [batch, height, width, channels] and type float32, float64, qint8, quint8, or qint32. + ksize : int or list of ints + An int or list of ints that has length 1, 3 or 5. The size of the window for each dimension of the input tensor. + strides : int or list of ints + An int or list of ints that has length 1, 3 or 5. + The stride of the sliding window for each dimension of the input tensor. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + 'NDHWC' and 'NCDHW' are supported. + name : string + Optional name for the operation. + + Returns + ------- + A Tensor with the same type as value. The average pooled output tensor. + """ + pass + + +def pool(input, window_shape, pooling_type, strides=None, padding='VALID', data_format=None, dilations=None, name=None): + """ + Performs an N-D pooling operation. + + Parameters + ---------- + input : tensor + Tensor of rank N+2, of shape [batch_size] + input_spatial_shape + [num_channels] + if data_format does not start with "NC" (default), or [batch_size, num_channels] + input_spatial_shape + if data_format starts with "NC". Pooling happens over the spatial dimensions only. + window_shape : int + Sequence of N ints >= 1. + pooling_type : string + Specifies pooling operation, must be "AVG" or "MAX". + strides : ints + Sequence of N ints >= 1. Defaults to [1]*N. If any value of strides is > 1, then all values of dilation_rate must be 1. + padding : string + The padding algorithm, must be "SAME" or "VALID". Defaults to "SAME". + See the "returns" section of tf.ops.convolution for details. + data_format : string + Specifies whether the channel dimension of the input and output is the last dimension (default, or if data_format does not start with "NC"), + or the second dimension (if data_format starts with "NC"). + For N=1, the valid values are "NWC" (default) and "NCW". For N=2, the valid values are "NHWC" (default) and "NCHW". + For N=3, the valid values are "NDHWC" (default) and "NCDHW". + dilations : list of ints + Dilation rate. List of N ints >= 1. Defaults to [1]*N. If any value of dilation_rate is > 1, then all values of strides must be 1. + name : string + Optional. Name of the op. + + Returns + ------- + Tensor of rank N+2, of shape [batch_size] + output_spatial_shape + [num_channels] + """ + pass + + +class DepthwiseConv2d(object): + + def __init__(self, strides, padding, data_format=None, dilations=None, ksize=None, channel_multiplier=1): + self.data_format, self.padding = preprocess_2d_format(data_format, padding) + self.stride = strides + self.dilations = dilations + + def __call__(self, input, filter): + raise NotImplementedError("Not implemented depthwiseconv2d") + + +def depthwise_conv2d(input, filter, strides, padding, data_format=None, dilations=None, name=None): + """ + Depthwise 2-D convolution. + + Parameters + ---------- + input : tensor + 4-D with shape according to data_format. + filter : tensor + 4-D with shape [filter_height, filter_width, in_channels, channel_multiplier]. + strides : list + 1-D of size 4. The stride of the sliding window for each dimension of input. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + The data format for input. Either "NHWC" (default) or "NCHW". + dilations : list + 1-D of size 2. The dilation rate in which we sample input values across the height and width dimensions in atrous convolution. + If it is greater than 1, then all values of strides must be 1. + name : string + A name for this operation (optional). + + Returns + ------- + A 4-D Tensor with shape according to data_format. + E.g., for "NHWC" format, shape is [batch, out_height, out_width, in_channels * channel_multiplier]. + """ + + pass + +class Conv1d_transpose(object): + + def __init__( + self, strides, padding, data_format='NWC', dilations=None, out_channel=None, k_size=None, in_channels=None + ): + self.strides = strides + self.dilations = dilations + self.data_format, self.padding = preprocess_1d_format(data_format, padding) + + def __call__(self, input, filters): + raise NotImplementedError + + +def conv1d_transpose( + input, filters, output_shape, strides, padding='SAME', data_format='NWC', dilations=None, name=None +): + """ + The transpose of conv1d. + + Parameters + ---------- + input : tensor + A 3-D Tensor of type float and shape [batch, in_width, in_channels] + for NWC data format or [batch, in_channels, in_width] for NCW data format. + filters : tensor + A 3-D Tensor with the same type as value and shape [filter_width, output_channels, in_channels]. + filter's in_channels dimension must match that of value. + output_shape : tensor + A 1-D Tensor, containing three elements, representing the output shape of the deconvolution op. + strides : list + An int or list of ints that has length 1 or 3. The number of entries by which the filter is moved right at each step. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + 'NWC' and 'NCW' are supported. + dilations : list + An int or list of ints that has length 1 or 3 which defaults to 1. + The dilation factor for each dimension of input. If set to k > 1, + there will be k-1 skipped cells between each filter element on that dimension. + Dilations in the batch and depth dimensions must be 1. + name : string + Optional name for the returned tensor. + + Returns + ------- + A Tensor with the same type as value. + """ + pass + + +class Conv2d_transpose(object): + + def __init__( + self, strides, padding, data_format='NHWC', dilations=None, name=None, out_channel=None, k_size=None, + in_channels=None + ): + self.strides = strides + self.dilations = dilations + self.name = name + self.data_format, self.padding = preprocess_2d_format(data_format, padding) + + def __call__(self, input, filters): + raise NotImplementedError + + +def conv2d_transpose( + input, filters, output_shape, strides, padding='SAME', data_format='NHWC', dilations=None, name=None +): + """ + The transpose of conv2d. + + Parameters + ---------- + input : tensor + A 4-D Tensor of type float and shape [batch, height, width, in_channels] + for NHWC data format or [batch, in_channels, height, width] for NCHW data format. + filters : tensor + A 4-D Tensor with the same type as input and shape [height, width, + output_channels, in_channels]. filter's in_channels dimension must match that of input. + output_shape : tensor + A 1-D Tensor representing the output shape of the deconvolution op. + strides : list + An int or list of ints that has length 1, 2 or 4. The stride of the sliding window for each dimension of input. + If a single value is given it is replicated in the H and W dimension. + By default the N and C dimensions are set to 0. + The dimension order is determined by the value of data_format, see below for details. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + 'NHWC' and 'NCHW' are supported. + dilations : list + An int or list of ints that has length 1, 2 or 4, defaults to 1. + name : string + Optional name for the returned tensor. + + Returns + ------- + A Tensor with the same type as input. + """ + pass + + +class Conv3d_transpose(object): + + def __init__( + self, strides, padding, data_format='NDHWC', dilations=None, name=None, out_channel=None, k_size=None, + in_channels=None + ): + self.strides = strides + self.dilations = dilations + self.name = name + self.out_channel = out_channel + self.data_format, self.padding = preprocess_3d_format(data_format, padding) + + def __call__(self, input, filters): + raise NotImplementedError + + +def conv3d_transpose( + input, filters, output_shape, strides, padding='SAME', data_format='NDHWC', dilations=None, name=None +): + """ + The transpose of conv3d. + + Parameters + ---------- + input : tensor + A 5-D Tensor of type float and shape [batch, height, width, in_channels] for + NHWC data format or [batch, in_channels, height, width] for NCHW data format. + filters : tensor + A 5-D Tensor with the same type as value and shape [height, width, output_channels, in_channels]. + filter's in_channels dimension must match that of value. + output_shape : tensor + A 1-D Tensor representing the output shape of the deconvolution op. + strides : list + An int or list of ints that has length 1, 3 or 5. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + 'NDHWC' and 'NCDHW' are supported. + dilations : list of ints + An int or list of ints that has length 1, 3 or 5, defaults to 1. + name : string + Optional name for the returned tensor. + + Returns + ------- + A Tensor with the same type as value. + """ + + pass + + +class BatchNorm(object): + + def __init__(self): + pass + + def __call__(self, *args, **kwargs): + pass diff --git a/tensorlayer/backend/ops/tensorflow_backend.py b/tensorlayer/backend/ops/tensorflow_backend.py new file mode 100644 index 0000000..74df53d --- /dev/null +++ b/tensorlayer/backend/ops/tensorflow_backend.py @@ -0,0 +1,1021 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +from __future__ import absolute_import, division, print_function +from .tensorflow_nn import nchw_to_nhwc, nhwc_to_nchw +import tensorflow as tf + +_dtypeDict = { + 'DType': tf.DType, + 'float16': tf.float16, + 'float32': tf.float32, + 'float64': tf.float64, + 'int8': tf.int8, + 'int16': tf.int16, + 'int32': tf.int32, + 'int64': tf.int64, + 'uint8': tf.uint8, + 'uint16': tf.uint16, + 'uint32': tf.uint32, + 'uint64': tf.uint64 +} + +DType = tf.DType +float16 = tf.float16 +float32 = tf.float32 +float64 = tf.float64 +int8 = tf.int8 +int16 = tf.int16 +int32 = tf.int32 +int64 = tf.int64 +uint8 = tf.uint8 +uint16 = tf.uint16 +uint32 = tf.uint32 +uint64 = tf.uint64 + +# isinstance input output +# TensorLike = tf_ops._TensorLike + + +def set_context(**kwargs): + raise Exception("Using TenosrFlow backend,You don't need to set context") + + +def get_tensor_shape(x): + return x.get_shape().as_list() + + +# initializers +def zeros(shape, dtype=tf.float32): + """ + Creates a tensor with all elements set to zero. + + Parameters + ---------- + shape : A list of integers + a tuple of integers, or a 1-D Tensor of type int32. + dtype : tensor + The DType of an element in the resulting Tensor + + Returns + ------- + A Tensor with all elements set to zero. + + """ + return tf.zeros(shape=shape, dtype=dtype) + + +def ones(shape, dtype=tf.float32): + """ + Creates a tensor with all elements set to ones. + + Parameters + ---------- + shape : A list of integers + a tuple of integers, or a 1-D Tensor of type int32. + dtype : tensor + The DType of an element in the resulting Tensor + + Returns + ------- + A Tensor with all elements set to zero. + + """ + return tf.ones(shape=shape, dtype=dtype) + + +def constant(value, dtype=tf.float32, shape=None): + """ + Creates a constant tensor from a tensor-like object. + + Parameters + ---------- + value : list + A constant value (or list) of output type dtype. + dtype : tensor + The type of the elements of the resulting tensor. + shape : tuple + Optional dimensions of resulting tensor. + + Returns + ------- + A Constant Tensor. + + """ + return tf.constant(value=value, dtype=dtype, shape=shape) + + +def random_uniform(shape, minval=0, maxval=None, dtype=tf.float32, seed=None): + """ + Outputs random values from a uniform distribution. + + Parameters + ---------- + shape : tuple + A 1-D integer Tensor or Python array. The shape of the output tensor. + minval : int + The lower bound on the range of random values to generate (inclusive). Defaults to 0. + maxval : int + The upper bound on the range of random values to generate (exclusive). Defaults to 1 if dtype is floating point. + dtype : tensor + The type of the output: float16, float32, float64, int32, or int64. + seed : int + Used in combination with tf.random.set_seed to create a reproducible sequence of tensors across multiple calls. + Returns + ------- + A tensor of the specified shape filled with random uniform values. + + """ + outputs = tf.random.uniform(shape=shape, minval=minval, maxval=maxval, dtype=dtype, seed=seed) + return outputs + + +def random_normal(shape, mean=0.0, stddev=1.0, dtype=tf.dtypes.float32, seed=None): + """ + Outputs random values from a normal distribution. + + Parameters + ---------- + shape : tuple + A 1-D integer Tensor or Python array. The shape of the output tensor. + mean : float + The mean of the normal distribution + stddev : float + The standard deviation of the normal distribution. + dtype : tensor + The type of the output. + seed : A Python integer + Used to create a random seed for the distribution + + Returns + ------- + A tensor of the specified shape filled with random normal values. + + """ + outputs = tf.random.normal(shape=shape, mean=mean, stddev=stddev, dtype=dtype, seed=seed) + return outputs + + +def truncated_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None): + """ + Outputs random values from a truncated normal distribution. + + Parameters + ---------- + shape : tuple + A 1-D integer Tensor or Python array. The shape of the output tensor. + mean : float + The mean of the normal distribution + stddev : float + The standard deviation of the normal distribution. + dtype : tensor + The type of the output. + seed : A Python integer + Used to create a random seed for the distribution + + Returns + ------- + A tensor of the specified shape filled with random truncated normal values. + + """ + outputs = tf.random.truncated_normal(shape=shape, mean=mean, stddev=stddev, dtype=dtype, seed=seed) + return outputs + + +def he_normal(shape, dtype, seed=None): + """ + He normal initializer. + + Parameters + ---------- + seed : A Python integer. + Used to seed the random generator. + shape : tuple + A 1-D integer Tensor or Python array. The shape of the output tensor. + dtype : tensor + The type of the output. + + Returns + ------- + A tensor of the specified shape filled with he normal values. + """ + return tf.initializers.he_normal(seed)(shape=shape, dtype=dtype) + + +def Variable(initial_value, name, trainable=True): + """ + Creates a new variable with value initial_value. + + Parameters + ---------- + initial_value : tensor + A Tensor, or Python object convertible to a Tensor + name : str + Optional name for the variable. Defaults to 'Variable' and gets uniquified automatically. + Returns + ------- + Variable + """ + + var = tf.Variable(initial_value=initial_value, name=name, trainable=trainable) + return var + + +class MatMul(object): + + def __init__(self): + pass + + def __call__(self, a, b): + return tf.matmul(a, b) + + +def matmul(a, b): + """ + Multiplies matrix a by matrix b, producing a * b. + + Parameters + ---------- + a : tensor + type float16, float32, float64, int32, complex64, complex128 and rank > 1. + b : tensor + with same type and rank as a. + + Returns + ------- + A Tensor of the same type as a and b + """ + + outputs = tf.matmul(a, b) + return outputs + + +def add(value, bias): + """ + Returns x + y element-wise. + + Parameters + ---------- + value : tensor. + Must be one of the following types: bfloat16, half, float32, float64, + uint8, int8, int16, int32, int64, complex64, complex128, string. + bias : tensor + Must have the same type as a + + Returns + ------- + A Tensor. Has the same type as a. + """ + + outputs = tf.add(value, bias) + return outputs + + +def dtypes(dt): + """ + Data dtypes. + + Parameters + ---------- + dt : string + It could be 'uint8', 'uint16', 'uint32', 'uint64', 'int8', 'int16', + 'int32', 'int64', 'float16', 'float32', 'float64', 'DType'. + + Returns + ------- + Data dtypes + """ + + if dt not in _dtypeDict.keys(): + raise Exception("Unsupported dtype: {}".format(dt)) + return _dtypeDict[dt] + + +class Maximum(object): + def __init__(self): + pass + + def __call__(self, x, y): + return tf.maximum(x=x, y=y) + + +class Minimum(object): + def __init__(self): + pass + + def __call__(self, x, y): + return tf.minimum(x=x, y=y) + + +def minimum(x, y): + """ + Returns the min of x and y (i.e. x < y ? x : y) element-wise. + + Parameters + ---------- + x : tensor. + Must be one of the following types: bfloat16, half, float32, float64, int32, int64. + y : A Tensor. + Must have the same type as x. + + Returns + ------- + A Tensor. Has the same type as x + """ + + outputs = tf.minimum(x=x, y=y) + return outputs + + +class FlattenReshape(object): + + def __init__(self): + pass + + def __call__(self, inputs): + dim = 1 + for d in get_tensor_shape(inputs)[1:]: + dim *= d + return tf.reshape(inputs, [-1, dim]) + + +class Reshape(object): + + def __init__(self, shape): + self.shape = shape + + def __call__(self, tensor): + return tf.reshape(tensor, self.shape) + + +def reshape(tensor, shape): + """ + Reshapes a tensor. + + Parameters + ---------- + tensor : tensor + A Tensor. + shape : tensor + Defines the shape of the output tensor. + Returns + ------- + A Tensor. Has the same type as tensor + """ + + return tf.reshape(tensor, shape) + + +class Concat(object): + + def __init__(self, axis): + super(Concat, self).__init__() + self.axis = axis + + def __call__(self, values): + return tf.concat(values=values, axis=self.axis) + + +def concat(values, axis): + """ + Concatenates tensors along one dimension. + + Parameters + ---------- + values : list + A list of Tensor objects or a single Tensor + axis : int + 0-D int32 Tensor. Dimension along which to concatenate + Returns + ------- + A Tensor resulting from concatenation of the input tensors. + """ + + return tf.concat(values, axis) + + +def convert_to_tensor(value, dtype=None): + """ + Converts the given value to a Tensor. + + Parameters + ---------- + value : object + An object whose type has a registered Tensor conversion function. + dtype : optional + Optional element type for the returned tensor. If missing, the type is inferred from the type of value. + + Returns + ------- + A Tensor based on value. + """ + + return tf.convert_to_tensor(value, dtype) + + +def sqrt(x): + """ + Computes square root of x element-wise. + + Parameters + ---------- + x : tensor + Must be one of the following types: bfloat16, half, float32, float64, complex64, complex128. + + Returns + ------- + A Tensor. Has the same type as x. + """ + return tf.sqrt(x) + + +class ReduceSum(object): + + def __init__(self, axis=None): + self.axis = axis + + def __call__(self, input): + return tf.reduce_sum(input, axis=self.axis) + + +class ReduceMean(object): + + def __init__(self, axis): + self.axis = axis + + def __call__(self, inputs): + output = tf.reduce_mean(inputs, self.axis) + return output + + +def reduce_mean(input_tensor, axis=None): + """ + Computes the mean of elements across dimensions of a tensor. + + Parameters + ---------- + input_tensor : tensor + The tensor to reduce. Should have numeric type. + axis : list + The dimensions to reduce. If None (the default), reduces all dimensions. + Must be in the range [-rank(input_tensor), rank(input_tensor)). + name : str + A name for the operation (optional). + + Returns + ------- + The reduced tensor. + """ + + return tf.reduce_mean(input_tensor, axis=axis) + + +class ReduceMax(object): + + def __init__(self, axis): + self.axis = axis + + def __call__(self, inputs): + output = tf.reduce_max(inputs, self.axis) + return output + + +def reduce_max(input_tensor, axis=None): + """ + Computes the maximum of elements across dimensions of a tensor. + + Parameters + ---------- + input_tensor : tensor + The tensor to reduce. Should have real numeric type. + axis : int + The dimensions to reduce. If None (the default), reduces all dimensions. + Must be in the range [-rank(input_tensor), rank(input_tensor)). + name : str + A name for the operation (optional). + + Returns + ------- + The reduced tensor. + """ + + return tf.reduce_max(input_tensor, axis=axis) + + +def reduce_min(input_tensor, axis=None): + """ + Computes the minimum of elements across dimensions of a tensor. + + Parameters + ---------- + input_tensor : tensor + The tensor to reduce. Should have real numeric type. + axis : int + The dimensions to reduce. If None (the default), reduces all dimensions. + Must be in the range [-rank(input_tensor), rank(input_tensor)). + name : str + A name for the operation (optional). + + Returns + ------- + The reduced tensor. + """ + + return tf.reduce_min(input_tensor, axis=axis) + + +class Pad(object): + def __init__(self, paddings, mode="REFLECT"): + if mode not in ['CONSTANT', 'REFLECT', 'SYMMETRIC']: + raise Exception("Unsupported mode: {}".format(mode)) + self.paddings = paddings + self.mode = mode + + def __call__(self, x): + outputs = tf.pad(x, self.paddings, mode=self.mode, constant_values=0) + return outputs + +def pad(tensor, paddings, mode='CONSTANT', constant_values=0): + """ + Pads a tensor. + + Parameters + ---------- + tensor : tensor + A Tensor. + paddings : tensor + A Tensor of type int32. + mode : str + One of "CONSTANT", "REFLECT", or "SYMMETRIC" (case-insensitive) + constant_values : int + In "CONSTANT" mode, the scalar pad value to use. Must be same type as tensor. + + Returns + ------- + A Tensor. Has the same type as tensor. + """ + + if mode not in ['CONSTANT', 'REFLECT', 'SYMMETRIC']: + raise Exception("Unsupported mode: {}".format(mode)) + outputs = tf.pad(tensor, paddings, mode=mode, constant_values=constant_values) + return outputs + + +class Unstack(object): + + def __init__(self, axis, num=None): + self.axis = axis + self.num = num + + def __call__(self, values): + return tf.unstack(values, num=self.num, axis=self.axis) + + +class Stack(object): + + def __init__(self, axis=0): + self.axis = axis + + def __call__(self, values): + return tf.stack(values, axis=self.axis) + + +def stack(values, axis=0): + """ + Stacks a list of rank-R tensors into one rank-(R+1) tensor. + + Parameters + ---------- + values : list + A list of Tensor objects with the same shape and type. + axis : int + An int. The axis to stack along. Defaults to the first dimension. + Negative values wrap around, so the valid range is [-(R+1), R+1). + + Returns + ------- + A stacked Tensor with the same type as values. + """ + + return tf.stack(values, axis=axis) + + +class Meshgrid(object): + def __init__(self, indexing='xy'): + super(Meshgrid, self).__init__() + self.index = indexing + + def __call__(self, inputs): + return tf.meshgrid(inputs) + + +def meshgrid(*args, **kwargs): + """ + Broadcasts parameters for evaluation on an N-D grid. + + Parameters + ---------- + x : tensor + Tensors with rank 1. + y : tensor + Tensors with rank 1. + + Returns + ------- + A list of N Tensors with rank N. + """ + + return tf.meshgrid(*args, **kwargs) + + +def range(start, limit=None, delta=1, dtype=None): + """ + Creates a sequence of numbers. + + Parameters + ---------- + start : tensor + A 0-D Tensor (scalar). Acts as first entry in the range if limit is not None; + otherwise, acts as range limit and first entry defaults to 0. + limit : tensor + A 0-D Tensor (scalar). Upper limit of sequence, exclusive. If None, + defaults to the value of start while the first entry of the range defaults to 0. + delta : tensor + A 0-D Tensor (scalar). Number that increments start. Defaults to 1. + dtype : type + The type of the elements of the resulting tensor. + + Returns + ------- + An 1-D Tensor of type dtype. + """ + + if limit is None: + outputs = tf.range(start, delta=delta, dtype=dtype) + else: + outputs = tf.range(start, limit, delta=delta, dtype=dtype) + return outputs + + +class ExpandDims(object): + + def __init__(self, axis): + self.axis = axis + + def __call__(self, input): + return tf.expand_dims(input, axis=self.axis) + + +def expand_dims(input, axis): + """ + Inserts a dimension of 1 into a tensor's shape. + + Parameters + ---------- + input : tensor + A Tensor. + axis : int + 0-D (scalar). Specifies the dimension index at which to expand the shape of input. + Must be in the range [-rank(input) - 1, rank(input)]. + + Returns + ------- + A Tensor with the same data as input, but its shape has an additional dimension of size 1 added. + """ + + return tf.expand_dims(input, axis) + + +class Tile(object): + + def __init__(self): + pass + + def __call__(self, input, multiples): + return tf.tile(input, multiples) + + +def tile(input, multiples): + """ + Constructs a tensor by tiling a given tensor. + + Parameters + ---------- + input : tensor + A Tensor. 1-D or higher. + multiples : tensor + Must be one of the following types: int32, int64. 1-D. + Length must be the same as the number of dimensions in input + + Returns + ------- + A Tensor. Has the same type as input. + """ + + return tf.tile(input, multiples) + + +class Cast(object): + + def __init__(self, dtype): + self.dtype = dtype + + def __call__(self, x): + return tf.cast(x, dtype=self.dtype) + + +def cast(x, dtype): + """ + Casts a tensor to a new type. + + Parameters + ---------- + x : tensor + A Tensor or SparseTensor or IndexedSlices of numeric type. + It could be uint8, uint16, uint32, uint64, int8, int16, int32, int64, float16, float32, float64. + dtype : dtpye + The destination type. The list of supported dtypes is the same as x + + Returns + ------- + A Tensor or SparseTensor or IndexedSlices with same shape as x and same type as dtype. + """ + + return tf.cast(x, dtype=dtype) + + +class Transpose(object): + + def __init__(self, perm, conjugate=False): + self.perm = perm + self.conjugate = conjugate + + def __call__(self, a): + return tf.transpose(a, self.perm, self.conjugate) + + +def transpose(a, perm=None, conjugate=False): + """ + Transposes a. + + Parameters + ---------- + a : tensor + A Tensor. + perm : list / int + A permutation of the dimensions of a. + conjugate : bool + Setting it to True is mathematically equivalent to tf.math.conj(tf.transpose(input)). + + Returns + ------- + A transposed Tensor. + """ + + return tf.transpose(a, perm, conjugate) + + +def gather_nd(params, indices, batch_dims=0): + """ + Gather slices from params into a Tensor with shape specified by indices. + + Parameters + ---------- + params : tensor + The tensor from which to gather values. + indices : tensor + Must be one of the following types: int32, int64. Index tensor. + batch_dims : int + An integer or a scalar 'Tensor'. The number of batch dimensions. + + Returns + ------- + A Tensor. Has the same type as params. + """ + + return tf.gather_nd(params, indices, batch_dims) + + +def clip_by_value(t, clip_value_min, clip_value_max): + """ + Clips tensor values to a specified min and max. + + Parameters + ---------- + t : tensor + A Tensor or IndexedSlices + clip_value_min : tensor + A 0-D (scalar) Tensor, or a Tensor with the same shape as t. The minimum value to clip by + clip_value_max : tensor + A 0-D (scalar) Tensor, or a Tensor with the same shape as t. The minimum value to clip by + + Returns + ------- + A clipped Tensor or IndexedSlices. + """ + + return tf.clip_by_value(t, clip_value_min, clip_value_max) + + +def split(value, num_or_size_splits, axis=0, num=None): + """ + Splits a tensor into sub tensors. + + Parameters + ---------- + value : tensor + The Tensor to split. + num_or_size_splits : list + Either an integer indicating the number of splits along split_dim or a 1-D integer Tensor or + Python list containing the sizes of each output tensor along split_dim. + axis : int + The dimension along which to split. Must be in the range [-rank(value), rank(value)). Defaults to 0. + num : int + used to specify the number of outputs when it cannot be inferred from the shape of size_splits. + + Returns + ------- + Tensor objects resulting from splitting value. + """ + + return tf.split(value=value, num_or_size_splits=num_or_size_splits, axis=axis, num=num) + + +def floor(x): + return tf.floor(x) + + +def gather(params, indices): + return tf.gather(params, indices) + + +def linspace(start, stop, num): + return tf.linspace(start, stop, num) + + +def slice(inputs, starts, sizes): + return tf.slice(inputs, starts, sizes) + + +def add_n(inputs): + return tf.add_n(inputs) + + +class OneHot(object): + + def __init__(self, depth, on_value, off_value, axis, dtype): + self.depth = depth + self.on_value = on_value + self.off_value = off_value + self.axis = axis + self.dtype = dtype + + def __call__(self, inputs, *args, **kwargs): + outputs = tf.one_hot( + inputs, self.depth, on_value=self.on_value, off_value=self.off_value, axis=self.axis, dtype=self.dtype + ) + return outputs + + +class L2Normalize(object): + + def __init__(self, axis=None, epsilon=1e-12): + self.axis = axis + self.epsilon = epsilon + + def __call__(self, input, *args, **kwargs): + outputs = tf.math.l2_normalize(input, axis=self.axis, epsilon=self.epsilon) + return outputs + + +class EmbeddingLookup(object): + + def __init__(self, max_norm=None): + self.max_norm = max_norm + + def __call__(self, params, ids, *args, **kwargs): + outputs = tf.nn.embedding_lookup(params=params, ids=ids, max_norm=self.max_norm) + return outputs + + +class NCELoss(object): + + def __init__(self, num_true=1, sampled_values=None, remove_accidental_hits=False): + self.num_true = num_true + self.sampled_values = sampled_values + self.remove_accidental_hits = remove_accidental_hits + + def __call__(self, weights, biases, labels, inputs, num_sampled, num_classes): + outputs = tf.nn.nce_loss( + weights=weights, biases=biases, inputs=inputs, labels=labels, num_sampled=num_sampled, + num_classes=num_classes + ) + return outputs + + +class Not_equal(object): + + def __init__(self): + pass + + def __call__(self, x, y): + return tf.not_equal(x, y) + + +class Count_nonzero(object): + + def __init__(self, keepdims=None, dtype=int64): + self.keepdims = keepdims + self.dtype = dtype + + def __call__(self, input, axis=None): + return tf.math.count_nonzero(input, axis=axis, keepdims=self.keepdims, dtype=self.dtype) + + + +class Resize: + + def __init__(self, scale, method, antialias=False, data_format='channels_last', ksize=None): + self.method = method + self.antialias = antialias + self.scale = scale + self.data_format = data_format + + def __call__(self, inputs): + if self.data_format == 'channels_first': + inputs = nchw_to_nhwc(inputs) + if len(get_tensor_shape(inputs)) == 4: + output_size = [int(inputs.shape[1] * self.scale[0]), int(inputs.shape[2] * self.scale[1])] + else: + raise ("The inputs shape must be 4-D Tensor.") + outputs = tf.image.resize(inputs, size=output_size, method=self.method, antialias=self.antialias) + if self.data_format == 'channels_first': + outputs = nhwc_to_nchw(outputs) + return outputs + + +def resize(inputs, output_size, method, antialias): + return tf.image.resize(inputs, size=output_size, method=method, antialias=antialias) + + +class ZeroPadding1D(object): + + def __init__(self, padding): + self.zeropad = tf.keras.layers.ZeroPadding1D(padding=padding) + + def __call__(self, inputs): + return self.zeropad(inputs) + + +class ZeroPadding2D(object): + + def __init__(self, padding): + self.zeropad = tf.keras.layers.ZeroPadding2D(padding=padding) + + def __call__(self, inputs): + return self.zeropad(inputs) + + +class ZeroPadding3D(object): + + def __init__(self, padding): + self.zeropad = tf.keras.layers.ZeroPadding3D(padding=padding) + + def __call__(self, inputs): + return self.zeropad(inputs) + + +class Sign(object): + + def __init__(self): + pass + + def __call__(self, x): + return tf.sign(x) + +def ceil(x): + return tf.math.ceil(x) + +def multiply(x, y): + return tf.multiply(x, y) + +def divide(x, y): + return tf.divide(x, y) + +def identity(x): + return tf.identity(x) + +class BatchToSpace(object): + def __init__(self, block_size, crops): + self.bolock_size = block_size + self.crops = crops + + def __call__(self, input_x): + return tf.batch_to_space(input=input_x, block_shape=self.bolock_size, crops=self.crops) + +class DepthToSpace(object): + def __init__(self, block_size, data_format='NHWC'): + self.block_size = block_size + self.data_format = data_format + + def __call__(self, input): + return tf.nn.depth_to_space(input, block_size=self.block_size, data_format=self.data_format) diff --git a/tensorlayer/backend/ops/tensorflow_nn.py b/tensorlayer/backend/ops/tensorflow_nn.py new file mode 100644 index 0000000..71d978f --- /dev/null +++ b/tensorlayer/backend/ops/tensorflow_nn.py @@ -0,0 +1,1519 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorflow as tf +from tensorflow.python.framework import ops +from tensorflow.python.ops import math_ops +from tensorflow.python.training import moving_averages + +# loss function +sparse_softmax_cross_entropy_with_logits = tf.nn.sparse_softmax_cross_entropy_with_logits +sigmoid_cross_entropy_with_logits = tf.nn.sigmoid_cross_entropy_with_logits + + +def padding_format(padding): + """ + Checks that the padding format correspond format. + + Parameters + ---------- + padding : str + Must be one of the following:"same", "SAME", "VALID", "valid" + + Returns + ------- + str "SAME" or "VALID" + """ + + if padding in ["SAME", "same"]: + padding = "SAME" + elif padding in ["VALID", "valid"]: + padding = "VALID" + elif padding == None: + padding = None + else: + raise Exception("Unsupported padding: " + str(padding)) + return padding + + +def preprocess_1d_format(data_format, padding): + """ + Checks that the 1-D dataformat format correspond format. + + Parameters + ---------- + data_format : str + Must be one of the following:"channels_last","NWC","NCW","channels_first" + padding : str + Must be one of the following:"same","valid","SAME","VALID" + + Returns + ------- + str "NWC" or "NCW" and "SAME" or "VALID" + """ + if data_format in ["channels_last", "NWC"]: + data_format = "NWC" + elif data_format in ["channels_first", "NCW"]: + data_format = "NCW" + elif data_format == None: + data_format = None + else: + raise Exception("Unsupported data format: " + str(data_format)) + padding = padding_format(padding) + return data_format, padding + + +def preprocess_2d_format(data_format, padding): + """ + Checks that the 2-D dataformat format correspond format. + + Parameters + ---------- + data_format : str + Must be one of the following:"channels_last","NHWC","NCHW","channels_first" + padding : str + Must be one of the following:"same","valid","SAME","VALID" + + Returns + ------- + str "NHWC" or "NCHW" and "SAME" or "VALID" + """ + + if data_format in ["channels_last", "NHWC"]: + data_format = "NHWC" + elif data_format in ["channels_first", "NCHW"]: + data_format = "NCHW" + elif data_format == None: + data_format = None + else: + raise Exception("Unsupported data format: " + str(data_format)) + padding = padding_format(padding) + return data_format, padding + + +def preprocess_3d_format(data_format, padding): + """ + Checks that the 3-D dataformat format correspond format. + + Parameters + ---------- + data_format : str + Must be one of the following:"channels_last","NDHWC","NCDHW","channels_first" + padding : str + Must be one of the following:"same","valid","SAME","VALID" + + Returns + ------- + str "NDHWC" or "NCDHW" and "SAME" or "VALID" + """ + + if data_format in ['channels_last', 'NDHWC']: + data_format = 'NDHWC' + elif data_format in ['channels_first', 'NCDHW']: + data_format = 'NCDHW' + elif data_format == None: + data_format = None + else: + raise Exception("Unsupported data format: " + str(data_format)) + padding = padding_format(padding) + return data_format, padding + + +def nchw_to_nhwc(x): + """ + Channels first to channels last + + Parameters + ---------- + x : tensor + channels first tensor data + + Returns + ------- + channels last tensor data + """ + + if len(x.shape) == 3: + x = tf.transpose(x, (0, 2, 1)) + elif len(x.shape) == 4: + x = tf.transpose(x, (0, 2, 3, 1)) + elif len(x.shape) == 5: + x = tf.transpose(x, (0, 2, 3, 4, 1)) + else: + raise Exception("Unsupported dimensions") + return x + + +def nhwc_to_nchw(x): + """ + Channles last to channels first + + Parameters + ---------- + x : tensor + channels last tensor data + + Returns + ------- + channels first tensor data + """ + + if len(x.shape) == 3: + x = tf.transpose(x, (0, 2, 1)) + elif len(x.shape) == 4: + x = tf.transpose(x, (0, 3, 1, 2)) + elif len(x.shape) == 5: + x = tf.transpose(x, (0, 4, 1, 2, 3)) + else: + raise Exception("Unsupported dimensions") + return x + + +class ReLU(object): + + def __init__(self): + pass + + def __call__(self, x): + return tf.nn.relu(x) + + +def relu(x): + """ + Computes rectified linear: max(features, 0). + + Parameters + ---------- + x : tensor + Must be one of the following types: float32, float64, int32, uint8, int16, + int8, int64, bfloat16, uint16, half, uint32, uint64, qint8. + + Returns + ------- + A Tensor. Has the same type as features. + """ + + return tf.nn.relu(x) + + +class ReLU6(object): + + def __init__(self): + pass + + def __call__(self, x): + return tf.nn.relu6(x) + + +def relu6(x): + """ + Computes Rectified Linear 6: min(max(features, 0), 6). + + Parameters + ---------- + x : tensor + Must be one of the following types: float32, float64, int32, uint8, int16, + int8, int64, bfloat16, uint16, half, uint32, uint64, qint8. + + Returns + ------- + A Tensor with the same type as features. + """ + + return tf.nn.relu6(x) + + +class LeakyReLU(object): + + def __init__(self, alpha=0.2): + self.alpha = alpha + + def __call__(self, x): + return tf.nn.leaky_relu(x, alpha=self.alpha) + + +def leaky_relu(x, alpha=0.2): + """ + Compute the Leaky ReLU activation function. + + Parameters + ---------- + x : tensor + representing preactivation values. Must be one of the following types: + float16, float32, float64, int32, int64. + + Returns + ------- + The activation value. + """ + + return tf.nn.leaky_relu(x, alpha=alpha) + + +class Softplus(object): + + def __init__(self): + pass + + def __call__(self, x): + return tf.nn.softplus(x) + + +def softplus(x): + """ + Computes softplus: log(exp(features) + 1). + + Parameters + ---------- + x : tensor + Must be one of the following types: half, bfloat16, float32, float64. + + Returns + ------- + A Tensor. Has the same type as features. + """ + + return tf.nn.softplus(x) + + +class Tanh(object): + + def __init__(self): + pass + + def __call__(self, x): + return tf.nn.tanh(x) + + +def tanh(x): + """ + Computes hyperbolic tangent of x element-wise. + + Parameters + ---------- + x : tensor + Must be one of the following types: bfloat16, half, float32, float64, complex64, complex128. + + Returns + ------- + A Tensor. Has the same type as x. + """ + + return tf.nn.tanh(x) + + +class Sigmoid(object): + + def __init__(self): + pass + + def __call__(self, x): + return tf.nn.sigmoid(x) + + +def sigmoid(x): + """ + Computes sigmoid of x element-wise. + + Parameters + ---------- + x : tensor + A Tensor with type float16, float32, float64, complex64, or complex128. + + Returns + ------- + A Tensor with the same type as x. + """ + + return tf.nn.sigmoid(x) + + +class Softmax(object): + + def __init__(self): + pass + + def __call__(self, x): + return tf.nn.softmax(x) + + +def softmax(logits, axis=None): + """ + Computes softmax activations. + + Parameters + ---------- + logits : tensor + Must be one of the following types: half, float32, float64. + axis : int + The dimension softmax would be performed on. The default is -1 which indicates the last dimension. + + Returns + ------- + A Tensor. Has the same type and shape as logits. + """ + + return tf.nn.softmax(logits, axis) + + +class Dropout(object): + + def __init__(self, keep, seed=0): + self.keep = keep + self.seed = seed + + def __call__(self, inputs, *args, **kwargs): + outputs = tf.nn.dropout(inputs, rate=1 - (self.keep), seed=self.seed) + return outputs + + +class BiasAdd(object): + """ + Adds bias to value. + + Parameters + ---------- + x : tensor + A Tensor with type float, double, int64, int32, uint8, int16, int8, complex64, or complex128. + bias : tensor + Must be the same type as value unless value is a quantized type, + in which case a different quantized type may be used. + Returns + ------- + A Tensor with the same type as value. + """ + + def __init__(self, data_format=None): + self.data_format = data_format + + def __call__(self, x, bias): + return tf.nn.bias_add(x, bias, data_format=self.data_format) + + +def bias_add(x, bias, data_format=None, name=None): + """ + Adds bias to value. + + Parameters + ---------- + x : tensor + A Tensor with type float, double, int64, int32, uint8, int16, int8, complex64, or complex128. + bias : tensor + Must be the same type as value unless value is a quantized type, + in which case a different quantized type may be used. + data_format : A string. + 'N...C' and 'NC...' are supported. + name : str + A name for the operation (optional). + Returns + ------- + A Tensor with the same type as value. + """ + + x = tf.nn.bias_add(x, bias, data_format=data_format, name=name) + return x + + +class Conv1D(object): + + def __init__(self, stride, padding, data_format='NWC', dilations=None, out_channel=None, k_size=None): + self.stride = stride + self.dilations = dilations + self.data_format, self.padding = preprocess_1d_format(data_format, padding) + + def __call__(self, input, filters): + outputs = tf.nn.conv1d( + input=input, + filters=filters, + stride=self.stride, + padding=self.padding, + data_format=self.data_format, + dilations=self.dilations, + # name=name + ) + return outputs + + +def conv1d(input, filters, stride, padding, data_format='NWC', dilations=None): + """ + Computes a 1-D convolution given 3-D input and filter tensors. + + Parameters + ---------- + input : tensor + A 3D Tensor. Must be of type float16, float32, or float64 + filters : tensor + A 3D Tensor. Must have the same type as input. + stride : int of list + An int or list of ints that has length 1 or 3. The number of entries by which the filter is moved right at each step. + padding : string + 'SAME' or 'VALID' + data_format : string + An optional string from "NWC", "NCW". Defaults to "NWC", the data is stored in the order of + [batch, in_width, in_channels]. The "NCW" format stores data as [batch, in_channels, in_width]. + dilations : int or list + An int or list of ints that has length 1 or 3 which defaults to 1. + The dilation factor for each dimension of input. If set to k > 1, + there will be k-1 skipped cells between each filter element on that dimension. + Dilations in the batch and depth dimensions must be 1. + name : string + A name for the operation (optional). + Returns + ------- + A Tensor. Has the same type as input. + """ + + data_format, padding = preprocess_1d_format(data_format, padding) + outputs = tf.nn.conv1d( + input=input, + filters=filters, + stride=stride, + padding=padding, + data_format=data_format, + dilations=dilations, + # name=name + ) + return outputs + + +class Conv2D(object): + + def __init__(self, strides, padding, data_format='NHWC', dilations=None, out_channel=None, k_size=None): + self.strides = strides + self.dilations = dilations + self.data_format, self.padding = preprocess_2d_format(data_format, padding) + + def __call__(self, input, filters): + outputs = tf.nn.conv2d( + input=input, + filters=filters, + strides=self.strides, + padding=self.padding, + data_format=self.data_format, + dilations=self.dilations, + ) + return outputs + + +def conv2d(input, filters, strides, padding, data_format='NHWC', dilations=None): + """ + Computes a 2-D convolution given 4-D input and filters tensors. + + Parameters + ---------- + input : tensor + Must be one of the following types: half, bfloat16, float32, float64. A 4-D tensor. + The dimension order is interpreted according to the value of data_format, see below for details. + filters : tensor + Must have the same type as input. A 4-D tensor of shape [filter_height, filter_width, in_channels, out_channels] + strides : int of list + The stride of the sliding window for each dimension of input. If a single value is given it is replicated in the H and W dimension. + By default the N and C dimensions are set to 1. The dimension order is determined by the value of data_format, see below for details. + padding : string + "SAME" or "VALID" + data_format : string + "NHWC", "NCHW". Defaults to "NHWC". + dilations : list or ints + list of ints that has length 1, 2 or 4, defaults to 1. The dilation factor for each dimension ofinput. + name : string + A name for the operation (optional). + + Returns + ------- + A Tensor. Has the same type as input. + """ + + data_format, padding = preprocess_2d_format(data_format, padding) + outputs = tf.nn.conv2d( + input=input, + filters=filters, + strides=strides, + padding=padding, + data_format=data_format, + dilations=dilations, + ) + return outputs + + +class Conv3D(object): + + def __init__(self, strides, padding, data_format='NDHWC', dilations=None, out_channel=None, k_size=None): + self.strides = strides + self.dilations = dilations + self.data_format, self.padding = preprocess_3d_format(data_format, padding) + + def __call__(self, input, filters): + outputs = tf.nn.conv3d( + input=input, + filters=filters, + strides=self.strides, + padding=self.padding, + data_format=self.data_format, + dilations=self.dilations, + ) + return outputs + + +def conv3d(input, filters, strides, padding, data_format='NDHWC', dilations=None): + """ + Computes a 3-D convolution given 5-D input and filters tensors. + + Parameters + ---------- + input : tensor + Must be one of the following types: half, bfloat16, float32, float64. + Shape [batch, in_depth, in_height, in_width, in_channels]. + filters : tensor + Must have the same type as input. Shape [filter_depth, filter_height, filter_width, in_channels, out_channels]. + in_channels must match between input and filters. + strides : list of ints + A list of ints that has length >= 5. 1-D tensor of length 5. + The stride of the sliding window for each dimension of input. + Must have strides[0] = strides[4] = 1. + padding : string + A string from: "SAME", "VALID". The type of padding algorithm to use. + data_format : string + An optional string from: "NDHWC", "NCDHW". Defaults to "NDHWC". The data format of the input and output data. + With the default format "NDHWC", the data is stored in the order of: [batch, in_depth, in_height, in_width, in_channels]. + Alternatively, the format could be "NCDHW", the data storage order is: [batch, in_channels, in_depth, in_height, in_width]. + dilations : list of ints + Defaults to [1, 1, 1, 1, 1]. 1-D tensor of length 5. The dilation factor for each dimension of input. + If set to k > 1, there will be k-1 skipped cells between each filter element on that dimension. + The dimension order is determined by the value of data_format, see above for details. + Dilations in the batch and depth dimensions must be 1. + name : string + A name for the operation (optional). + + Returns + ------- + A Tensor. Has the same type as input. + """ + + data_format, padding = preprocess_3d_format(data_format, padding) + outputs = tf.nn.conv3d( + input=input, + filters=filters, + strides=strides, + padding=padding, + data_format=data_format, # 'NDHWC', + dilations=dilations, # [1, 1, 1, 1, 1], + # name=name, + ) + return outputs + + +def lrn(inputs, depth_radius, bias, alpha, beta): + """ + Local Response Normalization. + + Parameters + ---------- + inputs : tensor + Must be one of the following types: half, bfloat16, float32. 4-D. + depth_radius : int + Defaults to 5. 0-D. Half-width of the 1-D normalization window. + bias : float + Defaults to 1. An offset (usually positive to avoid dividing by 0). + alpha : float + Defaults to 1. A scale factor, usually positive. + beta : float + Defaults to 0.5. An exponent. + + Returns + ------- + A Tensor. Has the same type as input. + """ + + outputs = tf.nn.lrn(inputs, depth_radius=depth_radius, bias=bias, alpha=alpha, beta=beta) + return outputs + + +def moments(x, axes, shift=None, keepdims=False): + """ + Calculates the mean and variance of x. + + Parameters + ---------- + x : tensor + A Tensor + axes : list or ints + Axes along which to compute mean and variance. + shift : int + Not used in the current implementation. + keepdims : bool + produce moments with the same dimensionality as the input. + + Returns + ------- + Two Tensor objects: mean and variance. + """ + + outputs = tf.nn.moments(x, axes, shift, keepdims) + return outputs + + +class MaxPool(object): + + def __init__(self, ksize, strides, padding, data_format=None): + self.ksize = ksize + self.strides = strides + self.data_format = data_format + self.padding = padding + + def __call__(self, inputs): + if inputs.ndim == 3: + self.data_format, self.padding = preprocess_1d_format(data_format=self.data_format, padding=self.padding) + elif inputs.ndim == 4: + self.data_format, self.padding = preprocess_2d_format(data_format=self.data_format, padding=self.padding) + elif inputs.ndim == 5: + self.data_format, self.padding = preprocess_3d_format(data_format=self.data_format, padding=self.padding) + + outputs = tf.nn.max_pool( + input=inputs, ksize=self.ksize, strides=self.strides, padding=self.padding, data_format=self.data_format + ) + return outputs + + +def max_pool(input, ksize, strides, padding, data_format=None): + """ + Performs the max pooling on the input. + + Parameters + ---------- + input : tensor + Tensor of rank N+2, of shape [batch_size] + input_spatial_shape + [num_channels] if data_format does not start + with "NC" (default), or [batch_size, num_channels] + input_spatial_shape if data_format starts with "NC". + Pooling happens over the spatial dimensions only. + ksize : int or list of ints + An int or list of ints that has length 1, N or N+2. + The size of the window for each dimension of the input tensor. + strides : int or list of ints + An int or list of ints that has length 1, N or N+2. + The stride of the sliding window for each dimension of the input tensor. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + name : string + A name for the operation (optional). + + Returns + ------- + A Tensor of format specified by data_format. The max pooled output tensor. + """ + + if input.ndim == 3: + data_format, padding = preprocess_1d_format(data_format=data_format, padding=padding) + elif input.ndim == 4: + data_format, padding = preprocess_2d_format(data_format=data_format, padding=padding) + elif input.ndim == 5: + data_format, padding = preprocess_3d_format(data_format=data_format, padding=padding) + + outputs = tf.nn.max_pool(input=input, ksize=ksize, strides=strides, padding=padding, data_format=data_format) + return outputs + + +class AvgPool(object): + + def __init__(self, ksize, strides, padding, data_format=None): + self.ksize = ksize + self.strides = strides + self.data_format = data_format + self.padding = padding_format(padding) + + def __call__(self, inputs): + outputs = tf.nn.avg_pool( + input=inputs, ksize=self.ksize, strides=self.strides, padding=self.padding, data_format=self.data_format + ) + return outputs + + +def avg_pool(input, ksize, strides, padding): + """ + Performs the avg pooling on the input. + + Parameters + ---------- + input : tensor + Tensor of rank N+2, of shape [batch_size] + input_spatial_shape + [num_channels] + if data_format does not start with "NC" (default), or [batch_size, num_channels] + input_spatial_shape + if data_format starts with "NC". Pooling happens over the spatial dimensions only. + ksize : int or list of ints + An int or list of ints that has length 1, N or N+2. + The size of the window for each dimension of the input tensor. + strides : int or list of ints + An int or list of ints that has length 1, N or N+2. + The stride of the sliding window for each dimension of the input tensor. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + name : string + Optional name for the operation. + + Returns + ------- + A Tensor of format specified by data_format. The average pooled output tensor. + """ + + padding = padding_format(padding) + outputs = tf.nn.avg_pool( + input=input, + ksize=ksize, + strides=strides, + padding=padding, + ) + return outputs + + +def max_pool3d(input, ksize, strides, padding, data_format=None): + """ + Performs the max pooling on the input. + + Parameters + ---------- + input : tensor + A 5-D Tensor of the format specified by data_format. + ksize : int or list of ints + An int or list of ints that has length 1, 3 or 5. + The size of the window for each dimension of the input tensor. + strides : int or list of ints + An int or list of ints that has length 1, 3 or 5. + The stride of the sliding window for each dimension of the input tensor. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + "NDHWC", "NCDHW". Defaults to "NDHWC". The data format of the input and output data. + With the default format "NDHWC", the data is stored in the order of: [batch, in_depth, in_height, in_width, in_channels]. + Alternatively, the format could be "NCDHW", the data storage order is: [batch, in_channels, in_depth, in_height, in_width]. + name : string + A name for the operation (optional). + + Returns + ------- + A Tensor of format specified by data_format. The max pooled output tensor. + """ + + data_format, padding = preprocess_3d_format(data_format, padding) + outputs = tf.nn.max_pool3d( + input=input, + ksize=ksize, + strides=strides, + padding=padding, + data_format=data_format, + ) + return outputs + + +def avg_pool3d(input, ksize, strides, padding, data_format=None): + """ + Performs the average pooling on the input. + + Parameters + ---------- + input : tensor + A 5-D Tensor of shape [batch, height, width, channels] and type float32, float64, qint8, quint8, or qint32. + ksize : int or list of ints + An int or list of ints that has length 1, 3 or 5. The size of the window for each dimension of the input tensor. + strides : int or list of ints + An int or list of ints that has length 1, 3 or 5. + The stride of the sliding window for each dimension of the input tensor. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + 'NDHWC' and 'NCDHW' are supported. + name : string + Optional name for the operation. + + Returns + ------- + A Tensor with the same type as value. The average pooled output tensor. + """ + + data_format, padding = preprocess_3d_format(data_format, padding) + outputs = tf.nn.avg_pool3d( + input=input, + ksize=ksize, + strides=strides, + padding=padding, + data_format=data_format, + ) + return outputs + + +def pool(input, window_shape, pooling_type, strides=None, padding='VALID', data_format=None, dilations=None, name=None): + """ + Performs an N-D pooling operation. + + Parameters + ---------- + input : tensor + Tensor of rank N+2, of shape [batch_size] + input_spatial_shape + [num_channels] + if data_format does not start with "NC" (default), or [batch_size, num_channels] + input_spatial_shape + if data_format starts with "NC". Pooling happens over the spatial dimensions only. + window_shape : int + Sequence of N ints >= 1. + pooling_type : string + Specifies pooling operation, must be "AVG" or "MAX". + strides : ints + Sequence of N ints >= 1. Defaults to [1]*N. If any value of strides is > 1, then all values of dilation_rate must be 1. + padding : string + The padding algorithm, must be "SAME" or "VALID". Defaults to "SAME". + See the "returns" section of tf.ops.convolution for details. + data_format : string + Specifies whether the channel dimension of the input and output is the last dimension (default, or if data_format does not start with "NC"), + or the second dimension (if data_format starts with "NC"). + For N=1, the valid values are "NWC" (default) and "NCW". For N=2, the valid values are "NHWC" (default) and "NCHW". + For N=3, the valid values are "NDHWC" (default) and "NCDHW". + dilations : list of ints + Dilation rate. List of N ints >= 1. Defaults to [1]*N. If any value of dilation_rate is > 1, then all values of strides must be 1. + name : string + Optional. Name of the op. + + Returns + ------- + Tensor of rank N+2, of shape [batch_size] + output_spatial_shape + [num_channels] + """ + if pooling_type in ["MAX", "max"]: + pooling_type = "MAX" + elif pooling_type in ["AVG", "avg"]: + pooling_type = "AVG" + else: + raise ValueError('Unsupported pool_mode: ' + str(pooling_type)) + padding = padding_format(padding) + outputs = tf.nn.pool( + input=input, + window_shape=window_shape, + pooling_type=pooling_type, + strides=strides, + padding=padding, + data_format=data_format, + dilations=dilations, + name=name, + ) + return outputs + + +class DepthwiseConv2d(object): + + def __init__(self, strides, padding, data_format=None, dilations=None, ksize=None, channel_multiplier=1): + self.data_format, self.padding = preprocess_2d_format(data_format, padding) + self.strides = strides + self.dilations = dilations + + def __call__(self, input, filter): + outputs = tf.nn.depthwise_conv2d( + input=input, + filter=filter, + strides=self.strides, + padding=self.padding, + data_format=self.data_format, + dilations=self.dilations, + ) + return outputs + + +def depthwise_conv2d(input, filter, strides, padding, data_format=None, dilations=None, name=None): + """ + Depthwise 2-D convolution. + + Parameters + ---------- + input : tensor + 4-D with shape according to data_format. + filter : tensor + 4-D with shape [filter_height, filter_width, in_channels, channel_multiplier]. + strides : list + 1-D of size 4. The stride of the sliding window for each dimension of input. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + The data format for input. Either "NHWC" (default) or "NCHW". + dilations : list + 1-D of size 2. The dilation rate in which we sample input values across the height and width dimensions in atrous convolution. + If it is greater than 1, then all values of strides must be 1. + name : string + A name for this operation (optional). + + Returns + ------- + A 4-D Tensor with shape according to data_format. + E.g., for "NHWC" format, shape is [batch, out_height, out_width, in_channels * channel_multiplier]. + """ + + data_format, padding = preprocess_2d_format(data_format, padding) + outputs = tf.nn.depthwise_conv2d( + input=input, + filter=filter, + strides=strides, + padding=padding, + data_format=data_format, + dilations=dilations, + name=name, + ) + return outputs + + +class Conv1d_transpose(object): + + def __init__( + self, strides, padding, data_format='NWC', dilations=None, out_channel=None, k_size=None, in_channels=None + ): + self.strides = strides + self.dilations = dilations + self.data_format, self.padding = preprocess_1d_format(data_format, padding) + + def __call__(self, input, filters): + batch_size = input.shape[0] + if self.data_format == 'NWC': + w_axis, c_axis = 1, 2 + else: + w_axis, c_axis = 2, 1 + + input_shape = input.shape.as_list() + filters_shape = filters.shape.as_list() + input_w = input_shape[w_axis] + filters_w = filters_shape[0] + output_channels = filters_shape[1] + dilations_w = 1 + + if isinstance(self.strides, int): + strides_w = self.strides + else: + strides_list = list(self.strides) + strides_w = strides_list[w_axis] + + if self.dilations is not None: + if isinstance(self.dilations, int): + dilations_w = self.dilations + else: + dilations_list = list(self.dilations) + dilations_w = dilations_list[w_axis] + + filters_w = filters_w + (filters_w - 1) * (dilations_w - 1) + assert self.padding in {'SAME', 'VALID'} + if self.padding == 'VALID': + output_w = input_w * strides_w + max(filters_w - strides_w, 0) + elif self.padding == 'SAME': + output_w = input_w * strides_w + + if self.data_format == 'NCW': + output_shape = (batch_size, output_channels, output_w) + else: + output_shape = (batch_size, output_w, output_channels) + output_shape = tf.stack(output_shape) + outputs = tf.nn.conv1d_transpose( + input=input, + filters=filters, + output_shape=output_shape, + strides=self.strides, + padding=self.padding, + data_format=self.data_format, + dilations=self.dilations, + ) + return outputs + + +def conv1d_transpose( + input, filters, output_shape, strides, padding='SAME', data_format='NWC', dilations=None, name=None +): + """ + The transpose of conv1d. + + Parameters + ---------- + input : tensor + A 3-D Tensor of type float and shape [batch, in_width, in_channels] + for NWC data format or [batch, in_channels, in_width] for NCW data format. + filters : tensor + A 3-D Tensor with the same type as value and shape [filter_width, output_channels, in_channels]. + filter's in_channels dimension must match that of value. + output_shape : tensor + A 1-D Tensor, containing three elements, representing the output shape of the deconvolution op. + strides : list + An int or list of ints that has length 1 or 3. The number of entries by which the filter is moved right at each step. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + 'NWC' and 'NCW' are supported. + dilations : list + An int or list of ints that has length 1 or 3 which defaults to 1. + The dilation factor for each dimension of input. If set to k > 1, + there will be k-1 skipped cells between each filter element on that dimension. + Dilations in the batch and depth dimensions must be 1. + name : string + Optional name for the returned tensor. + + Returns + ------- + A Tensor with the same type as value. + """ + + data_format, padding = preprocess_1d_format(data_format, padding) + outputs = tf.nn.conv1d_transpose( + input=input, + filters=filters, + output_shape=output_shape, + strides=strides, + padding=padding, + data_format=data_format, + dilations=dilations, + name=name, + ) + return outputs + + +class Conv2d_transpose(object): + + def __init__( + self, strides, padding, data_format='NHWC', dilations=None, name=None, out_channel=None, k_size=None, + in_channels=None + ): + self.strides = strides + self.dilations = dilations + self.name = name + self.data_format, self.padding = preprocess_2d_format(data_format, padding) + + def __call__(self, input, filters): + if self.data_format == 'NHWC': + h_axis, w_axis = 1, 2 + else: + h_axis, w_axis = 2, 3 + + input_shape = input.shape.as_list() + filters_shape = filters.shape.as_list() + batch_size = input.shape[0] + input_h, input_w = input_shape[h_axis], input_shape[w_axis] + kernel_h, kernel_w = filters_shape[0], filters_shape[1] + output_channels = filters_shape[2] + dilations_h, dilations_w = 1, 1 + + if isinstance(self.strides, int): + strides_h = self.strides + strides_w = self.strides + else: + strides_list = list(self.strides) + if len(strides_list) != 4: + strides_h = strides_list[0] + strides_w = strides_list[1] + else: + strides_h = strides_list[h_axis] + strides_w = strides_list[w_axis] + + if self.dilations is not None: + if isinstance(self.dilations, int): + dilations_h = self.dilations + dilations_w = self.dilations + else: + dilations_list = list(self.dilations) + if len(dilations_list) != 4: + dilations_h = dilations_list[0] + dilations_w = dilations_list[1] + else: + dilations_h = dilations_list[h_axis] + dilations_w = dilations_list[w_axis] + + kernel_h = kernel_h + (kernel_h - 1) * (dilations_h - 1) + kernel_w = kernel_w + (kernel_w - 1) * (dilations_w - 1) + + assert self.padding in {'SAME', 'VALID'} + if self.padding == 'VALID': + output_h = input_h * strides_h + max(kernel_h - strides_h, 0) + output_w = input_w * strides_w + max(kernel_w - strides_w, 0) + elif self.padding == 'SAME': + output_h = input_h * strides_h + output_w = input_w * strides_w + + if self.data_format == 'NCHW': + out_shape = (batch_size, output_channels, output_h, output_w) + else: + out_shape = (batch_size, output_h, output_w, output_channels) + + output_shape = tf.stack(out_shape) + + outputs = tf.nn.conv2d_transpose( + input=input, filters=filters, output_shape=output_shape, strides=self.strides, padding=self.padding, + data_format=self.data_format, dilations=self.dilations, name=self.name + ) + return outputs + + +def conv2d_transpose( + input, filters, output_shape, strides, padding='SAME', data_format='NHWC', dilations=None, name=None +): + """ + The transpose of conv2d. + + Parameters + ---------- + input : tensor + A 4-D Tensor of type float and shape [batch, height, width, in_channels] + for NHWC data format or [batch, in_channels, height, width] for NCHW data format. + filters : tensor + A 4-D Tensor with the same type as input and shape [height, width, + output_channels, in_channels]. filter's in_channels dimension must match that of input. + output_shape : tensor + A 1-D Tensor representing the output shape of the deconvolution op. + strides : list + An int or list of ints that has length 1, 2 or 4. The stride of the sliding window for each dimension of input. + If a single value is given it is replicated in the H and W dimension. + By default the N and C dimensions are set to 0. + The dimension order is determined by the value of data_format, see below for details. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + 'NHWC' and 'NCHW' are supported. + dilations : list + An int or list of ints that has length 1, 2 or 4, defaults to 1. + name : string + Optional name for the returned tensor. + + Returns + ------- + A Tensor with the same type as input. + """ + + data_format, padding = preprocess_2d_format(data_format, padding) + outputs = tf.nn.conv2d_transpose( + input=input, + filters=filters, + output_shape=output_shape, + strides=strides, + padding=padding, + data_format=data_format, + dilations=dilations, + name=name, + ) + return outputs + + +class Conv3d_transpose(object): + + def __init__( + self, strides, padding, data_format='NDHWC', dilations=None, name=None, out_channel=None, k_size=None, + in_channels=None + ): + self.strides = strides + self.dilations = dilations + self.name = name + self.out_channel = out_channel + self.data_format, self.padding = preprocess_3d_format(data_format, padding) + + def __call__(self, input, filters): + if self.data_format == 'NDHWC': + d_axis, h_axis, w_axis = 1, 2, 3 + else: + d_axis, h_axis, w_axis = 2, 3, 4 + + input_shape = input.shape.as_list() + filters_shape = filters.shape.as_list() + batch_size = input_shape[0] + input_d, input_h, input_w = input_shape[d_axis], input_shape[h_axis], input_shape[w_axis] + kernel_d, kernel_h, kernel_w = filters_shape[0], filters_shape[1], filters_shape[2] + dilations_d, dilations_h, dilations_w = 1, 1, 1 + + if isinstance(self.strides, int): + strides_d, strides_h, strides_w = self.strides + else: + strides_list = list(self.strides) + if len(strides_list) != 5: + strides_d, strides_h, strides_w = \ + strides_list[0], \ + strides_list[1], \ + strides_list[2] + else: + strides_d, strides_h, strides_w = \ + strides_list[d_axis], \ + strides_list[h_axis], \ + strides_list[w_axis] + + if self.dilations is not None: + if isinstance(self.dilations, int): + dilations_d, dilations_h, dilations_w = self.dilations + else: + dilations_list = list(self.dilations) + if len(dilations_list) != 5: + dilations_d, dilations_h, dilations_w = \ + dilations_list[0], \ + dilations_list[1], \ + dilations_list[2] + else: + dilations_d, dilations_h, dilations_w = \ + dilations_list[d_axis],\ + dilations_list[h_axis], \ + dilations_list[w_axis] + + assert self.padding in {'VALID', 'SAME'} + + kernel_d = kernel_d + (kernel_d - 1) * (dilations_d - 1) + kernel_h = kernel_h + (kernel_h - 1) * (dilations_h - 1) + kernel_w = kernel_w + (kernel_w - 1) * (dilations_w - 1) + + if self.padding == 'VALID': + output_d = input_d * strides_d + max(kernel_d - strides_d, 0) + output_h = input_h * strides_h + max(kernel_h - strides_h, 0) + output_w = input_w * strides_w + max(kernel_w - strides_w, 0) + elif self.padding == 'SAME': + output_d = input_d * strides_d + output_h = input_h * strides_h + output_w = input_w * strides_w + + if self.data_format == 'NDHWC': + output_shape = (batch_size, output_d, output_h, output_w, self.out_channel) + else: + output_shape = (batch_size, self.out_channel, output_d, output_h, output_w) + + output_shape = tf.stack(output_shape) + outputs = tf.nn.conv3d_transpose( + input=input, filters=filters, output_shape=output_shape, strides=self.strides, padding=self.padding, + data_format=self.data_format, dilations=self.dilations, name=self.name + ) + + return outputs + + +def conv3d_transpose( + input, filters, output_shape, strides, padding='SAME', data_format='NDHWC', dilations=None, name=None +): + """ + The transpose of conv3d. + + Parameters + ---------- + input : tensor + A 5-D Tensor of type float and shape [batch, height, width, in_channels] for + NHWC data format or [batch, in_channels, height, width] for NCHW data format. + filters : tensor + A 5-D Tensor with the same type as value and shape [height, width, output_channels, in_channels]. + filter's in_channels dimension must match that of value. + output_shape : tensor + A 1-D Tensor representing the output shape of the deconvolution op. + strides : list + An int or list of ints that has length 1, 3 or 5. + padding : string + 'VALID' or 'SAME'. The padding algorithm. See the "returns" section of tf.ops.convolution for details. + data_format : string + 'NDHWC' and 'NCDHW' are supported. + dilations : list of ints + An int or list of ints that has length 1, 3 or 5, defaults to 1. + name : string + Optional name for the returned tensor. + + Returns + ------- + A Tensor with the same type as value. + """ + + data_format, padding = preprocess_3d_format(data_format, padding) + outputs = tf.nn.conv3d_transpose( + input=input, filters=filters, output_shape=output_shape, strides=strides, padding=padding, + data_format=data_format, dilations=dilations, name=name + ) + return outputs + + +def depthwise_conv2d(input, filters, strides, padding='SAME', data_format='NHWC', dilations=None, name=None): + """ + Depthwise 2-D convolution. + + Parameters + ---------- + input : tensor + 4-D with shape according to data_format. + filters : tensor + 4-D with shape [filter_height, filter_width, in_channels, channel_multiplier]. + strides : tuple + 1-D of size 4. The stride of the sliding window for each dimension of input. + padding : string + 'VALID' or 'SAME' + data_format : string + "NHWC" (default) or "NCHW". + dilations : tuple + The dilation rate in which we sample input values across the height and width dimensions in atrous convolution. + If it is greater than 1, then all values of strides must be 1. + name : string + A name for this operation (optional). + + Returns + ------- + A 4-D Tensor with shape according to data_format. + """ + + data_format, padding = preprocess_2d_format(data_format, padding) + outputs = tf.nn.depthwise_conv2d( + input=input, + filter=filters, + strides=strides, + padding=padding, + data_format=data_format, + dilations=dilations, + name=name, + ) + return outputs + + +def _to_channel_first_bias(b): + """Reshape [c] to [c, 1, 1].""" + channel_size = int(b.shape[0]) + new_shape = (channel_size, 1, 1) + return tf.reshape(b, new_shape) + + +def _bias_scale(x, b, data_format): + """The multiplication counter part of tf.nn.bias_add.""" + if data_format == 'NHWC': + return x * b + elif data_format == 'NCHW': + return x * _to_channel_first_bias(b) + else: + raise ValueError('invalid data_format: %s' % data_format) + + +def _bias_add(x, b, data_format): + """Alternative implementation of tf.nn.bias_add which is compatiable with tensorRT.""" + if data_format == 'NHWC': + return tf.add(x, b) + elif data_format == 'NCHW': + return tf.add(x, _to_channel_first_bias(b)) + else: + raise ValueError('invalid data_format: %s' % data_format) + + +def batch_normalization(x, mean, variance, offset, scale, variance_epsilon, data_format, name=None): + """Data Format aware version of tf.nn.batch_normalization.""" + if data_format == 'channels_last': + mean = tf.reshape(mean, [1] * (len(x.shape) - 1) + [-1]) + variance = tf.reshape(variance, [1] * (len(x.shape) - 1) + [-1]) + offset = tf.reshape(offset, [1] * (len(x.shape) - 1) + [-1]) + scale = tf.reshape(scale, [1] * (len(x.shape) - 1) + [-1]) + elif data_format == 'channels_first': + mean = tf.reshape(mean, [1] + [-1] + [1] * (len(x.shape) - 2)) + variance = tf.reshape(variance, [1] + [-1] + [1] * (len(x.shape) - 2)) + offset = tf.reshape(offset, [1] + [-1] + [1] * (len(x.shape) - 2)) + scale = tf.reshape(scale, [1] + [-1] + [1] * (len(x.shape) - 2)) + else: + raise ValueError('invalid data_format: %s' % data_format) + + with ops.name_scope(name, 'batchnorm', [x, mean, variance, scale, offset]): + inv = math_ops.rsqrt(variance + variance_epsilon) + if scale is not None: + inv *= scale + + a = math_ops.cast(inv, x.dtype) + b = math_ops.cast(offset - mean * inv if offset is not None else -mean * inv, x.dtype) + # Return a * x + b with customized data_format. + # Currently TF doesn't have bias_scale, and tensorRT has bug in converting tf.nn.bias_add + # So we reimplemted them to allow make the model work with tensorRT. + # See https://github.com/tensorlayer/openpose-plus/issues/75 for more details. + # df = {'channels_first': 'NCHW', 'channels_last': 'NHWC'} + # return _bias_add(_bias_scale(x, a, df[data_format]), b, df[data_format]) + return a * x + b + + +class BatchNorm(object): + """ + The :class:`BatchNorm` is a batch normalization layer for both fully-connected and convolution outputs. + See ``tf.nn.batch_normalization`` and ``tf.nn.moments``. + + Parameters + ---------- + decay : float + A decay factor for `ExponentialMovingAverage`. + Suggest to use a large value for large dataset. + epsilon : float + Eplison. + act : activation function + The activation function of this layer. + is_train : boolean + Is being used for training or inference. + beta_init : initializer or None + The initializer for initializing beta, if None, skip beta. + Usually you should not skip beta unless you know what happened. + gamma_init : initializer or None + The initializer for initializing gamma, if None, skip gamma. + When the batch normalization layer is use instead of 'biases', or the next layer is linear, this can be + disabled since the scaling can be done by the next layer. see `Inception-ResNet-v2 `__ + moving_mean_init : initializer or None + The initializer for initializing moving mean, if None, skip moving mean. + moving_var_init : initializer or None + The initializer for initializing moving var, if None, skip moving var. + num_features: int + Number of features for input tensor. Useful to build layer if using BatchNorm1d, BatchNorm2d or BatchNorm3d, + but should be left as None if using BatchNorm. Default None. + data_format : str + channels_last 'channel_last' (default) or channels_first. + name : None or str + A unique layer name. + + Examples + --------- + With TensorLayer + + >>> net = tl.layers.Input([None, 50, 50, 32], name='input') + >>> net = tl.layers.BatchNorm()(net) + + Notes + ----- + The :class:`BatchNorm` is universally suitable for 3D/4D/5D input in static model, but should not be used + in dynamic model where layer is built upon class initialization. So the argument 'num_features' should only be used + for subclasses :class:`BatchNorm1d`, :class:`BatchNorm2d` and :class:`BatchNorm3d`. All the three subclasses are + suitable under all kinds of conditions. + + References + ---------- + - `Source `__ + - `stackoverflow `__ + + """ + + def __init__( + self, decay=0.9, epsilon=0.00001, beta=None, gamma=None, moving_mean=None, moving_var=None, num_features=None, + data_format='channels_last', is_train=False + ): + self.decay = decay + self.epsilon = epsilon + self.data_format = data_format + self.beta = beta + self.gamma = gamma + self.moving_mean = moving_mean + self.moving_var = moving_var + self.num_features = num_features + self.is_train = is_train + self.axes = None + + if self.decay < 0.0 or 1.0 < self.decay: + raise ValueError("decay should be between 0 to 1") + + def _get_param_shape(self, inputs_shape): + if self.data_format == 'channels_last': + axis = -1 + elif self.data_format == 'channels_first': + axis = 1 + else: + raise ValueError('data_format should be either %s or %s' % ('channels_last', 'channels_first')) + + channels = inputs_shape[axis] + params_shape = [channels] + + return params_shape + + def _check_input_shape(self, inputs): + if inputs.ndim <= 1: + raise ValueError('expected input at least 2D, but got {}D input'.format(inputs.ndim)) + + def __call__(self, inputs): + self._check_input_shape(inputs) + self.channel_axis = len(inputs.shape) - 1 if self.data_format == 'channels_last' else 1 + if self.axes is None: + self.axes = [i for i in range(len(inputs.shape)) if i != self.channel_axis] + + mean, var = tf.nn.moments(inputs, self.axes, keepdims=False) + if self.is_train: + # update moving_mean and moving_var + self.moving_mean = moving_averages.assign_moving_average( + self.moving_mean, mean, self.decay, zero_debias=False + ) + self.moving_var = moving_averages.assign_moving_average(self.moving_var, var, self.decay, zero_debias=False) + outputs = batch_normalization(inputs, mean, var, self.beta, self.gamma, self.epsilon, self.data_format) + else: + outputs = batch_normalization( + inputs, self.moving_mean, self.moving_var, self.beta, self.gamma, self.epsilon, self.data_format + ) + + return outputs diff --git a/tensorlayer/cli/__init__.py b/tensorlayer/cli/__init__.py new file mode 100644 index 0000000..e7522d7 --- /dev/null +++ b/tensorlayer/cli/__init__.py @@ -0,0 +1,3 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +"""The tensorlayer.cli module provides a command-line tool for some common tasks.""" diff --git a/tensorlayer/cli/__main__.py b/tensorlayer/cli/__main__.py new file mode 100644 index 0000000..838038b --- /dev/null +++ b/tensorlayer/cli/__main__.py @@ -0,0 +1,17 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import argparse + +from tensorlayer.cli import train + +if __name__ == "__main__": + parser = argparse.ArgumentParser(prog='tl') + subparsers = parser.add_subparsers(dest='cmd') + train_parser = subparsers.add_parser('train', help='train a model using multiple local GPUs or CPUs.') + train.build_arg_parser(train_parser) + args = parser.parse_args() + if args.cmd == 'train': + train.main(args) + else: + parser.print_help() diff --git a/tensorlayer/cli/train.py b/tensorlayer/cli/train.py new file mode 100644 index 0000000..d63b7f5 --- /dev/null +++ b/tensorlayer/cli/train.py @@ -0,0 +1,171 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +""" +tl train +======== + +(Alpha release - usage might change later) + +The tensorlayer.cli.train module provides the ``tl train`` subcommand. +It helps the user bootstrap a TensorFlow/TensorLayer program for distributed training +using multiple GPU cards or CPUs on a computer. + +You need to first setup the `CUDA_VISIBLE_DEVICES `_ +to tell ``tl train`` which GPUs are available. If the CUDA_VISIBLE_DEVICES is not given, +``tl train`` would try best to discover all available GPUs. + +In distribute training, each TensorFlow program needs a TF_CONFIG environment variable to describe +the cluster. It also needs a master daemon to +monitor all trainers. ``tl train`` is responsible +for automatically managing these two tasks. + +Usage +----- + +tl train [-h] [-p NUM_PSS] [-c CPU_TRAINERS] [args [args ...]] + +.. code-block:: bash + + # example of using GPU 0 and 1 for training mnist + CUDA_VISIBLE_DEVICES="0,1" + tl train example/tutorial_mnist_distributed.py + + # example of using CPU trainers for inception v3 + tl train -c 16 example/tutorial_imagenet_inceptionV3_distributed.py + + # example of using GPU trainers for inception v3 with customized arguments + # as CUDA_VISIBLE_DEVICES is not given, tl would try to discover all available GPUs + tl train example/tutorial_imagenet_inceptionV3_distributed.py -- --batch_size 16 + + +Command-line Arguments +---------------------- + +- ``file``: python file path. + +- ``NUM_PSS`` : The number of parameter servers. + +- ``CPU_TRAINERS``: The number of CPU trainers. + + It is recommended that ``NUM_PSS + CPU_TRAINERS <= cpu count`` + +- ``args``: Any parameter after ``--`` would be passed to the python program. + + +Notes +----- +A parallel training program would require multiple parameter servers +to help parallel trainers to exchange intermediate gradients. +The best number of parameter servers is often proportional to the +size of your model as well as the number of CPUs available. +You can control the number of parameter servers using the ``-p`` parameter. + +If you have a single computer with massive CPUs, you can use the ``-c`` parameter +to enable CPU-only parallel training. +The reason we are not supporting GPU-CPU co-training is because GPU and +CPU are running at different speeds. Using them together in training would +incur stragglers. + +""" + +import argparse +import json +import multiprocessing +import os +import platform +import re +import subprocess +import sys + +PORT_BASE = 10000 + + +def _get_gpu_ids(): + if 'CUDA_VISIBLE_DEVICES' in os.environ: + return [int(x) for x in os.environ.get('CUDA_VISIBLE_DEVICES', '').split(',')] + if platform.system() in ['Darwin', 'Linux']: + return [int(d.replace('nvidia', '')) for d in os.listdir('/dev') if re.match('^nvidia\d+$', d)] + else: + print('Please set CUDA_VISIBLE_DEVICES (see http://acceleware.com/blog/cudavisibledevices-masking-gpus)') + return [] + + +GPU_IDS = _get_gpu_ids() + + +def create_tf_config(cluster_spec, task_type, task_index): + return { + 'cluster': cluster_spec, + 'task': { + 'type': task_type, + 'index': task_index + }, + } + + +def create_tf_jobs(cluster_spec, prog, args): + gpu_assignment = dict((('worker', idx), gpu_idx) for (idx, gpu_idx) in enumerate(GPU_IDS)) + for job_type in cluster_spec: + for task_index in range(len(cluster_spec[job_type])): + new_env = os.environ.copy() + new_env.update( + { + 'CUDA_VISIBLE_DEVICES': str(gpu_assignment.get((job_type, task_index), '')), + 'TF_CONFIG': json.dumps(create_tf_config(cluster_spec, job_type, task_index)), + } + ) + yield subprocess.Popen(['python3', prog] + args, env=new_env) + + +def validate_arguments(args): + if args.num_pss < 1: + print('Value error: must have ore than one parameter servers.') + exit(1) + + if not GPU_IDS: + num_cpus = multiprocessing.cpu_count() + if args.cpu_trainers > num_cpus: + print('Value error: there are %s available CPUs but you are requiring %s.' % (num_cpus, args.cpu_trainers)) + exit(1) + + if not os.path.isfile(args.file): + print('Value error: model trainning file does not exist') + exit(1) + + +def main(args): + validate_arguments(args) + num_workers = len(GPU_IDS) if GPU_IDS else args.cpu_trainers + print('Using program %s with args %s' % (args.file, ' '.join(args.args))) + print('Using %d workers, %d parameter servers, %d GPUs.' % (num_workers, args.num_pss, len(GPU_IDS))) + cluster_spec = { + 'ps': ['localhost: %d' % (PORT_BASE + i) for i in range(args.num_pss)], + 'worker': ['localhost: %d' % (PORT_BASE + args.num_pss + i) for i in range(num_workers)] + } + processes = list(create_tf_jobs(cluster_spec, args.file, args.args)) + try: + print('Press ENTER to exit the training ...') + sys.stdin.readline() + except KeyboardInterrupt: # https://docs.python.org/3/library/exceptions.html#KeyboardInterrupt + print('Keyboard interrupt received') + finally: + print('stopping all subprocesses ...') + for p in processes: + p.kill() + for p in processes: + p.wait() + print('END') + + +def build_arg_parser(parser): + parser.add_argument('-p', '--pss', dest='num_pss', type=int, default=1, help='number of parameter servers') + parser.add_argument('-c', '--cpu_trainers', dest='cpu_trainers', type=int, default=1, help='number of CPU trainers') + parser.add_argument('file', help='model trainning file path') + parser.add_argument('args', nargs='*', type=str, help='arguments to ') + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + build_arg_parser(parser) + args = parser.parse_args() + main(args) diff --git a/tensorlayer/cost/__init__.py b/tensorlayer/cost/__init__.py new file mode 100644 index 0000000..3ca7c2c --- /dev/null +++ b/tensorlayer/cost/__init__.py @@ -0,0 +1,15 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +from tensorlayer.backend import BACKEND + +if BACKEND == 'tensorflow': + from .tensorflow_cost import * +elif BACKEND == 'mindspore': + from .mindspore_cost import * +elif BACKEND == 'dragon': + pass +elif BACKEND == 'paddle': + from .paddle_cost import * +else: + raise NotImplementedError("This backend is not supported") diff --git a/tensorlayer/cost/mindspore_cost.py b/tensorlayer/cost/mindspore_cost.py new file mode 100644 index 0000000..694c5fc --- /dev/null +++ b/tensorlayer/cost/mindspore_cost.py @@ -0,0 +1,763 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +from mindspore import nn +from mindspore.nn import Cell +import mindspore.ops as P + +__all__ = [ + 'cross_entropy', + 'sigmoid_cross_entropy', + 'binary_cross_entropy', + 'mean_squared_error', + 'normalized_mean_square_error', + 'absolute_difference_error', + 'dice_coe', + 'dice_hard_coe', + 'iou_coe', + 'cross_entropy_seq', + 'cross_entropy_seq_with_mask', + 'cosine_similarity', + 'li_regularizer', + 'lo_regularizer', + 'maxnorm_regularizer', + 'maxnorm_o_regularizer', + 'maxnorm_i_regularizer', +] + +cross_entropy = nn.SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean') + + +def sigmoid_cross_entropy(output, target, name=None): + """Sigmoid cross-entropy operation, see ``tf.ops.sigmoid_cross_entropy_with_logits``. + + Parameters + ---------- + output : Tensor + A batch of distribution with shape: [batch_size, num of classes]. + target : Tensor + A batch of index with shape: [batch_size, ]. + name : string + Name of this loss. + + """ + outputs = P.ReduceMean(cross_entropy(output, target)) + return outputs + + +def binary_cross_entropy(output, target, epsilon=1e-8, name='bce_loss'): + """Binary cross entropy operation. + + Parameters + ---------- + output : Tensor + Tensor with type of `float32` or `float64`. + target : Tensor + The target distribution, format the same with `output`. + epsilon : float + A small value to avoid output to be zero. + name : str + An optional name to attach to this function. + + References + ----------- + - `ericjang-DRAW `__ + + """ + + # return tf.reduce_mean( + # tf.reduce_sum( + # -(target * tf.math.log(output + epsilon) + (1. - target) * tf.math.log(1. - output + epsilon)), axis=1 + # ), name=name + # ) + raise NotImplementedError("Not Implemented.") + + +def mean_squared_error(output, target, is_mean=False, axis=-1, name="mean_squared_error"): + """Return the TensorFlow expression of mean-square-error (L2) of two batch of data. + + Parameters + ---------- + output : Tensor + 2D, 3D or 4D tensor i.e. [batch_size, n_feature], [batch_size, height, width] or [batch_size, height, width, channel]. + target : Tensor + The target distribution, format the same with `output`. + is_mean : boolean + Whether compute the mean or sum for each example. + - If True, use ``tf.reduce_mean`` to compute the loss between one target and predict data. + - If False, use ``tf.reduce_sum`` (default). + axis : int or list of int + The dimensions to reduce. + name : str + An optional name to attach to this function. + + References + ------------ + - `Wiki Mean Squared Error `__ + + """ + # with tf.name_scope(name): + # if len(output.shape) == 2: # [batch_size, n_feature] + # axis = 1 + # elif len(output.shape) == 3: # [batch_size, w, h] + # axis = [1, 2] + # elif len(output.shape) == 4: # [batch_size, w, h, c] + # axis = [1, 2, 3] + # else: + # raise Exception("Unknow dimension") + + return nn.MSELoss()(output, target) + + +def normalized_mean_square_error(output, target, axis=-1, name="normalized_mean_squared_error_loss"): + """Return the TensorFlow expression of normalized mean-square-error of two distributions. + + Parameters + ---------- + output : Tensor + 2D, 3D or 4D tensor i.e. [batch_size, n_feature], [batch_size, height, width] or [batch_size, height, width, channel]. + target : Tensor + The target distribution, format the same with `output`. + axis : int or list of int + The dimensions to reduce. + name : str + An optional name to attach to this function. + + """ + # with tf.name_scope("normalized_mean_squared_error_loss"): + # nmse_a = tf.sqrt(tf.reduce_sum(tf.math.squared_difference(output, target), axis=axis)) + # nmse_b = tf.sqrt(tf.reduce_sum(tf.square(target), axis=axis)) + # nmse = tf.reduce_mean(nmse_a / nmse_b, name=name) + raise NotImplementedError("Not Implemented.") + + +def absolute_difference_error(output, target, is_mean=False, axis=-1, name="absolute_difference_error_loss"): + """Return the TensorFlow expression of absolute difference error (L1) of two batch of data. + + Parameters + ---------- + output : Tensor + 2D, 3D or 4D tensor i.e. [batch_size, n_feature], [batch_size, height, width] or [batch_size, height, width, channel]. + target : Tensor + The target distribution, format the same with `output`. + is_mean : boolean + Whether compute the mean or sum for each example. + - If True, use ``tf.reduce_mean`` to compute the loss between one target and predict data. + - If False, use ``tf.reduce_sum`` (default). + axis : int or list of int + The dimensions to reduce. + name : str + An optional name to attach to this function. + + """ + + # if is_mean: + # loss = tf.reduce_mean(tf.reduce_mean(tf.abs(output - target), axis), name=name) + # else: + # loss = tf.reduce_mean(tf.reduce_sum(tf.abs(output - target), axis), name=name) + raise NotImplementedError("Not Implemented.") + + +def dice_coe(output, target, loss_type='jaccard', axis=(1, 2, 3), smooth=1e-5): + """Soft dice (Sørensen or Jaccard) coefficient for comparing the similarity + of two batch of data, usually be used for binary image segmentation + i.e. labels are binary. The coefficient between 0 to 1, 1 means totally match. + + Parameters + ----------- + output : Tensor + A distribution with shape: [batch_size, ....], (any dimensions). + target : Tensor + The target distribution, format the same with `output`. + loss_type : str + ``jaccard`` or ``sorensen``, default is ``jaccard``. + axis : tuple of int + All dimensions are reduced, default ``[1,2,3]``. + smooth : float + This small value will be added to the numerator and denominator. + - If both output and target are empty, it makes sure dice is 1. + - If either output or target are empty (all pixels are background), dice = ```smooth/(small_value + smooth)``, then if smooth is very small, dice close to 0 (even the image values lower than the threshold), so in this case, higher smooth can have a higher dice. + + Examples + --------- + >>> import tensorlayer as tl + >>> outputs = tl.act.pixel_wise_softmax(outputs) + >>> dice_loss = 1 - tl.cost.dice_coe(outputs, y_) + + References + ----------- + - `Wiki-Dice `__ + + """ + # inse = tf.reduce_sum(output * target, axis=axis) + # if loss_type == 'jaccard': + # l = tf.reduce_sum(output * output, axis=axis) + # r = tf.reduce_sum(target * target, axis=axis) + # elif loss_type == 'sorensen': + # l = tf.reduce_sum(output, axis=axis) + # r = tf.reduce_sum(target, axis=axis) + # else: + # raise Exception("Unknow loss_type") + # dice = (2. * inse + smooth) / (l + r + smooth) + # ## + # dice = tf.reduce_mean(dice, name='dice_coe') + raise NotImplementedError("Not Implemented.") + + +def dice_hard_coe(output, target, threshold=0.5, axis=(1, 2, 3), smooth=1e-5): + """Non-differentiable Sørensen–Dice coefficient for comparing the similarity + of two batch of data, usually be used for binary image segmentation i.e. labels are binary. + The coefficient between 0 to 1, 1 if totally match. + + Parameters + ----------- + output : tensor + A distribution with shape: [batch_size, ....], (any dimensions). + target : tensor + The target distribution, format the same with `output`. + threshold : float + The threshold value to be true. + axis : tuple of integer + All dimensions are reduced, default ``(1,2,3)``. + smooth : float + This small value will be added to the numerator and denominator, see ``dice_coe``. + + References + ----------- + - `Wiki-Dice `__ + + """ + # output = tf.cast(output > threshold, dtype=tf.float32) + # target = tf.cast(target > threshold, dtype=tf.float32) + # inse = tf.reduce_sum(tf.multiply(output, target), axis=axis) + # l = tf.reduce_sum(output, axis=axis) + # r = tf.reduce_sum(target, axis=axis) + # hard_dice = (2. * inse + smooth) / (l + r + smooth) + # ## + # hard_dice = tf.reduce_mean(hard_dice, name='hard_dice') + raise NotImplementedError("Not Implemented.") + + +def iou_coe(output, target, threshold=0.5, axis=(1, 2, 3), smooth=1e-5): + """Non-differentiable Intersection over Union (IoU) for comparing the + similarity of two batch of data, usually be used for evaluating binary image segmentation. + The coefficient between 0 to 1, and 1 means totally match. + + Parameters + ----------- + output : tensor + A batch of distribution with shape: [batch_size, ....], (any dimensions). + target : tensor + The target distribution, format the same with `output`. + threshold : float + The threshold value to be true. + axis : tuple of integer + All dimensions are reduced, default ``(1,2,3)``. + smooth : float + This small value will be added to the numerator and denominator, see ``dice_coe``. + + Notes + ------ + - IoU cannot be used as training loss, people usually use dice coefficient for training, IoU and hard-dice for evaluating. + + """ + # pre = tf.cast(output > threshold, dtype=tf.float32) + # truth = tf.cast(target > threshold, dtype=tf.float32) + # inse = tf.reduce_sum(tf.multiply(pre, truth), axis=axis) # AND + # union = tf.reduce_sum(tf.cast(tf.add(pre, truth) >= 1, dtype=tf.float32), axis=axis) # OR + # batch_iou = (inse + smooth) / (union + smooth) + # iou = tf.reduce_mean(batch_iou, name='iou_coe') + raise NotImplementedError("Not Implemented.") + + +def sequence_loss_by_example( + logits, targets, weights, average_across_timesteps=True, softmax_loss_function=None, name=None +): + """Weighted cross-entropy loss for a sequence of logits (per example). see original tensorflow code : + + + Parameters + ---------- + logits: List + List of 2D Tensors of shape [batch_size x num_decoder_symbols]. + targets: List + List of 1D batch-sized int32 Tensors of the same length as logits. + weights: List + List of 1D batch-sized float-Tensors of the same length as logits. + average_across_timesteps: Boolean + If set, divide the returned cost by the total label weight. + softmax_loss_function: None or Function + Function (labels, logits) -> loss-batch to be used instead of the standard softmax (the default if this is None). + **Note that to avoid confusion, it is required for the function to accept named arguments.** + name: None or str + Optional name for this operation, default: "sequence_loss_by_example". + + Returns + ------- + 1D batch-sized float Tensor: The log-perplexity for each sequence. + + Raises + ------ + ValueError: If len(logits) is different from len(targets) or len(weights). + + """ + # if len(targets) != len(logits) or len(weights) != len(logits): + # raise ValueError( + # "Lengths of logits, weights, and targets must be the same " + # "%d, %d, %d." % (len(logits), len(weights), len(targets)) + # ) + # with ops.name_scope(name, "sequence_loss_by_example", logits + targets + weights): + # log_perp_list = [] + # for logit, target, weight in zip(logits, targets, weights): + # if softmax_loss_function is None: + # # TODO(irving,ebrevdo): This reshape is needed because + # # sequence_loss_by_example is called with scalars sometimes, which + # # violates our general scalar strictness policy. + # target = array_ops.reshape(target, [-1]) + # crossent = nn_ops.sparse_softmax_cross_entropy_with_logits(labels=target, logits=logit) + # else: + # crossent = softmax_loss_function(labels=target, logits=logit) + # log_perp_list.append(crossent * weight) + # log_perps = math_ops.add_n(log_perp_list) + # if average_across_timesteps: + # total_size = math_ops.add_n(weights) + # total_size += 1e-12 # Just to avoid division by 0 for all-0 weights. + # log_perps /= total_size + raise NotImplementedError("Not Implemented.") + + +def cross_entropy_seq(logits, target_seqs, batch_size=None): + """Returns the expression of cross-entropy of two sequences, implement + softmax internally. Normally be used for fixed length RNN outputs, see `PTB example `__. + + Parameters + ---------- + logits : Tensor + 2D tensor with shape of `[batch_size * n_steps, n_classes]`. + target_seqs : Tensor + The target sequence, 2D tensor `[batch_size, n_steps]`, if the number of step is dynamic, please use ``tl.cost.cross_entropy_seq_with_mask`` instead. + batch_size : None or int. + Whether to divide the cost by batch size. + - If integer, the return cost will be divided by `batch_size`. + - If None (default), the return cost will not be divided by anything. + + Examples + -------- + >>> import tensorlayer as tl + >>> # see `PTB example `__.for more details + >>> # outputs shape : (batch_size * n_steps, n_classes) + >>> # targets shape : (batch_size, n_steps) + >>> cost = tl.cost.cross_entropy_seq(outputs, targets) + + """ + # sequence_loss_by_example_fn = sequence_loss_by_example + # + # loss = sequence_loss_by_example_fn( + # [logits], [tf.reshape(target_seqs, [-1])], [tf.ones_like(tf.reshape(target_seqs, [-1]), dtype=tf.float32)] + # ) + # # [tf.ones([batch_size * num_steps])]) + # cost = tf.reduce_sum(loss) # / batch_size + # if batch_size is not None: + # cost = cost / batch_size + raise NotImplementedError("Not Implemented.") + + +def cross_entropy_seq_with_mask(logits, target_seqs, input_mask, return_details=False, name=None): + """Returns the expression of cross-entropy of two sequences, implement + softmax internally. Normally be used for Dynamic RNN with Synced sequence input and output. + + Parameters + ----------- + logits : Tensor + 2D tensor with shape of [batch_size * ?, n_classes], `?` means dynamic IDs for each example. + - Can be get from `DynamicRNNLayer` by setting ``return_seq_2d`` to `True`. + target_seqs : Tensor + int of tensor, like word ID. [batch_size, ?], `?` means dynamic IDs for each example. + input_mask : Tensor + The mask to compute loss, it has the same size with `target_seqs`, normally 0 or 1. + return_details : boolean + Whether to return detailed losses. + - If False (default), only returns the loss. + - If True, returns the loss, losses, weights and targets (see source code). + + Examples + -------- + >>> import tensorlayer as tl + >>> import tensorflow as tf + >>> import numpy as np + >>> batch_size = 64 + >>> vocab_size = 10000 + >>> embedding_size = 256 + >>> ni = tl.layers.Input([batch_size, None], dtype=tf.int64) + >>> net = tl.layers.Embedding( + ... vocabulary_size = vocab_size, + ... embedding_size = embedding_size, + ... name = 'seq_embedding')(ni) + >>> net = tl.layers.RNN( + ... cell =tf.keras.layers.LSTMCell(units=embedding_size, dropout=0.1), + ... return_seq_2d = True, + ... name = 'dynamicrnn')(net) + >>> net = tl.layers.Dense(n_units=vocab_size, name="output")(net) + >>> model = tl.models.Model(inputs=ni, outputs=net) + >>> input_seqs = np.random.randint(0, 10, size=(batch_size, 10), dtype=np.int64) + >>> target_seqs = np.random.randint(0, 10, size=(batch_size, 10), dtype=np.int64) + >>> input_mask = np.random.randint(0, 2, size=(batch_size, 10), dtype=np.int64) + >>> outputs = model(input_seqs, is_train=True) + >>> loss = tl.cost.cross_entropy_seq_with_mask(outputs, target_seqs, input_mask) + + """ + # targets = tf.reshape(target_seqs, [-1]) # to one vector + # weights = tf.cast(tf.reshape(input_mask, [-1]), dtype=tf.float32) # to one vector like targets + # losses = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits, labels=targets, name=name) * weights + # # losses = tf.reduce_mean(tf.ops.sparse_softmax_cross_entropy_with_logits(logits=logits, labels=targets, name=name)) # for TF1.0 and others + # + # loss = tf.divide( + # tf.reduce_sum(losses), # loss from mask. reduce_sum before element-wise mul with mask !! + # tf.reduce_sum(weights), + # name="seq_loss_with_mask" + # ) + # + # if return_details: + # return loss, losses, weights, targets + # else: + # return loss + raise NotImplementedError("Not Implemented.") + + +def cosine_similarity(v1, v2): + """Cosine similarity [-1, 1]. + + Parameters + ---------- + v1, v2 : Tensor + Tensor with the same shape [batch_size, n_feature]. + + References + ---------- + - `Wiki `__. + + """ + + # return tf.reduce_sum(tf.multiply(v1, v2), 1) / \ + # (tf.sqrt(tf.reduce_sum(tf.multiply(v1, v1), 1)) * + # tf.sqrt(tf.reduce_sum(tf.multiply(v2, v2), 1))) + raise NotImplementedError("Not Implemented.") + + +# Regularization Functions +def li_regularizer(scale, scope=None): + """Li regularization removes the neurons of previous layer. The `i` represents `inputs`. + Returns a function that can be used to apply group li regularization to weights. + The implementation follows `TensorFlow contrib `__. + + Parameters + ---------- + scale : float + A scalar multiplier `Tensor`. 0.0 disables the regularizer. + scope: str + An optional scope name for this function. + + Returns + -------- + A function with signature `li(weights, name=None)` that apply Li regularization. + + Raises + ------ + ValueError : if scale is outside of the range [0.0, 1.0] or if scale is not a float. + + """ + # if isinstance(scale, numbers.Integral): + # raise ValueError('scale cannot be an integer: %s' % scale) + # if isinstance(scale, numbers.Real): + # if scale < 0.: + # raise ValueError('Setting a scale less than 0 on a regularizer: %g' % scale) + # if scale >= 1.: + # raise ValueError('Setting a scale greater than 1 on a regularizer: %g' % scale) + # if scale == 0.: + # logging.info('Scale of 0 disables regularizer.') + # return lambda _, name=None: None + # + # def li(weights): + # """Applies li regularization to weights.""" + # with tf.name_scope('li_regularizer') as scope: + # my_scale = ops.convert_to_tensor(scale, dtype=weights.dtype.base_dtype, name='scale') + # # if tf.__version__ <= '0.12': + # # standard_ops_fn = standard_ops.mul + # # else: + # standard_ops_fn = standard_ops.multiply + # return standard_ops_fn( + # my_scale, standard_ops.reduce_sum(standard_ops.sqrt(standard_ops.reduce_sum(tf.square(weights), 1))), + # name=scope + # ) + + raise NotImplementedError("Not Implemented.") + + +def lo_regularizer(scale): + """Lo regularization removes the neurons of current layer. The `o` represents `outputs` + Returns a function that can be used to apply group lo regularization to weights. + The implementation follows `TensorFlow contrib `__. + + Parameters + ---------- + scale : float + A scalar multiplier `Tensor`. 0.0 disables the regularizer. + + Returns + ------- + A function with signature `lo(weights, name=None)` that apply Lo regularization. + + Raises + ------ + ValueError : If scale is outside of the range [0.0, 1.0] or if scale is not a float. + + """ + # if isinstance(scale, numbers.Integral): + # raise ValueError('scale cannot be an integer: %s' % scale) + # + # if isinstance(scale, numbers.Real): + # if scale < 0.: + # raise ValueError('Setting a scale less than 0 on a regularizer: %g' % scale) + # if scale >= 1.: + # raise ValueError('Setting a scale greater than 1 on a regularizer: %g' % scale) + # if scale == 0.: + # logging.info('Scale of 0 disables regularizer.') + # return lambda _, name=None: None + # + # def lo(weights, name='lo_regularizer'): + # """Applies group column regularization to weights.""" + # with tf.name_scope(name) as scope: + # my_scale = ops.convert_to_tensor(scale, dtype=weights.dtype.base_dtype, name='scale') + # # if tf.__version__ <= '0.12': + # # standard_ops_fn = standard_ops.mul + # # else: + # standard_ops_fn = standard_ops.multiply + # return standard_ops_fn( + # my_scale, standard_ops.reduce_sum(standard_ops.sqrt(standard_ops.reduce_sum(tf.square(weights), 0))), + # name=scope + # ) + + raise NotImplementedError("Not Implemented.") + + +def maxnorm_regularizer(scale=1.0): + """Max-norm regularization returns a function that can be used to apply max-norm regularization to weights. + + More about max-norm, see `wiki-max norm `_. + The implementation follows `TensorFlow contrib `__. + + Parameters + ---------- + scale : float + A scalar multiplier `Tensor`. 0.0 disables the regularizer. + + Returns + --------- + A function with signature `mn(weights, name=None)` that apply Lo regularization. + + Raises + -------- + ValueError : If scale is outside of the range [0.0, 1.0] or if scale is not a float. + + """ + # if isinstance(scale, numbers.Integral): + # raise ValueError('scale cannot be an integer: %s' % scale) + # + # if isinstance(scale, numbers.Real): + # if scale < 0.: + # raise ValueError('Setting a scale less than 0 on a regularizer: %g' % scale) + # # if scale >= 1.: + # # raise ValueError('Setting a scale greater than 1 on a regularizer: %g' % + # # scale) + # if scale == 0.: + # logging.info('Scale of 0 disables regularizer.') + # return lambda _, name=None: None + # + # def mn(weights, name='max_regularizer'): + # """Applies max-norm regularization to weights.""" + # with tf.name_scope(name) as scope: + # my_scale = ops.convert_to_tensor(scale, dtype=weights.dtype.base_dtype, name='scale') + # # if tf.__version__ <= '0.12': + # # standard_ops_fn = standard_ops.mul + # # else: + # standard_ops_fn = standard_ops.multiply + # return standard_ops_fn(my_scale, standard_ops.reduce_max(standard_ops.abs(weights)), name=scope) + # + # return mn + raise NotImplementedError("Not Implemented.") + + +def maxnorm_o_regularizer(scale): + """Max-norm output regularization removes the neurons of current layer. + Returns a function that can be used to apply max-norm regularization to each column of weight matrix. + The implementation follows `TensorFlow contrib `__. + + Parameters + ---------- + scale : float + A scalar multiplier `Tensor`. 0.0 disables the regularizer. + + Returns + --------- + A function with signature `mn_o(weights, name=None)` that apply Lo regularization. + + Raises + --------- + ValueError : If scale is outside of the range [0.0, 1.0] or if scale is not a float. + + """ + # if isinstance(scale, numbers.Integral): + # raise ValueError('scale cannot be an integer: %s' % scale) + # + # if isinstance(scale, numbers.Real): + # if scale < 0.: + # raise ValueError('Setting a scale less than 0 on a regularizer: %g' % scale) + # # if scale >= 1.: + # # raise ValueError('Setting a scale greater than 1 on a regularizer: %g' % + # # scale) + # if scale == 0.: + # logging.info('Scale of 0 disables regularizer.') + # return lambda _, name=None: None + # + # def mn_o(weights, name='maxnorm_o_regularizer'): + # """Applies max-norm regularization to weights.""" + # with tf.name_scope(name) as scope: + # my_scale = ops.convert_to_tensor(scale, dtype=weights.dtype.base_dtype, name='scale') + # if tf.__version__ <= '0.12': + # standard_ops_fn = standard_ops.mul + # else: + # standard_ops_fn = standard_ops.multiply + # return standard_ops_fn( + # my_scale, standard_ops.reduce_sum(standard_ops.reduce_max(standard_ops.abs(weights), 0)), name=scope + # ) + # + # return mn_o + raise NotImplementedError("Not Implemented.") + + +def maxnorm_i_regularizer(scale): + """Max-norm input regularization removes the neurons of previous layer. + Returns a function that can be used to apply max-norm regularization to each row of weight matrix. + The implementation follows `TensorFlow contrib `__. + + Parameters + ---------- + scale : float + A scalar multiplier `Tensor`. 0.0 disables the regularizer. + + Returns + --------- + A function with signature `mn_i(weights, name=None)` that apply Lo regularization. + + Raises + --------- + ValueError : If scale is outside of the range [0.0, 1.0] or if scale is not a float. + + """ + # if isinstance(scale, numbers.Integral): + # raise ValueError('scale cannot be an integer: %s' % scale) + # + # if isinstance(scale, numbers.Real): + # if scale < 0.: + # raise ValueError('Setting a scale less than 0 on a regularizer: %g' % scale) + # # if scale >= 1.: + # # raise ValueError('Setting a scale greater than 1 on a regularizer: %g' % + # # scale) + # if scale == 0.: + # logging.info('Scale of 0 disables regularizer.') + # return lambda _, name=None: None + # + # def mn_i(weights, name='maxnorm_i_regularizer'): + # """Applies max-norm regularization to weights.""" + # with tf.name_scope(name) as scope: + # my_scale = ops.convert_to_tensor(scale, dtype=weights.dtype.base_dtype, name='scale') + # if tf.__version__ <= '0.12': + # standard_ops_fn = standard_ops.mul + # else: + # standard_ops_fn = standard_ops.multiply + # return standard_ops_fn( + # my_scale, standard_ops.reduce_sum(standard_ops.reduce_max(standard_ops.abs(weights), 1)), name=scope + # ) + # + # return mn_i + raise NotImplementedError("Not Implemented.") + + +def huber_loss( + output, target, is_mean=True, delta=1.0, dynamichuber=False, reverse=False, axis=-1, epsilon=0.00001, name=None +): + """Huber Loss operation, see ``https://en.wikipedia.org/wiki/Huber_loss`` . + Reverse Huber Loss operation, see ''https://statweb.stanford.edu/~owen/reports/hhu.pdf''. + Dynamic Reverse Huber Loss operation, see ''https://arxiv.org/pdf/1606.00373.pdf''. + + Parameters + ---------- + output : Tensor + A distribution with shape: [batch_size, ....], (any dimensions). + target : Tensor + The target distribution, format the same with `output`. + is_mean : boolean + Whether compute the mean or sum for each example. + - If True, use ``tf.reduce_mean`` to compute the loss between one target and predict data (default). + - If False, use ``tf.reduce_sum``. + delta: float + The point where the huber loss function changes from a quadratic to linear. + dynamichuber: boolean + Whether compute the coefficient c for each batch. + - If True, c is 20% of the maximal per-batch error. + - If False, c is delta. + reverse: boolean + Whether compute the reverse huber loss. + axis : int or list of int + The dimensions to reduce. + epsilon: + Eplison. + name : string + Name of this loss. + + """ + # if reverse: + # if dynamichuber: + # huber_c = 0.2 * tf.reduce_max(tf.abs(output - target)) + # else: + # huber_c = delta + # if is_mean: + # loss = tf.reduce_mean( + # tf.where( + # tf.less_equal(tf.abs(output - target), huber_c), tf.abs(output - target), + # tf.multiply( + # tf.pow(output - target, 2.0) + tf.pow(huber_c, 2.0), + # tf.math.divide_no_nan(.5, huber_c + epsilon) + # ) + # ), name=name + # ) + # else: + # loss = tf.reduce_mean( + # tf.reduce_sum( + # tf.where( + # tf.less_equal(tf.abs(output - target), huber_c), tf.abs(output - target), + # tf.multiply( + # tf.pow(output - target, 2.0) + tf.pow(huber_c, 2.0), + # tf.math.divide_no_nan(.5, huber_c + epsilon) + # ) + # ), axis + # ), name=name + # ) + # elif is_mean: + # loss = tf.reduce_mean( + # tf.where( + # tf.less_equal(tf.abs(output - target), delta), 0.5 * tf.pow(output - target, 2), + # delta * (tf.abs(output - target) - 0.5 * delta) + # ), name=name + # ) + # else: + # loss = tf.reduce_mean( + # tf.reduce_sum( + # tf.where( + # tf.less_equal(tf.abs(output - target), delta), 0.5 * tf.pow(output - target, 2), + # delta * (tf.abs(output - target) - 0.5 * delta) + # ), axis + # ), name=name + # ) + # return loss + raise NotImplementedError("Not Implemented.") diff --git a/tensorlayer/cost/paddle_cost.py b/tensorlayer/cost/paddle_cost.py new file mode 100644 index 0000000..d8a79f0 --- /dev/null +++ b/tensorlayer/cost/paddle_cost.py @@ -0,0 +1,548 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import paddle.nn.functional as F +import paddle as pd + +__all__ = [ + 'cross_entropy', + 'sigmoid_cross_entropy', + 'binary_cross_entropy', + 'mean_squared_error', + 'normalized_mean_square_error', + 'absolute_difference_error', + 'dice_coe', + 'dice_hard_coe', + 'iou_coe', + 'cross_entropy_seq', + 'cross_entropy_seq_with_mask', + 'cosine_similarity', + 'li_regularizer', + 'lo_regularizer', + 'maxnorm_regularizer', + 'maxnorm_o_regularizer', + 'maxnorm_i_regularizer', +] + +def cross_entropy(output, target): + """Softmax cross-entropy operation, returns the TensorFlow expression of cross-entropy for two distributions, + it implements softmax internally. See ``tf.ops.sparse_softmax_cross_entropy_with_logits``. + + Parameters + ---------- + output : Tensor + A batch of distribution with shape: [batch_size, num of classes]. + target : Tensor + A batch of index with shape: [batch_size, ]. + name : string + Name of this loss. + + Examples + -------- + >>> import tensorlayer as tl + >>> ce = tl.cost.cross_entropy(y_logits, y_target_logits) + + References + ----------- + - About cross-entropy: ``__. + - The code is borrowed from: ``__. + + """ + + return F.cross_entropy(input=output, label=target) + + +def sigmoid_cross_entropy(output, target): + """Sigmoid cross-entropy operation, see ``tf.ops.sigmoid_cross_entropy_with_logits``. + + Parameters + ---------- + output : Tensor + A batch of distribution with shape: [batch_size, num of classes]. + target : Tensor + A batch of index with shape: [batch_size, ]. + name : string + Name of this loss. + + """ + # tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(labels=target, logits=output), name=name) + depth = output.shape[-1] + label = pd.fluid.layers.one_hot(target, depth=depth) + out = pd.fluid.layers.sigmoid_cross_entropy_with_logits(x=output, label=label) + out = pd.fluid.layers.reduce_mean(out) + return out + + +def binary_cross_entropy(output, target, epsilon=1e-8): + """Binary cross entropy operation. + + Parameters + ---------- + output : Tensor + Tensor with type of `float32` or `float64`. + target : Tensor + The target distribution, format the same with `output`. + epsilon : float + A small value to avoid output to be zero. + name : str + An optional name to attach to this function. + + References + ----------- + - `ericjang-DRAW `__ + + """ + depth = output.shape[-1] + target = pd.fluid.layers.one_hot(target, depth=depth) + out = pd.fluid.layers.reduce_sum( + -(target * pd.log(output + epsilon) + (1. - target) * pd.log(1. - output + epsilon)) + ) + return out + + +def mean_squared_error(output, target, is_mean=False, axis=-1, name="mean_squared_error"): + """Return the TensorFlow expression of mean-square-error (L2) of two batch of data. + + Parameters + ---------- + output : Tensor + 2D, 3D or 4D tensor i.e. [batch_size, n_feature], [batch_size, height, width] or [batch_size, height, width, channel]. + target : Tensor + The target distribution, format the same with `output`. + is_mean : boolean + Whether compute the mean or sum for each example. + - If True, use ``tf.reduce_mean`` to compute the loss between one target and predict data. + - If False, use ``tf.reduce_sum`` (default). + axis : int or list of int + The dimensions to reduce. + name : str + An optional name to attach to this function. + + References + ------------ + - `Wiki Mean Squared Error `__ + + """ + depth = output.shape[-1] + target = pd.fluid.layers.one_hot(target, depth=depth) + + if is_mean: + mse = F.mse_loss(input=output, label=target, reduction='mean') + else: + mse = F.mse_loss(input=output, label=target, reduction='sum') + return mse + + +def normalized_mean_square_error(output, target, axis=-1, name="normalized_mean_squared_error_loss"): + """Return the TensorFlow expression of normalized mean-square-error of two distributions. + + Parameters + ---------- + output : Tensor + 2D, 3D or 4D tensor i.e. [batch_size, n_feature], [batch_size, height, width] or [batch_size, height, width, channel]. + target : Tensor + The target distribution, format the same with `output`. + axis : int or list of int + The dimensions to reduce. + name : str + An optional name to attach to this function. + + """ + + raise NotImplementedError("Not Implemented.") + + +def absolute_difference_error(output, target, is_mean=False, axis=-1, name="absolute_difference_error_loss"): + """Return the TensorFlow expression of absolute difference error (L1) of two batch of data. + + Parameters + ---------- + output : Tensor + 2D, 3D or 4D tensor i.e. [batch_size, n_feature], [batch_size, height, width] or [batch_size, height, width, channel]. + target : Tensor + The target distribution, format the same with `output`. + is_mean : boolean + Whether compute the mean or sum for each example. + - If True, use ``tf.reduce_mean`` to compute the loss between one target and predict data. + - If False, use ``tf.reduce_sum`` (default). + axis : int or list of int + The dimensions to reduce. + name : str + An optional name to attach to this function. + + """ + + raise NotImplementedError("Not Implemented.") + + +def dice_coe(output, target, loss_type='jaccard', axis=(1, 2, 3), smooth=1e-5): + """Soft dice (Sørensen or Jaccard) coefficient for comparing the similarity + of two batch of data, usually be used for binary image segmentation + i.e. labels are binary. The coefficient between 0 to 1, 1 means totally match. + + Parameters + ----------- + output : Tensor + A distribution with shape: [batch_size, ....], (any dimensions). + target : Tensor + The target distribution, format the same with `output`. + loss_type : str + ``jaccard`` or ``sorensen``, default is ``jaccard``. + axis : tuple of int + All dimensions are reduced, default ``[1,2,3]``. + smooth : float + This small value will be added to the numerator and denominator. + - If both output and target are empty, it makes sure dice is 1. + - If either output or target are empty (all pixels are background), dice = ```smooth/(small_value + smooth)``, then if smooth is very small, dice close to 0 (even the image values lower than the threshold), so in this case, higher smooth can have a higher dice. + + Examples + --------- + >>> import tensorlayer as tl + >>> outputs = tl.act.pixel_wise_softmax(outputs) + >>> dice_loss = 1 - tl.cost.dice_coe(outputs, y_) + + References + ----------- + - `Wiki-Dice `__ + + """ + + raise NotImplementedError("Not Implemented.") + + +def dice_hard_coe(output, target, threshold=0.5, axis=(1, 2, 3), smooth=1e-5): + """Non-differentiable Sørensen–Dice coefficient for comparing the similarity + of two batch of data, usually be used for binary image segmentation i.e. labels are binary. + The coefficient between 0 to 1, 1 if totally match. + + Parameters + ----------- + output : tensor + A distribution with shape: [batch_size, ....], (any dimensions). + target : tensor + The target distribution, format the same with `output`. + threshold : float + The threshold value to be true. + axis : tuple of integer + All dimensions are reduced, default ``(1,2,3)``. + smooth : float + This small value will be added to the numerator and denominator, see ``dice_coe``. + + References + ----------- + - `Wiki-Dice `__ + + """ + + raise NotImplementedError("Not Implemented.") + + +def iou_coe(output, target, threshold=0.5, axis=(1, 2, 3), smooth=1e-5): + """Non-differentiable Intersection over Union (IoU) for comparing the + similarity of two batch of data, usually be used for evaluating binary image segmentation. + The coefficient between 0 to 1, and 1 means totally match. + + Parameters + ----------- + output : tensor + A batch of distribution with shape: [batch_size, ....], (any dimensions). + target : tensor + The target distribution, format the same with `output`. + threshold : float + The threshold value to be true. + axis : tuple of integer + All dimensions are reduced, default ``(1,2,3)``. + smooth : float + This small value will be added to the numerator and denominator, see ``dice_coe``. + + Notes + ------ + - IoU cannot be used as training loss, people usually use dice coefficient for training, IoU and hard-dice for evaluating. + + """ + + raise NotImplementedError("Not Implemented.") + + +def sequence_loss_by_example( + logits, targets, weights, average_across_timesteps=True, softmax_loss_function=None, name=None +): + """Weighted cross-entropy loss for a sequence of logits (per example). see original tensorflow code : + + + Parameters + ---------- + logits: List + List of 2D Tensors of shape [batch_size x num_decoder_symbols]. + targets: List + List of 1D batch-sized int32 Tensors of the same length as logits. + weights: List + List of 1D batch-sized float-Tensors of the same length as logits. + average_across_timesteps: Boolean + If set, divide the returned cost by the total label weight. + softmax_loss_function: None or Function + Function (labels, logits) -> loss-batch to be used instead of the standard softmax (the default if this is None). + **Note that to avoid confusion, it is required for the function to accept named arguments.** + name: None or str + Optional name for this operation, default: "sequence_loss_by_example". + + Returns + ------- + 1D batch-sized float Tensor: The log-perplexity for each sequence. + + Raises + ------ + ValueError: If len(logits) is different from len(targets) or len(weights). + + """ + + raise NotImplementedError("Not Implemented.") + + +def cross_entropy_seq(logits, target_seqs, batch_size=None): + """Returns the expression of cross-entropy of two sequences, implement + softmax internally. Normally be used for fixed length RNN outputs, see `PTB example `__. + + Parameters + ---------- + logits : Tensor + 2D tensor with shape of `[batch_size * n_steps, n_classes]`. + target_seqs : Tensor + The target sequence, 2D tensor `[batch_size, n_steps]`, if the number of step is dynamic, please use ``tl.cost.cross_entropy_seq_with_mask`` instead. + batch_size : None or int. + Whether to divide the cost by batch size. + - If integer, the return cost will be divided by `batch_size`. + - If None (default), the return cost will not be divided by anything. + + Examples + -------- + >>> import tensorlayer as tl + >>> # see `PTB example `__.for more details + >>> # outputs shape : (batch_size * n_steps, n_classes) + >>> # targets shape : (batch_size, n_steps) + >>> cost = tl.cost.cross_entropy_seq(outputs, targets) + + """ + + raise NotImplementedError("Not Implemented.") + + +def cross_entropy_seq_with_mask(logits, target_seqs, input_mask, return_details=False, name=None): + """Returns the expression of cross-entropy of two sequences, implement + softmax internally. Normally be used for Dynamic RNN with Synced sequence input and output. + + Parameters + ----------- + logits : Tensor + 2D tensor with shape of [batch_size * ?, n_classes], `?` means dynamic IDs for each example. + - Can be get from `DynamicRNNLayer` by setting ``return_seq_2d`` to `True`. + target_seqs : Tensor + int of tensor, like word ID. [batch_size, ?], `?` means dynamic IDs for each example. + input_mask : Tensor + The mask to compute loss, it has the same size with `target_seqs`, normally 0 or 1. + return_details : boolean + Whether to return detailed losses. + - If False (default), only returns the loss. + - If True, returns the loss, losses, weights and targets (see source code). + + Examples + -------- + >>> import tensorlayer as tl + >>> import tensorflow as tf + >>> import numpy as np + >>> batch_size = 64 + >>> vocab_size = 10000 + >>> embedding_size = 256 + >>> ni = tl.layers.Input([batch_size, None], dtype=tf.int64) + >>> net = tl.layers.Embedding( + ... vocabulary_size = vocab_size, + ... embedding_size = embedding_size, + ... name = 'seq_embedding')(ni) + >>> net = tl.layers.RNN( + ... cell =tf.keras.layers.LSTMCell(units=embedding_size, dropout=0.1), + ... return_seq_2d = True, + ... name = 'dynamicrnn')(net) + >>> net = tl.layers.Dense(n_units=vocab_size, name="output")(net) + >>> model = tl.models.Model(inputs=ni, outputs=net) + >>> input_seqs = np.random.randint(0, 10, size=(batch_size, 10), dtype=np.int64) + >>> target_seqs = np.random.randint(0, 10, size=(batch_size, 10), dtype=np.int64) + >>> input_mask = np.random.randint(0, 2, size=(batch_size, 10), dtype=np.int64) + >>> outputs = model(input_seqs, is_train=True) + >>> loss = tl.cost.cross_entropy_seq_with_mask(outputs, target_seqs, input_mask) + + """ + + raise NotImplementedError("Not Implemented.") + + +def cosine_similarity(v1, v2): + """Cosine similarity [-1, 1]. + + Parameters + ---------- + v1, v2 : Tensor + Tensor with the same shape [batch_size, n_feature]. + + References + ---------- + - `Wiki `__. + + """ + + raise NotImplementedError("Not Implemented.") + + +# Regularization Functions +def li_regularizer(scale, scope=None): + """Li regularization removes the neurons of previous layer. The `i` represents `inputs`. + Returns a function that can be used to apply group li regularization to weights. + The implementation follows `TensorFlow contrib `__. + + Parameters + ---------- + scale : float + A scalar multiplier `Tensor`. 0.0 disables the regularizer. + scope: str + An optional scope name for this function. + + Returns + -------- + A function with signature `li(weights, name=None)` that apply Li regularization. + + Raises + ------ + ValueError : if scale is outside of the range [0.0, 1.0] or if scale is not a float. + + """ + + raise NotImplementedError("Not Implemented.") + + +def lo_regularizer(scale): + """Lo regularization removes the neurons of current layer. The `o` represents `outputs` + Returns a function that can be used to apply group lo regularization to weights. + The implementation follows `TensorFlow contrib `__. + + Parameters + ---------- + scale : float + A scalar multiplier `Tensor`. 0.0 disables the regularizer. + + Returns + ------- + A function with signature `lo(weights, name=None)` that apply Lo regularization. + + Raises + ------ + ValueError : If scale is outside of the range [0.0, 1.0] or if scale is not a float. + + """ + + raise NotImplementedError("Not Implemented.") + + +def maxnorm_regularizer(scale=1.0): + """Max-norm regularization returns a function that can be used to apply max-norm regularization to weights. + + More about max-norm, see `wiki-max norm `_. + The implementation follows `TensorFlow contrib `__. + + Parameters + ---------- + scale : float + A scalar multiplier `Tensor`. 0.0 disables the regularizer. + + Returns + --------- + A function with signature `mn(weights, name=None)` that apply Lo regularization. + + Raises + -------- + ValueError : If scale is outside of the range [0.0, 1.0] or if scale is not a float. + + """ + + raise NotImplementedError("Not Implemented.") + + +def maxnorm_o_regularizer(scale): + """Max-norm output regularization removes the neurons of current layer. + Returns a function that can be used to apply max-norm regularization to each column of weight matrix. + The implementation follows `TensorFlow contrib `__. + + Parameters + ---------- + scale : float + A scalar multiplier `Tensor`. 0.0 disables the regularizer. + + Returns + --------- + A function with signature `mn_o(weights, name=None)` that apply Lo regularization. + + Raises + --------- + ValueError : If scale is outside of the range [0.0, 1.0] or if scale is not a float. + + """ + + raise NotImplementedError("Not Implemented.") + + +def maxnorm_i_regularizer(scale): + """Max-norm input regularization removes the neurons of previous layer. + Returns a function that can be used to apply max-norm regularization to each row of weight matrix. + The implementation follows `TensorFlow contrib `__. + + Parameters + ---------- + scale : float + A scalar multiplier `Tensor`. 0.0 disables the regularizer. + + Returns + --------- + A function with signature `mn_i(weights, name=None)` that apply Lo regularization. + + Raises + --------- + ValueError : If scale is outside of the range [0.0, 1.0] or if scale is not a float. + + """ + + raise NotImplementedError("Not Implemented.") + + +def huber_loss( + output, target, is_mean=True, delta=1.0, dynamichuber=False, reverse=False, axis=-1, epsilon=0.00001, name=None +): + """Huber Loss operation, see ``https://en.wikipedia.org/wiki/Huber_loss`` . + Reverse Huber Loss operation, see ''https://statweb.stanford.edu/~owen/reports/hhu.pdf''. + Dynamic Reverse Huber Loss operation, see ''https://arxiv.org/pdf/1606.00373.pdf''. + + Parameters + ---------- + output : Tensor + A distribution with shape: [batch_size, ....], (any dimensions). + target : Tensor + The target distribution, format the same with `output`. + is_mean : boolean + Whether compute the mean or sum for each example. + - If True, use ``tf.reduce_mean`` to compute the loss between one target and predict data (default). + - If False, use ``tf.reduce_sum``. + delta: float + The point where the huber loss function changes from a quadratic to linear. + dynamichuber: boolean + Whether compute the coefficient c for each batch. + - If True, c is 20% of the maximal per-batch error. + - If False, c is delta. + reverse: boolean + Whether compute the reverse huber loss. + axis : int or list of int + The dimensions to reduce. + epsilon: + Eplison. + name : string + Name of this loss. + + """ + + raise NotImplementedError("Not Implemented.") diff --git a/tensorlayer/cost/tensorflow_cost.py b/tensorlayer/cost/tensorflow_cost.py new file mode 100644 index 0000000..b07acad --- /dev/null +++ b/tensorlayer/cost/tensorflow_cost.py @@ -0,0 +1,860 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import numbers + +import tensorflow as tf +from tensorflow.python.framework import ops +from tensorflow.python.ops import array_ops, math_ops, nn_ops, standard_ops + +from tensorlayer import logging + +__all__ = [ + 'cross_entropy', + 'sigmoid_cross_entropy', + 'binary_cross_entropy', + 'mean_squared_error', + 'normalized_mean_square_error', + 'absolute_difference_error', + 'dice_coe', + 'dice_hard_coe', + 'iou_coe', + 'cross_entropy_seq', + 'cross_entropy_seq_with_mask', + 'cosine_similarity', + 'li_regularizer', + 'lo_regularizer', + 'maxnorm_regularizer', + 'maxnorm_o_regularizer', + 'maxnorm_i_regularizer', +] + + +def cross_entropy(output, target, name=None): + """Softmax cross-entropy operation, returns the TensorFlow expression of cross-entropy for two distributions, + it implements softmax internally. See ``tf.ops.sparse_softmax_cross_entropy_with_logits``. + + Parameters + ---------- + output : Tensor + A batch of distribution with shape: [batch_size, num of classes]. + target : Tensor + A batch of index with shape: [batch_size, ]. + name : string + Name of this loss. + + Examples + -------- + >>> import tensorlayer as tl + >>> ce = tl.cost.cross_entropy(y_logits, y_target_logits, 'my_loss') + + References + ----------- + - About cross-entropy: ``__. + - The code is borrowed from: ``__. + + """ + # if name is None: + # raise Exception("Please give a unique name to tl.cost.cross_entropy for TF1.0+") + return tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(labels=target, logits=output), name=name) + + +def sigmoid_cross_entropy(output, target, name=None): + """Sigmoid cross-entropy operation, see ``tf.ops.sigmoid_cross_entropy_with_logits``. + + Parameters + ---------- + output : Tensor + A batch of distribution with shape: [batch_size, num of classes]. + target : Tensor + A batch of index with shape: [batch_size, ]. + name : string + Name of this loss. + + """ + return tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(labels=target, logits=output), name=name) + + +def binary_cross_entropy(output, target, epsilon=1e-8, name='bce_loss'): + """Binary cross entropy operation. + + Parameters + ---------- + output : Tensor + Tensor with type of `float32` or `float64`. + target : Tensor + The target distribution, format the same with `output`. + epsilon : float + A small value to avoid output to be zero. + name : str + An optional name to attach to this function. + + References + ----------- + - `ericjang-DRAW `__ + + """ + # with ops.op_scope([output, target], name, "bce_loss") as name: + # output = ops.convert_to_tensor(output, name="preds") + # target = ops.convert_to_tensor(targets, name="target") + + # with tf.name_scope(name): + return tf.reduce_mean( + tf.reduce_sum( + -(target * tf.math.log(output + epsilon) + (1. - target) * tf.math.log(1. - output + epsilon)), axis=1 + ), name=name + ) + + # For brevity, let `x = output`, `z = target`. The binary cross entropy loss is + # + # loss(x, z) = - sum_i (x[i] * log(z[i]) + (1 - x[i]) * log(1 - z[i])) + + +def mean_squared_error(output, target, is_mean=False, axis=-1, name="mean_squared_error"): + """Return the TensorFlow expression of mean-square-error (L2) of two batch of data. + + Parameters + ---------- + output : Tensor + 2D, 3D or 4D tensor i.e. [batch_size, n_feature], [batch_size, height, width] or [batch_size, height, width, channel]. + target : Tensor + The target distribution, format the same with `output`. + is_mean : boolean + Whether compute the mean or sum for each example. + - If True, use ``tf.reduce_mean`` to compute the loss between one target and predict data. + - If False, use ``tf.reduce_sum`` (default). + axis : int or list of int + The dimensions to reduce. + name : str + An optional name to attach to this function. + + References + ------------ + - `Wiki Mean Squared Error `__ + + """ + # with tf.name_scope(name): + # if len(output.shape) == 2: # [batch_size, n_feature] + # axis = 1 + # elif len(output.shape) == 3: # [batch_size, w, h] + # axis = [1, 2] + # elif len(output.shape) == 4: # [batch_size, w, h, c] + # axis = [1, 2, 3] + # else: + # raise Exception("Unknow dimension") + + if is_mean: + mse = tf.reduce_mean(tf.reduce_mean(tf.math.squared_difference(output, target), axis), name=name) + else: + mse = tf.reduce_mean(tf.reduce_sum(tf.math.squared_difference(output, target), axis), name=name) + return mse + + +def normalized_mean_square_error(output, target, axis=-1, name="normalized_mean_squared_error_loss"): + """Return the TensorFlow expression of normalized mean-square-error of two distributions. + + Parameters + ---------- + output : Tensor + 2D, 3D or 4D tensor i.e. [batch_size, n_feature], [batch_size, height, width] or [batch_size, height, width, channel]. + target : Tensor + The target distribution, format the same with `output`. + axis : int or list of int + The dimensions to reduce. + name : str + An optional name to attach to this function. + + """ + with tf.name_scope("normalized_mean_squared_error_loss"): + # if len(output.shape) == 2: # [batch_size, n_feature] + # axis = 1 + # elif len(output.shape) == 3: # [batch_size, w, h] + # axis = [1, 2] + # elif len(output.shape) == 4: # [batch_size, w, h, c] + # axis = [1, 2, 3] + nmse_a = tf.sqrt(tf.reduce_sum(tf.math.squared_difference(output, target), axis=axis)) + nmse_b = tf.sqrt(tf.reduce_sum(tf.square(target), axis=axis)) + nmse = tf.reduce_mean(nmse_a / nmse_b, name=name) + return nmse + + +def absolute_difference_error(output, target, is_mean=False, axis=-1, name="absolute_difference_error_loss"): + """Return the TensorFlow expression of absolute difference error (L1) of two batch of data. + + Parameters + ---------- + output : Tensor + 2D, 3D or 4D tensor i.e. [batch_size, n_feature], [batch_size, height, width] or [batch_size, height, width, channel]. + target : Tensor + The target distribution, format the same with `output`. + is_mean : boolean + Whether compute the mean or sum for each example. + - If True, use ``tf.reduce_mean`` to compute the loss between one target and predict data. + - If False, use ``tf.reduce_sum`` (default). + axis : int or list of int + The dimensions to reduce. + name : str + An optional name to attach to this function. + + """ + # # with tf.name_scope("absolute_difference_error_loss"): + # if len(output.shape) == 2: # [batch_size, n_feature] + # axis = 1 + # elif len(output.shape) == 3: # [batch_size, w, h] + # axis = [1, 2] + # elif len(output.shape) == 4: # [batch_size, w, h, c] + # axis = [1, 2, 3] + # else: + # raise Exception("Unknow dimension") + if is_mean: + loss = tf.reduce_mean(tf.reduce_mean(tf.abs(output - target), axis), name=name) + else: + loss = tf.reduce_mean(tf.reduce_sum(tf.abs(output - target), axis), name=name) + return loss + + +def dice_coe(output, target, loss_type='jaccard', axis=(1, 2, 3), smooth=1e-5): + """Soft dice (Sørensen or Jaccard) coefficient for comparing the similarity + of two batch of data, usually be used for binary image segmentation + i.e. labels are binary. The coefficient between 0 to 1, 1 means totally match. + + Parameters + ----------- + output : Tensor + A distribution with shape: [batch_size, ....], (any dimensions). + target : Tensor + The target distribution, format the same with `output`. + loss_type : str + ``jaccard`` or ``sorensen``, default is ``jaccard``. + axis : tuple of int + All dimensions are reduced, default ``[1,2,3]``. + smooth : float + This small value will be added to the numerator and denominator. + - If both output and target are empty, it makes sure dice is 1. + - If either output or target are empty (all pixels are background), dice = ```smooth/(small_value + smooth)``, then if smooth is very small, dice close to 0 (even the image values lower than the threshold), so in this case, higher smooth can have a higher dice. + + Examples + --------- + >>> import tensorlayer as tl + >>> outputs = tl.act.pixel_wise_softmax(outputs) + >>> dice_loss = 1 - tl.cost.dice_coe(outputs, y_) + + References + ----------- + - `Wiki-Dice `__ + + """ + inse = tf.reduce_sum(output * target, axis=axis) + if loss_type == 'jaccard': + l = tf.reduce_sum(output * output, axis=axis) + r = tf.reduce_sum(target * target, axis=axis) + elif loss_type == 'sorensen': + l = tf.reduce_sum(output, axis=axis) + r = tf.reduce_sum(target, axis=axis) + else: + raise Exception("Unknow loss_type") + # old axis=[0,1,2,3] + # dice = 2 * (inse) / (l + r) + # epsilon = 1e-5 + # dice = tf.clip_by_value(dice, 0, 1.0-epsilon) # if all empty, dice = 1 + # new haodong + dice = (2. * inse + smooth) / (l + r + smooth) + ## + dice = tf.reduce_mean(dice, name='dice_coe') + return dice + + +def dice_hard_coe(output, target, threshold=0.5, axis=(1, 2, 3), smooth=1e-5): + """Non-differentiable Sørensen–Dice coefficient for comparing the similarity + of two batch of data, usually be used for binary image segmentation i.e. labels are binary. + The coefficient between 0 to 1, 1 if totally match. + + Parameters + ----------- + output : tensor + A distribution with shape: [batch_size, ....], (any dimensions). + target : tensor + The target distribution, format the same with `output`. + threshold : float + The threshold value to be true. + axis : tuple of integer + All dimensions are reduced, default ``(1,2,3)``. + smooth : float + This small value will be added to the numerator and denominator, see ``dice_coe``. + + References + ----------- + - `Wiki-Dice `__ + + """ + output = tf.cast(output > threshold, dtype=tf.float32) + target = tf.cast(target > threshold, dtype=tf.float32) + inse = tf.reduce_sum(tf.multiply(output, target), axis=axis) + l = tf.reduce_sum(output, axis=axis) + r = tf.reduce_sum(target, axis=axis) + # old axis=[0,1,2,3] + # hard_dice = 2 * (inse) / (l + r) + # epsilon = 1e-5 + # hard_dice = tf.clip_by_value(hard_dice, 0, 1.0-epsilon) + # new haodong + hard_dice = (2. * inse + smooth) / (l + r + smooth) + ## + hard_dice = tf.reduce_mean(hard_dice, name='hard_dice') + return hard_dice + + +def iou_coe(output, target, threshold=0.5, axis=(1, 2, 3), smooth=1e-5): + """Non-differentiable Intersection over Union (IoU) for comparing the + similarity of two batch of data, usually be used for evaluating binary image segmentation. + The coefficient between 0 to 1, and 1 means totally match. + + Parameters + ----------- + output : tensor + A batch of distribution with shape: [batch_size, ....], (any dimensions). + target : tensor + The target distribution, format the same with `output`. + threshold : float + The threshold value to be true. + axis : tuple of integer + All dimensions are reduced, default ``(1,2,3)``. + smooth : float + This small value will be added to the numerator and denominator, see ``dice_coe``. + + Notes + ------ + - IoU cannot be used as training loss, people usually use dice coefficient for training, IoU and hard-dice for evaluating. + + """ + pre = tf.cast(output > threshold, dtype=tf.float32) + truth = tf.cast(target > threshold, dtype=tf.float32) + inse = tf.reduce_sum(tf.multiply(pre, truth), axis=axis) # AND + union = tf.reduce_sum(tf.cast(tf.add(pre, truth) >= 1, dtype=tf.float32), axis=axis) # OR + # old axis=[0,1,2,3] + # epsilon = 1e-5 + # batch_iou = inse / (union + epsilon) + # new haodong + batch_iou = (inse + smooth) / (union + smooth) + iou = tf.reduce_mean(batch_iou, name='iou_coe') + return iou # , pre, truth, inse, union + + +# ## test soft/hard dice and iou +# import numpy as np +# y = np.zeros((1,10,10,1)) +# # y[0,0:5,0:5]=1.0 +# o = np.zeros((1,10,10,1)) +# # o[:,:,:,:] = 0 # what we want: dice=0 iou=0 OK +# # o[0,0:2,0:2]=0.3 # what we want: dice larger iou=0 OK +# # o[0,0:2,0:2]=0.6 # what we want: dice larger iou small OK +# # o[0,0:3,0:3]=0.6 # what we want: dice larger iou larger OK +# # o[0,0:3,0:3]=1 # what we want: dice larger iou same OK +# # o[0,0:5,0:5]=1 # what we want: dice=1 iou=1 OK +# # o[0,0:5,0:5]=0.3 # what we want: dice smaller iou=0 OK +# # o[0,0:5,0:5]=1e-2 # what we want: dice≈0 iou=0 OK +# # o[0,8:10,8:10]=1.0 # what we want: dice=0 iou=0 OK +# # o[0,8:10,8:10]=1e-10 # what we want: dice=0 iou=0 OK +# # y[:,:,:,:] = o[:,:,:,:] = 0 # what we want: dice=1 iou=1 OK +# ## why in u-net, dice=1 hard-dice=1 iou=1 exist?? print bug? +# +# d = dice_coe(o, y, 'jaccard', smooth=1.) +# hd = dice_hard_coe(o, y, smooth=1e-5) +# i = iou_coe(o, y, smooth=1e-5) +# sess = tf.Session(config=tf.ConfigProto(allow_soft_placement=True)) +# # sess.run(tf.local_variables_initializer()) +# print(sess.run([d,hd,i])) +# # p, t, i, u = sess.run([pre, truth, inse, union]) +# # import pprint +# # pprint.pprint(((y>0.5)*(o>0.5)).astype(int).tolist()) +# # pprint.pprint(p.tolist()) +# # pprint.pprint(t.tolist()) +# # pprint.pprint(i) +# # pprint.pprint(u) +# exit() + + +def sequence_loss_by_example( + logits, targets, weights, average_across_timesteps=True, softmax_loss_function=None, name=None +): + """Weighted cross-entropy loss for a sequence of logits (per example). see original tensorflow code : + + + Parameters + ---------- + logits: List + List of 2D Tensors of shape [batch_size x num_decoder_symbols]. + targets: List + List of 1D batch-sized int32 Tensors of the same length as logits. + weights: List + List of 1D batch-sized float-Tensors of the same length as logits. + average_across_timesteps: Boolean + If set, divide the returned cost by the total label weight. + softmax_loss_function: None or Function + Function (labels, logits) -> loss-batch to be used instead of the standard softmax (the default if this is None). + **Note that to avoid confusion, it is required for the function to accept named arguments.** + name: None or str + Optional name for this operation, default: "sequence_loss_by_example". + + Returns + ------- + 1D batch-sized float Tensor: The log-perplexity for each sequence. + + Raises + ------ + ValueError: If len(logits) is different from len(targets) or len(weights). + + """ + if len(targets) != len(logits) or len(weights) != len(logits): + raise ValueError( + "Lengths of logits, weights, and targets must be the same " + "%d, %d, %d." % (len(logits), len(weights), len(targets)) + ) + with ops.name_scope(name, "sequence_loss_by_example", logits + targets + weights): + log_perp_list = [] + for logit, target, weight in zip(logits, targets, weights): + if softmax_loss_function is None: + # TODO(irving,ebrevdo): This reshape is needed because + # sequence_loss_by_example is called with scalars sometimes, which + # violates our general scalar strictness policy. + target = array_ops.reshape(target, [-1]) + crossent = nn_ops.sparse_softmax_cross_entropy_with_logits(labels=target, logits=logit) + else: + crossent = softmax_loss_function(labels=target, logits=logit) + log_perp_list.append(crossent * weight) + log_perps = math_ops.add_n(log_perp_list) + if average_across_timesteps: + total_size = math_ops.add_n(weights) + total_size += 1e-12 # Just to avoid division by 0 for all-0 weights. + log_perps /= total_size + return log_perps + + +def cross_entropy_seq(logits, target_seqs, batch_size=None): + """Returns the expression of cross-entropy of two sequences, implement + softmax internally. Normally be used for fixed length RNN outputs, see `PTB example `__. + + Parameters + ---------- + logits : Tensor + 2D tensor with shape of `[batch_size * n_steps, n_classes]`. + target_seqs : Tensor + The target sequence, 2D tensor `[batch_size, n_steps]`, if the number of step is dynamic, please use ``tl.cost.cross_entropy_seq_with_mask`` instead. + batch_size : None or int. + Whether to divide the cost by batch size. + - If integer, the return cost will be divided by `batch_size`. + - If None (default), the return cost will not be divided by anything. + + Examples + -------- + >>> import tensorlayer as tl + >>> # see `PTB example `__.for more details + >>> # outputs shape : (batch_size * n_steps, n_classes) + >>> # targets shape : (batch_size, n_steps) + >>> cost = tl.cost.cross_entropy_seq(outputs, targets) + + """ + sequence_loss_by_example_fn = sequence_loss_by_example + + loss = sequence_loss_by_example_fn( + [logits], [tf.reshape(target_seqs, [-1])], [tf.ones_like(tf.reshape(target_seqs, [-1]), dtype=tf.float32)] + ) + # [tf.ones([batch_size * num_steps])]) + cost = tf.reduce_sum(loss) # / batch_size + if batch_size is not None: + cost = cost / batch_size + return cost + + +def cross_entropy_seq_with_mask(logits, target_seqs, input_mask, return_details=False, name=None): + """Returns the expression of cross-entropy of two sequences, implement + softmax internally. Normally be used for Dynamic RNN with Synced sequence input and output. + + Parameters + ----------- + logits : Tensor + 2D tensor with shape of [batch_size * ?, n_classes], `?` means dynamic IDs for each example. + - Can be get from `DynamicRNNLayer` by setting ``return_seq_2d`` to `True`. + target_seqs : Tensor + int of tensor, like word ID. [batch_size, ?], `?` means dynamic IDs for each example. + input_mask : Tensor + The mask to compute loss, it has the same size with `target_seqs`, normally 0 or 1. + return_details : boolean + Whether to return detailed losses. + - If False (default), only returns the loss. + - If True, returns the loss, losses, weights and targets (see source code). + + Examples + -------- + >>> import tensorlayer as tl + >>> import tensorflow as tf + >>> import numpy as np + >>> batch_size = 64 + >>> vocab_size = 10000 + >>> embedding_size = 256 + >>> ni = tl.layers.Input([batch_size, None], dtype=tf.int64) + >>> net = tl.layers.Embedding( + ... vocabulary_size = vocab_size, + ... embedding_size = embedding_size, + ... name = 'seq_embedding')(ni) + >>> net = tl.layers.RNN( + ... cell =tf.keras.layers.LSTMCell(units=embedding_size, dropout=0.1), + ... return_seq_2d = True, + ... name = 'dynamicrnn')(net) + >>> net = tl.layers.Dense(n_units=vocab_size, name="output")(net) + >>> model = tl.models.Model(inputs=ni, outputs=net) + >>> input_seqs = np.random.randint(0, 10, size=(batch_size, 10), dtype=np.int64) + >>> target_seqs = np.random.randint(0, 10, size=(batch_size, 10), dtype=np.int64) + >>> input_mask = np.random.randint(0, 2, size=(batch_size, 10), dtype=np.int64) + >>> outputs = model(input_seqs, is_train=True) + >>> loss = tl.cost.cross_entropy_seq_with_mask(outputs, target_seqs, input_mask) + + """ + targets = tf.reshape(target_seqs, [-1]) # to one vector + weights = tf.cast(tf.reshape(input_mask, [-1]), dtype=tf.float32) # to one vector like targets + losses = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits, labels=targets, name=name) * weights + # losses = tf.reduce_mean(tf.ops.sparse_softmax_cross_entropy_with_logits(logits=logits, labels=targets, name=name)) # for TF1.0 and others + + loss = tf.divide( + tf.reduce_sum(losses), # loss from mask. reduce_sum before element-wise mul with mask !! + tf.reduce_sum(weights), + name="seq_loss_with_mask" + ) + + if return_details: + return loss, losses, weights, targets + else: + return loss + + +def cosine_similarity(v1, v2): + """Cosine similarity [-1, 1]. + + Parameters + ---------- + v1, v2 : Tensor + Tensor with the same shape [batch_size, n_feature]. + + References + ---------- + - `Wiki `__. + + """ + + return tf.reduce_sum(tf.multiply(v1, v2), 1) / \ + (tf.sqrt(tf.reduce_sum(tf.multiply(v1, v1), 1)) * + tf.sqrt(tf.reduce_sum(tf.multiply(v2, v2), 1))) + + +# Regularization Functions +def li_regularizer(scale, scope=None): + """Li regularization removes the neurons of previous layer. The `i` represents `inputs`. + Returns a function that can be used to apply group li regularization to weights. + The implementation follows `TensorFlow contrib `__. + + Parameters + ---------- + scale : float + A scalar multiplier `Tensor`. 0.0 disables the regularizer. + scope: str + An optional scope name for this function. + + Returns + -------- + A function with signature `li(weights, name=None)` that apply Li regularization. + + Raises + ------ + ValueError : if scale is outside of the range [0.0, 1.0] or if scale is not a float. + + """ + if isinstance(scale, numbers.Integral): + raise ValueError('scale cannot be an integer: %s' % scale) + if isinstance(scale, numbers.Real): + if scale < 0.: + raise ValueError('Setting a scale less than 0 on a regularizer: %g' % scale) + if scale >= 1.: + raise ValueError('Setting a scale greater than 1 on a regularizer: %g' % scale) + if scale == 0.: + logging.info('Scale of 0 disables regularizer.') + return lambda _, name=None: None + + def li(weights): + """Applies li regularization to weights.""" + with tf.name_scope('li_regularizer') as scope: + my_scale = ops.convert_to_tensor(scale, dtype=weights.dtype.base_dtype, name='scale') + # if tf.__version__ <= '0.12': + # standard_ops_fn = standard_ops.mul + # else: + standard_ops_fn = standard_ops.multiply + return standard_ops_fn( + my_scale, standard_ops.reduce_sum(standard_ops.sqrt(standard_ops.reduce_sum(tf.square(weights), 1))), + name=scope + ) + + return li + + +def lo_regularizer(scale): + """Lo regularization removes the neurons of current layer. The `o` represents `outputs` + Returns a function that can be used to apply group lo regularization to weights. + The implementation follows `TensorFlow contrib `__. + + Parameters + ---------- + scale : float + A scalar multiplier `Tensor`. 0.0 disables the regularizer. + + Returns + ------- + A function with signature `lo(weights, name=None)` that apply Lo regularization. + + Raises + ------ + ValueError : If scale is outside of the range [0.0, 1.0] or if scale is not a float. + + """ + if isinstance(scale, numbers.Integral): + raise ValueError('scale cannot be an integer: %s' % scale) + + if isinstance(scale, numbers.Real): + if scale < 0.: + raise ValueError('Setting a scale less than 0 on a regularizer: %g' % scale) + if scale >= 1.: + raise ValueError('Setting a scale greater than 1 on a regularizer: %g' % scale) + if scale == 0.: + logging.info('Scale of 0 disables regularizer.') + return lambda _, name=None: None + + def lo(weights, name='lo_regularizer'): + """Applies group column regularization to weights.""" + with tf.name_scope(name) as scope: + my_scale = ops.convert_to_tensor(scale, dtype=weights.dtype.base_dtype, name='scale') + # if tf.__version__ <= '0.12': + # standard_ops_fn = standard_ops.mul + # else: + standard_ops_fn = standard_ops.multiply + return standard_ops_fn( + my_scale, standard_ops.reduce_sum(standard_ops.sqrt(standard_ops.reduce_sum(tf.square(weights), 0))), + name=scope + ) + + return lo + + +def maxnorm_regularizer(scale=1.0): + """Max-norm regularization returns a function that can be used to apply max-norm regularization to weights. + + More about max-norm, see `wiki-max norm `_. + The implementation follows `TensorFlow contrib `__. + + Parameters + ---------- + scale : float + A scalar multiplier `Tensor`. 0.0 disables the regularizer. + + Returns + --------- + A function with signature `mn(weights, name=None)` that apply Lo regularization. + + Raises + -------- + ValueError : If scale is outside of the range [0.0, 1.0] or if scale is not a float. + + """ + if isinstance(scale, numbers.Integral): + raise ValueError('scale cannot be an integer: %s' % scale) + + if isinstance(scale, numbers.Real): + if scale < 0.: + raise ValueError('Setting a scale less than 0 on a regularizer: %g' % scale) + # if scale >= 1.: + # raise ValueError('Setting a scale greater than 1 on a regularizer: %g' % + # scale) + if scale == 0.: + logging.info('Scale of 0 disables regularizer.') + return lambda _, name=None: None + + def mn(weights, name='max_regularizer'): + """Applies max-norm regularization to weights.""" + with tf.name_scope(name) as scope: + my_scale = ops.convert_to_tensor(scale, dtype=weights.dtype.base_dtype, name='scale') + # if tf.__version__ <= '0.12': + # standard_ops_fn = standard_ops.mul + # else: + standard_ops_fn = standard_ops.multiply + return standard_ops_fn(my_scale, standard_ops.reduce_max(standard_ops.abs(weights)), name=scope) + + return mn + + +def maxnorm_o_regularizer(scale): + """Max-norm output regularization removes the neurons of current layer. + Returns a function that can be used to apply max-norm regularization to each column of weight matrix. + The implementation follows `TensorFlow contrib `__. + + Parameters + ---------- + scale : float + A scalar multiplier `Tensor`. 0.0 disables the regularizer. + + Returns + --------- + A function with signature `mn_o(weights, name=None)` that apply Lo regularization. + + Raises + --------- + ValueError : If scale is outside of the range [0.0, 1.0] or if scale is not a float. + + """ + if isinstance(scale, numbers.Integral): + raise ValueError('scale cannot be an integer: %s' % scale) + + if isinstance(scale, numbers.Real): + if scale < 0.: + raise ValueError('Setting a scale less than 0 on a regularizer: %g' % scale) + # if scale >= 1.: + # raise ValueError('Setting a scale greater than 1 on a regularizer: %g' % + # scale) + if scale == 0.: + logging.info('Scale of 0 disables regularizer.') + return lambda _, name=None: None + + def mn_o(weights, name='maxnorm_o_regularizer'): + """Applies max-norm regularization to weights.""" + with tf.name_scope(name) as scope: + my_scale = ops.convert_to_tensor(scale, dtype=weights.dtype.base_dtype, name='scale') + if tf.__version__ <= '0.12': + standard_ops_fn = standard_ops.mul + else: + standard_ops_fn = standard_ops.multiply + return standard_ops_fn( + my_scale, standard_ops.reduce_sum(standard_ops.reduce_max(standard_ops.abs(weights), 0)), name=scope + ) + + return mn_o + + +def maxnorm_i_regularizer(scale): + """Max-norm input regularization removes the neurons of previous layer. + Returns a function that can be used to apply max-norm regularization to each row of weight matrix. + The implementation follows `TensorFlow contrib `__. + + Parameters + ---------- + scale : float + A scalar multiplier `Tensor`. 0.0 disables the regularizer. + + Returns + --------- + A function with signature `mn_i(weights, name=None)` that apply Lo regularization. + + Raises + --------- + ValueError : If scale is outside of the range [0.0, 1.0] or if scale is not a float. + + """ + if isinstance(scale, numbers.Integral): + raise ValueError('scale cannot be an integer: %s' % scale) + + if isinstance(scale, numbers.Real): + if scale < 0.: + raise ValueError('Setting a scale less than 0 on a regularizer: %g' % scale) + # if scale >= 1.: + # raise ValueError('Setting a scale greater than 1 on a regularizer: %g' % + # scale) + if scale == 0.: + logging.info('Scale of 0 disables regularizer.') + return lambda _, name=None: None + + def mn_i(weights, name='maxnorm_i_regularizer'): + """Applies max-norm regularization to weights.""" + with tf.name_scope(name) as scope: + my_scale = ops.convert_to_tensor(scale, dtype=weights.dtype.base_dtype, name='scale') + if tf.__version__ <= '0.12': + standard_ops_fn = standard_ops.mul + else: + standard_ops_fn = standard_ops.multiply + return standard_ops_fn( + my_scale, standard_ops.reduce_sum(standard_ops.reduce_max(standard_ops.abs(weights), 1)), name=scope + ) + + return mn_i + + +def huber_loss( + output, target, is_mean=True, delta=1.0, dynamichuber=False, reverse=False, axis=-1, epsilon=0.00001, name=None +): + """Huber Loss operation, see ``https://en.wikipedia.org/wiki/Huber_loss`` . + Reverse Huber Loss operation, see ''https://statweb.stanford.edu/~owen/reports/hhu.pdf''. + Dynamic Reverse Huber Loss operation, see ''https://arxiv.org/pdf/1606.00373.pdf''. + + Parameters + ---------- + output : Tensor + A distribution with shape: [batch_size, ....], (any dimensions). + target : Tensor + The target distribution, format the same with `output`. + is_mean : boolean + Whether compute the mean or sum for each example. + - If True, use ``tf.reduce_mean`` to compute the loss between one target and predict data (default). + - If False, use ``tf.reduce_sum``. + delta: float + The point where the huber loss function changes from a quadratic to linear. + dynamichuber: boolean + Whether compute the coefficient c for each batch. + - If True, c is 20% of the maximal per-batch error. + - If False, c is delta. + reverse: boolean + Whether compute the reverse huber loss. + axis : int or list of int + The dimensions to reduce. + epsilon: + Eplison. + name : string + Name of this loss. + + """ + if reverse: + if dynamichuber: + huber_c = 0.2 * tf.reduce_max(tf.abs(output - target)) + else: + huber_c = delta + if is_mean: + loss = tf.reduce_mean( + tf.where( + tf.less_equal(tf.abs(output - target), huber_c), tf.abs(output - target), + tf.multiply( + tf.pow(output - target, 2.0) + tf.pow(huber_c, 2.0), + tf.math.divide_no_nan(.5, huber_c + epsilon) + ) + ), name=name + ) + else: + loss = tf.reduce_mean( + tf.reduce_sum( + tf.where( + tf.less_equal(tf.abs(output - target), huber_c), tf.abs(output - target), + tf.multiply( + tf.pow(output - target, 2.0) + tf.pow(huber_c, 2.0), + tf.math.divide_no_nan(.5, huber_c + epsilon) + ) + ), axis + ), name=name + ) + elif is_mean: + loss = tf.reduce_mean( + tf.where( + tf.less_equal(tf.abs(output - target), delta), 0.5 * tf.pow(output - target, 2), + delta * (tf.abs(output - target) - 0.5 * delta) + ), name=name + ) + else: + loss = tf.reduce_mean( + tf.reduce_sum( + tf.where( + tf.less_equal(tf.abs(output - target), delta), 0.5 * tf.pow(output - target, 2), + delta * (tf.abs(output - target) - 0.5 * delta) + ), axis + ), name=name + ) + return loss diff --git a/tensorlayer/dataflow/__init__.py b/tensorlayer/dataflow/__init__.py new file mode 100644 index 0000000..d26b322 --- /dev/null +++ b/tensorlayer/dataflow/__init__.py @@ -0,0 +1,23 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +from __future__ import absolute_import, division, print_function + +from tensorlayer.backend.ops.load_backend import BACKEND + +if BACKEND == 'tensorflow': + from .tensorflow_data import * + from .tensorflow_image import * + +elif BACKEND == 'mindspore': + from .mindspore_data import * + from .mindspore_image import * + +elif BACKEND == 'dragon': + pass + +elif BACKEND == 'paddle': + pass + +else: + raise NotImplementedError("This backend is not supported") + diff --git a/tensorlayer/dataflow/dataflow_examples.py b/tensorlayer/dataflow/dataflow_examples.py new file mode 100644 index 0000000..2bee246 --- /dev/null +++ b/tensorlayer/dataflow/dataflow_examples.py @@ -0,0 +1,56 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorlayer as tl +from tensorlayer.dataflow import Dataset +import numpy as np + +X_train, y_train, X_test, y_test = tl.files.load_cifar10_dataset(shape=(-1, 32, 32, 3), plotable=False) + + +def generator_train(): + inputs = X_train + targets = y_train + if len(inputs) != len(targets): + raise AssertionError("The length of inputs and targets should be equal") + for _input, _target in zip(inputs, targets): + # yield _input.encode('utf-8'), _target.encode('utf-8') + yield (_input, np.array(_target)) + + +batch_size = 128 +shuffle_buffer_size = 128 +n_epoch = 10 + +import tensorflow as tf + + +def _map_fn_train(img, target): + # 1. Randomly crop a [height, width] section of the image. + img = tf.image.random_crop(img, [24, 24, 3]) + # 2. Randomly flip the image horizontally. + img = tf.image.random_flip_left_right(img) + # 3. Randomly change brightness. + img = tf.image.random_brightness(img, max_delta=63) + # 4. Randomly change contrast. + img = tf.image.random_contrast(img, lower=0.2, upper=1.8) + # 5. Subtract off the mean and divide by the variance of the pixels. + img = tf.image.per_image_standardization(img) + target = tf.reshape(target, ()) + return img, target + + +import multiprocessing +train_ds = Dataset.from_generator( + generator=generator_train, output_types=(tl.float32, tl.int32) +) # , output_shapes=((24, 24, 3), (1))) + +train_ds = train_ds.map(_map_fn_train, num_parallel_calls=multiprocessing.cpu_count()) + +train_ds = train_ds.repeat(n_epoch) +train_ds = train_ds.shuffle(shuffle_buffer_size) +train_ds = train_ds.prefetch(buffer_size=4096) +train_ds = train_ds.batch(batch_size) + +for X_batch, y_batch in train_ds: + print(X_batch.shape, y_batch.shape) diff --git a/tensorlayer/dataflow/image/__init__.py b/tensorlayer/dataflow/image/__init__.py new file mode 100644 index 0000000..df05229 --- /dev/null +++ b/tensorlayer/dataflow/image/__init__.py @@ -0,0 +1,2 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- diff --git a/tensorlayer/dataflow/mindspore_data.py b/tensorlayer/dataflow/mindspore_data.py new file mode 100644 index 0000000..fab1261 --- /dev/null +++ b/tensorlayer/dataflow/mindspore_data.py @@ -0,0 +1,277 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import mindspore.dataset as ds +import mindspore as ms +from enum import Enum +__all__ = [ + 'Apply', + 'Batch', + 'Concat', + 'CsvDataset', + 'Filter', + 'Flat_map', + 'FromGenerator', + 'FromSlices', + 'Map', + 'Prefetch', + 'Repeat', + 'Shuffle', + 'Skip', + 'Take', + 'TextFlieDataset', + 'TFRecordDataset', +] + + +class Shuffle(str, Enum): + GLOBAL: str = "global" + FILES: str = "file" + + +def Apply(dataset, transformation_func): + + return dataset.apply(transformation_func) + + +def Batch( + dataset, batch_size, drop_remainder=False, num_parallel_workers=None, per_batch_map=None, inut_columns=None, + output_columns=None, column_order=None, pad_info=None +): + ''' + Combine batch_size number of consecutive rows into batches. + Parameters + ---------- + dataset + batch_size + drop_remainder + num_parallel_workers + per_batch_map + inut_columns + output_columns + column_order + pad_info + + Returns + ------- + + ''' + return dataset.batch( + batch_size=batch_size, drop_remainder=drop_remainder, num_parallel_workers=num_parallel_workers, + per_batch_map=per_batch_map, input_columns=inut_columns, output_columns=output_columns, + column_order=column_order, pad_info=pad_info + ) + + +def Concat(dataset_1, dataset_2): + + return dataset_1.concat(dataset_2) + + +def CsvDataset( + file_pattern, batch_size=1, column_names=None, column_defaults=None, label_name=None, select_columns=None, + field_delim=',', use_quote_delim=True, na_value='', header=True, num_epochs=None, shuffle=Shuffle.GLOBAL, + shuffle_buffer_size=10000, shuffle_seed=None, prefetch_buffer_size=None, num_parallel_reads=None, sloppy=False, + num_rows_for_inference=100, compression_type=None, ignore_errors=False, numples_samples=None, num_shards=None, + shard_id=None, cache=None +): + """ + A source dataset that reads and parses comma-separated values (CSV) datasets. + + Examples: + >>> import mindspore.dataset as dataset + >>> + >>> dataset_files = ["/path/to/1", "/path/to/2"] # contains 1 or multiple text files + >>> dataset = dataset.CSVDataset(dataset_files=dataset_files, column_names=['col1', 'col2', 'col3', 'col4']) + """ + return ds.CSVDataset( + dataset_files=file_pattern, field_delim=field_delim, column_defaults=column_defaults, column_names=column_names, + num_samples=numples_samples, num_parallel_workers=num_parallel_reads, shuffle=shuffle, num_shards=num_shards, + shard_id=shard_id, cache=cache + ) + + +def Filter(dataset, predicate): + + return dataset.filter(predicate) + + +def Flat_map(dataset, map_func): + + return dataset.flat_map(map_func) + + +def FromGenerator( + generator, output_types, output_shapes=None, args=None, column_names=None, column_types=None, schema=None, + num_samples=None, num_parallel_workers=1, shuffle=None, sampler=None, num_shards=None, shard_id=None, + python_multiprocessing=True +): + + return ds.GeneratorDataset( + source=generator, column_names=column_names, column_types=column_types, schema=schema, num_samples=num_samples, + num_parallel_workers=num_parallel_workers, shuffle=shuffle, sampler=sampler, num_shards=num_shards, + shard_id=shard_id, python_multiprocessing=python_multiprocessing + ) + + +def FromSlices( + tensor, column_names=None, num_samples=None, num_parallel_workers=1, shuffle=None, sampler=None, num_shards=None, + shard_id=None +): + + return ds.NumpySlicesDataset( + data=tensor, column_names=column_names, num_samples=num_samples, num_parallel_workers=num_parallel_workers, + shuffle=shuffle, sampler=sampler, num_shards=num_shards, shard_id=shard_id + ) + + +def Map( + dataset, map_func, num_parallel_calls=None, input_columns=None, output_columns=None, column_order=None, + num_parallel_workers=None, python_multiprocessing=False, cache=None, callbacks=None +): + """ Maps map_func across the elements of this dataset. + + Parameters + ---------- + dataset : DataFlow + input DataFlow + map_func : function + A function mapping a dataset element to another dataset element. + num_parallel_calls + + Returns + ------- + + """ + return dataset.map( + operations=map_func, input_columns=input_columns, output_columns=output_columns, column_order=column_order, + num_parallel_workers=num_parallel_workers, python_multiprocessing=python_multiprocessing, cache=cache, + callbacks=callbacks + ) + + +def Prefetch(dataset, buffer_size): + + batch_size = dataset.get_batch_size() + prefetch_size = batch_size * buffer_size + + return dataset.config.set_prefetch_size(prefetch_size) + + + +def Repeat(dataset, count=None): + + + return dataset.repeat(count) + + +def Shuffle(dataset, buffer_size, seed=None, reshuffle_each_iteration=None): + + #dataset.config.set_seed(seed) + + return dataset.shuffle(buffer_size) + + +def Skip(dataset, count): + ''' + Creates a Dataset that skips count elements from this dataset. + Parameters + ---------- + dataset: + A dataset + count: + A tf.int64 scalar tf.Tensor, representing the number of elements of this dataset that should be skipped to form the new dataset. + + + Returns + ------- + + ''' + return dataset.skip(count) + + +def Take(dataset, count): + ''' + Creates a Dataset with at most count elements from this dataset. + Parameters + ---------- + dataset: + A dataset + count: + A tf.int64 scalar tf.Tensor, representing the number of elements of this dataset that should be taken to form the new dataset. + If count is -1, or if count is greater than the size of this dataset, the new dataset will contain all elements of this dataset. + Returns + ------- + + ''' + return dataset.take(count) + + +def TextFlieDataset( + filenames, compression_type=None, buffer_size=None, num_parallel_reads=None, num_samples=None, shuffle=None, + num_shards=None, shard_id=None, cache=None +): + """ + A source dataset that reads and parses datasets stored on disk in text format. + The generated dataset has one column ['text']. + + Examples: + >>> import mindspore.dataset as dataset + >>> + >>> dataset_files = ["/path/to/1", "/path/to/2"] # contains 1 or multiple text files + >>> dataset = dataset.TextFileDataset(dataset_files=dataset_files) + """ + if shuffle is None: + shuffle = Shuffle.GLOBAL + return ds.TextFileDataset( + dataset_files=filenames, num_samples=num_samples, num_parallel_workers=num_parallel_reads, shuffle=shuffle, + num_shards=num_shards, shard_id=shard_id, cache=cache + ) + + +def TFRecordDataset( + filenames, compression_type=None, buffer_size=None, num_parallel_reads=None, schema=None, columns_list=None, + num_samples=None, shuffle=None, num_shards=None, shard_id=None, shard_equal_rows=False, cache=None +): + """ + A source dataset that reads and parses datasets stored on disk in TFData format. + + Examples: + >>> import mindspore.dataset as dataset + >>> import mindspore.common.dtype as mstype + >>> + >>> dataset_files = ["/path/to/1", "/path/to/2"] # contains 1 or multiple tf data files + >>> + >>> # 1) Get all rows from dataset_files with no explicit schema + >>> # The meta-data in the first row will be used as a schema. + >>> tfdataset = dataset.TFRecordDataset(dataset_files=dataset_files) + >>> + >>> # 2) Get all rows from dataset_files with user-defined schema + >>> schema = dataset.Schema() + >>> schema.add_column('col_1d', de_type=mindspore.int64, shape=[2]) + >>> tfdataset = dataset.TFRecordDataset(dataset_files=dataset_files, schema=schema) + >>> + >>> # 3) Get all rows from dataset_files with schema file "./schema.json" + >>> tfdataset = dataset.TFRecordDataset(dataset_files=dataset_files, schema="./schema.json") + """ + if shuffle is None: + shuffle = Shuffle.GLOBAL + return ds.TFRecordDataset( + dataset_files=filenames, schema=schema, columns_list=columns_list, num_samples=num_samples, + num_parallel_workers=num_parallel_reads, shuffle=shuffle, num_shards=num_shards, shard_id=shard_id, + shard_equal_rows=shard_equal_rows, cache=cache + ) + + +def Zip(datasets): + ''' + Creates a Dataset by zipping together the given datasets. + Parameters + ---------- + datasets: + A tuple of datasets to be zipped together. + Returns + ------- + + ''' + return ds.zip(datasets) diff --git a/tensorlayer/dataflow/mindspore_image.py b/tensorlayer/dataflow/mindspore_image.py new file mode 100644 index 0000000..e4c1fd9 --- /dev/null +++ b/tensorlayer/dataflow/mindspore_image.py @@ -0,0 +1,305 @@ +import mindspore.dataset as ms +import mindspore.dataset.vision.c_transforms as c_vision +import mindspore.dataset.vision.py_transforms as py_vision +import mindspore.dataset.vision.py_transforms_util as py_util +import numpy as np +from PIL import Image, ImageOps, ImageEnhance, __version__ + +__all__ = [ + 'CentralCrop', 'HsvToRgb', 'AdjustBrightness', 'AdjustContrast', 'AdjustHue', 'Crop', 'FlipHorizontal', + 'FlipVertical', 'GrayToRgb', 'RgbToGray', 'PadToBoundingBox' +] + +augment_error_message = 'img should be PIL image. Got {}. Use Decode() for encoded data or ToPIL() for decoded data.' + + +def CentralCrop(image, central_fraction=None, size=None): + ''' + + Parameters + ---------- + image : + input Either a 3-D float Tensor of shape [height, width, depth], + or a 4-D Tensor of shape [batch_size, height, width, depth]. + central_fraction : + float (0, 1], fraction of size to crop + size: + size (Union[int, sequence]) – The output size of the cropped image. If size is an integer, a square crop of size (size, size) is returned. + If size is a sequence of length 2, it should be (height, width). + Returns : + 3-D / 4-D float Tensor, as per the input. + ------- + ''' + if size is None and central_fraction is None: + raise ValueError('central_fraction and size can not be both None') + + if size is None: + outshape = np.shape(image) + if len(outshape) == 3: + h_axis = 0 + w_axis = 1 + elif len(outshape) == 4: + h_axis = 1 + w_axis = 2 + + height = outshape[h_axis] + width = outshape[w_axis] + + target_height = height * central_fraction + target_width = width * central_fraction + + size = (target_height, target_width) + + return py_util.center_crop(image, size) + + +def HsvToRgb(image, is_hwc=True): + + image = np.asarray(image) + + return py_util.hsv_to_rgbs(image, is_hwc=is_hwc) + + +def AdjustBrightness(image, factor): + ''' + + Parameters + ---------- + image: + input NumPy image array or PIL image + factor: + factor should be in the range (-1,1) + Returns: + ------- + np darray image + ''' + + image = np.asarray(image) + image = image / 255 + image = image + factor + index = np.where(image > 1) + image[index] = 1 + index = np.where(image < 0) + image[index] = 0 + image = image * 255 + + return image + + +def AdjustContrast(image, factor): + + if isinstance(image, np.ndarray): + image = Image.fromarray(image) + if not isinstance(image, Image.Image): + raise TypeError(augment_error_message.format(type(image))) + + image = ImageEnhance.Contrast(image).enhance(factor) + + image = np.array(image) + + return image + + +def AdjustHue(image, factor): + + if isinstance(image, np.ndarray): + image = Image.fromarray(image) + if not isinstance(image, Image.Image): + raise TypeError(augment_error_message.format(type(image))) + + image_hue_factor = factor + if not -1 <= image_hue_factor <= 1: + raise ValueError('image_hue_factor {} is not in [-1, 1].'.format(image_hue_factor)) + + if not isinstance(image, Image.Image): + raise TypeError(augment_error_message.format(type(image))) + + mode = image.mode + if mode in {'L', '1', 'I', 'F'}: + return image + + hue, saturation, value = image.convert('HSV').split() + + np_hue = np.array(hue, dtype=np.uint8) + + with np.errstate(over='ignore'): + np_hue += np.uint8(image_hue_factor * 255) + hue = Image.fromarray(np_hue, 'L') + + image = Image.merge('HSV', (hue, saturation, value)).convert(mode) + return image + + +def AdjustSaturation(image, factor): + + if isinstance(image, np.ndarray): + image = Image.fromarray(image) + if not isinstance(image, Image.Image): + raise TypeError(augment_error_message.format(type(image))) + + enhancer = ImageEnhance.Color(image) + image = enhancer.enhance(factor) + return image + + +def Crop(image, offset_height, offset_width, target_height, target_width): + + if isinstance(image, np.ndarray): + image = Image.fromarray(image) + if not isinstance(image, Image.Image): + raise TypeError(augment_error_message.format(type(image))) + image = np.array( + image.crop((offset_width, offset_height, offset_width + target_width, offset_width + target_height)) + ) + return image + + +def FlipHorizontal(image): + + if isinstance(image, np.ndarray): + image = Image.fromarray(image) + if not isinstance(image, Image.Image): + raise TypeError(augment_error_message.format(type(image))) + + image = np.array(image.transpose(Image.FLIP_LEFT_RIGHT)) + + return image + + +def FlipVertical(image): + + if isinstance(image, np.ndarray): + image = Image.fromarray(image) + if not isinstance(image, Image.Image): + raise TypeError(augment_error_message.format(type(image))) + + image = np.array(image.transpose(Image.FLIP_TOP_BOTTOM)) + + return image + + +def GrayToRgb(image): + + image = np.asarray(image) + shape = image.shape + output_image = np.zeros((shape[0], shape[1], 3), dtype=np.uint8) + if len(shape) == 3: + for i in range(3): + output_image[:, :, i] = image[:, :, 1] + elif len(shape) == 2: + for i in range(3): + output_image[:, :, i] = image + + return output_image + + +def RgbToGray(image): + + if isinstance(image, np.ndarray): + image = Image.fromarray(image) + if not isinstance(image, Image.Image): + raise TypeError(augment_error_message.format(type(image))) + ''' + 将彩色图像转换为灰度(模式“L”)时,库使用ITU-R 601-2 Luma转换: + L = R * 299/1000 + G * 587/1000 + B * 114/1000 + ''' + image = image.convert('L') + image = np.asarray(image) + + return image + + +def PadToBoundingBox(image, offset_height, offset_width, target_height, target_width): + ''' + + Parameters + ---------- + image: + A PIL image + offset_height: + Number of rows of zeros to add on top. + offset_width: + Number of columns of zeros to add on the left. + target_height: + Height of output image. + target_width + Width of output image. + Returns + A numpy ndarray image + ------- + ''' + + if offset_height < 0: + raise ValueError("offset_height must be >= 0") + if offset_width < 0: + raise ValueError("offset_width must be >= 0") + image = np.array(image) + shape = image.shape + top = offset_height + bottom = target_height - shape[0] - top + left = offset_width + right = target_width - shape[1] - left + + if bottom < 0: + raise ValueError("target_height must be >= offset_height + height") + + if right < 0: + raise ValueError("target_width must be >= offset_width + width") + + return np.pad(image, ((top, bottom), (left, right), (0, 0)), mode='constant') + + +def Standardization(image, mean=None, std=None, channel_mode=False): + ''' + + Parameters + ---------- + image: + An n-D Tensor with at least 3 dimensions, the last 3 of which are the dimensions of each image. + mean: + List or tuple of mean values for each channel, with respect to channel order. + std: + List or tuple of standard deviations for each channel. + channel_mode: + Decide to implement standardization on whole image or each channel of image. + Returns: + A Tensor with the same shape and dtype as image. + ------- + ''' + image = np.array(image, dtype=np.float32) + num_shape = image.shape + if mean is not None and std is not None: + if len(mean) != len(std): + raise ValueError("Length of mean and std must be equal") + if len(mean) == 1: + mean = [mean[0]] * num_shape[2] + std = [std[0]] * num_shape[2] + mean = np.array(mean, dtype=image.dtype) + std = np.array(std, dtype=image.dtype) + return (image - mean[:, None, None]) / std[:, None, None] + elif mean is None and std is None: + if channel_mode: + num_pixels = num_shape[0] * num_shape[1] + image_mean = np.mean(image, axis=(0, 1)) + stddev = np.std(image, axis=(0, 1)) + min_sttdev = 1 / np.sqrt(num_pixels) + min_sttdev = [min_sttdev] * num_shape[2] + adjusted_sttdev = np.maximum(stddev, min_sttdev) + + image -= image_mean + image = np.divide(image, adjusted_sttdev) + return image + else: + num_pixels = num_shape[0] * num_shape[1] * num_shape[2] + image_mean = np.mean(image, axis=(0, 1, 2)) + image_mean = [image_mean] * 3 + stddev = np.std(image, axis=(0, 1, 2)) + min_sttdev = 1 / np.sqrt(num_pixels) + adjusted_sttdev = np.maximum(stddev, min_sttdev) + adjusted_sttdev = [adjusted_sttdev] * 3 + + image -= image_mean + image = np.divide(image, adjusted_sttdev) + return image + else: + raise ValueError('std and mean must both be None or not None') diff --git a/tensorlayer/dataflow/tensorflow_data.py b/tensorlayer/dataflow/tensorflow_data.py new file mode 100644 index 0000000..ce50c77 --- /dev/null +++ b/tensorlayer/dataflow/tensorflow_data.py @@ -0,0 +1,254 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorflow as tf + +__all__ = [ + 'Apply', + 'Batch', + 'Concat', + 'CsvDataset', + 'Filter', + 'Flat_map', + 'FromGenerator', + 'FromSlices', + 'Map', + 'Prefetch', + 'Repeat', + 'Shuffle', + 'Skip', + 'Take', + 'TextFlieDataset', + 'TFRecordDataset', + 'Zip', +] + + +def Apply(dataset, transformation_func): + """Applies a transformation function to this dataset. + `apply` enables chaining of custom `Dataset` transformations, which are + represented as functions that take one `Dataset` argument and return a + transformed `Dataset`. + >>> dataset = tf.data.Dataset.range(100) + >>> def dataset_fn(dataset): + ... return dataset.filter(lambda x: x < 5) + >>> dataset = dataset.apply(dataset_fn) + >>> list(dataset.as_numpy_iterator()) + [0, 1, 2, 3, 4] + Args: + transformation_func: A function that takes one `Dataset` argument and + returns a `Dataset`. + Returns: + Dataset: The `Dataset` returned by applying `transformation_func` to this + dataset. + """ + return dataset.apply(transformation_func) + + +def Batch(dataset, batch_size, drop_remainder=False): + ''' + + Parameters + ---------- + dataset + batch_size + drop_remainder + + Returns + ------- + + ''' + return dataset.batch(batch_size=batch_size, drop_remainder=drop_remainder) + + +def Concat(dataset_1, dataset_2): + + return dataset_1.concatenate(dataset_2) + + +def CsvDataset( + file_pattern, batch_size=1, column_names=None, column_defaults=None, label_name=None, select_columns=None, + field_delim=',', use_quote_delim=True, na_value='', header=True, num_epochs=None, shuffle=True, + shuffle_buffer_size=10000, shuffle_seed=None, prefetch_buffer_size=None, num_parallel_reads=None, sloppy=False, + num_rows_for_inference=100, compression_type=None, ignore_errors=False, numples_samples=None, num_shards=None, + shard_id=None, cache=None +): + """Reads CSV files into a dataset. + Reads CSV files into a dataset, where each element is a (features, labels) + tuple that corresponds to a batch of CSV rows. The features dictionary + maps feature column names to `Tensor`s containing the corresponding + feature data, and labels is a `Tensor` containing the batch's label data. + """ + return tf.data.experimental.make_csv_dataset( + file_pattern, batch_size, column_names=None, column_defaults=None, label_name=None, select_columns=None, + field_delim=',', use_quote_delim=True, na_value='', header=True, num_epochs=None, shuffle=True, + shuffle_buffer_size=10000, shuffle_seed=None, prefetch_buffer_size=None, num_parallel_reads=None, sloppy=False, + num_rows_for_inference=100, compression_type=None, ignore_errors=False + ) + + +def Filter(dataset, predicate): + ''' + Filters this dataset according to predicate. + Parameters + ---------- + dataset : + A dataset + predicate : + A function mapping a dataset element to a boolean. + Returns : + The Dataset containing the elements of this dataset for which predicate is True. + ------- + + ''' + return dataset.filter(predicate) + + +def Flat_map(dataset, map_func): + ''' + Maps map_func across this dataset and flattens the result. + Parameters + ---------- + dataset: + A dataset + map_func + A function mapping a dataset element to a dataset. + Returns + A Dataset. + ------- + + ''' + return dataset.flat_map(map_func) + + +def FromGenerator( + generator, output_types, output_shapes=None, args=None, column_names=None, column_types=None, schema=None, + num_samples=None, num_parallel_workers=1, shuffle=None, sampler=None, num_shards=None, shard_id=None, + python_multiprocessing=True +): + """Creates a `Dataset` whose elements are generated by `generator`. + + generator: + A callable object + """ + return tf.data.Dataset.from_generator(generator, output_types, output_shapes=output_shapes, args=args) + + +def FromSlices( + tensor, column_names=None, num_samples=None, num_parallel_workers=1, shuffle=None, sampler=None, num_shards=None, + shard_id=None +): + + return tf.data.Dataset.from_tensor_slices(tensor) + + +def Map( + dataset, map_func, num_parallel_calls=None, input_columns=None, output_columns=None, column_order=None, + num_parallel_workers=None, python_multiprocessing=False, cache=None, callbacks=None +): + """ Maps map_func across the elements of this dataset. + + Parameters + ---------- + dataset : DataFlow + input DataFlow + map_func : function + A function mapping a dataset element to another dataset element. + num_parallel_calls + + Returns + ------- + + """ + return dataset.map(map_func, num_parallel_calls=num_parallel_calls) + + +def Prefetch(dataset, buffer_size): + ''' + Creates a Dataset that prefetches elements from this dataset. + Parameters + ---------- + dataset: Dataflow + A dataset + buffer_size : + A tf.int64 scalar tf.Tensor, representing the maximum number of elements that will be buffered when prefetching. + Returns + A Dataset + ------- + + ''' + return dataset.prefetch(buffer_size=buffer_size) + + +def Repeat(dataset, count=None): + return dataset.repeat(count=count) + + +def Shuffle(dataset, buffer_size, seed=None, reshuffle_each_iteration=None): + return dataset.shuffle(buffer_size, seed=seed, reshuffle_each_iteration=reshuffle_each_iteration) + + +def Skip(dataset, count): + ''' + Creates a Dataset that skips count elements from this dataset. + Parameters + ---------- + dataset: + A dataset + count: + A tf.int64 scalar tf.Tensor, representing the number of elements of this dataset that should be skipped to form the new dataset. + If count is greater than the size of this dataset, the new dataset will contain no elements. + If count is -1, skips the entire dataset. + + Returns + ------- + + ''' + return dataset.skip(count) + + +def Take(dataset, count): + ''' + Creates a Dataset with at most count elements from this dataset. + Parameters + ---------- + dataset: + A dataset + count: + A tf.int64 scalar tf.Tensor, representing the number of elements of this dataset that should be taken to form the new dataset. + If count is -1, or if count is greater than the size of this dataset, the new dataset will contain all elements of this dataset. + Returns + ------- + + ''' + return dataset.take(count) + + +def TextFlieDataset( + filenames, compression_type=None, buffer_size=None, num_parallel_reads=None, num_samples=None, shuffle=None, + num_shards=None, shard_id=None, cache=None +): + + return tf.data.TextLineDataset(filenames, compression_type, buffer_size, num_parallel_reads) + + +def TFRecordDataset( + filenames, compression_type=None, buffer_size=None, num_parallel_reads=None, schema=None, columns_list=None, + num_samples=None, shuffle=None, num_shards=None, shard_id=None, shard_equal_rows=False, cache=None +): + + return tf.data.TFRecordDataset(filenames, compression_type, buffer_size, num_parallel_reads) + + +def Zip(datasets): + ''' + Creates a Dataset by zipping together the given datasets. + Parameters + ---------- + datasets: + A tuple of datasets to be zipped together. + Returns + ------- + + ''' + return tf.data.Dataset.zip(datasets) diff --git a/tensorlayer/dataflow/tensorflow_image.py b/tensorlayer/dataflow/tensorflow_image.py new file mode 100644 index 0000000..39419b9 --- /dev/null +++ b/tensorlayer/dataflow/tensorflow_image.py @@ -0,0 +1,200 @@ +import tensorflow as tf +import numpy as np +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import array_ops +from tensorflow.python.framework import ops +from tensorflow.python.ops.image_ops_impl import _AssertAtLeast3DImage +from tensorflow.python.framework import dtypes +from tensorflow.python.ops.image_ops_impl import convert_image_dtype +__all__ = [ + 'CentralCrop', + 'HsvToRgb', + 'AdjustBrightness', + 'AdjustContrast', + 'AdjustHue', + 'AdjustSaturation', + 'Crop', + 'FlipHorizontal', + 'FlipVertical', + 'GrayToRgb', + 'Standardization', +] + + +def CentralCrop(image, central_fraction=None, size=None): + ''' + + Parameters + ---------- + image : + input Either a 3-D float Tensor of shape [height, width, depth], + or a 4-D Tensor of shape [batch_size, height, width, depth]. + central_fraction : + float (0, 1], fraction of size to crop + size: + size (Union[int, sequence]) – The output size of the cropped image. If size is an integer, a square crop of size (size, size) is returned. + If size is a sequence of length 2, it should be (height, width). + Returns : + 3-D / 4-D float Tensor, as per the input. + ------- + ''' + if size is None and central_fraction is None: + raise ValueError('central_fraction and size can not be both None') + + if central_fraction is None: + outshape = np.shape(image) + if len(outshape) == 3: + h_axis = 0 + w_axis = 1 + elif len(outshape) == 4: + h_axis = 1 + w_axis = 2 + + if isinstance(size, int): + target_height = size + target_width = size + elif isinstance(size, tuple): + target_height = size[0] + target_width = size[1] + + central_fraction = max(target_height // outshape[h_axis], target_width // outshape[w_axis]) + + return tf.image.central_crop(image, central_fraction) + + +def HsvToRgb(image): + + return tf.image.hsv_to_rgb(image) + + +def AdjustBrightness(image, factor): + + return tf.image.adjust_brightness(image, delta=factor) + + +def AdjustContrast(image, factor): + + return tf.image.adjust_contrast(image, contrast_factor=factor) + + +def AdjustHue(image, factor): + + return tf.image.adjust_hue(image, delta=factor) + + +def AdjustSaturation(image, factor): + + return tf.image.adjust_saturation(image, saturation_factor=factor) + + +def Crop(image, offset_height, offset_width, target_height, target_width): + ''' + + Parameters + ---------- + image: + A image or a batch of images + offset_height: + Vertical coordinate of the top-left corner of the result in the input. + offset_width: + Horizontal coordinate of the top-left corner of the result in the input. + target_height: + Height of the result. + target_width: + Width of the result. + + Returns: + Output [batch, target_height, target_width, channels] or [target_height, target_width, channels] + ------- + ''' + + return tf.image.crop_to_bounding_box(image, offset_height, offset_width, target_height, target_width) + + +def FlipHorizontal(image): + + return tf.image.flip_left_right(image) + + +def FlipVertical(image): + + return tf.image.flip_up_down(image) + + +def GrayToRgb(image): + + return tf.image.grayscale_to_rgb(image) + + +def RgbToGray(image): + + return tf.image.rgb_to_grayscale(image) + + +def PadToBoundingBox(image, offset_height, offset_width, target_height, target_width): + + return tf.image.pad_to_bounding_box(image, offset_height, offset_width, target_height, target_width) + + +def Standardization(image, mean=None, std=None, channel_mode=False): + ''' + + Parameters + ---------- + image: + An n-D Tensor with at least 3 dimensions, the last 3 of which are the dimensions of each image. + mean: + List or tuple of mean values for each channel, with respect to channel order. + std: + List or tuple of standard deviations for each channel. + channel_mode: + Decide to implement standardization on whole image or each channel of image. + Returns: + A Tensor with the same shape and dtype as image. + ------- + ''' + with ops.name_scope(None, 'Standardization', [image]) as scope: + image = ops.convert_to_tensor(image, name='image') + image = _AssertAtLeast3DImage(image) + + orig_dtype = image.dtype + if orig_dtype not in [dtypes.float16, dtypes.float32]: + image = convert_image_dtype(image, dtypes.float32) + + if mean is not None and std is not None: + mean = np.array(mean, dtype=np.float32) + std = np.array(std, dtype=np.float32) + image -= mean + image = math_ops.divide(image, std, name=scope) + return convert_image_dtype(image, orig_dtype, saturate=True) + + elif mean is None and std is None: + if channel_mode: + num_pixels = math_ops.reduce_prod(array_ops.shape(image)[-3:-1]) + #`num_pixels` is the number of elements in each channels of 'image' + image_mean = math_ops.reduce_mean(image, axis=[-2, -3], keepdims=True) + # `image_mean` is the mean of elements in each channels of 'image' + + stddev = math_ops.reduce_std(image, axis=[-2, -3], keepdims=True) + min_stddev = math_ops.rsqrt(math_ops.cast(num_pixels, image.dtype)) + adjusted_sttdev = math_ops.maximum(stddev, min_stddev) + + image -= image_mean + image = math_ops.divide(image, adjusted_sttdev, name=scope) + return convert_image_dtype(image, orig_dtype, saturate=True) + + else: + num_pixels = math_ops.reduce_prod(array_ops.shape(image)[-3:]) + #`num_pixels` is the number of elements in `image` + image_mean = math_ops.reduce_mean(image, axis=[-1, -2, -3], keepdims=True) + + # Apply a minimum normalization that protects us against uniform images. + stddev = math_ops.reduce_std(image, axis=[-1, -2, -3], keepdims=True) + min_stddev = math_ops.rsqrt(math_ops.cast(num_pixels, image.dtype)) + adjusted_stddev = math_ops.maximum(stddev, min_stddev) + + image -= image_mean + image = math_ops.divide(image, adjusted_stddev, name=scope) + return convert_image_dtype(image, orig_dtype, saturate=True) + else: + raise ValueError('std and mean must both be None or not None') diff --git a/tensorlayer/db.py b/tensorlayer/db.py new file mode 100644 index 0000000..129e251 --- /dev/null +++ b/tensorlayer/db.py @@ -0,0 +1,746 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import os +import pickle +import sys +import time +from datetime import datetime + +import numpy as np +import tensorflow as tf + +import gridfs +import pymongo +from tensorlayer import logging +from tensorlayer.files import ( + assign_weights, del_folder, exists_or_mkdir, load_hdf5_to_weights, save_weights_to_hdf5, static_graph2net +) + + +class TensorHub(object): + """It is a MongoDB based manager that help you to manage data, network architecture, parameters and logging. + + Parameters + ------------- + ip : str + Localhost or IP address. + port : int + Port number. + dbname : str + Database name. + username : str or None + User name, set to None if you do not need authentication. + password : str + Password. + project_name : str or None + Experiment key for this entire project, similar with the repository name of Github. + + Attributes + ------------ + ip, port, dbname and other input parameters : see above + See above. + project_name : str + The given project name, if no given, set to the script name. + db : mongodb client + See ``pymongo.MongoClient``. + """ + + # @deprecated_alias(db_name='dbname', user_name='username', end_support_version=2.1) + def __init__( + self, ip='localhost', port=27017, dbname='dbname', username='None', password='password', project_name=None + ): + self.ip = ip + self.port = port + self.dbname = dbname + self.username = username + + print("[Database] Initializing ...") + # connect mongodb + client = pymongo.MongoClient(ip, port) + self.db = client[dbname] + if username is None: + print(username, password) + self.db.authenticate(username, password) + else: + print("[Database] No username given, it works if authentication is not required") + if project_name is None: + self.project_name = sys.argv[0].split('.')[0] + print("[Database] No project_name given, use {}".format(self.project_name)) + else: + self.project_name = project_name + + # define file system (Buckets) + self.dataset_fs = gridfs.GridFS(self.db, collection="datasetFilesystem") + self.model_fs = gridfs.GridFS(self.db, collection="modelfs") + # self.params_fs = gridfs.GridFS(self.db, collection="parametersFilesystem") + # self.architecture_fs = gridfs.GridFS(self.db, collection="architectureFilesystem") + + print("[Database] Connected ") + _s = "[Database] Info:\n" + _s += " ip : {}\n".format(self.ip) + _s += " port : {}\n".format(self.port) + _s += " dbname : {}\n".format(self.dbname) + _s += " username : {}\n".format(self.username) + _s += " password : {}\n".format("*******") + _s += " project_name : {}\n".format(self.project_name) + self._s = _s + print(self._s) + + def __str__(self): + """Print information of databset.""" + return self._s + + def _fill_project_info(self, args): + """Fill in project_name for all studies, architectures and parameters.""" + return args.update({'project_name': self.project_name}) + + @staticmethod + def _serialization(ps): + """Serialize data.""" + return pickle.dumps(ps, protocol=pickle.HIGHEST_PROTOCOL) # protocol=2) + # with open('_temp.pkl', 'wb') as file: + # return pickle.dump(ps, file, protocol=pickle.HIGHEST_PROTOCOL) + + @staticmethod + def _deserialization(ps): + """Deseralize data.""" + return pickle.loads(ps) + + # =========================== MODELS ================================ + def save_model(self, network=None, model_name='model', **kwargs): + """Save model architecture and parameters into database, timestamp will be added automatically. + + Parameters + ---------- + network : TensorLayer Model + TensorLayer Model instance. + model_name : str + The name/key of model. + kwargs : other events + Other events, such as name, accuracy, loss, step number and etc (optinal). + + Examples + --------- + Save model architecture and parameters into database. + >>> db.save_model(net, accuracy=0.8, loss=2.3, name='second_model') + + Load one model with parameters from database (run this in other script) + >>> net = db.find_top_model(accuracy=0.8, loss=2.3) + + Find and load the latest model. + >>> net = db.find_top_model(sort=[("time", pymongo.DESCENDING)]) + >>> net = db.find_top_model(sort=[("time", -1)]) + + Find and load the oldest model. + >>> net = db.find_top_model(sort=[("time", pymongo.ASCENDING)]) + >>> net = db.find_top_model(sort=[("time", 1)]) + + Get model information + >>> net._accuracy + ... 0.8 + + Returns + --------- + boolean : True for success, False for fail. + """ + kwargs.update({'model_name': model_name}) + self._fill_project_info(kwargs) # put project_name into kwargs + + # params = network.get_all_params() + params = network.all_weights + + s = time.time() + + # kwargs.update({'architecture': network.all_graphs, 'time': datetime.utcnow()}) + kwargs.update({'architecture': network.config, 'time': datetime.utcnow()}) + + try: + params_id = self.model_fs.put(self._serialization(params)) + kwargs.update({'params_id': params_id, 'time': datetime.utcnow()}) + self.db.Model.insert_one(kwargs) + print("[Database] Save model: SUCCESS, took: {}s".format(round(time.time() - s, 2))) + return True + except Exception as e: + exc_type, exc_obj, exc_tb = sys.exc_info() + fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] + logging.info("{} {} {} {} {}".format(exc_type, exc_obj, fname, exc_tb.tb_lineno, e)) + print("[Database] Save model: FAIL") + return False + + def find_top_model(self, sort=None, model_name='model', **kwargs): + """Finds and returns a model architecture and its parameters from the database which matches the requirement. + + Parameters + ---------- + sort : List of tuple + PyMongo sort comment, search "PyMongo find one sorting" and `collection level operations `__ for more details. + model_name : str or None + The name/key of model. + kwargs : other events + Other events, such as name, accuracy, loss, step number and etc (optinal). + + Examples + --------- + - see ``save_model``. + + Returns + --------- + network : TensorLayer Model + Note that, the returned network contains all information of the document (record), e.g. if you saved accuracy in the document, you can get the accuracy by using ``net._accuracy``. + """ + # print(kwargs) # {} + kwargs.update({'model_name': model_name}) + self._fill_project_info(kwargs) + + s = time.time() + + d = self.db.Model.find_one(filter=kwargs, sort=sort) + + # _temp_file_name = '_find_one_model_ztemp_file' + if d is not None: + params_id = d['params_id'] + graphs = d['architecture'] + _datetime = d['time'] + # exists_or_mkdir(_temp_file_name, False) + # with open(os.path.join(_temp_file_name, 'graph.pkl'), 'wb') as file: + # pickle.dump(graphs, file, protocol=pickle.HIGHEST_PROTOCOL) + else: + print("[Database] FAIL! Cannot find model: {}".format(kwargs)) + return False + try: + params = self._deserialization(self.model_fs.get(params_id).read()) + # TODO : restore model and load weights + network = static_graph2net(graphs) + assign_weights(weights=params, network=network) + # np.savez(os.path.join(_temp_file_name, 'params.npz'), params=params) + # + # network = load_graph_and_params(name=_temp_file_name, sess=sess) + # del_folder(_temp_file_name) + + pc = self.db.Model.find(kwargs) + print( + "[Database] Find one model SUCCESS. kwargs:{} sort:{} save time:{} took: {}s".format( + kwargs, sort, _datetime, round(time.time() - s, 2) + ) + ) + + # FIXME : not sure what's this for + # put all informations of model into the TL layer + # for key in d: + # network.__dict__.update({"_%s" % key: d[key]}) + + # check whether more parameters match the requirement + params_id_list = pc.distinct('params_id') + n_params = len(params_id_list) + if n_params != 1: + print(" Note that there are {} models match the kwargs".format(n_params)) + return network + except Exception as e: + exc_type, exc_obj, exc_tb = sys.exc_info() + fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] + logging.info("{} {} {} {} {}".format(exc_type, exc_obj, fname, exc_tb.tb_lineno, e)) + return False + + def delete_model(self, **kwargs): + """Delete model. + + Parameters + ----------- + kwargs : logging information + Find items to delete, leave it empty to delete all log. + """ + self._fill_project_info(kwargs) + self.db.Model.delete_many(kwargs) + logging.info("[Database] Delete Model SUCCESS") + + # =========================== DATASET =============================== + def save_dataset(self, dataset=None, dataset_name=None, **kwargs): + """Saves one dataset into database, timestamp will be added automatically. + + Parameters + ---------- + dataset : any type + The dataset you want to store. + dataset_name : str + The name of dataset. + kwargs : other events + Other events, such as description, author and etc (optinal). + + Examples + ---------- + Save dataset + >>> db.save_dataset([X_train, y_train, X_test, y_test], 'mnist', description='this is a tutorial') + + Get dataset + >>> dataset = db.find_top_dataset('mnist') + + Returns + --------- + boolean : Return True if save success, otherwise, return False. + """ + self._fill_project_info(kwargs) + if dataset_name is None: + raise Exception("dataset_name is None, please give a dataset name") + kwargs.update({'dataset_name': dataset_name}) + + s = time.time() + try: + dataset_id = self.dataset_fs.put(self._serialization(dataset)) + kwargs.update({'dataset_id': dataset_id, 'time': datetime.utcnow()}) + self.db.Dataset.insert_one(kwargs) + # print("[Database] Save params: {} SUCCESS, took: {}s".format(file_name, round(time.time()-s, 2))) + print("[Database] Save dataset: SUCCESS, took: {}s".format(round(time.time() - s, 2))) + return True + except Exception as e: + exc_type, exc_obj, exc_tb = sys.exc_info() + fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] + logging.info("{} {} {} {} {}".format(exc_type, exc_obj, fname, exc_tb.tb_lineno, e)) + print("[Database] Save dataset: FAIL") + return False + + def find_top_dataset(self, dataset_name=None, sort=None, **kwargs): + """Finds and returns a dataset from the database which matches the requirement. + + Parameters + ---------- + dataset_name : str + The name of dataset. + sort : List of tuple + PyMongo sort comment, search "PyMongo find one sorting" and `collection level operations `__ for more details. + kwargs : other events + Other events, such as description, author and etc (optinal). + + Examples + --------- + Save dataset + >>> db.save_dataset([X_train, y_train, X_test, y_test], 'mnist', description='this is a tutorial') + + Get dataset + >>> dataset = db.find_top_dataset('mnist') + >>> datasets = db.find_datasets('mnist') + + Returns + -------- + dataset : the dataset or False + Return False if nothing found. + + """ + + self._fill_project_info(kwargs) + if dataset_name is None: + raise Exception("dataset_name is None, please give a dataset name") + kwargs.update({'dataset_name': dataset_name}) + + s = time.time() + + d = self.db.Dataset.find_one(filter=kwargs, sort=sort) + + if d is not None: + dataset_id = d['dataset_id'] + else: + print("[Database] FAIL! Cannot find dataset: {}".format(kwargs)) + return False + try: + dataset = self._deserialization(self.dataset_fs.get(dataset_id).read()) + pc = self.db.Dataset.find(kwargs) + print("[Database] Find one dataset SUCCESS, {} took: {}s".format(kwargs, round(time.time() - s, 2))) + + # check whether more datasets match the requirement + dataset_id_list = pc.distinct('dataset_id') + n_dataset = len(dataset_id_list) + if n_dataset != 1: + print(" Note that there are {} datasets match the requirement".format(n_dataset)) + return dataset + except Exception as e: + exc_type, exc_obj, exc_tb = sys.exc_info() + fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] + logging.info("{} {} {} {} {}".format(exc_type, exc_obj, fname, exc_tb.tb_lineno, e)) + return False + + def find_datasets(self, dataset_name=None, **kwargs): + """Finds and returns all datasets from the database which matches the requirement. + In some case, the data in a dataset can be stored separately for better management. + + Parameters + ---------- + dataset_name : str + The name/key of dataset. + kwargs : other events + Other events, such as description, author and etc (optional). + + Returns + -------- + params : the parameters, return False if nothing found. + + """ + + self._fill_project_info(kwargs) + if dataset_name is None: + raise Exception("dataset_name is None, please give a dataset name") + kwargs.update({'dataset_name': dataset_name}) + + s = time.time() + pc = self.db.Dataset.find(kwargs) + + if pc is not None: + dataset_id_list = pc.distinct('dataset_id') + dataset_list = [] + for dataset_id in dataset_id_list: # you may have multiple Buckets files + tmp = self.dataset_fs.get(dataset_id).read() + dataset_list.append(self._deserialization(tmp)) + else: + print("[Database] FAIL! Cannot find any dataset: {}".format(kwargs)) + return False + + print("[Database] Find {} datasets SUCCESS, took: {}s".format(len(dataset_list), round(time.time() - s, 2))) + return dataset_list + + def delete_datasets(self, **kwargs): + """Delete datasets. + + Parameters + ----------- + kwargs : logging information + Find items to delete, leave it empty to delete all log. + + """ + + self._fill_project_info(kwargs) + self.db.Dataset.delete_many(kwargs) + logging.info("[Database] Delete Dataset SUCCESS") + + # =========================== LOGGING =============================== + def save_training_log(self, **kwargs): + """Saves the training log, timestamp will be added automatically. + + Parameters + ----------- + kwargs : logging information + Events, such as accuracy, loss, step number and etc. + + Examples + --------- + >>> db.save_training_log(accuracy=0.33, loss=0.98) + + """ + + self._fill_project_info(kwargs) + kwargs.update({'time': datetime.utcnow()}) + _result = self.db.TrainLog.insert_one(kwargs) + _log = self._print_dict(kwargs) + logging.info("[Database] train log: " + _log) + + def save_validation_log(self, **kwargs): + """Saves the validation log, timestamp will be added automatically. + + Parameters + ----------- + kwargs : logging information + Events, such as accuracy, loss, step number and etc. + + Examples + --------- + >>> db.save_validation_log(accuracy=0.33, loss=0.98) + + """ + + self._fill_project_info(kwargs) + kwargs.update({'time': datetime.utcnow()}) + _result = self.db.ValidLog.insert_one(kwargs) + _log = self._print_dict(kwargs) + logging.info("[Database] valid log: " + _log) + + def save_testing_log(self, **kwargs): + """Saves the testing log, timestamp will be added automatically. + + Parameters + ----------- + kwargs : logging information + Events, such as accuracy, loss, step number and etc. + + Examples + --------- + >>> db.save_testing_log(accuracy=0.33, loss=0.98) + + """ + + self._fill_project_info(kwargs) + kwargs.update({'time': datetime.utcnow()}) + _result = self.db.TestLog.insert_one(kwargs) + _log = self._print_dict(kwargs) + logging.info("[Database] test log: " + _log) + + def delete_training_log(self, **kwargs): + """Deletes training log. + + Parameters + ----------- + kwargs : logging information + Find items to delete, leave it empty to delete all log. + + Examples + --------- + Save training log + >>> db.save_training_log(accuracy=0.33) + >>> db.save_training_log(accuracy=0.44) + + Delete logs that match the requirement + >>> db.delete_training_log(accuracy=0.33) + + Delete all logs + >>> db.delete_training_log() + """ + self._fill_project_info(kwargs) + self.db.TrainLog.delete_many(kwargs) + logging.info("[Database] Delete TrainLog SUCCESS") + + def delete_validation_log(self, **kwargs): + """Deletes validation log. + + Parameters + ----------- + kwargs : logging information + Find items to delete, leave it empty to delete all log. + + Examples + --------- + - see ``save_training_log``. + """ + self._fill_project_info(kwargs) + self.db.ValidLog.delete_many(kwargs) + logging.info("[Database] Delete ValidLog SUCCESS") + + def delete_testing_log(self, **kwargs): + """Deletes testing log. + + Parameters + ----------- + kwargs : logging information + Find items to delete, leave it empty to delete all log. + + Examples + --------- + - see ``save_training_log``. + """ + self._fill_project_info(kwargs) + self.db.TestLog.delete_many(kwargs) + logging.info("[Database] Delete TestLog SUCCESS") + + # def find_training_logs(self, **kwargs): + # pass + # + # def find_validation_logs(self, **kwargs): + # pass + # + # def find_testing_logs(self, **kwargs): + # pass + + # =========================== Task =================================== + def create_task(self, task_name=None, script=None, hyper_parameters=None, saved_result_keys=None, **kwargs): + """Uploads a task to the database, timestamp will be added automatically. + + Parameters + ----------- + task_name : str + The task name. + script : str + File name of the python script. + hyper_parameters : dictionary + The hyper parameters pass into the script. + saved_result_keys : list of str + The keys of the task results to keep in the database when the task finishes. + kwargs : other parameters + Users customized parameters such as description, version number. + + Examples + ----------- + Uploads a task + >>> db.create_task(task_name='mnist', script='example/tutorial_mnist_simple.py', description='simple tutorial') + + Finds and runs the latest task + >>> db.run_top_task(sort=[("time", pymongo.DESCENDING)]) + >>> db.run_top_task(sort=[("time", -1)]) + + Finds and runs the oldest task + >>> db.run_top_task(sort=[("time", pymongo.ASCENDING)]) + >>> db.run_top_task(sort=[("time", 1)]) + + """ + if not isinstance(task_name, str): # is None: + raise Exception("task_name should be string") + if not isinstance(script, str): # is None: + raise Exception("script should be string") + if hyper_parameters is None: + hyper_parameters = {} + if saved_result_keys is None: + saved_result_keys = [] + + self._fill_project_info(kwargs) + kwargs.update({'time': datetime.utcnow()}) + kwargs.update({'hyper_parameters': hyper_parameters}) + kwargs.update({'saved_result_keys': saved_result_keys}) + + _script = open(script, 'rb').read() + + kwargs.update({'status': 'pending', 'script': _script, 'result': {}}) + self.db.Task.insert_one(kwargs) + logging.info("[Database] Saved Task - task_name: {} script: {}".format(task_name, script)) + + def run_top_task(self, task_name=None, sort=None, **kwargs): + """Finds and runs a pending task that in the first of the sorting list. + + Parameters + ----------- + task_name : str + The task name. + sort : List of tuple + PyMongo sort comment, search "PyMongo find one sorting" and `collection level operations `__ for more details. + kwargs : other parameters + Users customized parameters such as description, version number. + + Examples + --------- + Monitors the database and pull tasks to run + >>> while True: + >>> print("waiting task from distributor") + >>> db.run_top_task(task_name='mnist', sort=[("time", -1)]) + >>> time.sleep(1) + + Returns + -------- + boolean : True for success, False for fail. + """ + if not isinstance(task_name, str): # is None: + raise Exception("task_name should be string") + self._fill_project_info(kwargs) + kwargs.update({'status': 'pending'}) + + # find task and set status to running + task = self.db.Task.find_one_and_update(kwargs, {'$set': {'status': 'running'}}, sort=sort) + + # try: + # get task info e.g. hyper parameters, python script + if task is None: + logging.info("[Database] Find Task FAIL: key: {} sort: {}".format(task_name, sort)) + return False + else: + logging.info("[Database] Find Task SUCCESS: key: {} sort: {}".format(task_name, sort)) + _datetime = task['time'] + _script = task['script'] + _id = task['_id'] + _hyper_parameters = task['hyper_parameters'] + _saved_result_keys = task['saved_result_keys'] + logging.info(" hyper parameters:") + for key in _hyper_parameters: + globals()[key] = _hyper_parameters[key] + logging.info(" {}: {}".format(key, _hyper_parameters[key])) + # run task + s = time.time() + logging.info("[Database] Start Task: key: {} sort: {} push time: {}".format(task_name, sort, _datetime)) + _script = _script.decode('utf-8') + with tf.Graph().as_default(): # # as graph: # clear all TF graphs + exec(_script, globals()) + + # set status to finished + _ = self.db.Task.find_one_and_update({'_id': _id}, {'$set': {'status': 'finished'}}) + + # return results + __result = {} + for _key in _saved_result_keys: + logging.info(" result: {}={} {}".format(_key, globals()[_key], type(globals()[_key]))) + __result.update({"%s" % _key: globals()[_key]}) + _ = self.db.Task.find_one_and_update( + {'_id': _id}, {'$set': { + 'result': __result + }}, return_document=pymongo.ReturnDocument.AFTER + ) + logging.info( + "[Database] Finished Task: task_name - {} sort: {} push time: {} took: {}s".format( + task_name, sort, _datetime, + time.time() - s + ) + ) + return True + # except Exception as e: + # exc_type, exc_obj, exc_tb = sys.exc_info() + # fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] + # logging.info("{} {} {} {} {}".format(exc_type, exc_obj, fname, exc_tb.tb_lineno, e)) + # logging.info("[Database] Fail to run task") + # # if fail, set status back to pending + # _ = self.db.Task.find_one_and_update({'_id': _id}, {'$set': {'status': 'pending'}}) + # return False + + def delete_tasks(self, **kwargs): + """Delete tasks. + + Parameters + ----------- + kwargs : logging information + Find items to delete, leave it empty to delete all log. + + Examples + --------- + >>> db.delete_tasks() + + """ + + self._fill_project_info(kwargs) + self.db.Task.delete_many(kwargs) + logging.info("[Database] Delete Task SUCCESS") + + def check_unfinished_task(self, task_name=None, **kwargs): + """Finds and runs a pending task. + + Parameters + ----------- + task_name : str + The task name. + kwargs : other parameters + Users customized parameters such as description, version number. + + Examples + --------- + Wait until all tasks finish in user's local console + + >>> while not db.check_unfinished_task(): + >>> time.sleep(1) + >>> print("all tasks finished") + >>> sess = tf.InteractiveSession() + >>> net = db.find_top_model(sess=sess, sort=[("test_accuracy", -1)]) + >>> print("the best accuracy {} is from model {}".format(net._test_accuracy, net._name)) + + Returns + -------- + boolean : True for success, False for fail. + + """ + + if not isinstance(task_name, str): # is None: + raise Exception("task_name should be string") + self._fill_project_info(kwargs) + + kwargs.update({'$or': [{'status': 'pending'}, {'status': 'running'}]}) + + # ## find task + # task = self.db.Task.find_one(kwargs) + task = self.db.Task.find(kwargs) + + task_id_list = task.distinct('_id') + n_task = len(task_id_list) + + if n_task == 0: + logging.info("[Database] No unfinished task - task_name: {}".format(task_name)) + return False + else: + + logging.info("[Database] Find {} unfinished task - task_name: {}".format(n_task, task_name)) + return True + + @staticmethod + def _print_dict(args): + string = '' + for key, value in args.items(): + if key is not '_id': + string += str(key) + ": " + str(value) + " / " + return string diff --git a/tensorlayer/decorators/__init__.py b/tensorlayer/decorators/__init__.py new file mode 100644 index 0000000..ba8d5eb --- /dev/null +++ b/tensorlayer/decorators/__init__.py @@ -0,0 +1,16 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +""" +TensorLayer provides rich layer implementations trailed for +various benchmarks and domain-specific problems. In addition, we also +support transparent access to native TensorFlow parameters. +For example, we provide not only layers for local response normalization, but also +layers that allow user to apply ``tf.ops.lrn`` on ``network.outputs``. +More functions can be found in `TensorFlow API `__. +""" + +from .deprecated import deprecated +from .deprecated_alias import deprecated_alias +from .method_decorator import private_method, protected_method + +__all__ = ['deprecated', 'deprecated_alias', 'private_method', 'protected_method'] diff --git a/tensorlayer/decorators/deprecated.py b/tensorlayer/decorators/deprecated.py new file mode 100644 index 0000000..ba5154d --- /dev/null +++ b/tensorlayer/decorators/deprecated.py @@ -0,0 +1,61 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import functools +import inspect +import sys + +import wrapt + +from tensorlayer.decorators.utils import ( + add_deprecation_notice_to_docstring, get_qualified_name, validate_deprecation_args +) + +__all__ = ['deprecated'] + +# Allow deprecation warnings to be silenced temporarily with a context manager. +_PRINT_DEPRECATION_WARNINGS = True + +# Remember which deprecation warnings have been printed already. +_PRINTED_WARNING = {} + + +def deprecated(wrapped=None, date='', instructions='', warn_once=True): + + if wrapped is None: + return functools.partial(deprecated, date=date, instructions=instructions, warn_once=warn_once) + + @wrapt.decorator + def wrapper(wrapped, instance=None, args=None, kwargs=None): + + validate_deprecation_args(date, instructions) + + if _PRINT_DEPRECATION_WARNINGS: + + class_or_func_name = get_qualified_name(wrapped) + + if class_or_func_name not in _PRINTED_WARNING: + if warn_once: + _PRINTED_WARNING[class_or_func_name] = True + + from tensorlayer import logging + + logging.warning( + '%s: `%s.%s` (in file: %s) is deprecated and will be removed %s.\n' + 'Instructions for updating: %s\n' % ( + "Class" if inspect.isclass(wrapped) else "Function", wrapped.__module__, class_or_func_name, + wrapped.__code__.co_filename, 'in a future version' if date is None else + ('after %s' % date), instructions + ) + ) + + return wrapped(*args, **kwargs) + + decorated = wrapper(wrapped) + + if sys.version_info > (3, 0): # docstring can only be edited with Python 3 + wrapt.FunctionWrapper.__setattr__( + decorated, "__doc__", add_deprecation_notice_to_docstring(wrapped.__doc__, date, instructions) + ) + + return decorated diff --git a/tensorlayer/decorators/deprecated_alias.py b/tensorlayer/decorators/deprecated_alias.py new file mode 100644 index 0000000..acd2c0f --- /dev/null +++ b/tensorlayer/decorators/deprecated_alias.py @@ -0,0 +1,46 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import functools +import warnings + +from tensorlayer import logging + + +def deprecated_alias(end_support_version, **aliases): + + def deco(f): + + @functools.wraps(f) + def wrapper(*args, **kwargs): + + try: + func_name = "{}.{}".format(args[0].__class__.__name__, f.__name__) + except (NameError, IndexError): + func_name = f.__name__ + + rename_kwargs(kwargs, aliases, end_support_version, func_name) + + return f(*args, **kwargs) + + return wrapper + + return deco + + +def rename_kwargs(kwargs, aliases, end_support_version, func_name): + + for alias, new in aliases.items(): + + if alias in kwargs: + + if new in kwargs: + raise TypeError('{}() received both {} and {}'.format(func_name, alias, new)) + + warnings.warn('{}() - {} is deprecated; use {}'.format(func_name, alias, new), DeprecationWarning) + logging.warning( + "DeprecationWarning: {}(): " + "`{}` argument is deprecated and will be removed in version {}, " + "please change for `{}.`".format(func_name, alias, end_support_version, new) + ) + kwargs[new] = kwargs.pop(alias) diff --git a/tensorlayer/decorators/method_decorator.py b/tensorlayer/decorators/method_decorator.py new file mode 100644 index 0000000..5d26220 --- /dev/null +++ b/tensorlayer/decorators/method_decorator.py @@ -0,0 +1,44 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import inspect + + +def private_method(func): + """Decorator for making an instance method private.""" + + def func_wrapper(*args, **kwargs): + """Decorator wrapper function.""" + outer_frame = inspect.stack()[1][0] + if 'self' not in outer_frame.f_locals or outer_frame.f_locals['self'] is not args[0]: + raise RuntimeError('%s.%s is a private method' % (args[0].__class__.__name__, func.__name__)) + + return func(*args, **kwargs) + + return func_wrapper + + +def protected_method(func): + """Decorator for making an instance method private.""" + + def func_wrapper(*args, **kwargs): + """Decorator wrapper function.""" + outer_frame = inspect.stack()[1][0] + + caller = inspect.getmro(outer_frame.f_locals['self'].__class__)[:-1] + target = inspect.getmro(args[0].__class__)[:-1] + + share_subsclass = False + + for cls_ in target: + if issubclass(caller[0], cls_) or caller[0] is cls_: + share_subsclass = True + break + + if ('self' not in outer_frame.f_locals or + outer_frame.f_locals['self'] is not args[0]) and (not share_subsclass): + raise RuntimeError('%s.%s is a protected method' % (args[0].__class__.__name__, func.__name__)) + + return func(*args, **kwargs) + + return func_wrapper diff --git a/tensorlayer/decorators/utils.py b/tensorlayer/decorators/utils.py new file mode 100644 index 0000000..c0d294e --- /dev/null +++ b/tensorlayer/decorators/utils.py @@ -0,0 +1,122 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +""" +NOTE: DO NOT REMOVE THESE FILES. They are copied from Tensorflow repository and are necessary to build the library without installing TF. + +Source: https://github.com/tensorflow/tensorflow/tree/master/tensorflow/python/util + +They replace the following imports: +>>> from tensorflow.python.util import decorator_utils +>>> from tensorflow.python.util.deprecation import _validate_deprecation_args +""" + +import re +import sys + +__all__ = ["add_deprecation_notice_to_docstring", "get_qualified_name", "validate_deprecation_args"] + + +def add_deprecation_notice_to_docstring(doc, date, instructions): + return _add_deprecated_function_notice_to_docstring(doc, date, instructions) + + +def get_qualified_name(function): + # Python 3 + if hasattr(function, '__qualname__'): + return function.__qualname__ + + # Python 2 + if hasattr(function, 'im_class'): + return function.im_class.__name__ + '.' + function.__name__ + return function.__name__ + + +def validate_deprecation_args(date, instructions): + if date is not None and not re.match(r'20\d\d-[01]\d-[0123]\d', date): + raise ValueError('Date must be YYYY-MM-DD.') + if not instructions: + raise ValueError('Don\'t deprecate things without conversion instructions!') + + +def _add_deprecated_function_notice_to_docstring(doc, date, instructions): + """Adds a deprecation notice to a docstring for deprecated functions.""" + + if instructions: + deprecation_message = """ + .. warning:: + **THIS FUNCTION IS DEPRECATED:** It will be removed after %s. + *Instructions for updating:* %s. + """ % (('in a future version' if date is None else ('after %s' % date)), instructions) + + else: + deprecation_message = """ + .. warning:: + **THIS FUNCTION IS DEPRECATED:** It will be removed after %s. + """ % (('in a future version' if date is None else ('after %s' % date))) + + main_text = [deprecation_message] + + return _add_notice_to_docstring(doc, 'DEPRECATED FUNCTION', main_text) + + +def _add_notice_to_docstring(doc, no_doc_str, notice): + """Adds a deprecation notice to a docstring.""" + if not doc: + lines = [no_doc_str] + + else: + lines = _normalize_docstring(doc).splitlines() + + notice = [''] + notice + + if len(lines) > 1: + # Make sure that we keep our distance from the main body + if lines[1].strip(): + notice.append('') + + lines[1:1] = notice + else: + lines += notice + + return '\n'.join(lines) + + +def _normalize_docstring(docstring): + """Normalizes the docstring. + + Replaces tabs with spaces, removes leading and trailing blanks lines, and + removes any indentation. + + Copied from PEP-257: + https://www.python.org/dev/peps/pep-0257/#handling-docstring-indentation + + Args: + docstring: the docstring to normalize + + Returns: + The normalized docstring + """ + if not docstring: + return '' + # Convert tabs to spaces (following the normal Python rules) + # and split into a list of lines: + lines = docstring.expandtabs().splitlines() + # Determine minimum indentation (first line doesn't count): + # (we use sys.maxsize because sys.maxint doesn't exist in Python 3) + indent = sys.maxsize + for line in lines[1:]: + stripped = line.lstrip() + if stripped: + indent = min(indent, len(line) - len(stripped)) + # Remove indentation (first line is special): + trimmed = [lines[0].strip()] + if indent < sys.maxsize: + for line in lines[1:]: + trimmed.append(line[indent:].rstrip()) + # Strip off trailing and leading blank lines: + while trimmed and not trimmed[-1]: + trimmed.pop() + while trimmed and not trimmed[0]: + trimmed.pop(0) + # Return a single string: + return '\n'.join(trimmed) diff --git a/tensorlayer/distributed.py b/tensorlayer/distributed.py new file mode 100644 index 0000000..3b426f8 --- /dev/null +++ b/tensorlayer/distributed.py @@ -0,0 +1,545 @@ +# -*- coding: utf-8 -*- + +import json +import os +import time + +import tensorflow as tf +from tensorflow.python.training import session_run_hook + +from tensorlayer import logging +from tensorlayer.decorators import deprecated +from tensorlayer.lazy_imports import LazyImport + +hvd = LazyImport('horovod.tensorflow') + +__all__ = ['TaskSpecDef', 'TaskSpec', 'DistributedSession', 'StopAtTimeHook', 'LoadCheckpoint', 'Trainer'] + + +class Trainer(object): + """Trainer for neural networks in a distributed environment. + + TensorLayer Trainer is a high-level training interface built on top of TensorFlow MonitoredSession and + `Horovod `__. It transparently scales the training of a TensorLayer model + from a single GPU to multiple GPUs that be placed on different machines in a single cluster. + + To run the trainer, you will need to install Horovod on your machine. Check the installation script at + `tensorlayer/scripts/download_and_install_openmpi3_ubuntu.sh` + + The minimal inputs to the Trainer include (1) a training dataset defined using the TensorFlow DataSet API, + and (2) a model build function given the inputs of the training dataset, and returns the neural network + to train, the loss function to minimize, and the names of the tensor to log during training, and (3) + an optimizer and its arguments. + + The default parameter choices of Trainer is inspired by the Facebook paper: + `Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour `__ + + Parameters + ---------- + training_dataset : class TensorFlow ``DataSet`` + The training dataset which zips samples and labels. The trainer automatically + shards the training dataset based on the number of GPUs. + build_training_func : function + A function that builds the training operator. It takes the training dataset as an input, + and returns the neural network, the loss function and a dictionary that maps + string tags to tensors to log during training. + optimizer : class TensorFlow ``Optimizer`` + The loss function optimizer. The trainer automatically linearly scale the learning rate based on + the number of GPUs. + optimizer_args : dict + The optimizer argument dictionary. It must contain a `learning_rate` field in type of float. + Note that the learning rate is linearly scaled according to the number of GPU by default. + You can disable it using the option `scaling_learning_rate` + batch_size : int + The training mini-batch size (i.e., number of samples per batch). + prefetch_size: int or None + The dataset prefetch buffer size. Set this parameter to overlap the GPU training and data preparation + if the data preparation is heavy. + checkpoint_dir : None or str + The path to the TensorFlow model checkpoint. Note that only one trainer master would checkpoints its model. + If None, checkpoint is disabled. + log_step_size : int + The trainer logs training information every N mini-batches (i.e., step size). + validation_dataset: None or class TensorFlow ``DataSet`` + The optional validation dataset that zips samples and labels. Note that + only the trainer master needs to the validation often. + build_validation_func: None or function + The function that builds the validation operator. It returns the validation neural network (which + share the weights of the training network) and a custom number of validation metrics. + scaling_learning_rate: Boolean + Linearly scale the learning rate by the number of GPUs. Default is True. + This `linear scaling rule` is generally effective and is highly recommended by the practioners. + Check `Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour `__ + max_iteration: int + The maximum iteration (i.e., mini-batch) to train. + The default is `math.inf`. You can set it to a small number to end the training earlier. This is + usually set for testing purpose. + + Attributes + ---------- + training_network : class TensorLayer ``Layer`` + The training model. + session : class TensorFlow ``MonitoredTrainingSession`` + The training session tha the Trainer wraps. + global_step : int + The number of training mini-batch by far. + validation_metrics : list of tuples + The validation metrics that zips the validation metric property and the average value. + + Examples + -------- + See `tutorial_mnist_distributed_trainer.py + `__. + + """ + + def __init__( + self, training_dataset, build_training_func, optimizer, optimizer_args, batch_size=32, prefetch_size=None, + checkpoint_dir=None, scaling_learning_rate=True, log_step_size=1, validation_dataset=None, + build_validation_func=None, max_iteration=float('inf') + ): + # Initialize Horovod. + hvd.init() + self.is_master = hvd.rank() == 0 + self._last_global_step = 0 + + if prefetch_size is None: + prefetch_size = batch_size + + # Define the loss for validation dataset + if validation_dataset: + validation_dataset = validation_dataset.shard(num_shards=hvd.size(), index=hvd.rank()).batch(batch_size) + validation_dataset.prefetch(buffer_size=prefetch_size) + self._validation_iterator = validation_dataset.make_initializable_iterator() + next_example, next_label = self._validation_iterator.get_next() + _, self._validation_metrics = build_validation_func(next_example, next_label) + if not isinstance(self._validation_metrics, list): + self._validation_metrics = list(self._validation_metrics) + else: + self._validation_iterator = None + self._validation_metrics = None + + # Get the shard of the dataset based on my local rank + training_dataset = training_dataset.shard(num_shards=hvd.size(), index=hvd.rank()).batch(batch_size) + + training_dataset.prefetch(buffer_size=prefetch_size) + training_iterator = training_dataset.make_one_shot_iterator() + self._training_network, loss, log_tensors = build_training_func(*training_iterator.get_next()) + + # Adjust learning rate based on number of GPUs. + lr = optimizer_args['learning_rate'] + optimizer_args['learning_rate'] = lr * hvd.size() if scaling_learning_rate else lr + opt = optimizer(**optimizer_args) + + # Add Horovod Distributed Optimizer. + opt = hvd.DistributedOptimizer(opt) + + self._global_step = tf.train.get_or_create_global_step() + if isinstance(log_tensors, list): + log_tensors.append(self._global_step) + else: + log_tensors['global_step'] = self._global_step + self._train_op = opt.minimize(loss, global_step=self._global_step) + + hooks = [ + # Horovod: BroadcastGlobalVariablesHook broadcasts initial variable states + # from rank 0 to all other processes. This is necessary to ensure consistent + # initialization of all workers when training is started with random weights + # or restored from a checkpoint. + hvd.BroadcastGlobalVariablesHook(0), + + # Horovod: adjust number of steps based on number of GPUs. + tf.train.StopAtStepHook(last_step=max_iteration // hvd.size()), + tf.train.LoggingTensorHook(tensors=log_tensors, every_n_iter=log_step_size), + ] + + # Pin GPU to be used to process local rank (one GPU per process) + config = tf.ConfigProto() + config.gpu_options.allow_growth = True + config.gpu_options.visible_device_list = str(hvd.local_rank()) + + # Save checkpoints only on worker 0 to prevent other workers from + # corrupting them. + checkpoint_dir = checkpoint_dir if self.is_master else None + + # The MonitoredTrainingSession takes care of session initialization, + # restoring from a checkpoint, saving to a checkpoint, and closing when done + # or an error occurs. + self._sess = tf.train.MonitoredTrainingSession(checkpoint_dir=checkpoint_dir, hooks=hooks, config=config) + + @property + def global_step(self): + if self._sess.should_stop(): + return self._last_global_step + self._last_global_step = self._sess.run(self._global_step) + return self._last_global_step + + @property + def session(self): + return self._sess + + @property + def training_network(self): + return self._training_network + + @property + def validation_metrics(self): + """A helper function to compute validation related metrics""" + + if (self._validation_iterator is None) or (self._validation_metrics is None): + raise AttributeError('Validation is not setup.') + + n = 0.0 + metric_sums = [0.0] * len(self._validation_metrics) + self._sess.run(self._validation_iterator.initializer) + while True: + try: + metrics = self._sess.run(self._validation_metrics) + for i, m in enumerate(metrics): + metric_sums[i] += m + n += 1.0 + except tf.errors.OutOfRangeError: + break + for i, m in enumerate(metric_sums): + metric_sums[i] = metric_sums[i] / n + return zip(self._validation_metrics, metric_sums) + + def train_on_batch(self): + """Train a mini-batch.""" + self._sess.run(self._train_op) + + def train_and_validate_to_end(self, validate_step_size=50): + """A helper function that shows how to train and validate a model at the same time. + + Parameters + ---------- + validate_step_size : int + Validate the training network every N steps. + + """ + while not self._sess.should_stop(): + self.train_on_batch() # Run a training step synchronously. + if self.global_step % validate_step_size == 0: + # logging.info("Average loss for validation dataset: %s" % self.get_validation_metrics()) + log_str = 'step: %d, ' % self.global_step + for n, m in self.validation_metrics: + log_str += '%s: %f, ' % (n.name, m) + logging.info(log_str) + + +@deprecated(date="2018-10-30", instructions="Using the TensorLayer distributed trainer.") +class TaskSpecDef(object): + """Specification for a distributed task. + + It contains the job name, index of the task, + the parameter servers and the worker servers. If you want to use the last worker + for continuous evaluation you can call the method `use_last_worker_as_evaluator` + which returns a new :class:`TaskSpecDef` object without the last worker in the + cluster specification. + + Parameters + ---------- + task_type : str + Task type. One of `master`, `worker` or `ps`. + index : int + The zero-based index of the task. Distributed training jobs will have a single + master task, one or more parameter servers, and one or more workers. + trial : int + The identifier of the trial being run. + ps_hosts : str OR list of str + A string with a coma separate list of hosts for the parameter servers + or a list of hosts. + worker_hosts : str OR list of str + A string with a coma separate list of hosts for the worker servers + or a list of hosts. + master : str + A string with the master hosts + + Notes + ---------- + master might not be included in TF_CONFIG and can be None. The shard_index is adjusted + in any case to assign 0 to master and >= 1 to workers. + This implementation doesn't support sparse arrays in the `TF_CONFIG` variable as the + official TensorFlow documentation shows, as it is not a supported by the json + definition. + + References + ---------- + - `ML-engine trainer considerations `__ + + """ + + def __init__(self, task_type='master', index=0, trial=None, ps_hosts=None, worker_hosts=None, master=None): + self.type = task_type + self._index = int(index) + self._cluster_spec = None + self.num_workers = 1 + self.num_ps = 0 + self.shard_index = int(index) + self._master = True + self.trial = trial + self.ps_hosts = ps_hosts + self.worker_hosts = worker_hosts + self.master = master + self._server = None + + if ps_hosts and worker_hosts: + self.ps_hosts = ps_hosts if isinstance(ps_hosts, list) else ps_hosts.split(',') + self.num_ps = len(self.ps_hosts) + self.worker_hosts = worker_hosts if isinstance(worker_hosts, list) else worker_hosts.split(',') + if master is not None and len(master) > 0: + self._cluster_spec = tf.train.ClusterSpec( + { + 'ps': self.ps_hosts, + 'worker': self.worker_hosts, + 'master': master + } + ) + # master is a worker too + self.num_workers = len(self.worker_hosts) + 1 + if self.type == 'worker': + self.shard_index = self._index + 1 + self._master = self.type == 'master' + else: + self._cluster_spec = tf.train.ClusterSpec({'ps': self.ps_hosts, 'worker': self.worker_hosts}) + self.num_workers = len(self.worker_hosts) + if self.type == 'worker': + self.shard_index = self._index + self._master = self.type == 'worker' and self._index == 0 + + def is_ps(self): + """Returns true if this server is a parameter server""" + return self.type == 'ps' + + def is_worker(self): + """Returns true if this server is a worker server""" + return self.type == 'worker' + + def is_master(self): + """Returns true if this server is the master server""" + return self._master + + def is_evaluator(self): + """Returns true if this server is the evaluator server""" + return self.type == 'worker' and self.num_workers == self._index + + def device_fn(self): + """Returns the function with the specification to create the graph in this server""" + current_device = '/job:{}/task:{}'.format(self.type, self._index) + ps_devices = '/job:ps' + return tf.train.replica_device_setter( + ps_device=ps_devices, worker_device=current_device, cluster=self._cluster_spec + ) + + def create_server(self): + if self._server is None and self.ps_hosts and self.worker_hosts and not self.is_evaluator(): + # create server and join if it is a parameter server + self._server = tf.train.Server(self._cluster_spec, job_name=self.type, task_index=self._index) + if self.is_ps(): + self._server.join() + + def target(self): + if self._server is None: + self.create_server() + if self._server is not None: + return self._server.target + else: + return None + + def use_last_worker_as_evaluator(self): + """Returns a new :class:`TaskSpecDef` where the last worker has been removed from + the list of worker_hosts, so it is not used for training anymore. You can call + is_evaluator to know whether this server is the evaluator one or not. + In case there is only one server for training this method raises an exception, as + you cannot use any server for evaluation. + + """ + if self.num_workers <= 1: + raise Exception('You need more than one worker instance to use one as evaluator') + + return TaskSpecDef( + task_type=self.type, index=self._index, trial=self.trial, ps_hosts=self.ps_hosts, + worker_hosts=self.worker_hosts[:-1], master=self.master + ) + + +@deprecated(date="2018-10-30", instructions="Using the TensorLayer distributed trainer.") +def create_task_spec_def(): + """Returns the a :class:`TaskSpecDef` based on the environment variables for distributed training. + + References + ---------- + - `ML-engine trainer considerations `__ + - `TensorPort Distributed Computing `__ + + """ + if 'TF_CONFIG' in os.environ: + # TF_CONFIG is used in ML-engine + env = json.loads(os.environ.get('TF_CONFIG', '{}')) + task_data = env.get('task', None) or {'type': 'master', 'index': 0} + cluster_data = env.get('cluster', None) or {'ps': None, 'worker': None, 'master': None} + return TaskSpecDef( + task_type=task_data['type'], index=task_data['index'], + trial=task_data['trial'] if 'trial' in task_data else None, ps_hosts=cluster_data['ps'], + worker_hosts=cluster_data['worker'], master=cluster_data['master'] if 'master' in cluster_data else None + ) + elif 'JOB_NAME' in os.environ: + # JOB_NAME, TASK_INDEX, PS_HOSTS, WORKER_HOSTS and MASTER_HOST are used in TensorPort + return TaskSpecDef( + task_type=os.environ['JOB_NAME'], index=os.environ['TASK_INDEX'], ps_hosts=os.environ.get('PS_HOSTS', None), + worker_hosts=os.environ.get('WORKER_HOSTS', None), master=os.environ.get('MASTER_HOST', None) + ) + else: + raise Exception('You need to setup TF_CONFIG or JOB_NAME to define the task.') + + +@deprecated(date="2018-10-30", instructions="Using the TensorLayer distributed trainer.") +def create_distributed_session( + task_spec=None, checkpoint_dir=None, scaffold=None, hooks=None, chief_only_hooks=None, save_checkpoint_secs=600, + save_summaries_steps=object(), save_summaries_secs=object(), config=None, stop_grace_period_secs=120, + log_step_count_steps=100 +): + """Creates a distributed session. + + It calls `MonitoredTrainingSession` to create a :class:`MonitoredSession` for distributed training. + + Parameters + ---------- + task_spec : :class:`TaskSpecDef`. + The task spec definition from create_task_spec_def() + checkpoint_dir : str. + Optional path to a directory where to restore variables. + scaffold : ``Scaffold`` + A `Scaffold` used for gathering or building supportive ops. + If not specified, a default one is created. It's used to finalize the graph. + hooks : list of ``SessionRunHook`` objects. + Optional + chief_only_hooks : list of ``SessionRunHook`` objects. + Activate these hooks if `is_chief==True`, ignore otherwise. + save_checkpoint_secs : int + The frequency, in seconds, that a checkpoint is saved + using a default checkpoint saver. If `save_checkpoint_secs` is set to + `None`, then the default checkpoint saver isn't used. + save_summaries_steps : int + The frequency, in number of global steps, that the + summaries are written to disk using a default summary saver. If both + `save_summaries_steps` and `save_summaries_secs` are set to `None`, then + the default summary saver isn't used. Default 100. + save_summaries_secs : int + The frequency, in secs, that the summaries are written + to disk using a default summary saver. If both `save_summaries_steps` and + `save_summaries_secs` are set to `None`, then the default summary saver + isn't used. Default not enabled. + config : ``tf.ConfigProto`` + an instance of `tf.ConfigProto` proto used to configure the session. + It's the `config` argument of constructor of `tf.Session`. + stop_grace_period_secs : int + Number of seconds given to threads to stop after + `close()` has been called. + log_step_count_steps : int + The frequency, in number of global steps, that the + global step/sec is logged. + + Examples + -------- + A simple example for distributed training where all the workers use the same dataset: + + >>> task_spec = TaskSpec() + >>> with tf.device(task_spec.device_fn()): + >>> tensors = create_graph() + >>> with tl.DistributedSession(task_spec=task_spec, + ... checkpoint_dir='/tmp/ckpt') as session: + >>> while not session.should_stop(): + >>> session.run(tensors) + + An example where the dataset is shared among the workers + (see https://www.tensorflow.org/programmers_guide/datasets): + + >>> task_spec = TaskSpec() + >>> # dataset is a :class:`tf.data.Dataset` with the raw data + >>> dataset = create_dataset() + >>> if task_spec is not None: + >>> dataset = dataset.shard(task_spec.num_workers, task_spec.shard_index) + >>> # shuffle or apply a map function to the new sharded dataset, for example: + >>> dataset = dataset.shuffle(buffer_size=10000) + >>> dataset = dataset.batch(batch_size) + >>> dataset = dataset.repeat(num_epochs) + >>> # create the iterator for the dataset and the input tensor + >>> iterator = dataset.make_one_shot_iterator() + >>> next_element = iterator.get_next() + >>> with tf.device(task_spec.device_fn()): + >>> # next_element is the input for the graph + >>> tensors = create_graph(next_element) + >>> with tl.DistributedSession(task_spec=task_spec, + ... checkpoint_dir='/tmp/ckpt') as session: + >>> while not session.should_stop(): + >>> session.run(tensors) + + References + ---------- + - `MonitoredTrainingSession `__ + + """ + target = task_spec.target() if task_spec is not None else None + is_chief = task_spec.is_master() if task_spec is not None else True + return tf.train.MonitoredTrainingSession( + master=target, is_chief=is_chief, checkpoint_dir=checkpoint_dir, scaffold=scaffold, + save_checkpoint_secs=save_checkpoint_secs, save_summaries_steps=save_summaries_steps, + save_summaries_secs=save_summaries_secs, log_step_count_steps=log_step_count_steps, + stop_grace_period_secs=stop_grace_period_secs, config=config, hooks=hooks, chief_only_hooks=chief_only_hooks + ) + + +@deprecated(date="2018-10-30", instructions="Using the TensorLayer distributed trainer.") +class StopAtTimeHook(session_run_hook.SessionRunHook): + """Hook that requests stop after a specified time. + + Parameters + ---------- + time_running: int + Maximum time running in seconds + + """ + + def __init__(self, time_running): + self._time_running = time_running + self._end_time = 0 + + def begin(self): + self._end_time = time.time() + self._time_running + + def after_run(self, run_context, run_values): + if time.time() > self._end_time: + run_context.request_stop() + + +@deprecated(date="2018-10-30", instructions="Using the TensorLayer distributed trainer.") +class LoadCheckpoint(session_run_hook.SessionRunHook): + """Hook that loads a checkpoint after the session is created. + + >>> from tensorflow.python.ops import variables as tf_variables + >>> from tensorflow.python.training.monitored_session import SingularMonitoredSession + >>> + >>> tensors = create_graph() + >>> saver = tf.train.Saver(var_list=tf_variables.trainable_variables()) + >>> checkpoint_hook = LoadCheckpoint(saver, my_checkpoint_file) + >>> with tf.SingularMonitoredSession(hooks=[checkpoint_hook]) as session: + >>> while not session.should_stop(): + >>> session.run(tensors) + + """ + + def __init__(self, saver, checkpoint): + self._saver = saver + self._checkpoint = checkpoint + self._loaded = False + + def after_create_session(self, session, coord): + if not self._loaded: + self._loaded = True + self._saver.restore(self._checkpoint) + + +# Alias +TaskSpec = create_task_spec_def +DistributedSession = create_distributed_session diff --git a/tensorlayer/files/__init__.py b/tensorlayer/files/__init__.py new file mode 100644 index 0000000..8d985af --- /dev/null +++ b/tensorlayer/files/__init__.py @@ -0,0 +1,77 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +""" +TensorLayer provides rich layer implementations trailed for +various benchmarks and domain-specific problems. In addition, we also +support transparent access to native TensorFlow parameters. +For example, we provide not only layers for local response normalization, but also +layers that allow user to apply ``tf.ops.lrn`` on ``network.outputs``. +More functions can be found in `TensorFlow API `__. +""" + +from tensorlayer.lazy_imports import LazyImport + +from .dataset_loaders.celebA_dataset import * +from .dataset_loaders.cifar10_dataset import * +from .dataset_loaders.cyclegan_dataset import * +from .dataset_loaders.flickr_1M_dataset import * +from .dataset_loaders.flickr_25k_dataset import * +from .dataset_loaders.imdb_dataset import * +from .dataset_loaders.matt_mahoney_dataset import * +from .dataset_loaders.mnist_dataset import * +from .dataset_loaders.mnist_fashion_dataset import * +from .dataset_loaders.mpii_dataset import * +from .dataset_loaders.nietzsche_dataset import * +from .dataset_loaders.ptb_dataset import * +from .dataset_loaders.voc_dataset import * +from .dataset_loaders.wmt_en_fr_dataset import * +from .utils import * + +__all__ = [ + # Dataset Loaders + 'load_celebA_dataset', + 'load_cifar10_dataset', + 'load_cyclegan_dataset', + 'load_fashion_mnist_dataset', + 'load_flickr1M_dataset', + 'load_flickr25k_dataset', + 'load_imdb_dataset', + 'load_matt_mahoney_text8_dataset', + 'load_mnist_dataset', + 'load_mpii_pose_dataset', + 'load_nietzsche_dataset', + 'load_ptb_dataset', + 'load_voc_dataset', + 'load_wmt_en_fr_dataset', + + # Util Functions + 'assign_params', + 'del_file', + 'del_folder', + 'download_file_from_google_drive', + 'exists_or_mkdir', + 'file_exists', + 'folder_exists', + 'load_and_assign_npz', + 'load_and_assign_npz_dict', + 'load_ckpt', + 'load_cropped_svhn', + 'load_file_list', + 'load_folder_list', + 'load_npy_to_any', + 'load_npz', + 'maybe_download_and_extract', + 'natural_keys', + 'npz_to_W_pdf', + 'read_file', + 'save_any_to_npy', + 'save_ckpt', + 'save_npz', + 'save_npz_dict', + 'load_and_assign_ckpt', + 'ckpt_to_npz_dict' + #'save_graph', + #'load_graph', + #'save_graph_and_params', + #'load_graph_and_params', +] diff --git a/tensorlayer/files/dataset_loaders/__init__.py b/tensorlayer/files/dataset_loaders/__init__.py new file mode 100644 index 0000000..59a5551 --- /dev/null +++ b/tensorlayer/files/dataset_loaders/__init__.py @@ -0,0 +1,34 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +from .celebA_dataset import * +from .cifar10_dataset import * +from .cyclegan_dataset import * +from .flickr_1M_dataset import * +from .flickr_25k_dataset import * +from .imdb_dataset import * +from .matt_mahoney_dataset import * +from .mnist_dataset import * +from .mnist_fashion_dataset import * +from .mpii_dataset import * +from .nietzsche_dataset import * +from .ptb_dataset import * +from .voc_dataset import * +from .wmt_en_fr_dataset import * + +__all__ = [ + 'load_celebA_dataset', + 'load_cifar10_dataset', + 'load_cyclegan_dataset', + 'load_fashion_mnist_dataset', + 'load_flickr1M_dataset', + 'load_flickr25k_dataset', + 'load_imdb_dataset', + 'load_matt_mahoney_text8_dataset', + 'load_mnist_dataset', + 'load_mpii_pose_dataset', + 'load_nietzsche_dataset', + 'load_ptb_dataset', + 'load_voc_dataset', + 'load_wmt_en_fr_dataset', +] diff --git a/tensorlayer/files/dataset_loaders/celebA_dataset.py b/tensorlayer/files/dataset_loaders/celebA_dataset.py new file mode 100644 index 0000000..d5dc575 --- /dev/null +++ b/tensorlayer/files/dataset_loaders/celebA_dataset.py @@ -0,0 +1,43 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import os +import zipfile + +from tensorlayer import logging +from tensorlayer.files.utils import (download_file_from_google_drive, exists_or_mkdir, load_file_list) + +__all__ = ['load_celebA_dataset'] + + +def load_celebA_dataset(path='data'): + """Load CelebA dataset + + Return a list of image path. + + Parameters + ----------- + path : str + The path that the data is downloaded to, defaults is ``data/celebA/``. + + """ + data_dir = 'celebA' + filename, drive_id = "img_align_celeba.zip", "0B7EVK8r0v71pZjFTYXZWM3FlRnM" + save_path = os.path.join(path, filename) + image_path = os.path.join(path, data_dir) + if os.path.exists(image_path): + logging.info('[*] {} already exists'.format(save_path)) + else: + exists_or_mkdir(path) + download_file_from_google_drive(drive_id, save_path) + zip_dir = '' + with zipfile.ZipFile(save_path) as zf: + zip_dir = zf.namelist()[0] + zf.extractall(path) + os.remove(save_path) + os.rename(os.path.join(path, zip_dir), image_path) + + data_files = load_file_list(path=image_path, regx='\\.jpg', printable=False) + for i, _v in enumerate(data_files): + data_files[i] = os.path.join(image_path, data_files[i]) + return data_files diff --git a/tensorlayer/files/dataset_loaders/cifar10_dataset.py b/tensorlayer/files/dataset_loaders/cifar10_dataset.py new file mode 100644 index 0000000..9af3f61 --- /dev/null +++ b/tensorlayer/files/dataset_loaders/cifar10_dataset.py @@ -0,0 +1,134 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import os +import pickle +import sys + +import numpy as np + +from tensorlayer import logging +from tensorlayer.files.utils import maybe_download_and_extract + +__all__ = ['load_cifar10_dataset'] + + +def load_cifar10_dataset(shape=(-1, 32, 32, 3), path='data', plotable=False): + """Load CIFAR-10 dataset. + + It consists of 60000 32x32 colour images in 10 classes, with + 6000 images per class. There are 50000 training images and 10000 test images. + + The dataset is divided into five training batches and one test batch, each with + 10000 images. The test batch contains exactly 1000 randomly-selected images from + each class. The training batches contain the remaining images in random order, + but some training batches may contain more images from one class than another. + Between them, the training batches contain exactly 5000 images from each class. + + Parameters + ---------- + shape : tupe + The shape of digit images e.g. (-1, 3, 32, 32) and (-1, 32, 32, 3). + path : str + The path that the data is downloaded to, defaults is ``data/cifar10/``. + plotable : boolean + Whether to plot some image examples, False as default. + + Examples + -------- + >>> X_train, y_train, X_test, y_test = tl.files.load_cifar10_dataset(shape=(-1, 32, 32, 3)) + + References + ---------- + - `CIFAR website `__ + - `Data download link `__ + - ``__ + + """ + path = os.path.join(path, 'cifar10') + logging.info("Load or Download cifar10 > {}".format(path)) + + #Helper function to unpickle the data + def unpickle(file): + fp = open(file, 'rb') + if sys.version_info.major == 2: + data = pickle.load(fp) + elif sys.version_info.major == 3: + data = pickle.load(fp, encoding='latin-1') + else: + raise RuntimeError("Sys Version Unsupported") + fp.close() + return data + + filename = 'cifar-10-python.tar.gz' + url = 'https://www.cs.toronto.edu/~kriz/' + #Download and uncompress file + maybe_download_and_extract(filename, path, url, extract=True) + + #Unpickle file and fill in data + X_train = None + y_train = [] + for i in range(1, 6): + data_dic = unpickle(os.path.join(path, 'cifar-10-batches-py/', "data_batch_{}".format(i))) + if i == 1: + X_train = data_dic['data'] + else: + X_train = np.vstack((X_train, data_dic['data'])) + y_train += data_dic['labels'] + + test_data_dic = unpickle(os.path.join(path, 'cifar-10-batches-py/', "test_batch")) + X_test = test_data_dic['data'] + y_test = np.array(test_data_dic['labels']) + + if shape == (-1, 3, 32, 32): + X_test = X_test.reshape(shape) + X_train = X_train.reshape(shape) + elif shape == (-1, 32, 32, 3): + X_test = X_test.reshape(shape, order='F') + X_train = X_train.reshape(shape, order='F') + X_test = np.transpose(X_test, (0, 2, 1, 3)) + X_train = np.transpose(X_train, (0, 2, 1, 3)) + else: + X_test = X_test.reshape(shape) + X_train = X_train.reshape(shape) + + y_train = np.array(y_train) + + if plotable: + logging.info('\nCIFAR-10') + import matplotlib.pyplot as plt + fig = plt.figure(1) + + logging.info('Shape of a training image: X_train[0] %s' % X_train[0].shape) + + plt.ion() # interactive mode + count = 1 + for _ in range(10): # each row + for _ in range(10): # each column + _ = fig.add_subplot(10, 10, count) + if shape == (-1, 3, 32, 32): + # plt.imshow(X_train[count-1], interpolation='nearest') + plt.imshow(np.transpose(X_train[count - 1], (1, 2, 0)), interpolation='nearest') + # plt.imshow(np.transpose(X_train[count-1], (2, 1, 0)), interpolation='nearest') + elif shape == (-1, 32, 32, 3): + plt.imshow(X_train[count - 1], interpolation='nearest') + # plt.imshow(np.transpose(X_train[count-1], (1, 0, 2)), interpolation='nearest') + else: + raise Exception("Do not support the given 'shape' to plot the image examples") + plt.gca().xaxis.set_major_locator(plt.NullLocator()) + plt.gca().yaxis.set_major_locator(plt.NullLocator()) + count = count + 1 + plt.draw() # interactive mode + plt.pause(3) # interactive mode + + logging.info("X_train: %s" % X_train.shape) + logging.info("y_train: %s" % y_train.shape) + logging.info("X_test: %s" % X_test.shape) + logging.info("y_test: %s" % y_test.shape) + + X_train = np.asarray(X_train, dtype=np.float32) + X_test = np.asarray(X_test, dtype=np.float32) + y_train = np.asarray(y_train, dtype=np.int32) + y_test = np.asarray(y_test, dtype=np.int32) + + return X_train, y_train, X_test, y_test diff --git a/tensorlayer/files/dataset_loaders/cyclegan_dataset.py b/tensorlayer/files/dataset_loaders/cyclegan_dataset.py new file mode 100644 index 0000000..e327b3b --- /dev/null +++ b/tensorlayer/files/dataset_loaders/cyclegan_dataset.py @@ -0,0 +1,58 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import os + +import numpy as np + +from tensorlayer import logging, visualize +from tensorlayer.files.utils import (del_file, folder_exists, load_file_list, maybe_download_and_extract) + +__all__ = ['load_cyclegan_dataset'] + + +def load_cyclegan_dataset(filename='summer2winter_yosemite', path='data'): + """Load images from CycleGAN's database, see `this link `__. + + Parameters + ------------ + filename : str + The dataset you want, see `this link `__. + path : str + The path that the data is downloaded to, defaults is `data/cyclegan` + + Examples + --------- + >>> im_train_A, im_train_B, im_test_A, im_test_B = load_cyclegan_dataset(filename='summer2winter_yosemite') + + """ + path = os.path.join(path, 'cyclegan') + url = 'https://people.eecs.berkeley.edu/~taesung_park/CycleGAN/datasets/' + + if folder_exists(os.path.join(path, filename)) is False: + logging.info("[*] {} is nonexistent in {}".format(filename, path)) + maybe_download_and_extract(filename + '.zip', path, url, extract=True) + del_file(os.path.join(path, filename + '.zip')) + + def load_image_from_folder(path): + path_imgs = load_file_list(path=path, regx='\\.jpg', printable=False) + return visualize.read_images(path_imgs, path=path, n_threads=10, printable=False) + + im_train_A = load_image_from_folder(os.path.join(path, filename, "trainA")) + im_train_B = load_image_from_folder(os.path.join(path, filename, "trainB")) + im_test_A = load_image_from_folder(os.path.join(path, filename, "testA")) + im_test_B = load_image_from_folder(os.path.join(path, filename, "testB")) + + def if_2d_to_3d(images): # [h, w] --> [h, w, 3] + for i, _v in enumerate(images): + if len(images[i].shape) == 2: + images[i] = images[i][:, :, np.newaxis] + images[i] = np.tile(images[i], (1, 1, 3)) + return images + + im_train_A = if_2d_to_3d(im_train_A) + im_train_B = if_2d_to_3d(im_train_B) + im_test_A = if_2d_to_3d(im_test_A) + im_test_B = if_2d_to_3d(im_test_B) + + return im_train_A, im_train_B, im_test_A, im_test_B diff --git a/tensorlayer/files/dataset_loaders/flickr_1M_dataset.py b/tensorlayer/files/dataset_loaders/flickr_1M_dataset.py new file mode 100644 index 0000000..f2e582a --- /dev/null +++ b/tensorlayer/files/dataset_loaders/flickr_1M_dataset.py @@ -0,0 +1,116 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import os + +from tensorlayer import logging, visualize +from tensorlayer.files.utils import ( + del_file, folder_exists, load_file_list, load_folder_list, maybe_download_and_extract, read_file +) + +__all__ = ['load_flickr1M_dataset'] + + +def load_flickr1M_dataset(tag='sky', size=10, path="data", n_threads=50, printable=False): + """Load Flick1M dataset. + + Returns a list of images by a given tag from Flickr1M dataset, + it will download Flickr1M from `the official website `__ + at the first time you use it. + + Parameters + ------------ + tag : str or None + What images to return. + - If you want to get images with tag, use string like 'dog', 'red', see `Flickr Search `__. + - If you want to get all images, set to ``None``. + + size : int + integer between 1 to 10. 1 means 100k images ... 5 means 500k images, 10 means all 1 million images. Default is 10. + path : str + The path that the data is downloaded to, defaults is ``data/flickr25k/``. + n_threads : int + The number of thread to read image. + printable : boolean + Whether to print infomation when reading images, default is ``False``. + + Examples + ---------- + Use 200k images + + >>> images = tl.files.load_flickr1M_dataset(tag='zebra', size=2) + + Use 1 Million images + + >>> images = tl.files.load_flickr1M_dataset(tag='zebra') + + """ + import shutil + + path = os.path.join(path, 'flickr1M') + logging.info("[Flickr1M] using {}% of images = {}".format(size * 10, size * 100000)) + images_zip = [ + 'images0.zip', 'images1.zip', 'images2.zip', 'images3.zip', 'images4.zip', 'images5.zip', 'images6.zip', + 'images7.zip', 'images8.zip', 'images9.zip' + ] + tag_zip = 'tags.zip' + url = 'http://press.liacs.nl/mirflickr/mirflickr1m/' + + # download dataset + for image_zip in images_zip[0:size]: + image_folder = image_zip.split(".")[0] + # logging.info(path+"/"+image_folder) + if folder_exists(os.path.join(path, image_folder)) is False: + # logging.info(image_zip) + logging.info("[Flickr1M] {} is missing in {}".format(image_folder, path)) + maybe_download_and_extract(image_zip, path, url, extract=True) + del_file(os.path.join(path, image_zip)) + # os.system("mv {} {}".format(os.path.join(path, 'images'), os.path.join(path, image_folder))) + shutil.move(os.path.join(path, 'images'), os.path.join(path, image_folder)) + else: + logging.info("[Flickr1M] {} exists in {}".format(image_folder, path)) + + # download tag + if folder_exists(os.path.join(path, "tags")) is False: + logging.info("[Flickr1M] tag files is nonexistent in {}".format(path)) + maybe_download_and_extract(tag_zip, path, url, extract=True) + del_file(os.path.join(path, tag_zip)) + else: + logging.info("[Flickr1M] tags exists in {}".format(path)) + + # 1. image path list + images_list = [] + images_folder_list = [] + for i in range(0, size): + images_folder_list += load_folder_list(path=os.path.join(path, 'images%d' % i)) + images_folder_list.sort(key=lambda s: int(s.split('/')[-1])) # folder/images/ddd + + for folder in images_folder_list[0:size * 10]: + tmp = load_file_list(path=folder, regx='\\.jpg', printable=False) + tmp.sort(key=lambda s: int(s.split('.')[-2])) # ddd.jpg + images_list.extend([os.path.join(folder, x) for x in tmp]) + + # 2. tag path list + tag_list = [] + tag_folder_list = load_folder_list(os.path.join(path, "tags")) + + # tag_folder_list.sort(key=lambda s: int(s.split("/")[-1])) # folder/images/ddd + tag_folder_list.sort(key=lambda s: int(os.path.basename(s))) + + for folder in tag_folder_list[0:size * 10]: + tmp = load_file_list(path=folder, regx='\\.txt', printable=False) + tmp.sort(key=lambda s: int(s.split('.')[-2])) # ddd.txt + tmp = [os.path.join(folder, s) for s in tmp] + tag_list += tmp + + # 3. select images + logging.info("[Flickr1M] searching tag: {}".format(tag)) + select_images_list = [] + for idx, _val in enumerate(tag_list): + tags = read_file(tag_list[idx]).split('\n') + if tag in tags: + select_images_list.append(images_list[idx]) + + logging.info("[Flickr1M] reading images with tag: {}".format(tag)) + images = visualize.read_images(select_images_list, '', n_threads=n_threads, printable=printable) + return images diff --git a/tensorlayer/files/dataset_loaders/flickr_25k_dataset.py b/tensorlayer/files/dataset_loaders/flickr_25k_dataset.py new file mode 100644 index 0000000..8049a06 --- /dev/null +++ b/tensorlayer/files/dataset_loaders/flickr_25k_dataset.py @@ -0,0 +1,81 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import os + +from tensorlayer import logging, visualize +from tensorlayer.files.utils import ( + del_file, folder_exists, load_file_list, maybe_download_and_extract, natural_keys, read_file +) + +__all__ = ['load_flickr25k_dataset'] + + +def load_flickr25k_dataset(tag='sky', path="data", n_threads=50, printable=False): + """Load Flickr25K dataset. + + Returns a list of images by a given tag from Flick25k dataset, + it will download Flickr25k from `the official website `__ + at the first time you use it. + + Parameters + ------------ + tag : str or None + What images to return. + - If you want to get images with tag, use string like 'dog', 'red', see `Flickr Search `__. + - If you want to get all images, set to ``None``. + + path : str + The path that the data is downloaded to, defaults is ``data/flickr25k/``. + n_threads : int + The number of thread to read image. + printable : boolean + Whether to print infomation when reading images, default is ``False``. + + Examples + ----------- + Get images with tag of sky + + >>> images = tl.files.load_flickr25k_dataset(tag='sky') + + Get all images + + >>> images = tl.files.load_flickr25k_dataset(tag=None, n_threads=100, printable=True) + + """ + path = os.path.join(path, 'flickr25k') + + filename = 'mirflickr25k.zip' + url = 'http://press.liacs.nl/mirflickr/mirflickr25k/' + + # download dataset + if folder_exists(os.path.join(path, "mirflickr")) is False: + logging.info("[*] Flickr25k is nonexistent in {}".format(path)) + maybe_download_and_extract(filename, path, url, extract=True) + del_file(os.path.join(path, filename)) + + # return images by the given tag. + # 1. image path list + folder_imgs = os.path.join(path, "mirflickr") + path_imgs = load_file_list(path=folder_imgs, regx='\\.jpg', printable=False) + path_imgs.sort(key=natural_keys) + + # 2. tag path list + folder_tags = os.path.join(path, "mirflickr", "meta", "tags") + path_tags = load_file_list(path=folder_tags, regx='\\.txt', printable=False) + path_tags.sort(key=natural_keys) + + # 3. select images + if tag is None: + logging.info("[Flickr25k] reading all images") + else: + logging.info("[Flickr25k] reading images with tag: {}".format(tag)) + images_list = [] + for idx, _v in enumerate(path_tags): + tags = read_file(os.path.join(folder_tags, path_tags[idx])).split('\n') + # logging.info(idx+1, tags) + if tag is None or tag in tags: + images_list.append(path_imgs[idx]) + + images = visualize.read_images(images_list, folder_imgs, n_threads=n_threads, printable=printable) + return images diff --git a/tensorlayer/files/dataset_loaders/imdb_dataset.py b/tensorlayer/files/dataset_loaders/imdb_dataset.py new file mode 100644 index 0000000..2967e7e --- /dev/null +++ b/tensorlayer/files/dataset_loaders/imdb_dataset.py @@ -0,0 +1,115 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import gzip +import os + +import numpy as np +import six.moves.cPickle as pickle + +from tensorlayer.files.utils import maybe_download_and_extract + +__all__ = ['load_imdb_dataset'] + + +def load_imdb_dataset( + path='data', nb_words=None, skip_top=0, maxlen=None, test_split=0.2, seed=113, start_char=1, oov_char=2, + index_from=3 +): + """Load IMDB dataset. + + Parameters + ---------- + path : str + The path that the data is downloaded to, defaults is ``data/imdb/``. + nb_words : int + Number of words to get. + skip_top : int + Top most frequent words to ignore (they will appear as oov_char value in the sequence data). + maxlen : int + Maximum sequence length. Any longer sequence will be truncated. + seed : int + Seed for reproducible data shuffling. + start_char : int + The start of a sequence will be marked with this character. Set to 1 because 0 is usually the padding character. + oov_char : int + Words that were cut out because of the num_words or skip_top limit will be replaced with this character. + index_from : int + Index actual words with this index and higher. + + Examples + -------- + >>> X_train, y_train, X_test, y_test = tl.files.load_imdb_dataset( + ... nb_words=20000, test_split=0.2) + >>> print('X_train.shape', X_train.shape) + (20000,) [[1, 62, 74, ... 1033, 507, 27],[1, 60, 33, ... 13, 1053, 7]..] + >>> print('y_train.shape', y_train.shape) + (20000,) [1 0 0 ..., 1 0 1] + + References + ----------- + - `Modified from keras. `__ + + """ + path = os.path.join(path, 'imdb') + + filename = "imdb.pkl" + url = 'https://s3.amazonaws.com/text-datasets/' + maybe_download_and_extract(filename, path, url) + + if filename.endswith(".gz"): + f = gzip.open(os.path.join(path, filename), 'rb') + else: + f = open(os.path.join(path, filename), 'rb') + + X, labels = pickle.load(f) + f.close() + + np.random.seed(seed) + np.random.shuffle(X) + np.random.seed(seed) + np.random.shuffle(labels) + + if start_char is not None: + X = [[start_char] + [w + index_from for w in x] for x in X] + elif index_from: + X = [[w + index_from for w in x] for x in X] + + if maxlen: + new_X = [] + new_labels = [] + for x, y in zip(X, labels): + if len(x) < maxlen: + new_X.append(x) + new_labels.append(y) + X = new_X + labels = new_labels + if not X: + raise Exception( + 'After filtering for sequences shorter than maxlen=' + str(maxlen) + ', no sequence was kept. ' + 'Increase maxlen.' + ) + if not nb_words: + nb_words = max([max(x) for x in X]) + + # by convention, use 2 as OOV word + # reserve 'index_from' (=3 by default) characters: 0 (padding), 1 (start), 2 (OOV) + if oov_char is not None: + X = [[oov_char if (w >= nb_words or w < skip_top) else w for w in x] for x in X] + else: + nX = [] + for x in X: + nx = [] + for w in x: + if (w >= nb_words or w < skip_top): + nx.append(w) + nX.append(nx) + X = nX + + X_train = np.array(X[:int(len(X) * (1 - test_split))]) + y_train = np.array(labels[:int(len(X) * (1 - test_split))]) + + X_test = np.array(X[int(len(X) * (1 - test_split)):]) + y_test = np.array(labels[int(len(X) * (1 - test_split)):]) + + return X_train, y_train, X_test, y_test diff --git a/tensorlayer/files/dataset_loaders/matt_mahoney_dataset.py b/tensorlayer/files/dataset_loaders/matt_mahoney_dataset.py new file mode 100644 index 0000000..17a3e08 --- /dev/null +++ b/tensorlayer/files/dataset_loaders/matt_mahoney_dataset.py @@ -0,0 +1,48 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import os +import zipfile + +from tensorlayer import logging +from tensorlayer.files.utils import maybe_download_and_extract + +__all__ = ['load_matt_mahoney_text8_dataset'] + + +def load_matt_mahoney_text8_dataset(path='data'): + """Load Matt Mahoney's dataset. + + Download a text file from Matt Mahoney's website + if not present, and make sure it's the right size. + Extract the first file enclosed in a zip file as a list of words. + This dataset can be used for Word Embedding. + + Parameters + ---------- + path : str + The path that the data is downloaded to, defaults is ``data/mm_test8/``. + + Returns + -------- + list of str + The raw text data e.g. [.... 'their', 'families', 'who', 'were', 'expelled', 'from', 'jerusalem', ...] + + Examples + -------- + >>> words = tl.files.load_matt_mahoney_text8_dataset() + >>> print('Data size', len(words)) + + """ + path = os.path.join(path, 'mm_test8') + logging.info("Load or Download matt_mahoney_text8 Dataset> {}".format(path)) + + filename = 'text8.zip' + url = 'http://mattmahoney.net/dc/' + maybe_download_and_extract(filename, path, url, expected_bytes=31344016) + + with zipfile.ZipFile(os.path.join(path, filename)) as f: + word_list = f.read(f.namelist()[0]).split() + for idx, _ in enumerate(word_list): + word_list[idx] = word_list[idx].decode() + return word_list diff --git a/tensorlayer/files/dataset_loaders/mnist_dataset.py b/tensorlayer/files/dataset_loaders/mnist_dataset.py new file mode 100644 index 0000000..4e1346d --- /dev/null +++ b/tensorlayer/files/dataset_loaders/mnist_dataset.py @@ -0,0 +1,31 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +from tensorlayer.files.utils import _load_mnist_dataset + +__all__ = ['load_mnist_dataset'] + + +def load_mnist_dataset(shape=(-1, 784), path='data'): + """Load the original mnist. + + Automatically download MNIST dataset and return the training, validation and test set with 50000, 10000 and 10000 digit images respectively. + + Parameters + ---------- + shape : tuple + The shape of digit images (the default is (-1, 784), alternatively (-1, 28, 28, 1)). + path : str + The path that the data is downloaded to. + + Returns + ------- + X_train, y_train, X_val, y_val, X_test, y_test: tuple + Return splitted training/validation/test set respectively. + + Examples + -------- + >>> X_train, y_train, X_val, y_val, X_test, y_test = tl.files.load_mnist_dataset(shape=(-1,784), path='datasets') + >>> X_train, y_train, X_val, y_val, X_test, y_test = tl.files.load_mnist_dataset(shape=(-1, 28, 28, 1)) + """ + return _load_mnist_dataset(shape, path, name='mnist', url='http://yann.lecun.com/exdb/mnist/') diff --git a/tensorlayer/files/dataset_loaders/mnist_fashion_dataset.py b/tensorlayer/files/dataset_loaders/mnist_fashion_dataset.py new file mode 100644 index 0000000..c7f1bb9 --- /dev/null +++ b/tensorlayer/files/dataset_loaders/mnist_fashion_dataset.py @@ -0,0 +1,33 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +from tensorlayer.files.utils import _load_mnist_dataset + +__all__ = ['load_fashion_mnist_dataset'] + + +def load_fashion_mnist_dataset(shape=(-1, 784), path='data'): + """Load the fashion mnist. + + Automatically download fashion-MNIST dataset and return the training, validation and test set with 50000, 10000 and 10000 fashion images respectively, `examples `__. + + Parameters + ---------- + shape : tuple + The shape of digit images (the default is (-1, 784), alternatively (-1, 28, 28, 1)). + path : str + The path that the data is downloaded to. + + Returns + ------- + X_train, y_train, X_val, y_val, X_test, y_test: tuple + Return splitted training/validation/test set respectively. + + Examples + -------- + >>> X_train, y_train, X_val, y_val, X_test, y_test = tl.files.load_fashion_mnist_dataset(shape=(-1,784), path='datasets') + >>> X_train, y_train, X_val, y_val, X_test, y_test = tl.files.load_fashion_mnist_dataset(shape=(-1, 28, 28, 1)) + """ + return _load_mnist_dataset( + shape, path, name='fashion_mnist', url='http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/' + ) diff --git a/tensorlayer/files/dataset_loaders/mnist_utils.py b/tensorlayer/files/dataset_loaders/mnist_utils.py new file mode 100644 index 0000000..b2f27b3 --- /dev/null +++ b/tensorlayer/files/dataset_loaders/mnist_utils.py @@ -0,0 +1,75 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import gzip +import os + +import numpy as np + +from tensorlayer import logging +from tensorlayer.files.utils import maybe_download_and_extract + +__all__ = ["_load_mnist_dataset"] + + +def _load_mnist_dataset(shape, path, name='mnist', url='http://yann.lecun.com/exdb/mnist/'): + """A generic function to load mnist-like dataset. + + Parameters: + ---------- + shape : tuple + The shape of digit images. + path : str + The path that the data is downloaded to. + name : str + The dataset name you want to use(the default is 'mnist'). + url : str + The url of dataset(the default is 'http://yann.lecun.com/exdb/mnist/'). + """ + path = os.path.join(path, name) + + # Define functions for loading mnist-like data's images and labels. + # For convenience, they also download the requested files if needed. + def load_mnist_images(path, filename): + filepath = maybe_download_and_extract(filename, path, url) + + logging.info(filepath) + # Read the inputs in Yann LeCun's binary format. + with gzip.open(filepath, 'rb') as f: + data = np.frombuffer(f.read(), np.uint8, offset=16) + # The inputs are vectors now, we reshape them to monochrome 2D images, + # following the shape convention: (examples, channels, rows, columns) + data = data.reshape(shape) + # The inputs come as bytes, we convert them to float32 in range [0,1]. + # (Actually to range [0, 255/256], for compatibility to the version + # provided at http://deeplearning.net/data/mnist/mnist.pkl.gz.) + return data / np.float32(256) + + def load_mnist_labels(path, filename): + filepath = maybe_download_and_extract(filename, path, url) + # Read the labels in Yann LeCun's binary format. + with gzip.open(filepath, 'rb') as f: + data = np.frombuffer(f.read(), np.uint8, offset=8) + # The labels are vectors of integers now, that's exactly what we want. + return data + + # Download and read the training and test set images and labels. + logging.info("Load or Download {0} > {1}".format(name.upper(), path)) + X_train = load_mnist_images(path, 'train-images-idx3-ubyte.gz') + y_train = load_mnist_labels(path, 'train-labels-idx1-ubyte.gz') + X_test = load_mnist_images(path, 't10k-images-idx3-ubyte.gz') + y_test = load_mnist_labels(path, 't10k-labels-idx1-ubyte.gz') + + # We reserve the last 10000 training examples for validation. + X_train, X_val = X_train[:-10000], X_train[-10000:] + y_train, y_val = y_train[:-10000], y_train[-10000:] + + # We just return all the arrays in order, as expected in main(). + # (It doesn't matter how we do this as long as we can read them again.) + X_train = np.asarray(X_train, dtype=np.float32) + y_train = np.asarray(y_train, dtype=np.int32) + X_val = np.asarray(X_val, dtype=np.float32) + y_val = np.asarray(y_val, dtype=np.int32) + X_test = np.asarray(X_test, dtype=np.float32) + y_test = np.asarray(y_test, dtype=np.int32) + return X_train, y_train, X_val, y_val, X_test, y_test diff --git a/tensorlayer/files/dataset_loaders/mpii_dataset.py b/tensorlayer/files/dataset_loaders/mpii_dataset.py new file mode 100644 index 0000000..a6f88f6 --- /dev/null +++ b/tensorlayer/files/dataset_loaders/mpii_dataset.py @@ -0,0 +1,252 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import os + +from tensorlayer import logging +from tensorlayer.files.utils import (del_file, folder_exists, load_file_list, maybe_download_and_extract) + +__all__ = ['load_mpii_pose_dataset'] + + +def load_mpii_pose_dataset(path='data', is_16_pos_only=False): + """Load MPII Human Pose Dataset. + + Parameters + ----------- + path : str + The path that the data is downloaded to. + is_16_pos_only : boolean + If True, only return the peoples contain 16 pose keypoints. (Usually be used for single person pose estimation) + + Returns + ---------- + img_train_list : list of str + The image directories of training data. + ann_train_list : list of dict + The annotations of training data. + img_test_list : list of str + The image directories of testing data. + ann_test_list : list of dict + The annotations of testing data. + + Examples + -------- + >>> import pprint + >>> import tensorlayer as tl + >>> img_train_list, ann_train_list, img_test_list, ann_test_list = tl.files.load_mpii_pose_dataset() + >>> image = tl.vis.read_image(img_train_list[0]) + >>> tl.vis.draw_mpii_pose_to_image(image, ann_train_list[0], 'image.png') + >>> pprint.pprint(ann_train_list[0]) + + References + ----------- + - `MPII Human Pose Dataset. CVPR 14 `__ + - `MPII Human Pose Models. CVPR 16 `__ + - `MPII Human Shape, Poselet Conditioned Pictorial Structures and etc `__ + - `MPII Keyponts and ID `__ + """ + path = os.path.join(path, 'mpii_human_pose') + logging.info("Load or Download MPII Human Pose > {}".format(path)) + + # annotation + url = "http://datasets.d2.mpi-inf.mpg.de/andriluka14cvpr/" + tar_filename = "mpii_human_pose_v1_u12_2.zip" + extracted_filename = "mpii_human_pose_v1_u12_2" + if folder_exists(os.path.join(path, extracted_filename)) is False: + logging.info("[MPII] (annotation) {} is nonexistent in {}".format(extracted_filename, path)) + maybe_download_and_extract(tar_filename, path, url, extract=True) + del_file(os.path.join(path, tar_filename)) + + # images + url = "http://datasets.d2.mpi-inf.mpg.de/andriluka14cvpr/" + tar_filename = "mpii_human_pose_v1.tar.gz" + extracted_filename2 = "images" + if folder_exists(os.path.join(path, extracted_filename2)) is False: + logging.info("[MPII] (images) {} is nonexistent in {}".format(extracted_filename, path)) + maybe_download_and_extract(tar_filename, path, url, extract=True) + del_file(os.path.join(path, tar_filename)) + + # parse annotation, format see http://human-pose.mpi-inf.mpg.de/#download + import scipy.io as sio + logging.info("reading annotations from mat file ...") + # mat = sio.loadmat(os.path.join(path, extracted_filename, "mpii_human_pose_v1_u12_1.mat")) + + # def fix_wrong_joints(joint): # https://github.com/mitmul/deeppose/blob/master/datasets/mpii_dataset.py + # if '12' in joint and '13' in joint and '2' in joint and '3' in joint: + # if ((joint['12'][0] < joint['13'][0]) and + # (joint['3'][0] < joint['2'][0])): + # joint['2'], joint['3'] = joint['3'], joint['2'] + # if ((joint['12'][0] > joint['13'][0]) and + # (joint['3'][0] > joint['2'][0])): + # joint['2'], joint['3'] = joint['3'], joint['2'] + # return joint + + ann_train_list = [] + ann_test_list = [] + img_train_list = [] + img_test_list = [] + + def save_joints(): + # joint_data_fn = os.path.join(path, 'data.json') + # fp = open(joint_data_fn, 'w') + mat = sio.loadmat(os.path.join(path, extracted_filename, "mpii_human_pose_v1_u12_1.mat")) + + for _, (anno, train_flag) in enumerate( # all images + zip(mat['RELEASE']['annolist'][0, 0][0], mat['RELEASE']['img_train'][0, 0][0])): + + img_fn = anno['image']['name'][0, 0][0] + train_flag = int(train_flag) + + # print(i, img_fn, train_flag) # DEBUG print all images + + if train_flag: + img_train_list.append(img_fn) + ann_train_list.append([]) + else: + img_test_list.append(img_fn) + ann_test_list.append([]) + + head_rect = [] + if 'x1' in str(anno['annorect'].dtype): + head_rect = zip( + [x1[0, 0] for x1 in anno['annorect']['x1'][0]], [y1[0, 0] for y1 in anno['annorect']['y1'][0]], + [x2[0, 0] for x2 in anno['annorect']['x2'][0]], [y2[0, 0] for y2 in anno['annorect']['y2'][0]] + ) + else: + head_rect = [] # TODO + + if 'annopoints' in str(anno['annorect'].dtype): + annopoints = anno['annorect']['annopoints'][0] + head_x1s = anno['annorect']['x1'][0] + head_y1s = anno['annorect']['y1'][0] + head_x2s = anno['annorect']['x2'][0] + head_y2s = anno['annorect']['y2'][0] + + for annopoint, head_x1, head_y1, head_x2, head_y2 in zip(annopoints, head_x1s, head_y1s, head_x2s, + head_y2s): + # if annopoint != []: + # if len(annopoint) != 0: + if annopoint.size: + head_rect = [ + float(head_x1[0, 0]), + float(head_y1[0, 0]), + float(head_x2[0, 0]), + float(head_y2[0, 0]) + ] + + # joint coordinates + annopoint = annopoint['point'][0, 0] + j_id = [str(j_i[0, 0]) for j_i in annopoint['id'][0]] + x = [x[0, 0] for x in annopoint['x'][0]] + y = [y[0, 0] for y in annopoint['y'][0]] + joint_pos = {} + for _j_id, (_x, _y) in zip(j_id, zip(x, y)): + joint_pos[int(_j_id)] = [float(_x), float(_y)] + # joint_pos = fix_wrong_joints(joint_pos) + + # visibility list + if 'is_visible' in str(annopoint.dtype): + vis = [v[0] if v.size > 0 else [0] for v in annopoint['is_visible'][0]] + vis = dict([(k, int(v[0])) if len(v) > 0 else v for k, v in zip(j_id, vis)]) + else: + vis = None + + # if len(joint_pos) == 16: + if ((is_16_pos_only ==True) and (len(joint_pos) == 16)) or (is_16_pos_only == False): + # only use image with 16 key points / or use all + data = { + 'filename': img_fn, + 'train': train_flag, + 'head_rect': head_rect, + 'is_visible': vis, + 'joint_pos': joint_pos + } + # print(json.dumps(data), file=fp) # py3 + if train_flag: + ann_train_list[-1].append(data) + else: + ann_test_list[-1].append(data) + + # def write_line(datum, fp): + # joints = sorted([[int(k), v] for k, v in datum['joint_pos'].items()]) + # joints = np.array([j for i, j in joints]).flatten() + # + # out = [datum['filename']] + # out.extend(joints) + # out = [str(o) for o in out] + # out = ','.join(out) + # + # print(out, file=fp) + + # def split_train_test(): + # # fp_test = open('data/mpii/test_joints.csv', 'w') + # fp_test = open(os.path.join(path, 'test_joints.csv'), 'w') + # # fp_train = open('data/mpii/train_joints.csv', 'w') + # fp_train = open(os.path.join(path, 'train_joints.csv'), 'w') + # # all_data = open('data/mpii/data.json').readlines() + # all_data = open(os.path.join(path, 'data.json')).readlines() + # N = len(all_data) + # N_test = int(N * 0.1) + # N_train = N - N_test + # + # print('N:{}'.format(N)) + # print('N_train:{}'.format(N_train)) + # print('N_test:{}'.format(N_test)) + # + # np.random.seed(1701) + # perm = np.random.permutation(N) + # test_indices = perm[:N_test] + # train_indices = perm[N_test:] + # + # print('train_indices:{}'.format(len(train_indices))) + # print('test_indices:{}'.format(len(test_indices))) + # + # for i in train_indices: + # datum = json.loads(all_data[i].strip()) + # write_line(datum, fp_train) + # + # for i in test_indices: + # datum = json.loads(all_data[i].strip()) + # write_line(datum, fp_test) + + save_joints() + # split_train_test() # + + ## read images dir + logging.info("reading images list ...") + img_dir = os.path.join(path, extracted_filename2) + _img_list = load_file_list(path=os.path.join(path, extracted_filename2), regx='\\.jpg', printable=False) + # ann_list = json.load(open(os.path.join(path, 'data.json'))) + for i, im in enumerate(img_train_list): + if im not in _img_list: + print('missing training image {} in {} (remove from img(ann)_train_list)'.format(im, img_dir)) + # img_train_list.remove(im) + del img_train_list[i] + del ann_train_list[i] + for i, im in enumerate(img_test_list): + if im not in _img_list: + print('missing testing image {} in {} (remove from img(ann)_test_list)'.format(im, img_dir)) + # img_test_list.remove(im) + del img_train_list[i] + del ann_train_list[i] + + ## check annotation and images + n_train_images = len(img_train_list) + n_test_images = len(img_test_list) + n_images = n_train_images + n_test_images + logging.info("n_images: {} n_train_images: {} n_test_images: {}".format(n_images, n_train_images, n_test_images)) + n_train_ann = len(ann_train_list) + n_test_ann = len(ann_test_list) + n_ann = n_train_ann + n_test_ann + logging.info("n_ann: {} n_train_ann: {} n_test_ann: {}".format(n_ann, n_train_ann, n_test_ann)) + n_train_people = len(sum(ann_train_list, [])) + n_test_people = len(sum(ann_test_list, [])) + n_people = n_train_people + n_test_people + logging.info("n_people: {} n_train_people: {} n_test_people: {}".format(n_people, n_train_people, n_test_people)) + # add path to all image file name + for i, value in enumerate(img_train_list): + img_train_list[i] = os.path.join(img_dir, value) + for i, value in enumerate(img_test_list): + img_test_list[i] = os.path.join(img_dir, value) + return img_train_list, ann_train_list, img_test_list, ann_test_list diff --git a/tensorlayer/files/dataset_loaders/nietzsche_dataset.py b/tensorlayer/files/dataset_loaders/nietzsche_dataset.py new file mode 100644 index 0000000..3cd0e27 --- /dev/null +++ b/tensorlayer/files/dataset_loaders/nietzsche_dataset.py @@ -0,0 +1,42 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import os + +from tensorlayer import logging +from tensorlayer.files.utils import maybe_download_and_extract + +__all__ = ['load_nietzsche_dataset'] + + +def load_nietzsche_dataset(path='data'): + """Load Nietzsche dataset. + + Parameters + ---------- + path : str + The path that the data is downloaded to, defaults is ``data/nietzsche/``. + + Returns + -------- + str + The content. + + Examples + -------- + >>> see tutorial_generate_text.py + >>> words = tl.files.load_nietzsche_dataset() + >>> words = basic_clean_str(words) + >>> words = words.split() + + """ + logging.info("Load or Download nietzsche dataset > {}".format(path)) + path = os.path.join(path, 'nietzsche') + + filename = "nietzsche.txt" + url = 'https://s3.amazonaws.com/text-datasets/' + filepath = maybe_download_and_extract(filename, path, url) + + with open(filepath, "r") as f: + words = f.read() + return words diff --git a/tensorlayer/files/dataset_loaders/ptb_dataset.py b/tensorlayer/files/dataset_loaders/ptb_dataset.py new file mode 100644 index 0000000..30746fd --- /dev/null +++ b/tensorlayer/files/dataset_loaders/ptb_dataset.py @@ -0,0 +1,72 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import os + +from tensorlayer import logging, nlp +from tensorlayer.files.utils import maybe_download_and_extract + +__all__ = ['load_ptb_dataset'] + + +def load_ptb_dataset(path='data'): + """Load Penn TreeBank (PTB) dataset. + + It is used in many LANGUAGE MODELING papers, + including "Empirical Evaluation and Combination of Advanced Language + Modeling Techniques", "Recurrent Neural Network Regularization". + It consists of 929k training words, 73k validation words, and 82k test + words. It has 10k words in its vocabulary. + + Parameters + ---------- + path : str + The path that the data is downloaded to, defaults is ``data/ptb/``. + + Returns + -------- + train_data, valid_data, test_data : list of int + The training, validating and testing data in integer format. + vocab_size : int + The vocabulary size. + + Examples + -------- + >>> train_data, valid_data, test_data, vocab_size = tl.files.load_ptb_dataset() + + References + --------------- + - ``tensorflow.models.rnn.ptb import reader`` + - `Manual download `__ + + Notes + ------ + - If you want to get the raw data, see the source code. + + """ + path = os.path.join(path, 'ptb') + logging.info("Load or Download Penn TreeBank (PTB) dataset > {}".format(path)) + + #Maybe dowload and uncompress tar, or load exsisting files + filename = 'simple-examples.tgz' + url = 'http://www.fit.vutbr.cz/~imikolov/rnnlm/' + maybe_download_and_extract(filename, path, url, extract=True) + + data_path = os.path.join(path, 'simple-examples', 'data') + train_path = os.path.join(data_path, "ptb.train.txt") + valid_path = os.path.join(data_path, "ptb.valid.txt") + test_path = os.path.join(data_path, "ptb.test.txt") + + word_to_id = nlp.build_vocab(nlp.read_words(train_path)) + + train_data = nlp.words_to_word_ids(nlp.read_words(train_path), word_to_id) + valid_data = nlp.words_to_word_ids(nlp.read_words(valid_path), word_to_id) + test_data = nlp.words_to_word_ids(nlp.read_words(test_path), word_to_id) + vocab_size = len(word_to_id) + + # logging.info(nlp.read_words(train_path)) # ... 'according', 'to', 'mr.', '', ''] + # logging.info(train_data) # ... 214, 5, 23, 1, 2] + # logging.info(word_to_id) # ... 'beyond': 1295, 'anti-nuclear': 9599, 'trouble': 1520, '': 2 ... } + # logging.info(vocabulary) # 10000 + # exit() + return train_data, valid_data, test_data, vocab_size diff --git a/tensorlayer/files/dataset_loaders/voc_dataset.py b/tensorlayer/files/dataset_loaders/voc_dataset.py new file mode 100644 index 0000000..5584864 --- /dev/null +++ b/tensorlayer/files/dataset_loaders/voc_dataset.py @@ -0,0 +1,335 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import os + +import tensorflow as tf + +from tensorlayer import logging, utils +from tensorlayer.files.utils import (del_file, del_folder, folder_exists, load_file_list, maybe_download_and_extract) + +__all__ = ['load_voc_dataset'] + + +def load_voc_dataset(path='data', dataset='2012', contain_classes_in_person=False): + """Pascal VOC 2007/2012 Dataset. + + It has 20 objects: + aeroplane, bicycle, bird, boat, bottle, bus, car, cat, chair, cow, diningtable, dog, horse, motorbike, person, pottedplant, sheep, sofa, train, tvmonitor + and additional 3 classes : head, hand, foot for person. + + Parameters + ----------- + path : str + The path that the data is downloaded to, defaults is ``data/VOC``. + dataset : str + The VOC dataset version, `2012`, `2007`, `2007test` or `2012test`. We usually train model on `2007+2012` and test it on `2007test`. + contain_classes_in_person : boolean + Whether include head, hand and foot annotation, default is False. + + Returns + --------- + imgs_file_list : list of str + Full paths of all images. + imgs_semseg_file_list : list of str + Full paths of all maps for semantic segmentation. Note that not all images have this map! + imgs_insseg_file_list : list of str + Full paths of all maps for instance segmentation. Note that not all images have this map! + imgs_ann_file_list : list of str + Full paths of all annotations for bounding box and object class, all images have this annotations. + classes : list of str + Classes in order. + classes_in_person : list of str + Classes in person. + classes_dict : dictionary + Class label to integer. + n_objs_list : list of int + Number of objects in all images in ``imgs_file_list`` in order. + objs_info_list : list of str + Darknet format for the annotation of all images in ``imgs_file_list`` in order. ``[class_id x_centre y_centre width height]`` in ratio format. + objs_info_dicts : dictionary + The annotation of all images in ``imgs_file_list``, ``{imgs_file_list : dictionary for annotation}``, + format from `TensorFlow/Models/object-detection `__. + + Examples + ---------- + >>> imgs_file_list, imgs_semseg_file_list, imgs_insseg_file_list, imgs_ann_file_list, + >>> classes, classes_in_person, classes_dict, + >>> n_objs_list, objs_info_list, objs_info_dicts = tl.files.load_voc_dataset(dataset="2012", contain_classes_in_person=False) + >>> idx = 26 + >>> print(classes) + ['aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse', 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor'] + >>> print(classes_dict) + {'sheep': 16, 'horse': 12, 'bicycle': 1, 'bottle': 4, 'cow': 9, 'sofa': 17, 'car': 6, 'dog': 11, 'cat': 7, 'person': 14, 'train': 18, 'diningtable': 10, 'aeroplane': 0, 'bus': 5, 'pottedplant': 15, 'tvmonitor': 19, 'chair': 8, 'bird': 2, 'boat': 3, 'motorbike': 13} + >>> print(imgs_file_list[idx]) + data/VOC/VOC2012/JPEGImages/2007_000423.jpg + >>> print(n_objs_list[idx]) + 2 + >>> print(imgs_ann_file_list[idx]) + data/VOC/VOC2012/Annotations/2007_000423.xml + >>> print(objs_info_list[idx]) + 14 0.173 0.461333333333 0.142 0.496 + 14 0.828 0.542666666667 0.188 0.594666666667 + >>> ann = tl.prepro.parse_darknet_ann_str_to_list(objs_info_list[idx]) + >>> print(ann) + [[14, 0.173, 0.461333333333, 0.142, 0.496], [14, 0.828, 0.542666666667, 0.188, 0.594666666667]] + >>> c, b = tl.prepro.parse_darknet_ann_list_to_cls_box(ann) + >>> print(c, b) + [14, 14] [[0.173, 0.461333333333, 0.142, 0.496], [0.828, 0.542666666667, 0.188, 0.594666666667]] + + References + ------------- + - `Pascal VOC2012 Website `__. + - `Pascal VOC2007 Website `__. + + """ + try: + import lxml.etree as etree + except ImportError as e: + print(e) + raise ImportError("Module lxml not found. Please install lxml via pip or other package managers.") + + path = os.path.join(path, 'VOC') + + def _recursive_parse_xml_to_dict(xml): + """Recursively parses XML contents to python dict. + + We assume that `object` tags are the only ones that can appear + multiple times at the same level of a tree. + + Args: + xml: xml tree obtained by parsing XML file contents using lxml.etree + + Returns: + Python dictionary holding XML contents. + + """ + if xml is not None: + return {xml.tag: xml.text} + result = {} + for child in xml: + child_result = _recursive_parse_xml_to_dict(child) + if child.tag != 'object': + result[child.tag] = child_result[child.tag] + else: + if child.tag not in result: + result[child.tag] = [] + result[child.tag].append(child_result[child.tag]) + return {xml.tag: result} + + import xml.etree.ElementTree as ET + + if dataset == "2012": + url = "http://pjreddie.com/media/files/" + tar_filename = "VOCtrainval_11-May-2012.tar" + extracted_filename = "VOC2012" #"VOCdevkit/VOC2012" + logging.info(" [============= VOC 2012 =============]") + elif dataset == "2012test": + extracted_filename = "VOC2012test" #"VOCdevkit/VOC2012" + logging.info(" [============= VOC 2012 Test Set =============]") + logging.info( + " \nAuthor: 2012test only have person annotation, so 2007test is highly recommended for testing !\n" + ) + import time + time.sleep(3) + if os.path.isdir(os.path.join(path, extracted_filename)) is False: + logging.info("For VOC 2012 Test data - online registration required") + logging.info( + " Please download VOC2012test.tar from: \n register: http://host.robots.ox.ac.uk:8080 \n voc2012 : http://host.robots.ox.ac.uk:8080/eval/challenges/voc2012/ \ndownload: http://host.robots.ox.ac.uk:8080/eval/downloads/VOC2012test.tar" + ) + logging.info(" unzip VOC2012test.tar,rename the folder to VOC2012test and put it into %s" % path) + exit() + # # http://host.robots.ox.ac.uk:8080/eval/downloads/VOC2012test.tar + # url = "http://host.robots.ox.ac.uk:8080/eval/downloads/" + # tar_filename = "VOC2012test.tar" + elif dataset == "2007": + url = "http://pjreddie.com/media/files/" + tar_filename = "VOCtrainval_06-Nov-2007.tar" + extracted_filename = "VOC2007" + logging.info(" [============= VOC 2007 =============]") + elif dataset == "2007test": + # http://host.robots.ox.ac.uk/pascal/VOC/voc2007/index.html#testdata + # http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtest_06-Nov-2007.tar + url = "http://pjreddie.com/media/files/" + tar_filename = "VOCtest_06-Nov-2007.tar" + extracted_filename = "VOC2007test" + logging.info(" [============= VOC 2007 Test Set =============]") + else: + raise Exception("Please set the dataset aug to 2012, 2012test or 2007.") + + # download dataset + if dataset != "2012test": + from sys import platform as _platform + if folder_exists(os.path.join(path, extracted_filename)) is False: + logging.info("[VOC] {} is nonexistent in {}".format(extracted_filename, path)) + maybe_download_and_extract(tar_filename, path, url, extract=True) + del_file(os.path.join(path, tar_filename)) + if dataset == "2012": + if _platform == "win32": + os.system("move {}\VOCdevkit\VOC2012 {}\VOC2012".format(path, path)) + else: + os.system("mv {}/VOCdevkit/VOC2012 {}/VOC2012".format(path, path)) + elif dataset == "2007": + if _platform == "win32": + os.system("move {}\VOCdevkit\VOC2007 {}\VOC2007".format(path, path)) + else: + os.system("mv {}/VOCdevkit/VOC2007 {}/VOC2007".format(path, path)) + elif dataset == "2007test": + if _platform == "win32": + os.system("move {}\VOCdevkit\VOC2007 {}\VOC2007test".format(path, path)) + else: + os.system("mv {}/VOCdevkit/VOC2007 {}/VOC2007test".format(path, path)) + del_folder(os.path.join(path, 'VOCdevkit')) + # object classes(labels) NOTE: YOU CAN CUSTOMIZE THIS LIST + classes = [ + "aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", + "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor" + ] + if contain_classes_in_person: + classes_in_person = ["head", "hand", "foot"] + else: + classes_in_person = [] + + classes += classes_in_person # use extra 3 classes for person + + classes_dict = utils.list_string_to_dict(classes) + logging.info("[VOC] object classes {}".format(classes_dict)) + + # 1. image path list + # folder_imgs = path+"/"+extracted_filename+"/JPEGImages/" + folder_imgs = os.path.join(path, extracted_filename, "JPEGImages") + imgs_file_list = load_file_list(path=folder_imgs, regx='\\.jpg', printable=False) + logging.info("[VOC] {} images found".format(len(imgs_file_list))) + + imgs_file_list.sort( + key=lambda s: int(s.replace('.', ' ').replace('_', '').split(' ')[-2]) + ) # 2007_000027.jpg --> 2007000027 + + imgs_file_list = [os.path.join(folder_imgs, s) for s in imgs_file_list] + # logging.info('IM',imgs_file_list[0::3333], imgs_file_list[-1]) + if dataset != "2012test": + ##======== 2. semantic segmentation maps path list + # folder_semseg = path+"/"+extracted_filename+"/SegmentationClass/" + folder_semseg = os.path.join(path, extracted_filename, "SegmentationClass") + imgs_semseg_file_list = load_file_list(path=folder_semseg, regx='\\.png', printable=False) + logging.info("[VOC] {} maps for semantic segmentation found".format(len(imgs_semseg_file_list))) + imgs_semseg_file_list.sort( + key=lambda s: int(s.replace('.', ' ').replace('_', '').split(' ')[-2]) + ) # 2007_000032.png --> 2007000032 + imgs_semseg_file_list = [os.path.join(folder_semseg, s) for s in imgs_semseg_file_list] + # logging.info('Semantic Seg IM',imgs_semseg_file_list[0::333], imgs_semseg_file_list[-1]) + ##======== 3. instance segmentation maps path list + # folder_insseg = path+"/"+extracted_filename+"/SegmentationObject/" + folder_insseg = os.path.join(path, extracted_filename, "SegmentationObject") + imgs_insseg_file_list = load_file_list(path=folder_insseg, regx='\\.png', printable=False) + logging.info("[VOC] {} maps for instance segmentation found".format(len(imgs_semseg_file_list))) + imgs_insseg_file_list.sort( + key=lambda s: int(s.replace('.', ' ').replace('_', '').split(' ')[-2]) + ) # 2007_000032.png --> 2007000032 + imgs_insseg_file_list = [os.path.join(folder_insseg, s) for s in imgs_insseg_file_list] + # logging.info('Instance Seg IM',imgs_insseg_file_list[0::333], imgs_insseg_file_list[-1]) + else: + imgs_semseg_file_list = [] + imgs_insseg_file_list = [] + # 4. annotations for bounding box and object class + # folder_ann = path+"/"+extracted_filename+"/Annotations/" + folder_ann = os.path.join(path, extracted_filename, "Annotations") + imgs_ann_file_list = load_file_list(path=folder_ann, regx='\\.xml', printable=False) + logging.info( + "[VOC] {} XML annotation files for bounding box and object class found".format(len(imgs_ann_file_list)) + ) + imgs_ann_file_list.sort( + key=lambda s: int(s.replace('.', ' ').replace('_', '').split(' ')[-2]) + ) # 2007_000027.xml --> 2007000027 + imgs_ann_file_list = [os.path.join(folder_ann, s) for s in imgs_ann_file_list] + # logging.info('ANN',imgs_ann_file_list[0::3333], imgs_ann_file_list[-1]) + + if dataset == "2012test": # remove unused images in JPEG folder + imgs_file_list_new = [] + for ann in imgs_ann_file_list: + ann = os.path.split(ann)[-1].split('.')[0] + for im in imgs_file_list: + if ann in im: + imgs_file_list_new.append(im) + break + imgs_file_list = imgs_file_list_new + logging.info("[VOC] keep %d images" % len(imgs_file_list_new)) + + # parse XML annotations + def convert(size, box): + dw = 1. / size[0] + dh = 1. / size[1] + x = (box[0] + box[1]) / 2.0 + y = (box[2] + box[3]) / 2.0 + w = box[1] - box[0] + h = box[3] - box[2] + x = x * dw + w = w * dw + y = y * dh + h = h * dh + return x, y, w, h + + def convert_annotation(file_name): + """Given VOC2012 XML Annotations, returns number of objects and info.""" + in_file = open(file_name) + out_file = "" + tree = ET.parse(in_file) + root = tree.getroot() + size = root.find('size') + w = int(size.find('width').text) + h = int(size.find('height').text) + n_objs = 0 + + for obj in root.iter('object'): + if dataset != "2012test": + difficult = obj.find('difficult').text + cls = obj.find('name').text + if cls not in classes or int(difficult) == 1: + continue + else: + cls = obj.find('name').text + if cls not in classes: + continue + cls_id = classes.index(cls) + xmlbox = obj.find('bndbox') + b = ( + float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), + float(xmlbox.find('ymax').text) + ) + bb = convert((w, h), b) + + out_file += str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n' + n_objs += 1 + if cls in "person": + for part in obj.iter('part'): + cls = part.find('name').text + if cls not in classes_in_person: + continue + cls_id = classes.index(cls) + xmlbox = part.find('bndbox') + b = ( + float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), + float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text) + ) + bb = convert((w, h), b) + # out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n') + out_file += str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n' + n_objs += 1 + in_file.close() + return n_objs, out_file + + logging.info("[VOC] Parsing xml annotations files") + n_objs_list = [] + objs_info_list = [] # Darknet Format list of string + objs_info_dicts = {} + for idx, ann_file in enumerate(imgs_ann_file_list): + n_objs, objs_info = convert_annotation(ann_file) + n_objs_list.append(n_objs) + objs_info_list.append(objs_info) + with tf.io.gfile.GFile(ann_file, 'r') as fid: + xml_str = fid.read() + xml = etree.fromstring(xml_str) + data = _recursive_parse_xml_to_dict(xml)['annotation'] + objs_info_dicts.update({imgs_file_list[idx]: data}) + + return imgs_file_list, imgs_semseg_file_list, imgs_insseg_file_list, imgs_ann_file_list, classes, classes_in_person, classes_dict, n_objs_list, objs_info_list, objs_info_dicts diff --git a/tensorlayer/files/dataset_loaders/wmt_en_fr_dataset.py b/tensorlayer/files/dataset_loaders/wmt_en_fr_dataset.py new file mode 100644 index 0000000..0261a85 --- /dev/null +++ b/tensorlayer/files/dataset_loaders/wmt_en_fr_dataset.py @@ -0,0 +1,80 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import gzip +import os +import tarfile + +from tensorflow.python.platform import gfile + +from tensorlayer import logging +from tensorlayer.files.utils import maybe_download_and_extract + +__all__ = ['load_wmt_en_fr_dataset'] + + +def load_wmt_en_fr_dataset(path='data'): + """Load WMT'15 English-to-French translation dataset. + + It will download the data from the WMT'15 Website (10^9-French-English corpus), and the 2013 news test from the same site as development set. + Returns the directories of training data and test data. + + Parameters + ---------- + path : str + The path that the data is downloaded to, defaults is ``data/wmt_en_fr/``. + + References + ---------- + - Code modified from /tensorflow/models/rnn/translation/data_utils.py + + Notes + ----- + Usually, it will take a long time to download this dataset. + + """ + path = os.path.join(path, 'wmt_en_fr') + # URLs for WMT data. + _WMT_ENFR_TRAIN_URL = "http://www.statmt.org/wmt10/" + _WMT_ENFR_DEV_URL = "http://www.statmt.org/wmt15/" + + def gunzip_file(gz_path, new_path): + """Unzips from gz_path into new_path.""" + logging.info("Unpacking %s to %s" % (gz_path, new_path)) + with gzip.open(gz_path, "rb") as gz_file: + with open(new_path, "wb") as new_file: + for line in gz_file: + new_file.write(line) + + def get_wmt_enfr_train_set(path): + """Download the WMT en-fr training corpus to directory unless it's there.""" + filename = "training-giga-fren.tar" + maybe_download_and_extract(filename, path, _WMT_ENFR_TRAIN_URL, extract=True) + train_path = os.path.join(path, "giga-fren.release2.fixed") + gunzip_file(train_path + ".fr.gz", train_path + ".fr") + gunzip_file(train_path + ".en.gz", train_path + ".en") + return train_path + + def get_wmt_enfr_dev_set(path): + """Download the WMT en-fr training corpus to directory unless it's there.""" + filename = "dev-v2.tgz" + dev_file = maybe_download_and_extract(filename, path, _WMT_ENFR_DEV_URL, extract=False) + dev_name = "newstest2013" + dev_path = os.path.join(path, "newstest2013") + if not (gfile.Exists(dev_path + ".fr") and gfile.Exists(dev_path + ".en")): + logging.info("Extracting tgz file %s" % dev_file) + with tarfile.open(dev_file, "r:gz") as dev_tar: + fr_dev_file = dev_tar.getmember("dev/" + dev_name + ".fr") + en_dev_file = dev_tar.getmember("dev/" + dev_name + ".en") + fr_dev_file.name = dev_name + ".fr" # Extract without "dev/" prefix. + en_dev_file.name = dev_name + ".en" + dev_tar.extract(fr_dev_file, path) + dev_tar.extract(en_dev_file, path) + return dev_path + + logging.info("Load or Download WMT English-to-French translation > {}".format(path)) + + train_path = get_wmt_enfr_train_set(path) + dev_path = get_wmt_enfr_dev_set(path) + + return train_path, dev_path diff --git a/tensorlayer/files/utils.py b/tensorlayer/files/utils.py new file mode 100644 index 0000000..d05a0c3 --- /dev/null +++ b/tensorlayer/files/utils.py @@ -0,0 +1,2932 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import base64 +import datetime +import gzip +import json +import math +import os +import pickle +import re +import shutil +# import ast +import sys +import tarfile +import time +import zipfile + +import cloudpickle +import h5py +import numpy as np +import progressbar +import scipy.io as sio +import tensorflow as tf +from six.moves import cPickle +from tensorflow.python.keras.saving import model_config as model_config_lib +from tensorflow.python.platform import gfile +from tensorflow.python.util import serialization +from tensorflow.python.util.tf_export import keras_export +from tensorflow.python import pywrap_tensorflow + +import tensorlayer as tl +from tensorlayer import logging, nlp, utils, visualize + +if tl.BACKEND == 'mindspore': + from mindspore.ops.operations import Assign + from mindspore.nn import Cell + from mindspore import Tensor + import mindspore as ms + +if sys.version_info[0] == 2: + from urllib import urlretrieve +else: + from urllib.request import urlretrieve + +# import tensorflow.contrib.eager.python.saver as tfes +# TODO: tf2.0 not stable, cannot import tensorflow.contrib.eager.python.saver + +__all__ = [ + 'assign_weights', + 'del_file', + 'del_folder', + 'download_file_from_google_drive', + 'exists_or_mkdir', + 'file_exists', + 'folder_exists', + 'load_and_assign_npz', + 'load_and_assign_npz_dict', + 'load_ckpt', + 'load_cropped_svhn', + 'load_file_list', + 'load_folder_list', + 'load_npy_to_any', + 'load_npz', + 'maybe_download_and_extract', + 'natural_keys', + 'npz_to_W_pdf', + 'read_file', + 'save_any_to_npy', + 'save_ckpt', + 'save_npz', + 'save_npz_dict', + 'tf_variables_to_numpy', + 'ms_variables_to_numpy', + 'assign_tf_variable', + 'assign_ms_variable', + 'save_weights_to_hdf5', + 'load_hdf5_to_weights_in_order', + 'load_hdf5_to_weights', + 'save_hdf5_graph', + 'load_hdf5_graph', + # 'net2static_graph', + 'static_graph2net', + # 'save_pkl_graph', + # 'load_pkl_graph', + 'load_and_assign_ckpt', + 'ckpt_to_npz_dict' +] + + +def func2str(expr): + b = cloudpickle.dumps(expr) + s = base64.b64encode(b).decode() + return s + + +def str2func(s): + b = base64.b64decode(s) + expr = cloudpickle.loads(b) + return expr + + +# def net2static_graph(network): +# saved_file = dict() +# # if network._NameNone is True: +# # saved_file.update({"name": None}) +# # else: +# # saved_file.update({"name": network.name}) +# # if not isinstance(network.inputs, list): +# # saved_file.update({"inputs": network.inputs._info[0].name}) +# # else: +# # saved_inputs = [] +# # for saved_input in network.inputs: +# # saved_inputs.append(saved_input._info[0].name) +# # saved_file.update({"inputs": saved_inputs}) +# # if not isinstance(network.outputs, list): +# # saved_file.update({"outputs": network.outputs._info[0].name}) +# # else: +# # saved_outputs = [] +# # for saved_output in network.outputs: +# # saved_outputs.append(saved_output._info[0].name) +# # saved_file.update({"outputs": saved_outputs}) +# saved_file.update({"config": network.config}) +# +# return saved_file + + +@keras_export('keras.models.save_model') +def save_keras_model(model): + # f.attrs['keras_model_config'] = json.dumps( + # { + # 'class_name': model.__class__.__name__, + # 'config': model.get_config() + # }, + # default=serialization.get_json_type).encode('utf8') + # + # f.flush() + + return json.dumps( + { + 'class_name': model.__class__.__name__, + 'config': model.get_config() + }, default=serialization.get_json_type + ).encode('utf8') + + +@keras_export('keras.models.load_model') +def load_keras_model(model_config): + + custom_objects = {} + + if model_config is None: + raise ValueError('No model found in config.') + model_config = json.loads(model_config.decode('utf-8')) + model = model_config_lib.model_from_config(model_config, custom_objects=custom_objects) + + return model + + +def save_hdf5_graph(network, filepath='model.hdf5', save_weights=False, customized_data=None): + """Save the architecture of TL model into a hdf5 file. Support saving model weights. + + Parameters + ----------- + network : TensorLayer Model. + The network to save. + filepath : str + The name of model file. + save_weights : bool + Whether to save model weights. + customized_data : dict + The user customized meta data. + + Examples + -------- + >>> # Save the architecture (with parameters) + >>> tl.files.save_hdf5_graph(network, filepath='model.hdf5', save_weights=True) + >>> # Save the architecture (without parameters) + >>> tl.files.save_hdf5_graph(network, filepath='model.hdf5', save_weights=False) + >>> # Load the architecture in another script (no parameters restore) + >>> net = tl.files.load_hdf5_graph(filepath='model.hdf5', load_weights=False) + >>> # Load the architecture in another script (restore parameters) + >>> net = tl.files.load_hdf5_graph(filepath='model.hdf5', load_weights=True) + """ + if network.outputs is None: + raise RuntimeError("save_hdf5_graph not support dynamic mode yet") + + logging.info("[*] Saving TL model into {}, saving weights={}".format(filepath, save_weights)) + + model_config = network.config # net2static_graph(network) + model_config["version_info"]["save_date"] = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc + ).isoformat() + model_config_str = str(model_config) + customized_data_str = str(customized_data) + # version_info = { + # "tensorlayer_version": tl.__version__, + # "backend": "tensorflow", + # "backend_version": tf.__version__, + # "training_device": "gpu", + # "save_date": datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + # } + # version_info_str = str(version_info) + + with h5py.File(filepath, 'w') as f: + f.attrs["model_config"] = model_config_str.encode('utf8') + f.attrs["customized_data"] = customized_data_str.encode('utf8') + # f.attrs["version_info"] = version_info_str.encode('utf8') + if save_weights: + _save_weights_to_hdf5_group(f, network.all_layers) + f.flush() + + logging.info("[*] Saved TL model into {}, saving weights={}".format(filepath, save_weights)) + + +def generate_func(args): + for key in args: + if isinstance(args[key], tuple) and args[key][0] == 'is_Func': + fn = str2func(args[key][1]) + args[key] = fn + # if key in ['act']: + # # fn_dict = args[key] + # # module_path = fn_dict['module_path'] + # # func_name = fn_dict['func_name'] + # # lib = importlib.import_module(module_path) + # # fn = getattr(lib, func_name) + # # args[key] = fn + # fn = str2func(args[key]) + # args[key] = fn + # elif key in ['fn']: + # fn = str2func(args[key]) + # args[key] = fn + + +def eval_layer(layer_kwargs): + layer_class = layer_kwargs.pop('class') + args = layer_kwargs['args'] + layer_type = args.pop('layer_type') + if layer_type == "normal": + generate_func(args) + return eval('tl.layers.' + layer_class)(**args) + elif layer_type == "layerlist": + ret_layer = [] + layers = args["layers"] + for layer_graph in layers: + ret_layer.append(eval_layer(layer_graph)) + args['layers'] = ret_layer + return eval('tl.layers.' + layer_class)(**args) + elif layer_type == "modellayer": + M = static_graph2net(args['model']) + args['model'] = M + return eval('tl.layers.' + layer_class)(**args) + elif layer_type == "keraslayer": + M = load_keras_model(args['fn']) + input_shape = args.pop('keras_input_shape') + _ = M(np.random.random(input_shape).astype(np.float32)) + args['fn'] = M + args['fn_weights'] = M.trainable_variables + return eval('tl.layers.' + layer_class)(**args) + else: + raise RuntimeError("Unknown layer type.") + + +def static_graph2net(model_config): + layer_dict = {} + model_name = model_config["name"] + inputs_tensors = model_config["inputs"] + outputs_tensors = model_config["outputs"] + all_args = model_config["model_architecture"] + for idx, layer_kwargs in enumerate(all_args): + layer_class = layer_kwargs["class"] # class of current layer + prev_layers = layer_kwargs.pop("prev_layer") # name of previous layers + net = eval_layer(layer_kwargs) + if layer_class in tl.layers.inputs.__all__: + net = net._nodes[0].out_tensors[0] + if prev_layers is not None: + for prev_layer in prev_layers: + if not isinstance(prev_layer, list): + output = net(layer_dict[prev_layer]) + layer_dict[output._info[0].name] = output + else: + list_layers = [layer_dict[layer] for layer in prev_layer] + output = net(list_layers) + layer_dict[output._info[0].name] = output + else: + layer_dict[net._info[0].name] = net + + if not isinstance(inputs_tensors, list): + model_inputs = layer_dict[inputs_tensors] + else: + model_inputs = [] + for inputs_tensor in inputs_tensors: + model_inputs.append(layer_dict[inputs_tensor]) + if not isinstance(outputs_tensors, list): + model_outputs = layer_dict[outputs_tensors] + else: + model_outputs = [] + for outputs_tensor in outputs_tensors: + model_outputs.append(layer_dict[outputs_tensor]) + from tensorlayer.models import Model + M = Model(inputs=model_inputs, outputs=model_outputs, name=model_name) + logging.info("[*] Load graph finished") + return M + + +def load_hdf5_graph(filepath='model.hdf5', load_weights=False): + """Restore TL model archtecture from a a pickle file. Support loading model weights. + + Parameters + ----------- + filepath : str + The name of model file. + load_weights : bool + Whether to load model weights. + + Returns + -------- + network : TensorLayer Model. + + Examples + -------- + - see ``tl.files.save_hdf5_graph`` + """ + logging.info("[*] Loading TL model from {}, loading weights={}".format(filepath, load_weights)) + + f = h5py.File(filepath, 'r') + + model_config_str = f.attrs["model_config"].decode('utf8') + model_config = eval(model_config_str) + + # version_info_str = f.attrs["version_info"].decode('utf8') + # version_info = eval(version_info_str) + version_info = model_config["version_info"] + backend_version = version_info["backend_version"] + tensorlayer_version = version_info["tensorlayer_version"] + if backend_version != tf.__version__: + logging.warning( + "Saved model uses tensorflow version {}, but now you are using tensorflow version {}".format( + backend_version, tf.__version__ + ) + ) + if tensorlayer_version != tl.__version__: + logging.warning( + "Saved model uses tensorlayer version {}, but now you are using tensorlayer version {}".format( + tensorlayer_version, tl.__version__ + ) + ) + + M = static_graph2net(model_config) + if load_weights: + if not ('layer_names' in f.attrs.keys()): + raise RuntimeError("Saved model does not contain weights.") + M.load_weights(filepath=filepath) + + f.close() + + logging.info("[*] Loaded TL model from {}, loading weights={}".format(filepath, load_weights)) + + return M + + +# def load_pkl_graph(name='model.pkl'): +# """Restore TL model archtecture from a a pickle file. No parameters be restored. +# +# Parameters +# ----------- +# name : str +# The name of graph file. +# +# Returns +# -------- +# network : TensorLayer Model. +# +# Examples +# -------- +# >>> # It is better to use load_hdf5_graph +# """ +# logging.info("[*] Loading TL graph from {}".format(name)) +# with open(name, 'rb') as file: +# saved_file = pickle.load(file) +# +# M = static_graph2net(saved_file) +# +# return M +# +# +# def save_pkl_graph(network, name='model.pkl'): +# """Save the architecture of TL model into a pickle file. No parameters be saved. +# +# Parameters +# ----------- +# network : TensorLayer layer +# The network to save. +# name : str +# The name of graph file. +# +# Example +# -------- +# >>> # It is better to use save_hdf5_graph +# """ +# if network.outputs is None: +# raise AssertionError("save_graph not support dynamic mode yet") +# +# logging.info("[*] Saving TL graph into {}".format(name)) +# +# saved_file = net2static_graph(network) +# +# with open(name, 'wb') as file: +# pickle.dump(saved_file, file, protocol=pickle.HIGHEST_PROTOCOL) +# logging.info("[*] Saved graph") + + +# Load dataset functions +def load_mnist_dataset(shape=(-1, 784), path='data'): + """Load the original mnist. + + Automatically download MNIST dataset and return the training, validation and test set with 50000, 10000 and 10000 digit images respectively. + + Parameters + ---------- + shape : tuple + The shape of digit images (the default is (-1, 784), alternatively (-1, 28, 28, 1)). + path : str + The path that the data is downloaded to. + + Returns + ------- + X_train, y_train, X_val, y_val, X_test, y_test: tuple + Return splitted training/validation/test set respectively. + + Examples + -------- + >>> X_train, y_train, X_val, y_val, X_test, y_test = tl.files.load_mnist_dataset(shape=(-1,784), path='datasets') + >>> X_train, y_train, X_val, y_val, X_test, y_test = tl.files.load_mnist_dataset(shape=(-1, 28, 28, 1)) + """ + return _load_mnist_dataset(shape, path, name='mnist', url='http://yann.lecun.com/exdb/mnist/') + + +def load_fashion_mnist_dataset(shape=(-1, 784), path='data'): + """Load the fashion mnist. + + Automatically download fashion-MNIST dataset and return the training, validation and test set with 50000, 10000 and 10000 fashion images respectively, `examples `__. + + Parameters + ---------- + shape : tuple + The shape of digit images (the default is (-1, 784), alternatively (-1, 28, 28, 1)). + path : str + The path that the data is downloaded to. + + Returns + ------- + X_train, y_train, X_val, y_val, X_test, y_test: tuple + Return splitted training/validation/test set respectively. + + Examples + -------- + >>> X_train, y_train, X_val, y_val, X_test, y_test = tl.files.load_fashion_mnist_dataset(shape=(-1,784), path='datasets') + >>> X_train, y_train, X_val, y_val, X_test, y_test = tl.files.load_fashion_mnist_dataset(shape=(-1, 28, 28, 1)) + """ + return _load_mnist_dataset( + shape, path, name='fashion_mnist', url='http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/' + ) + + +def _load_mnist_dataset(shape, path, name='mnist', url='http://yann.lecun.com/exdb/mnist/'): + """A generic function to load mnist-like dataset. + + Parameters: + ---------- + shape : tuple + The shape of digit images. + path : str + The path that the data is downloaded to. + name : str + The dataset name you want to use(the default is 'mnist'). + url : str + The url of dataset(the default is 'http://yann.lecun.com/exdb/mnist/'). + """ + path = os.path.join(path, name) + + # Define functions for loading mnist-like data's images and labels. + # For convenience, they also download the requested files if needed. + def load_mnist_images(path, filename): + filepath = maybe_download_and_extract(filename, path, url) + + logging.info(filepath) + # Read the inputs in Yann LeCun's binary format. + with gzip.open(filepath, 'rb') as f: + data = np.frombuffer(f.read(), np.uint8, offset=16) + # The inputs are vectors now, we reshape them to monochrome 2D images, + # following the shape convention: (examples, channels, rows, columns) + data = data.reshape(shape) + # The inputs come as bytes, we convert them to float32 in range [0,1]. + # (Actually to range [0, 255/256], for compatibility to the version + # provided at http://deeplearning.net/data/mnist/mnist.pkl.gz.) + return data / np.float32(256) + + def load_mnist_labels(path, filename): + filepath = maybe_download_and_extract(filename, path, url) + # Read the labels in Yann LeCun's binary format. + with gzip.open(filepath, 'rb') as f: + data = np.frombuffer(f.read(), np.uint8, offset=8) + # The labels are vectors of integers now, that's exactly what we want. + return data + + # Download and read the training and test set images and labels. + logging.info("Load or Download {0} > {1}".format(name.upper(), path)) + X_train = load_mnist_images(path, 'train-images-idx3-ubyte.gz') + y_train = load_mnist_labels(path, 'train-labels-idx1-ubyte.gz') + X_test = load_mnist_images(path, 't10k-images-idx3-ubyte.gz') + y_test = load_mnist_labels(path, 't10k-labels-idx1-ubyte.gz') + + # We reserve the last 10000 training examples for validation. + X_train, X_val = X_train[:-10000], X_train[-10000:] + y_train, y_val = y_train[:-10000], y_train[-10000:] + + # We just return all the arrays in order, as expected in main(). + # (It doesn't matter how we do this as long as we can read them again.) + X_train = np.asarray(X_train, dtype=np.float32) + y_train = np.asarray(y_train, dtype=np.int32) + X_val = np.asarray(X_val, dtype=np.float32) + y_val = np.asarray(y_val, dtype=np.int32) + X_test = np.asarray(X_test, dtype=np.float32) + y_test = np.asarray(y_test, dtype=np.int32) + return X_train, y_train, X_val, y_val, X_test, y_test + + +def load_cifar10_dataset(shape=(-1, 32, 32, 3), path='data', plotable=False): + """Load CIFAR-10 dataset. + + It consists of 60000 32x32 colour images in 10 classes, with + 6000 images per class. There are 50000 training images and 10000 test images. + + The dataset is divided into five training batches and one test batch, each with + 10000 images. The test batch contains exactly 1000 randomly-selected images from + each class. The training batches contain the remaining images in random order, + but some training batches may contain more images from one class than another. + Between them, the training batches contain exactly 5000 images from each class. + + Parameters + ---------- + shape : tupe + The shape of digit images e.g. (-1, 3, 32, 32) and (-1, 32, 32, 3). + path : str + The path that the data is downloaded to, defaults is ``data/cifar10/``. + plotable : boolean + Whether to plot some image examples, False as default. + + Examples + -------- + >>> X_train, y_train, X_test, y_test = tl.files.load_cifar10_dataset(shape=(-1, 32, 32, 3)) + + References + ---------- + - `CIFAR website `__ + - `Data download link `__ + - ``__ + + """ + path = os.path.join(path, 'cifar10') + logging.info("Load or Download cifar10 > {}".format(path)) + + # Helper function to unpickle the data + def unpickle(file): + fp = open(file, 'rb') + if sys.version_info.major == 2: + data = pickle.load(fp) + elif sys.version_info.major == 3: + data = pickle.load(fp, encoding='latin-1') + fp.close() + return data + + filename = 'cifar-10-python.tar.gz' + url = 'https://www.cs.toronto.edu/~kriz/' + # Download and uncompress file + maybe_download_and_extract(filename, path, url, extract=True) + + # Unpickle file and fill in data + X_train = None + y_train = [] + for i in range(1, 6): + data_dic = unpickle(os.path.join(path, 'cifar-10-batches-py/', "data_batch_{}".format(i))) + if i == 1: + X_train = data_dic['data'] + else: + X_train = np.vstack((X_train, data_dic['data'])) + y_train += data_dic['labels'] + + test_data_dic = unpickle(os.path.join(path, 'cifar-10-batches-py/', "test_batch")) + X_test = test_data_dic['data'] + y_test = np.array(test_data_dic['labels']) + + if shape == (-1, 3, 32, 32): + X_test = X_test.reshape(shape) + X_train = X_train.reshape(shape) + elif shape == (-1, 32, 32, 3): + X_test = X_test.reshape(shape, order='F') + X_train = X_train.reshape(shape, order='F') + X_test = np.transpose(X_test, (0, 2, 1, 3)) + X_train = np.transpose(X_train, (0, 2, 1, 3)) + else: + X_test = X_test.reshape(shape) + X_train = X_train.reshape(shape) + + y_train = np.array(y_train) + + if plotable: + + if sys.platform.startswith('darwin'): + import matplotlib + matplotlib.use('TkAgg') + import matplotlib.pyplot as plt + + logging.info('\nCIFAR-10') + fig = plt.figure(1) + + logging.info('Shape of a training image: X_train[0] %s' % X_train[0].shape) + + plt.ion() # interactive mode + count = 1 + for _ in range(10): # each row + for _ in range(10): # each column + _ = fig.add_subplot(10, 10, count) + if shape == (-1, 3, 32, 32): + # plt.imshow(X_train[count-1], interpolation='nearest') + plt.imshow(np.transpose(X_train[count - 1], (1, 2, 0)), interpolation='nearest') + # plt.imshow(np.transpose(X_train[count-1], (2, 1, 0)), interpolation='nearest') + elif shape == (-1, 32, 32, 3): + plt.imshow(X_train[count - 1], interpolation='nearest') + # plt.imshow(np.transpose(X_train[count-1], (1, 0, 2)), interpolation='nearest') + else: + raise Exception("Do not support the given 'shape' to plot the image examples") + plt.gca().xaxis.set_major_locator(plt.NullLocator()) # 不显示刻度(tick) + plt.gca().yaxis.set_major_locator(plt.NullLocator()) + count = count + 1 + plt.draw() # interactive mode + plt.pause(3) # interactive mode + + logging.info("X_train: %s" % X_train.shape) + logging.info("y_train: %s" % y_train.shape) + logging.info("X_test: %s" % X_test.shape) + logging.info("y_test: %s" % y_test.shape) + + X_train = np.asarray(X_train, dtype=np.float32) + X_test = np.asarray(X_test, dtype=np.float32) + y_train = np.asarray(y_train, dtype=np.int32) + y_test = np.asarray(y_test, dtype=np.int32) + + return X_train, y_train, X_test, y_test + + +def load_cropped_svhn(path='data', include_extra=True): + """Load Cropped SVHN. + + The Cropped Street View House Numbers (SVHN) Dataset contains 32x32x3 RGB images. + Digit '1' has label 1, '9' has label 9 and '0' has label 0 (the original dataset uses 10 to represent '0'), see `ufldl website `__. + + Parameters + ---------- + path : str + The path that the data is downloaded to. + include_extra : boolean + If True (default), add extra images to the training set. + + Returns + ------- + X_train, y_train, X_test, y_test: tuple + Return splitted training/test set respectively. + + Examples + --------- + >>> X_train, y_train, X_test, y_test = tl.files.load_cropped_svhn(include_extra=False) + >>> tl.vis.save_images(X_train[0:100], [10, 10], 'svhn.png') + + """ + start_time = time.time() + + path = os.path.join(path, 'cropped_svhn') + logging.info("Load or Download Cropped SVHN > {} | include extra images: {}".format(path, include_extra)) + url = "http://ufldl.stanford.edu/housenumbers/" + + np_file = os.path.join(path, "train_32x32.npz") + if file_exists(np_file) is False: + filename = "train_32x32.mat" + filepath = maybe_download_and_extract(filename, path, url) + mat = sio.loadmat(filepath) + X_train = mat['X'] / 255.0 # to [0, 1] + X_train = np.transpose(X_train, (3, 0, 1, 2)) + y_train = np.squeeze(mat['y'], axis=1) + y_train[y_train == 10] = 0 # replace 10 to 0 + np.savez(np_file, X=X_train, y=y_train) + del_file(filepath) + else: + v = np.load(np_file, allow_pickle=True) + X_train = v['X'] + y_train = v['y'] + logging.info(" n_train: {}".format(len(y_train))) + + np_file = os.path.join(path, "test_32x32.npz") + if file_exists(np_file) is False: + filename = "test_32x32.mat" + filepath = maybe_download_and_extract(filename, path, url) + mat = sio.loadmat(filepath) + X_test = mat['X'] / 255.0 + X_test = np.transpose(X_test, (3, 0, 1, 2)) + y_test = np.squeeze(mat['y'], axis=1) + y_test[y_test == 10] = 0 + np.savez(np_file, X=X_test, y=y_test) + del_file(filepath) + else: + v = np.load(np_file, allow_pickle=True) + X_test = v['X'] + y_test = v['y'] + logging.info(" n_test: {}".format(len(y_test))) + + if include_extra: + logging.info(" getting extra 531131 images, please wait ...") + np_file = os.path.join(path, "extra_32x32.npz") + if file_exists(np_file) is False: + logging.info(" the first time to load extra images will take long time to convert the file format ...") + filename = "extra_32x32.mat" + filepath = maybe_download_and_extract(filename, path, url) + mat = sio.loadmat(filepath) + X_extra = mat['X'] / 255.0 + X_extra = np.transpose(X_extra, (3, 0, 1, 2)) + y_extra = np.squeeze(mat['y'], axis=1) + y_extra[y_extra == 10] = 0 + np.savez(np_file, X=X_extra, y=y_extra) + del_file(filepath) + else: + v = np.load(np_file, allow_pickle=True) + X_extra = v['X'] + y_extra = v['y'] + # print(X_train.shape, X_extra.shape) + logging.info(" adding n_extra {} to n_train {}".format(len(y_extra), len(y_train))) + t = time.time() + X_train = np.concatenate((X_train, X_extra), 0) + y_train = np.concatenate((y_train, y_extra), 0) + # X_train = np.append(X_train, X_extra, axis=0) + # y_train = np.append(y_train, y_extra, axis=0) + logging.info(" added n_extra {} to n_train {} took {}s".format(len(y_extra), len(y_train), time.time() - t)) + else: + logging.info(" no extra images are included") + logging.info(" image size: %s n_train: %d n_test: %d" % (str(X_train.shape[1:4]), len(y_train), len(y_test))) + logging.info(" took: {}s".format(int(time.time() - start_time))) + return X_train, y_train, X_test, y_test + + +def load_ptb_dataset(path='data'): + """Load Penn TreeBank (PTB) dataset. + + It is used in many LANGUAGE MODELING papers, + including "Empirical Evaluation and Combination of Advanced Language + Modeling Techniques", "Recurrent Neural Network Regularization". + It consists of 929k training words, 73k validation words, and 82k test + words. It has 10k words in its vocabulary. + + Parameters + ---------- + path : str + The path that the data is downloaded to, defaults is ``data/ptb/``. + + Returns + -------- + train_data, valid_data, test_data : list of int + The training, validating and testing data in integer format. + vocab_size : int + The vocabulary size. + + Examples + -------- + >>> train_data, valid_data, test_data, vocab_size = tl.files.load_ptb_dataset() + + References + --------------- + - ``tensorflow.models.rnn.ptb import reader`` + - `Manual download `__ + + Notes + ------ + - If you want to get the raw data, see the source code. + + """ + path = os.path.join(path, 'ptb') + logging.info("Load or Download Penn TreeBank (PTB) dataset > {}".format(path)) + + # Maybe dowload and uncompress tar, or load exsisting files + filename = 'simple-examples.tgz' + url = 'http://www.fit.vutbr.cz/~imikolov/rnnlm/' + maybe_download_and_extract(filename, path, url, extract=True) + + data_path = os.path.join(path, 'simple-examples', 'data') + train_path = os.path.join(data_path, "ptb.train.txt") + valid_path = os.path.join(data_path, "ptb.valid.txt") + test_path = os.path.join(data_path, "ptb.test.txt") + + word_to_id = nlp.build_vocab(nlp.read_words(train_path)) + + train_data = nlp.words_to_word_ids(nlp.read_words(train_path), word_to_id) + valid_data = nlp.words_to_word_ids(nlp.read_words(valid_path), word_to_id) + test_data = nlp.words_to_word_ids(nlp.read_words(test_path), word_to_id) + vocab_size = len(word_to_id) + + # logging.info(nlp.read_words(train_path)) # ... 'according', 'to', 'mr.', '', ''] + # logging.info(train_data) # ... 214, 5, 23, 1, 2] + # logging.info(word_to_id) # ... 'beyond': 1295, 'anti-nuclear': 9599, 'trouble': 1520, '': 2 ... } + # logging.info(vocabulary) # 10000 + # exit() + return train_data, valid_data, test_data, vocab_size + + +def load_matt_mahoney_text8_dataset(path='data'): + """Load Matt Mahoney's dataset. + + Download a text file from Matt Mahoney's website + if not present, and make sure it's the right size. + Extract the first file enclosed in a zip file as a list of words. + This dataset can be used for Word Embedding. + + Parameters + ---------- + path : str + The path that the data is downloaded to, defaults is ``data/mm_test8/``. + + Returns + -------- + list of str + The raw text data e.g. [.... 'their', 'families', 'who', 'were', 'expelled', 'from', 'jerusalem', ...] + + Examples + -------- + >>> words = tl.files.load_matt_mahoney_text8_dataset() + >>> print('Data size', len(words)) + + """ + path = os.path.join(path, 'mm_test8') + logging.info("Load or Download matt_mahoney_text8 Dataset> {}".format(path)) + + filename = 'text8.zip' + url = 'http://mattmahoney.net/dc/' + maybe_download_and_extract(filename, path, url, expected_bytes=31344016) + + with zipfile.ZipFile(os.path.join(path, filename)) as f: + word_list = f.read(f.namelist()[0]).split() + for idx, _ in enumerate(word_list): + word_list[idx] = word_list[idx].decode() + return word_list + + +def load_imdb_dataset( + path='data', nb_words=None, skip_top=0, maxlen=None, test_split=0.2, seed=113, start_char=1, oov_char=2, + index_from=3 +): + """Load IMDB dataset. + + Parameters + ---------- + path : str + The path that the data is downloaded to, defaults is ``data/imdb/``. + nb_words : int + Number of words to get. + skip_top : int + Top most frequent words to ignore (they will appear as oov_char value in the sequence data). + maxlen : int + Maximum sequence length. Any longer sequence will be truncated. + seed : int + Seed for reproducible data shuffling. + start_char : int + The start of a sequence will be marked with this character. Set to 1 because 0 is usually the padding character. + oov_char : int + Words that were cut out because of the num_words or skip_top limit will be replaced with this character. + index_from : int + Index actual words with this index and higher. + + Examples + -------- + >>> X_train, y_train, X_test, y_test = tl.files.load_imdb_dataset( + ... nb_words=20000, test_split=0.2) + >>> print('X_train.shape', X_train.shape) + (20000,) [[1, 62, 74, ... 1033, 507, 27],[1, 60, 33, ... 13, 1053, 7]..] + >>> print('y_train.shape', y_train.shape) + (20000,) [1 0 0 ..., 1 0 1] + + References + ----------- + - `Modified from keras. `__ + + """ + path = os.path.join(path, 'imdb') + + filename = "imdb.pkl" + url = 'https://s3.amazonaws.com/text-datasets/' + maybe_download_and_extract(filename, path, url) + + if filename.endswith(".gz"): + f = gzip.open(os.path.join(path, filename), 'rb') + else: + f = open(os.path.join(path, filename), 'rb') + + X, labels = cPickle.load(f) + f.close() + + np.random.seed(seed) + np.random.shuffle(X) + np.random.seed(seed) + np.random.shuffle(labels) + + if start_char is not None: + X = [[start_char] + [w + index_from for w in x] for x in X] + elif index_from: + X = [[w + index_from for w in x] for x in X] + + if maxlen: + new_X = [] + new_labels = [] + for x, y in zip(X, labels): + if len(x) < maxlen: + new_X.append(x) + new_labels.append(y) + X = new_X + labels = new_labels + if not X: + raise Exception( + 'After filtering for sequences shorter than maxlen=' + str(maxlen) + ', no sequence was kept. ' + 'Increase maxlen.' + ) + if not nb_words: + nb_words = max([max(x) for x in X]) + + # by convention, use 2 as OOV word + # reserve 'index_from' (=3 by default) characters: 0 (padding), 1 (start), 2 (OOV) + if oov_char is not None: + X = [[oov_char if (w >= nb_words or w < skip_top) else w for w in x] for x in X] + else: + nX = [] + for x in X: + nx = [] + for w in x: + if (w >= nb_words or w < skip_top): + nx.append(w) + nX.append(nx) + X = nX + + X_train = np.array(X[:int(len(X) * (1 - test_split))]) + y_train = np.array(labels[:int(len(X) * (1 - test_split))]) + + X_test = np.array(X[int(len(X) * (1 - test_split)):]) + y_test = np.array(labels[int(len(X) * (1 - test_split)):]) + + return X_train, y_train, X_test, y_test + + +def load_nietzsche_dataset(path='data'): + """Load Nietzsche dataset. + + Parameters + ---------- + path : str + The path that the data is downloaded to, defaults is ``data/nietzsche/``. + + Returns + -------- + str + The content. + + Examples + -------- + >>> see tutorial_generate_text.py + >>> words = tl.files.load_nietzsche_dataset() + >>> words = basic_clean_str(words) + >>> words = words.split() + + """ + logging.info("Load or Download nietzsche dataset > {}".format(path)) + path = os.path.join(path, 'nietzsche') + + filename = "nietzsche.txt" + url = 'https://s3.amazonaws.com/text-datasets/' + filepath = maybe_download_and_extract(filename, path, url) + + with open(filepath, "r") as f: + words = f.read() + return words + + +def load_wmt_en_fr_dataset(path='data'): + """Load WMT'15 English-to-French translation dataset. + + It will download the data from the WMT'15 Website (10^9-French-English corpus), and the 2013 news test from the same site as development set. + Returns the directories of training data and test data. + + Parameters + ---------- + path : str + The path that the data is downloaded to, defaults is ``data/wmt_en_fr/``. + + References + ---------- + - Code modified from /tensorflow/models/rnn/translation/data_utils.py + + Notes + ----- + Usually, it will take a long time to download this dataset. + + """ + path = os.path.join(path, 'wmt_en_fr') + # URLs for WMT data. + _WMT_ENFR_TRAIN_URL = "http://www.statmt.org/wmt10/" + _WMT_ENFR_DEV_URL = "http://www.statmt.org/wmt15/" + + def gunzip_file(gz_path, new_path): + """Unzips from gz_path into new_path.""" + logging.info("Unpacking %s to %s" % (gz_path, new_path)) + with gzip.open(gz_path, "rb") as gz_file: + with open(new_path, "wb") as new_file: + for line in gz_file: + new_file.write(line) + + def get_wmt_enfr_train_set(path): + """Download the WMT en-fr training corpus to directory unless it's there.""" + filename = "training-giga-fren.tar" + maybe_download_and_extract(filename, path, _WMT_ENFR_TRAIN_URL, extract=True) + train_path = os.path.join(path, "giga-fren.release2.fixed") + gunzip_file(train_path + ".fr.gz", train_path + ".fr") + gunzip_file(train_path + ".en.gz", train_path + ".en") + return train_path + + def get_wmt_enfr_dev_set(path): + """Download the WMT en-fr training corpus to directory unless it's there.""" + filename = "dev-v2.tgz" + dev_file = maybe_download_and_extract(filename, path, _WMT_ENFR_DEV_URL, extract=False) + dev_name = "newstest2013" + dev_path = os.path.join(path, "newstest2013") + if not (gfile.Exists(dev_path + ".fr") and gfile.Exists(dev_path + ".en")): + logging.info("Extracting tgz file %s" % dev_file) + with tarfile.open(dev_file, "r:gz") as dev_tar: + fr_dev_file = dev_tar.getmember("dev/" + dev_name + ".fr") + en_dev_file = dev_tar.getmember("dev/" + dev_name + ".en") + fr_dev_file.name = dev_name + ".fr" # Extract without "dev/" prefix. + en_dev_file.name = dev_name + ".en" + dev_tar.extract(fr_dev_file, path) + dev_tar.extract(en_dev_file, path) + return dev_path + + logging.info("Load or Download WMT English-to-French translation > {}".format(path)) + + train_path = get_wmt_enfr_train_set(path) + dev_path = get_wmt_enfr_dev_set(path) + + return train_path, dev_path + + +def load_flickr25k_dataset(tag='sky', path="data", n_threads=50, printable=False): + """Load Flickr25K dataset. + + Returns a list of images by a given tag from Flick25k dataset, + it will download Flickr25k from `the official website `__ + at the first time you use it. + + Parameters + ------------ + tag : str or None + What images to return. + - If you want to get images with tag, use string like 'dog', 'red', see `Flickr Search `__. + - If you want to get all images, set to ``None``. + + path : str + The path that the data is downloaded to, defaults is ``data/flickr25k/``. + n_threads : int + The number of thread to read image. + printable : boolean + Whether to print infomation when reading images, default is ``False``. + + Examples + ----------- + Get images with tag of sky + + >>> images = tl.files.load_flickr25k_dataset(tag='sky') + + Get all images + + >>> images = tl.files.load_flickr25k_dataset(tag=None, n_threads=100, printable=True) + + """ + path = os.path.join(path, 'flickr25k') + + filename = 'mirflickr25k.zip' + url = 'http://press.liacs.nl/mirflickr/mirflickr25k/' + + # download dataset + if folder_exists(os.path.join(path, "mirflickr")) is False: + logging.info("[*] Flickr25k is nonexistent in {}".format(path)) + maybe_download_and_extract(filename, path, url, extract=True) + del_file(os.path.join(path, filename)) + + # return images by the given tag. + # 1. image path list + folder_imgs = os.path.join(path, "mirflickr") + path_imgs = load_file_list(path=folder_imgs, regx='\\.jpg', printable=False) + path_imgs.sort(key=natural_keys) + + # 2. tag path list + folder_tags = os.path.join(path, "mirflickr", "meta", "tags") + path_tags = load_file_list(path=folder_tags, regx='\\.txt', printable=False) + path_tags.sort(key=natural_keys) + + # 3. select images + if tag is None: + logging.info("[Flickr25k] reading all images") + else: + logging.info("[Flickr25k] reading images with tag: {}".format(tag)) + images_list = [] + for idx, _v in enumerate(path_tags): + tags = read_file(os.path.join(folder_tags, path_tags[idx])).split('\n') + # logging.info(idx+1, tags) + if tag is None or tag in tags: + images_list.append(path_imgs[idx]) + + images = visualize.read_images(images_list, folder_imgs, n_threads=n_threads, printable=printable) + return images + + +def load_flickr1M_dataset(tag='sky', size=10, path="data", n_threads=50, printable=False): + """Load Flick1M dataset. + + Returns a list of images by a given tag from Flickr1M dataset, + it will download Flickr1M from `the official website `__ + at the first time you use it. + + Parameters + ------------ + tag : str or None + What images to return. + - If you want to get images with tag, use string like 'dog', 'red', see `Flickr Search `__. + - If you want to get all images, set to ``None``. + + size : int + integer between 1 to 10. 1 means 100k images ... 5 means 500k images, 10 means all 1 million images. Default is 10. + path : str + The path that the data is downloaded to, defaults is ``data/flickr25k/``. + n_threads : int + The number of thread to read image. + printable : boolean + Whether to print infomation when reading images, default is ``False``. + + Examples + ---------- + Use 200k images + + >>> images = tl.files.load_flickr1M_dataset(tag='zebra', size=2) + + Use 1 Million images + + >>> images = tl.files.load_flickr1M_dataset(tag='zebra') + + """ + path = os.path.join(path, 'flickr1M') + logging.info("[Flickr1M] using {}% of images = {}".format(size * 10, size * 100000)) + images_zip = [ + 'images0.zip', 'images1.zip', 'images2.zip', 'images3.zip', 'images4.zip', 'images5.zip', 'images6.zip', + 'images7.zip', 'images8.zip', 'images9.zip' + ] + tag_zip = 'tags.zip' + url = 'http://press.liacs.nl/mirflickr/mirflickr1m/' + + # download dataset + for image_zip in images_zip[0:size]: + image_folder = image_zip.split(".")[0] + # logging.info(path+"/"+image_folder) + if folder_exists(os.path.join(path, image_folder)) is False: + # logging.info(image_zip) + logging.info("[Flickr1M] {} is missing in {}".format(image_folder, path)) + maybe_download_and_extract(image_zip, path, url, extract=True) + del_file(os.path.join(path, image_zip)) + # os.system("mv {} {}".format(os.path.join(path, 'images'), os.path.join(path, image_folder))) + shutil.move(os.path.join(path, 'images'), os.path.join(path, image_folder)) + else: + logging.info("[Flickr1M] {} exists in {}".format(image_folder, path)) + + # download tag + if folder_exists(os.path.join(path, "tags")) is False: + logging.info("[Flickr1M] tag files is nonexistent in {}".format(path)) + maybe_download_and_extract(tag_zip, path, url, extract=True) + del_file(os.path.join(path, tag_zip)) + else: + logging.info("[Flickr1M] tags exists in {}".format(path)) + + # 1. image path list + images_list = [] + images_folder_list = [] + for i in range(0, size): + images_folder_list += load_folder_list(path=os.path.join(path, 'images%d' % i)) + images_folder_list.sort(key=lambda s: int(s.split('/')[-1])) # folder/images/ddd + + for folder in images_folder_list[0:size * 10]: + tmp = load_file_list(path=folder, regx='\\.jpg', printable=False) + tmp.sort(key=lambda s: int(s.split('.')[-2])) # ddd.jpg + images_list.extend([os.path.join(folder, x) for x in tmp]) + + # 2. tag path list + tag_list = [] + tag_folder_list = load_folder_list(os.path.join(path, "tags")) + + # tag_folder_list.sort(key=lambda s: int(s.split("/")[-1])) # folder/images/ddd + tag_folder_list.sort(key=lambda s: int(os.path.basename(s))) + + for folder in tag_folder_list[0:size * 10]: + tmp = load_file_list(path=folder, regx='\\.txt', printable=False) + tmp.sort(key=lambda s: int(s.split('.')[-2])) # ddd.txt + tmp = [os.path.join(folder, s) for s in tmp] + tag_list += tmp + + # 3. select images + logging.info("[Flickr1M] searching tag: {}".format(tag)) + select_images_list = [] + for idx, _val in enumerate(tag_list): + tags = read_file(tag_list[idx]).split('\n') + if tag in tags: + select_images_list.append(images_list[idx]) + + logging.info("[Flickr1M] reading images with tag: {}".format(tag)) + images = visualize.read_images(select_images_list, '', n_threads=n_threads, printable=printable) + return images + + +def load_cyclegan_dataset(filename='summer2winter_yosemite', path='data'): + """Load images from CycleGAN's database, see `this link `__. + + Parameters + ------------ + filename : str + The dataset you want, see `this link `__. + path : str + The path that the data is downloaded to, defaults is `data/cyclegan` + + Examples + --------- + >>> im_train_A, im_train_B, im_test_A, im_test_B = load_cyclegan_dataset(filename='summer2winter_yosemite') + + """ + path = os.path.join(path, 'cyclegan') + url = 'https://people.eecs.berkeley.edu/~taesung_park/CycleGAN/datasets/' + + if folder_exists(os.path.join(path, filename)) is False: + logging.info("[*] {} is nonexistent in {}".format(filename, path)) + maybe_download_and_extract(filename + '.zip', path, url, extract=True) + del_file(os.path.join(path, filename + '.zip')) + + def load_image_from_folder(path): + path_imgs = load_file_list(path=path, regx='\\.jpg', printable=False) + return visualize.read_images(path_imgs, path=path, n_threads=10, printable=False) + + im_train_A = load_image_from_folder(os.path.join(path, filename, "trainA")) + im_train_B = load_image_from_folder(os.path.join(path, filename, "trainB")) + im_test_A = load_image_from_folder(os.path.join(path, filename, "testA")) + im_test_B = load_image_from_folder(os.path.join(path, filename, "testB")) + + def if_2d_to_3d(images): # [h, w] --> [h, w, 3] + for i, _v in enumerate(images): + if len(images[i].shape) == 2: + images[i] = images[i][:, :, np.newaxis] + images[i] = np.tile(images[i], (1, 1, 3)) + return images + + im_train_A = if_2d_to_3d(im_train_A) + im_train_B = if_2d_to_3d(im_train_B) + im_test_A = if_2d_to_3d(im_test_A) + im_test_B = if_2d_to_3d(im_test_B) + + return im_train_A, im_train_B, im_test_A, im_test_B + + +def download_file_from_google_drive(ID, destination): + """Download file from Google Drive. + + See ``tl.files.load_celebA_dataset`` for example. + + Parameters + -------------- + ID : str + The driver ID. + destination : str + The destination for save file. + + """ + try: + from tqdm import tqdm + except ImportError as e: + print(e) + raise ImportError("Module tqdm not found. Please install tqdm via pip or other package managers.") + + try: + import requests + except ImportError as e: + print(e) + raise ImportError("Module requests not found. Please install requests via pip or other package managers.") + + def save_response_content(response, destination, chunk_size=32 * 1024): + + total_size = int(response.headers.get('content-length', 0)) + with open(destination, "wb") as f: + for chunk in tqdm(response.iter_content(chunk_size), total=total_size, unit='B', unit_scale=True, + desc=destination): + if chunk: # filter out keep-alive new chunks + f.write(chunk) + + def get_confirm_token(response): + for key, value in response.cookies.items(): + if key.startswith('download_warning'): + return value + return None + + URL = "https://docs.google.com/uc?export=download" + session = requests.Session() + + response = session.get(URL, params={'id': ID}, stream=True) + token = get_confirm_token(response) + + if token: + params = {'id': ID, 'confirm': token} + response = session.get(URL, params=params, stream=True) + save_response_content(response, destination) + + +def load_celebA_dataset(path='data'): + """Load CelebA dataset + + Return a list of image path. + + Parameters + ----------- + path : str + The path that the data is downloaded to, defaults is ``data/celebA/``. + + """ + data_dir = 'celebA' + filename, drive_id = "img_align_celeba.zip", "0B7EVK8r0v71pZjFTYXZWM3FlRnM" + save_path = os.path.join(path, filename) + image_path = os.path.join(path, data_dir) + if os.path.exists(image_path): + logging.info('[*] {} already exists'.format(save_path)) + else: + exists_or_mkdir(path) + download_file_from_google_drive(drive_id, save_path) + zip_dir = '' + with zipfile.ZipFile(save_path) as zf: + zip_dir = zf.namelist()[0] + zf.extractall(path) + os.remove(save_path) + os.rename(os.path.join(path, zip_dir), image_path) + + data_files = load_file_list(path=image_path, regx='\\.jpg', printable=False) + for i, _v in enumerate(data_files): + data_files[i] = os.path.join(image_path, data_files[i]) + return data_files + + +def load_voc_dataset(path='data', dataset='2012', contain_classes_in_person=False): + """Pascal VOC 2007/2012 Dataset. + + It has 20 objects: + aeroplane, bicycle, bird, boat, bottle, bus, car, cat, chair, cow, diningtable, dog, horse, motorbike, person, pottedplant, sheep, sofa, train, tvmonitor + and additional 3 classes : head, hand, foot for person. + + Parameters + ----------- + path : str + The path that the data is downloaded to, defaults is ``data/VOC``. + dataset : str + The VOC dataset version, `2012`, `2007`, `2007test` or `2012test`. We usually train model on `2007+2012` and test it on `2007test`. + contain_classes_in_person : boolean + Whether include head, hand and foot annotation, default is False. + + Returns + --------- + imgs_file_list : list of str + Full paths of all images. + imgs_semseg_file_list : list of str + Full paths of all maps for semantic segmentation. Note that not all images have this map! + imgs_insseg_file_list : list of str + Full paths of all maps for instance segmentation. Note that not all images have this map! + imgs_ann_file_list : list of str + Full paths of all annotations for bounding box and object class, all images have this annotations. + classes : list of str + Classes in order. + classes_in_person : list of str + Classes in person. + classes_dict : dictionary + Class label to integer. + n_objs_list : list of int + Number of objects in all images in ``imgs_file_list`` in order. + objs_info_list : list of str + Darknet format for the annotation of all images in ``imgs_file_list`` in order. ``[class_id x_centre y_centre width height]`` in ratio format. + objs_info_dicts : dictionary + The annotation of all images in ``imgs_file_list``, ``{imgs_file_list : dictionary for annotation}``, + format from `TensorFlow/Models/object-detection `__. + + Examples + ---------- + >>> imgs_file_list, imgs_semseg_file_list, imgs_insseg_file_list, imgs_ann_file_list, + >>> classes, classes_in_person, classes_dict, + >>> n_objs_list, objs_info_list, objs_info_dicts = tl.files.load_voc_dataset(dataset="2012", contain_classes_in_person=False) + >>> idx = 26 + >>> print(classes) + ['aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse', 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor'] + >>> print(classes_dict) + {'sheep': 16, 'horse': 12, 'bicycle': 1, 'bottle': 4, 'cow': 9, 'sofa': 17, 'car': 6, 'dog': 11, 'cat': 7, 'person': 14, 'train': 18, 'diningtable': 10, 'aeroplane': 0, 'bus': 5, 'pottedplant': 15, 'tvmonitor': 19, 'chair': 8, 'bird': 2, 'boat': 3, 'motorbike': 13} + >>> print(imgs_file_list[idx]) + data/VOC/VOC2012/JPEGImages/2007_000423.jpg + >>> print(n_objs_list[idx]) + 2 + >>> print(imgs_ann_file_list[idx]) + data/VOC/VOC2012/Annotations/2007_000423.xml + >>> print(objs_info_list[idx]) + 14 0.173 0.461333333333 0.142 0.496 + 14 0.828 0.542666666667 0.188 0.594666666667 + >>> ann = tl.prepro.parse_darknet_ann_str_to_list(objs_info_list[idx]) + >>> print(ann) + [[14, 0.173, 0.461333333333, 0.142, 0.496], [14, 0.828, 0.542666666667, 0.188, 0.594666666667]] + >>> c, b = tl.prepro.parse_darknet_ann_list_to_cls_box(ann) + >>> print(c, b) + [14, 14] [[0.173, 0.461333333333, 0.142, 0.496], [0.828, 0.542666666667, 0.188, 0.594666666667]] + + References + ------------- + - `Pascal VOC2012 Website `__. + - `Pascal VOC2007 Website `__. + + """ + + import xml.etree.ElementTree as ET + + try: + import lxml.etree as etree + except ImportError as e: + print(e) + raise ImportError("Module lxml not found. Please install lxml via pip or other package managers.") + + path = os.path.join(path, 'VOC') + + def _recursive_parse_xml_to_dict(xml): + """Recursively parses XML contents to python dict. + + We assume that `object` tags are the only ones that can appear + multiple times at the same level of a tree. + + Args: + xml: xml tree obtained by parsing XML file contents using lxml.etree + + Returns: + Python dictionary holding XML contents. + + """ + if not xml: + # if xml is not None: + return {xml.tag: xml.text} + result = {} + for child in xml: + child_result = _recursive_parse_xml_to_dict(child) + if child.tag != 'object': + result[child.tag] = child_result[child.tag] + else: + if child.tag not in result: + result[child.tag] = [] + result[child.tag].append(child_result[child.tag]) + return {xml.tag: result} + + if dataset == "2012": + url = "http://host.robots.ox.ac.uk/pascal/VOC/voc2012/" + tar_filename = "VOCtrainval_11-May-2012.tar" + extracted_filename = "VOC2012" # "VOCdevkit/VOC2012" + logging.info(" [============= VOC 2012 =============]") + elif dataset == "2012test": + extracted_filename = "VOC2012test" # "VOCdevkit/VOC2012" + logging.info(" [============= VOC 2012 Test Set =============]") + logging.info( + " \nAuthor: 2012test only have person annotation, so 2007test is highly recommended for testing !\n" + ) + time.sleep(3) + if os.path.isdir(os.path.join(path, extracted_filename)) is False: + logging.info("For VOC 2012 Test data - online registration required") + logging.info( + " Please download VOC2012test.tar from: \n register: http://host.robots.ox.ac.uk:8080 \n voc2012 : http://host.robots.ox.ac.uk:8080/eval/challenges/voc2012/ \ndownload: http://host.robots.ox.ac.uk:8080/eval/downloads/VOC2012test.tar" + ) + logging.info(" unzip VOC2012test.tar,rename the folder to VOC2012test and put it into %s" % path) + exit() + # # http://host.robots.ox.ac.uk:8080/eval/downloads/VOC2012test.tar + # url = "http://host.robots.ox.ac.uk:8080/eval/downloads/" + # tar_filename = "VOC2012test.tar" + elif dataset == "2007": + url = "http://host.robots.ox.ac.uk/pascal/VOC/voc2007/" + tar_filename = "VOCtrainval_06-Nov-2007.tar" + extracted_filename = "VOC2007" + logging.info(" [============= VOC 2007 =============]") + elif dataset == "2007test": + # http://host.robots.ox.ac.uk/pascal/VOC/voc2007/index.html#testdata + # http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtest_06-Nov-2007.tar + url = "http://host.robots.ox.ac.uk/pascal/VOC/voc2007/" + tar_filename = "VOCtest_06-Nov-2007.tar" + extracted_filename = "VOC2007test" + logging.info(" [============= VOC 2007 Test Set =============]") + else: + raise Exception("Please set the dataset aug to 2012, 2012test or 2007.") + + # download dataset + if dataset != "2012test": + _platform = sys.platform + if folder_exists(os.path.join(path, extracted_filename)) is False: + logging.info("[VOC] {} is nonexistent in {}".format(extracted_filename, path)) + maybe_download_and_extract(tar_filename, path, url, extract=True) + del_file(os.path.join(path, tar_filename)) + if dataset == "2012": + if _platform == "win32": + os.system("mv {}\VOCdevkit\VOC2012 {}\VOC2012".format(path, path)) + else: + os.system("mv {}/VOCdevkit/VOC2012 {}/VOC2012".format(path, path)) + elif dataset == "2007": + if _platform == "win32": + os.system("mv {}\VOCdevkit\VOC2007 {}\VOC2007".format(path, path)) + else: + os.system("mv {}/VOCdevkit/VOC2007 {}/VOC2007".format(path, path)) + elif dataset == "2007test": + if _platform == "win32": + os.system("mv {}\VOCdevkit\VOC2007 {}\VOC2007test".format(path, path)) + else: + os.system("mv {}/VOCdevkit/VOC2007 {}/VOC2007test".format(path, path)) + del_folder(os.path.join(path, 'VOCdevkit')) + # object classes(labels) NOTE: YOU CAN CUSTOMIZE THIS LIST + classes = [ + "aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", + "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor" + ] + if contain_classes_in_person: + classes_in_person = ["head", "hand", "foot"] + else: + classes_in_person = [] + + classes += classes_in_person # use extra 3 classes for person + + classes_dict = utils.list_string_to_dict(classes) + logging.info("[VOC] object classes {}".format(classes_dict)) + + # 1. image path list + # folder_imgs = path+"/"+extracted_filename+"/JPEGImages/" + folder_imgs = os.path.join(path, extracted_filename, "JPEGImages") + imgs_file_list = load_file_list(path=folder_imgs, regx='\\.jpg', printable=False) + logging.info("[VOC] {} images found".format(len(imgs_file_list))) + + imgs_file_list.sort( + key=lambda s: int(s.replace('.', ' ').replace('_', '').split(' ')[-2]) + ) # 2007_000027.jpg --> 2007000027 + + imgs_file_list = [os.path.join(folder_imgs, s) for s in imgs_file_list] + # logging.info('IM',imgs_file_list[0::3333], imgs_file_list[-1]) + if dataset != "2012test": + # ======== 2. semantic segmentation maps path list + # folder_semseg = path+"/"+extracted_filename+"/SegmentationClass/" + folder_semseg = os.path.join(path, extracted_filename, "SegmentationClass") + imgs_semseg_file_list = load_file_list(path=folder_semseg, regx='\\.png', printable=False) + logging.info("[VOC] {} maps for semantic segmentation found".format(len(imgs_semseg_file_list))) + imgs_semseg_file_list.sort( + key=lambda s: int(s.replace('.', ' ').replace('_', '').split(' ')[-2]) + ) # 2007_000032.png --> 2007000032 + imgs_semseg_file_list = [os.path.join(folder_semseg, s) for s in imgs_semseg_file_list] + # logging.info('Semantic Seg IM',imgs_semseg_file_list[0::333], imgs_semseg_file_list[-1]) + # ======== 3. instance segmentation maps path list + # folder_insseg = path+"/"+extracted_filename+"/SegmentationObject/" + folder_insseg = os.path.join(path, extracted_filename, "SegmentationObject") + imgs_insseg_file_list = load_file_list(path=folder_insseg, regx='\\.png', printable=False) + logging.info("[VOC] {} maps for instance segmentation found".format(len(imgs_semseg_file_list))) + imgs_insseg_file_list.sort( + key=lambda s: int(s.replace('.', ' ').replace('_', '').split(' ')[-2]) + ) # 2007_000032.png --> 2007000032 + imgs_insseg_file_list = [os.path.join(folder_insseg, s) for s in imgs_insseg_file_list] + # logging.info('Instance Seg IM',imgs_insseg_file_list[0::333], imgs_insseg_file_list[-1]) + else: + imgs_semseg_file_list = [] + imgs_insseg_file_list = [] + # 4. annotations for bounding box and object class + # folder_ann = path+"/"+extracted_filename+"/Annotations/" + folder_ann = os.path.join(path, extracted_filename, "Annotations") + imgs_ann_file_list = load_file_list(path=folder_ann, regx='\\.xml', printable=False) + logging.info( + "[VOC] {} XML annotation files for bounding box and object class found".format(len(imgs_ann_file_list)) + ) + imgs_ann_file_list.sort( + key=lambda s: int(s.replace('.', ' ').replace('_', '').split(' ')[-2]) + ) # 2007_000027.xml --> 2007000027 + imgs_ann_file_list = [os.path.join(folder_ann, s) for s in imgs_ann_file_list] + # logging.info('ANN',imgs_ann_file_list[0::3333], imgs_ann_file_list[-1]) + + if dataset == "2012test": # remove unused images in JPEG folder + imgs_file_list_new = [] + for ann in imgs_ann_file_list: + ann = os.path.split(ann)[-1].split('.')[0] + for im in imgs_file_list: + if ann in im: + imgs_file_list_new.append(im) + break + imgs_file_list = imgs_file_list_new + logging.info("[VOC] keep %d images" % len(imgs_file_list_new)) + + # parse XML annotations + def convert(size, box): + dw = 1. / size[0] + dh = 1. / size[1] + x = (box[0] + box[1]) / 2.0 + y = (box[2] + box[3]) / 2.0 + w = box[1] - box[0] + h = box[3] - box[2] + x = x * dw + w = w * dw + y = y * dh + h = h * dh + return x, y, w, h + + def convert_annotation(file_name): + """Given VOC2012 XML Annotations, returns number of objects and info.""" + in_file = open(file_name) + out_file = "" + tree = ET.parse(in_file) + root = tree.getroot() + size = root.find('size') + w = int(size.find('width').text) + h = int(size.find('height').text) + n_objs = 0 + + for obj in root.iter('object'): + if dataset != "2012test": + difficult = obj.find('difficult').text + cls = obj.find('name').text + if cls not in classes or int(difficult) == 1: + continue + else: + cls = obj.find('name').text + if cls not in classes: + continue + cls_id = classes.index(cls) + xmlbox = obj.find('bndbox') + b = ( + float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), + float(xmlbox.find('ymax').text) + ) + bb = convert((w, h), b) + + out_file += str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n' + n_objs += 1 + if cls in "person": + for part in obj.iter('part'): + cls = part.find('name').text + if cls not in classes_in_person: + continue + cls_id = classes.index(cls) + xmlbox = part.find('bndbox') + b = ( + float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), + float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text) + ) + bb = convert((w, h), b) + # out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n') + out_file += str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n' + n_objs += 1 + in_file.close() + return n_objs, out_file + + logging.info("[VOC] Parsing xml annotations files") + n_objs_list = [] + objs_info_list = [] # Darknet Format list of string + objs_info_dicts = {} + for idx, ann_file in enumerate(imgs_ann_file_list): + n_objs, objs_info = convert_annotation(ann_file) + n_objs_list.append(n_objs) + objs_info_list.append(objs_info) + with tf.io.gfile.GFile(ann_file, 'r') as fid: + xml_str = fid.read() + xml = etree.fromstring(xml_str) + data = _recursive_parse_xml_to_dict(xml)['annotation'] + objs_info_dicts.update({imgs_file_list[idx]: data}) + + return imgs_file_list, imgs_semseg_file_list, imgs_insseg_file_list, imgs_ann_file_list, classes, classes_in_person, classes_dict, n_objs_list, objs_info_list, objs_info_dicts + + +def load_mpii_pose_dataset(path='data', is_16_pos_only=False): + """Load MPII Human Pose Dataset. + + Parameters + ----------- + path : str + The path that the data is downloaded to. + is_16_pos_only : boolean + If True, only return the peoples contain 16 pose keypoints. (Usually be used for single person pose estimation) + + Returns + ---------- + img_train_list : list of str + The image directories of training data. + ann_train_list : list of dict + The annotations of training data. + img_test_list : list of str + The image directories of testing data. + ann_test_list : list of dict + The annotations of testing data. + + Examples + -------- + >>> import pprint + >>> import tensorlayer as tl + >>> img_train_list, ann_train_list, img_test_list, ann_test_list = tl.files.load_mpii_pose_dataset() + >>> image = tl.vis.read_image(img_train_list[0]) + >>> tl.vis.draw_mpii_pose_to_image(image, ann_train_list[0], 'image.png') + >>> pprint.pprint(ann_train_list[0]) + + References + ----------- + - `MPII Human Pose Dataset. CVPR 14 `__ + - `MPII Human Pose Models. CVPR 16 `__ + - `MPII Human Shape, Poselet Conditioned Pictorial Structures and etc `__ + - `MPII Keyponts and ID `__ + """ + path = os.path.join(path, 'mpii_human_pose') + logging.info("Load or Download MPII Human Pose > {}".format(path)) + + # annotation + url = "http://datasets.d2.mpi-inf.mpg.de/andriluka14cvpr/" + tar_filename = "mpii_human_pose_v1_u12_2.zip" + extracted_filename = "mpii_human_pose_v1_u12_2" + if folder_exists(os.path.join(path, extracted_filename)) is False: + logging.info("[MPII] (annotation) {} is nonexistent in {}".format(extracted_filename, path)) + maybe_download_and_extract(tar_filename, path, url, extract=True) + del_file(os.path.join(path, tar_filename)) + + # images + url = "http://datasets.d2.mpi-inf.mpg.de/andriluka14cvpr/" + tar_filename = "mpii_human_pose_v1.tar.gz" + extracted_filename2 = "images" + if folder_exists(os.path.join(path, extracted_filename2)) is False: + logging.info("[MPII] (images) {} is nonexistent in {}".format(extracted_filename, path)) + maybe_download_and_extract(tar_filename, path, url, extract=True) + del_file(os.path.join(path, tar_filename)) + + # parse annotation, format see http://human-pose.mpi-inf.mpg.de/#download + logging.info("reading annotations from mat file ...") + # mat = sio.loadmat(os.path.join(path, extracted_filename, "mpii_human_pose_v1_u12_1.mat")) + + # def fix_wrong_joints(joint): # https://github.com/mitmul/deeppose/blob/master/datasets/mpii_dataset.py + # if '12' in joint and '13' in joint and '2' in joint and '3' in joint: + # if ((joint['12'][0] < joint['13'][0]) and + # (joint['3'][0] < joint['2'][0])): + # joint['2'], joint['3'] = joint['3'], joint['2'] + # if ((joint['12'][0] > joint['13'][0]) and + # (joint['3'][0] > joint['2'][0])): + # joint['2'], joint['3'] = joint['3'], joint['2'] + # return joint + + ann_train_list = [] + ann_test_list = [] + img_train_list = [] + img_test_list = [] + + def save_joints(): + # joint_data_fn = os.path.join(path, 'data.json') + # fp = open(joint_data_fn, 'w') + mat = sio.loadmat(os.path.join(path, extracted_filename, "mpii_human_pose_v1_u12_1.mat")) + + for _, (anno, train_flag) in enumerate( # all images + zip(mat['RELEASE']['annolist'][0, 0][0], mat['RELEASE']['img_train'][0, 0][0])): + + img_fn = anno['image']['name'][0, 0][0] + train_flag = int(train_flag) + + # print(i, img_fn, train_flag) # DEBUG print all images + + if train_flag: + img_train_list.append(img_fn) + ann_train_list.append([]) + else: + img_test_list.append(img_fn) + ann_test_list.append([]) + + head_rect = [] + if 'x1' in str(anno['annorect'].dtype): + head_rect = zip( + [x1[0, 0] for x1 in anno['annorect']['x1'][0]], [y1[0, 0] for y1 in anno['annorect']['y1'][0]], + [x2[0, 0] for x2 in anno['annorect']['x2'][0]], [y2[0, 0] for y2 in anno['annorect']['y2'][0]] + ) + else: + head_rect = [] # TODO + + if 'annopoints' in str(anno['annorect'].dtype): + annopoints = anno['annorect']['annopoints'][0] + head_x1s = anno['annorect']['x1'][0] + head_y1s = anno['annorect']['y1'][0] + head_x2s = anno['annorect']['x2'][0] + head_y2s = anno['annorect']['y2'][0] + + for annopoint, head_x1, head_y1, head_x2, head_y2 in zip(annopoints, head_x1s, head_y1s, head_x2s, + head_y2s): + # if annopoint != []: + # if len(annopoint) != 0: + if annopoint.size: + head_rect = [ + float(head_x1[0, 0]), + float(head_y1[0, 0]), + float(head_x2[0, 0]), + float(head_y2[0, 0]) + ] + + # joint coordinates + annopoint = annopoint['point'][0, 0] + j_id = [str(j_i[0, 0]) for j_i in annopoint['id'][0]] + x = [x[0, 0] for x in annopoint['x'][0]] + y = [y[0, 0] for y in annopoint['y'][0]] + joint_pos = {} + for _j_id, (_x, _y) in zip(j_id, zip(x, y)): + joint_pos[int(_j_id)] = [float(_x), float(_y)] + # joint_pos = fix_wrong_joints(joint_pos) + + # visibility list + if 'is_visible' in str(annopoint.dtype): + vis = [v[0] if v.size > 0 else [0] for v in annopoint['is_visible'][0]] + vis = dict([(k, int(v[0])) if len(v) > 0 else v for k, v in zip(j_id, vis)]) + else: + vis = None + + # if len(joint_pos) == 16: + if ((is_16_pos_only ==True) and (len(joint_pos) == 16)) or (is_16_pos_only == False): + # only use image with 16 key points / or use all + data = { + 'filename': img_fn, + 'train': train_flag, + 'head_rect': head_rect, + 'is_visible': vis, + 'joint_pos': joint_pos + } + # print(json.dumps(data), file=fp) # py3 + if train_flag: + ann_train_list[-1].append(data) + else: + ann_test_list[-1].append(data) + + # def write_line(datum, fp): + # joints = sorted([[int(k), v] for k, v in datum['joint_pos'].items()]) + # joints = np.array([j for i, j in joints]).flatten() + # + # out = [datum['filename']] + # out.extend(joints) + # out = [str(o) for o in out] + # out = ','.join(out) + # + # print(out, file=fp) + + # def split_train_test(): + # # fp_test = open('data/mpii/test_joints.csv', 'w') + # fp_test = open(os.path.join(path, 'test_joints.csv'), 'w') + # # fp_train = open('data/mpii/train_joints.csv', 'w') + # fp_train = open(os.path.join(path, 'train_joints.csv'), 'w') + # # all_data = open('data/mpii/data.json').readlines() + # all_data = open(os.path.join(path, 'data.json')).readlines() + # N = len(all_data) + # N_test = int(N * 0.1) + # N_train = N - N_test + # + # print('N:{}'.format(N)) + # print('N_train:{}'.format(N_train)) + # print('N_test:{}'.format(N_test)) + # + # np.random.seed(1701) + # perm = np.random.permutation(N) + # test_indices = perm[:N_test] + # train_indices = perm[N_test:] + # + # print('train_indices:{}'.format(len(train_indices))) + # print('test_indices:{}'.format(len(test_indices))) + # + # for i in train_indices: + # datum = json.loads(all_data[i].strip()) + # write_line(datum, fp_train) + # + # for i in test_indices: + # datum = json.loads(all_data[i].strip()) + # write_line(datum, fp_test) + + save_joints() + # split_train_test() # + + # read images dir + logging.info("reading images list ...") + img_dir = os.path.join(path, extracted_filename2) + _img_list = load_file_list(path=os.path.join(path, extracted_filename2), regx='\\.jpg', printable=False) + # ann_list = json.load(open(os.path.join(path, 'data.json'))) + for i, im in enumerate(img_train_list): + if im not in _img_list: + print('missing training image {} in {} (remove from img(ann)_train_list)'.format(im, img_dir)) + # img_train_list.remove(im) + del img_train_list[i] + del ann_train_list[i] + for i, im in enumerate(img_test_list): + if im not in _img_list: + print('missing testing image {} in {} (remove from img(ann)_test_list)'.format(im, img_dir)) + # img_test_list.remove(im) + del img_train_list[i] + del ann_train_list[i] + + # check annotation and images + n_train_images = len(img_train_list) + n_test_images = len(img_test_list) + n_images = n_train_images + n_test_images + logging.info("n_images: {} n_train_images: {} n_test_images: {}".format(n_images, n_train_images, n_test_images)) + n_train_ann = len(ann_train_list) + n_test_ann = len(ann_test_list) + n_ann = n_train_ann + n_test_ann + logging.info("n_ann: {} n_train_ann: {} n_test_ann: {}".format(n_ann, n_train_ann, n_test_ann)) + n_train_people = len(sum(ann_train_list, [])) + n_test_people = len(sum(ann_test_list, [])) + n_people = n_train_people + n_test_people + logging.info("n_people: {} n_train_people: {} n_test_people: {}".format(n_people, n_train_people, n_test_people)) + # add path to all image file name + for i, value in enumerate(img_train_list): + img_train_list[i] = os.path.join(img_dir, value) + for i, value in enumerate(img_test_list): + img_test_list[i] = os.path.join(img_dir, value) + return img_train_list, ann_train_list, img_test_list, ann_test_list + + +def save_npz(save_list=None, name='model.npz'): + """Input parameters and the file name, save parameters into .npz file. Use tl.utils.load_npz() to restore. + + Parameters + ---------- + save_list : list of tensor + A list of parameters (tensor) to be saved. + name : str + The name of the `.npz` file. + + Examples + -------- + Save model to npz + + >>> tl.files.save_npz(network.all_weights, name='model.npz') + + Load model from npz (Method 1) + + >>> load_params = tl.files.load_npz(name='model.npz') + >>> tl.files.assign_weights(load_params, network) + + Load model from npz (Method 2) + + >>> tl.files.load_and_assign_npz(name='model.npz', network=network) + + References + ---------- + `Saving dictionary using numpy `__ + + """ + logging.info("[*] Saving TL weights into %s" % name) + if save_list is None: + save_list = [] + + if tl.BACKEND == 'tensorflow': + save_list_var = tf_variables_to_numpy(save_list) + elif tl.BACKEND == 'mindspore': + save_list_var = ms_variables_to_numpy(save_list) + else: + raise NotImplementedError("This backend is not supported") + # print(name, save_list_var) + np.savez(name, params=save_list_var) + save_list_var = None + del save_list_var + logging.info("[*] Saved") + + +def load_npz(path='', name='model.npz'): + """Load the parameters of a Model saved by tl.files.save_npz(). + + Parameters + ---------- + path : str + Folder path to `.npz` file. + name : str + The name of the `.npz` file. + + Returns + -------- + list of array + A list of parameters in order. + + Examples + -------- + - See ``tl.files.save_npz`` + + References + ---------- + - `Saving dictionary using numpy `__ + + """ + d = np.load(os.path.join(path, name), allow_pickle=True) + return d['params'] + + +def assign_params(**kwargs): + raise Exception("please change assign_params --> assign_weights") + + +def assign_weights(weights, network): + """Assign the given parameters to the TensorLayer network. + + Parameters + ---------- + weights : list of array + A list of model weights (array) in order. + network : :class:`Layer` + The network to be assigned. + + Returns + -------- + 1) list of operations if in graph mode + A list of tf ops in order that assign weights. Support sess.run(ops) manually. + 2) list of tf variables if in eager mode + A list of tf variables (assigned weights) in order. + + Examples + -------- + + References + ---------- + - `Assign value to a TensorFlow variable `__ + + """ + ops = [] + if tl.BACKEND == 'tensorflow': + for idx, param in enumerate(weights): + ops.append(network.all_weights[idx].assign(param)) + + elif tl.BACKEND == 'mindspore': + + class Assign_net(Cell): + + def __init__(self, y): + super(Assign_net, self).__init__() + self.y = y + + def construct(self, x): + Assign()(self.y, x) + + for idx, param in enumerate(weights): + assign_param = Tensor(param, dtype=ms.float32) + # net = Assign_net(network.all_weights[idx]) + # net(assign_param) + Assign()(network.all_weights[idx], assign_param) + return ops + + +def load_and_assign_npz(name=None, network=None): + """Load model from npz and assign to a network. + + Parameters + ------------- + name : str + The name of the `.npz` file. + network : :class:`Model` + The network to be assigned. + + Examples + -------- + - See ``tl.files.save_npz`` + + """ + if network is None: + raise ValueError("network is None.") + + if not os.path.exists(name): + logging.error("file {} doesn't exist.".format(name)) + return False + else: + weights = load_npz(name=name) + assign_weights(weights, network) + logging.info("[*] Load {} SUCCESS!".format(name)) + + +def save_npz_dict(save_list=None, name='model.npz'): + """Input parameters and the file name, save parameters as a dictionary into .npz file. + + Use ``tl.files.load_and_assign_npz_dict()`` to restore. + + Parameters + ---------- + save_list : list of parameters + A list of parameters (tensor) to be saved. + name : str + The name of the `.npz` file. + + """ + if save_list is None: + save_list = [] + + save_list_names = [tensor.name for tensor in save_list] + if tl.BACKEND == 'tensorflow': + save_list_var = tf_variables_to_numpy(save_list) + elif tl.BACKEND == 'mindspore': + save_list_var = ms_variables_to_numpy(save_list) + else: + raise NotImplementedError('Not implemented') + save_var_dict = {save_list_names[idx]: val for idx, val in enumerate(save_list_var)} + np.savez(name, **save_var_dict) + save_list_var = None + save_var_dict = None + del save_list_var + del save_var_dict + logging.info("[*] Model saved in npz_dict %s" % name) + + +def load_and_assign_npz_dict(name='model.npz', network=None, skip=False): + """Restore the parameters saved by ``tl.files.save_npz_dict()``. + + Parameters + ------------- + name : str + The name of the `.npz` file. + network : :class:`Model` + The network to be assigned. + skip : boolean + If 'skip' == True, loaded weights whose name is not found in network's weights will be skipped. + If 'skip' is False, error will be raised when mismatch is found. Default False. + + """ + if not os.path.exists(name): + logging.error("file {} doesn't exist.".format(name)) + return False + + weights = np.load(name, allow_pickle=True) + if len(weights.keys()) != len(set(weights.keys())): + raise Exception("Duplication in model npz_dict %s" % name) + + net_weights_name = [w.name for w in network.all_weights] + + for key in weights.keys(): + if key not in net_weights_name: + if skip: + logging.warning("Weights named '%s' not found in network. Skip it." % key) + else: + raise RuntimeError( + "Weights named '%s' not found in network. Hint: set argument skip=Ture " + "if you want to skip redundant or mismatch weights." % key + ) + else: + if tl.BACKEND == 'tensorflow': + assign_tf_variable(network.all_weights[net_weights_name.index(key)], weights[key]) + elif tl.BACKEND == 'mindspore': + assign_param = Tensor(weights[key], dtype=ms.float32) + assign_ms_variable(network.all_weights[net_weights_name.index(key)], assign_param) + logging.info("[*] Model restored from npz_dict %s" % name) + + +def save_ckpt(mode_name='model.ckpt', save_dir='checkpoint', var_list=None, global_step=None, printable=False): + """Save parameters into `ckpt` file. + + Parameters + ------------ + mode_name : str + The name of the model, default is ``model.ckpt``. + save_dir : str + The path / file directory to the `ckpt`, default is ``checkpoint``. + var_list : list of tensor + The parameters / variables (tensor) to be saved. If empty, save all global variables (default). + global_step : int or None + Step number. + printable : boolean + Whether to print all parameters information. + + See Also + -------- + load_ckpt + + """ + + if var_list is None: + if sess is None: + # FIXME: not sure whether global variables can be accessed in eager mode + raise ValueError( + "If var_list is None, sess must be specified. " + "In eager mode, can not access global variables easily. " + ) + var_list = [] + + ckpt_file = os.path.join(save_dir, mode_name) + if var_list == []: + var_list = tf.global_variables() + + logging.info("[*] save %s n_weights: %d" % (ckpt_file, len(var_list))) + + if printable: + for idx, v in enumerate(var_list): + logging.info(" param {:3}: {:15} {}".format(idx, v.name, str(v.get_shape()))) + + if sess: + # graph mode + saver = tf.train.Saver(var_list) + saver.save(sess, ckpt_file, global_step=global_step) + else: + # eager mode + # saver = tfes.Saver(var_list) + # saver.save(ckpt_file, global_step=global_step) + # TODO: tf2.0 not stable, cannot import tensorflow.contrib.eager.python.saver + pass + + +def load_ckpt(sess=None, mode_name='model.ckpt', save_dir='checkpoint', var_list=None, is_latest=True, printable=False): + """Load parameters from `ckpt` file. + + Parameters + ------------ + sess : Session + TensorFlow Session. + mode_name : str + The name of the model, default is ``model.ckpt``. + save_dir : str + The path / file directory to the `ckpt`, default is ``checkpoint``. + var_list : list of tensor + The parameters / variables (tensor) to be saved. If empty, save all global variables (default). + is_latest : boolean + Whether to load the latest `ckpt`, if False, load the `ckpt` with the name of ```mode_name``. + printable : boolean + Whether to print all parameters information. + + Examples + ---------- + - Save all global parameters. + + >>> tl.files.save_ckpt(sess=sess, mode_name='model.ckpt', save_dir='model', printable=True) + + - Save specific parameters. + + >>> tl.files.save_ckpt(sess=sess, mode_name='model.ckpt', var_list=net.all_params, save_dir='model', printable=True) + + - Load latest ckpt. + + >>> tl.files.load_ckpt(sess=sess, var_list=net.all_params, save_dir='model', printable=True) + + - Load specific ckpt. + + >>> tl.files.load_ckpt(sess=sess, mode_name='model.ckpt', var_list=net.all_params, save_dir='model', is_latest=False, printable=True) + + """ + # if sess is None: + # raise ValueError("session is None.") + if var_list is None: + if sess is None: + # FIXME: not sure whether global variables can be accessed in eager mode + raise ValueError( + "If var_list is None, sess must be specified. " + "In eager mode, can not access global variables easily. " + ) + var_list = [] + + if is_latest: + ckpt_file = tf.train.latest_checkpoint(save_dir) + else: + ckpt_file = os.path.join(save_dir, mode_name) + + if not var_list: + var_list = tf.global_variables() + + logging.info("[*] load %s n_weights: %d" % (ckpt_file, len(var_list))) + + if printable: + for idx, v in enumerate(var_list): + logging.info(" weights {:3}: {:15} {}".format(idx, v.name, str(v.get_shape()))) + + try: + if sess: + # graph mode + saver = tf.train.Saver(var_list) + saver.restore(sess, ckpt_file) + else: + # eager mode + # saver = tfes.Saver(var_list) + # saver.restore(ckpt_file) + # TODO: tf2.0 not stable, cannot import tensorflow.contrib.eager.python.saver + pass + + except Exception as e: + logging.info(e) + logging.info("[*] load ckpt fail ...") + + +def save_any_to_npy(save_dict=None, name='file.npy'): + """Save variables to `.npy` file. + + Parameters + ------------ + save_dict : directory + The variables to be saved. + name : str + File name. + + Examples + --------- + >>> tl.files.save_any_to_npy(save_dict={'data': ['a','b']}, name='test.npy') + >>> data = tl.files.load_npy_to_any(name='test.npy') + >>> print(data) + {'data': ['a','b']} + + """ + if save_dict is None: + save_dict = {} + np.save(name, save_dict) + + +def load_npy_to_any(path='', name='file.npy'): + """Load `.npy` file. + + Parameters + ------------ + path : str + Path to the file (optional). + name : str + File name. + + Examples + --------- + - see tl.files.save_any_to_npy() + + """ + file_path = os.path.join(path, name) + try: + return np.load(file_path, allow_pickle=True).item() + except Exception: + return np.load(file_path, allow_pickle=True) + raise Exception("[!] Fail to load %s" % file_path) + + +def file_exists(filepath): + """Check whether a file exists by given file path.""" + return os.path.isfile(filepath) + + +def folder_exists(folderpath): + """Check whether a folder exists by given folder path.""" + return os.path.isdir(folderpath) + + +def del_file(filepath): + """Delete a file by given file path.""" + os.remove(filepath) + + +def del_folder(folderpath): + """Delete a folder by given folder path.""" + shutil.rmtree(folderpath) + + +def read_file(filepath): + """Read a file and return a string. + + Examples + --------- + >>> data = tl.files.read_file('data.txt') + + """ + with open(filepath, 'r') as afile: + return afile.read() + + +def load_file_list(path=None, regx='\.jpg', printable=True, keep_prefix=False): + r"""Return a file list in a folder by given a path and regular expression. + + Parameters + ---------- + path : str or None + A folder path, if `None`, use the current directory. + regx : str + The regx of file name. + printable : boolean + Whether to print the files infomation. + keep_prefix : boolean + Whether to keep path in the file name. + + Examples + ---------- + >>> file_list = tl.files.load_file_list(path=None, regx='w1pre_[0-9]+\.(npz)') + + """ + if path is None: + path = os.getcwd() + file_list = os.listdir(path) + return_list = [] + for _, f in enumerate(file_list): + if re.search(regx, f): + return_list.append(f) + # return_list.sort() + if keep_prefix: + for i, f in enumerate(return_list): + return_list[i] = os.path.join(path, f) + + if printable: + logging.info('Match file list = %s' % return_list) + logging.info('Number of files = %d' % len(return_list)) + return return_list + + +def load_folder_list(path=""): + """Return a folder list in a folder by given a folder path. + + Parameters + ---------- + path : str + A folder path. + + """ + return [os.path.join(path, o) for o in os.listdir(path) if os.path.isdir(os.path.join(path, o))] + + +def exists_or_mkdir(path, verbose=True): + """Check a folder by given name, if not exist, create the folder and return False, + if directory exists, return True. + + Parameters + ---------- + path : str + A folder path. + verbose : boolean + If True (default), prints results. + + Returns + -------- + boolean + True if folder already exist, otherwise, returns False and create the folder. + + Examples + -------- + >>> tl.files.exists_or_mkdir("checkpoints/train") + + """ + if not os.path.exists(path): + if verbose: + logging.info("[*] creates %s ..." % path) + os.makedirs(path) + return False + else: + if verbose: + logging.info("[!] %s exists ..." % path) + return True + + +def maybe_download_and_extract(filename, working_directory, url_source, extract=False, expected_bytes=None): + """Checks if file exists in working_directory otherwise tries to dowload the file, + and optionally also tries to extract the file if format is ".zip" or ".tar" + + Parameters + ----------- + filename : str + The name of the (to be) dowloaded file. + working_directory : str + A folder path to search for the file in and dowload the file to + url : str + The URL to download the file from + extract : boolean + If True, tries to uncompress the dowloaded file is ".tar.gz/.tar.bz2" or ".zip" file, default is False. + expected_bytes : int or None + If set tries to verify that the downloaded file is of the specified size, otherwise raises an Exception, defaults is None which corresponds to no check being performed. + + Returns + ---------- + str + File path of the dowloaded (uncompressed) file. + + Examples + -------- + >>> down_file = tl.files.maybe_download_and_extract(filename='train-images-idx3-ubyte.gz', + ... working_directory='data/', + ... url_source='http://yann.lecun.com/exdb/mnist/') + >>> tl.files.maybe_download_and_extract(filename='ADEChallengeData2016.zip', + ... working_directory='data/', + ... url_source='http://sceneparsing.csail.mit.edu/data/', + ... extract=True) + + """ + + # We first define a download function, supporting both Python 2 and 3. + def _download(filename, working_directory, url_source): + + progress_bar = progressbar.ProgressBar() + + def _dlProgress(count, blockSize, totalSize, pbar=progress_bar): + if (totalSize != 0): + + if not pbar.max_value: + totalBlocks = math.ceil(float(totalSize) / float(blockSize)) + pbar.max_value = int(totalBlocks) + + pbar.update(count, force=True) + + filepath = os.path.join(working_directory, filename) + + logging.info('Downloading %s...\n' % filename) + + urlretrieve(url_source + filename, filepath, reporthook=_dlProgress) + + exists_or_mkdir(working_directory, verbose=False) + filepath = os.path.join(working_directory, filename) + + if not os.path.exists(filepath): + + _download(filename, working_directory, url_source) + statinfo = os.stat(filepath) + logging.info('Succesfully downloaded %s %s bytes.' % (filename, statinfo.st_size)) # , 'bytes.') + if (not (expected_bytes is None) and (expected_bytes != statinfo.st_size)): + raise Exception('Failed to verify ' + filename + '. Can you get to it with a browser?') + if (extract): + if tarfile.is_tarfile(filepath): + logging.info('Trying to extract tar file') + tarfile.open(filepath, 'r').extractall(working_directory) + logging.info('... Success!') + elif zipfile.is_zipfile(filepath): + logging.info('Trying to extract zip file') + with zipfile.ZipFile(filepath) as zf: + zf.extractall(working_directory) + logging.info('... Success!') + else: + logging.info("Unknown compression_format only .tar.gz/.tar.bz2/.tar and .zip supported") + return filepath + + +def natural_keys(text): + """Sort list of string with number in human order. + + Examples + ---------- + >>> l = ['im1.jpg', 'im31.jpg', 'im11.jpg', 'im21.jpg', 'im03.jpg', 'im05.jpg'] + >>> l.sort(key=tl.files.natural_keys) + ['im1.jpg', 'im03.jpg', 'im05', 'im11.jpg', 'im21.jpg', 'im31.jpg'] + >>> l.sort() # that is what we dont want + ['im03.jpg', 'im05', 'im1.jpg', 'im11.jpg', 'im21.jpg', 'im31.jpg'] + + References + ---------- + - `link `__ + + """ + + # - alist.sort(key=natural_keys) sorts in human order + # http://nedbatchelder.com/blog/200712/human_sorting.html + # (See Toothy's implementation in the comments) + def atoi(text): + return int(text) if text.isdigit() else text + + return [atoi(c) for c in re.split('(\d+)', text)] + + +# Visualizing npz files +def npz_to_W_pdf(path=None, regx='w1pre_[0-9]+\.(npz)'): + r"""Convert the first weight matrix of `.npz` file to `.pdf` by using `tl.visualize.W()`. + + Parameters + ---------- + path : str + A folder path to `npz` files. + regx : str + Regx for the file name. + + Examples + --------- + Convert the first weight matrix of w1_pre...npz file to w1_pre...pdf. + + >>> tl.files.npz_to_W_pdf(path='/Users/.../npz_file/', regx='w1pre_[0-9]+\.(npz)') + + """ + file_list = load_file_list(path=path, regx=regx) + for f in file_list: + W = load_npz(path, f)[0] + logging.info("%s --> %s" % (f, f.split('.')[0] + '.pdf')) + visualize.draw_weights(W, second=10, saveable=True, name=f.split('.')[0], fig_idx=2012) + + +def tf_variables_to_numpy(variables): + """Convert TF tensor or a list of tensors into a list of numpy array""" + if not isinstance(variables, list): + var_list = [variables] + else: + var_list = variables + + results = [v.numpy() for v in var_list] + return results + + +def ms_variables_to_numpy(variables): + """Convert MS tensor or list of tensors into a list of numpy array""" + if not isinstance(variables, list): + var_list = [variables] + else: + var_list = variables + + results = [v.data.asnumpy() for v in var_list] + return results + + +def assign_tf_variable(variable, value): + """Assign value to a TF variable""" + variable.assign(value) + + +def assign_ms_variable(variable, value): + + class Assign_net(Cell): + + def __init__(self, y): + super(Assign_net, self).__init__() + self.y = y + + def construct(self, x): + Assign()(self.y, x) + + # net = Assign_net(variable) + # net(value) + Assign()(variable, value) + + +def _save_weights_to_hdf5_group(f, layers): + """ + Save layer/model weights into hdf5 group recursively. + + Parameters + ---------- + f: hdf5 group + A hdf5 group created by h5py.File() or create_group(). + layers: list + A list of layers to save weights. + + """ + f.attrs['layer_names'] = [layer.name.encode('utf8') for layer in layers] + + for layer in layers: + g = f.create_group(layer.name) + if isinstance(layer, tl.models.Model): + _save_weights_to_hdf5_group(g, layer.all_layers) + elif isinstance(layer, tl.layers.ModelLayer): + _save_weights_to_hdf5_group(g, layer.model.all_layers) + elif isinstance(layer, tl.layers.LayerList): + _save_weights_to_hdf5_group(g, layer.layers) + elif isinstance(layer, tl.layers.Layer): + if layer.all_weights is not None: + weight_values = tf_variables_to_numpy(layer.all_weights) + weight_names = [w.name.encode('utf8') for w in layer.all_weights] + else: + weight_values = [] + weight_names = [] + g.attrs['weight_names'] = weight_names + for name, val in zip(weight_names, weight_values): + val_dataset = g.create_dataset(name, val.shape, dtype=val.dtype) + if not val.shape: + # scalar + val_dataset[()] = val + else: + val_dataset[:] = val + else: + raise Exception("Only layer or model can be saved into hdf5.") + + +def _load_weights_from_hdf5_group_in_order(f, layers): + """ + Load layer weights from a hdf5 group sequentially. + + Parameters + ---------- + f: hdf5 group + A hdf5 group created by h5py.File() or create_group(). + layers: list + A list of layers to load weights. + + """ + layer_names = [n.decode('utf8') for n in f.attrs["layer_names"]] + + for idx, name in enumerate(layer_names): + g = f[name] + layer = layers[idx] + if isinstance(layer, tl.models.Model): + _load_weights_from_hdf5_group_in_order(g, layer.all_layers) + elif isinstance(layer, tl.layers.ModelLayer): + _load_weights_from_hdf5_group_in_order(g, layer.model.all_layers) + elif isinstance(layer, tl.layers.LayerList): + _load_weights_from_hdf5_group_in_order(g, layer.layers) + elif isinstance(layer, tl.layers.Layer): + weight_names = [n.decode('utf8') for n in g.attrs['weight_names']] + for iid, w_name in enumerate(weight_names): + assign_tf_variable(layer.all_weights[iid], np.asarray(g[w_name])) + else: + raise Exception("Only layer or model can be saved into hdf5.") + if idx == len(layers) - 1: + break + + +def _load_weights_from_hdf5_group(f, layers, skip=False): + """ + Load layer weights from a hdf5 group by layer name. + + Parameters + ---------- + f: hdf5 group + A hdf5 group created by h5py.File() or create_group(). + layers: list + A list of layers to load weights. + skip : boolean + If 'skip' == True, loaded layer whose name is not found in 'layers' will be skipped. If 'skip' is False, + error will be raised when mismatch is found. Default False. + + """ + layer_names = [n.decode('utf8') for n in f.attrs["layer_names"]] + layer_index = {layer.name: layer for layer in layers} + + for idx, name in enumerate(layer_names): + if name not in layer_index.keys(): + if skip: + logging.warning("Layer named '%s' not found in network. Skip it." % name) + else: + raise RuntimeError( + "Layer named '%s' not found in network. Hint: set argument skip=Ture " + "if you want to skip redundant or mismatch Layers." % name + ) + else: + g = f[name] + layer = layer_index[name] + if isinstance(layer, tl.models.Model): + _load_weights_from_hdf5_group(g, layer.all_layers, skip) + elif isinstance(layer, tl.layers.ModelLayer): + _load_weights_from_hdf5_group(g, layer.model.all_layers, skip) + elif isinstance(layer, tl.layers.LayerList): + _load_weights_from_hdf5_group(g, layer.layers, skip) + elif isinstance(layer, tl.layers.Layer): + weight_names = [n.decode('utf8') for n in g.attrs['weight_names']] + for iid, w_name in enumerate(weight_names): + # FIXME : this is only for compatibility + if isinstance(layer, tl.layers.BatchNorm) and np.asarray(g[w_name]).ndim > 1: + assign_tf_variable(layer.all_weights[iid], np.asarray(g[w_name]).squeeze()) + continue + assign_tf_variable(layer.all_weights[iid], np.asarray(g[w_name])) + else: + raise Exception("Only layer or model can be saved into hdf5.") + + +def save_weights_to_hdf5(filepath, network): + """Input filepath and save weights in hdf5 format. + + Parameters + ---------- + filepath : str + Filename to which the weights will be saved. + network : Model + TL model. + + Returns + ------- + + """ + logging.info("[*] Saving TL weights into %s" % filepath) + + with h5py.File(filepath, 'w') as f: + _save_weights_to_hdf5_group(f, network.all_layers) + + logging.info("[*] Saved") + + +def load_hdf5_to_weights_in_order(filepath, network): + """Load weights sequentially from a given file of hdf5 format + + Parameters + ---------- + filepath : str + Filename to which the weights will be loaded, should be of hdf5 format. + network : Model + TL model. + + Notes: + If the file contains more weights than given 'weights', then the redundant ones will be ignored + if all previous weights match perfectly. + + Returns + ------- + + """ + f = h5py.File(filepath, 'r') + try: + layer_names = [n.decode('utf8') for n in f.attrs["layer_names"]] + except Exception: + raise NameError( + "The loaded hdf5 file needs to have 'layer_names' as attributes. " + "Please check whether this hdf5 file is saved from TL." + ) + + if len(network.all_layers) != len(layer_names): + logging.warning( + "Number of weights mismatch." + "Trying to load a saved file with " + str(len(layer_names)) + " layers into a model with " + + str(len(network.all_layers)) + " layers." + ) + + _load_weights_from_hdf5_group_in_order(f, network.all_layers) + + f.close() + logging.info("[*] Load %s SUCCESS!" % filepath) + + +def load_hdf5_to_weights(filepath, network, skip=False): + """Load weights by name from a given file of hdf5 format + + Parameters + ---------- + filepath : str + Filename to which the weights will be loaded, should be of hdf5 format. + network : Model + TL model. + skip : bool + If 'skip' == True, loaded weights whose name is not found in 'weights' will be skipped. If 'skip' is False, + error will be raised when mismatch is found. Default False. + + Returns + ------- + + """ + f = h5py.File(filepath, 'r') + try: + layer_names = [n.decode('utf8') for n in f.attrs["layer_names"]] + except Exception: + raise NameError( + "The loaded hdf5 file needs to have 'layer_names' as attributes. " + "Please check whether this hdf5 file is saved from TL." + ) + + net_index = {layer.name: layer for layer in network.all_layers} + + if len(network.all_layers) != len(layer_names): + logging.warning( + "Number of weights mismatch." + "Trying to load a saved file with " + str(len(layer_names)) + " layers into a model with " + + str(len(network.all_layers)) + " layers." + ) + + # check mismatch form network weights to hdf5 + for name in net_index.keys(): + if name not in layer_names: + logging.warning("Network layer named '%s' not found in loaded hdf5 file. It will be skipped." % name) + + # load weights from hdf5 to network + _load_weights_from_hdf5_group(f, network.all_layers, skip) + + f.close() + logging.info("[*] Load %s SUCCESS!" % filepath) + + +def load_and_assign_ckpt(model_dir, network=None, skip=True): + """Load weights by name from a given file of ckpt format + + Parameters + ---------- + model_dir : str + Filename to which the weights will be loaded, should be of ckpt format. + Examples: model_dir = /root/cnn_model/ + network : Model + TL model. + skip : bool + If 'skip' == True, loaded weights whose name is not found in 'weights' will be skipped. If 'skip' is False, + error will be raised when mismatch is found. Default False. + + Returns + ------- + + """ + model_dir = model_dir + model_path = None + for root, dirs, files in os.walk(model_dir): + for file in files: + filename, extension = os.path.splitext(file) + if extension in ['.data-00000-of-00001', '.index', '.meta']: + model_path = model_dir + '/' + filename + break + if model_path == None: + raise Exception('The ckpt file is not found') + + reader = pywrap_tensorflow.NewCheckpointReader(model_path) + var_to_shape_map = reader.get_variable_to_shape_map() + + net_weights_name = [w.name for w in network.all_weights] + + for key in var_to_shape_map: + if key not in net_weights_name: + if skip: + logging.warning("Weights named '%s' not found in network. Skip it." % key) + else: + raise RuntimeError( + "Weights named '%s' not found in network. Hint: set argument skip=Ture " + "if you want to skip redundant or mismatch weights." % key + ) + else: + assign_tf_variable(network.all_weights[net_weights_name.index(key)], reader.get_tensor(key)) + logging.info("[*] Model restored from ckpt %s" % filename) + + +def ckpt_to_npz_dict(model_dir, save_name='model.npz'): + """ Save ckpt weights to npz file + + Parameters + ---------- + model_dir : str + Filename to which the weights will be loaded, should be of ckpt format. + Examples: model_dir = /root/cnn_model/ + save_name : str + The save_name of the `.npz` file. + + Returns + ------- + + """ + model_dir = model_dir + model_path = None + for root, dirs, files in os.walk(model_dir): + for file in files: + filename, extension = os.path.splitext(file) + if extension in ['.data-00000-of-00001', '.index', '.meta']: + model_path = model_dir + '/' + filename + break + if model_path == None: + raise Exception('The ckpt file is not found') + + reader = pywrap_tensorflow.NewCheckpointReader(model_path) + var_to_shape_map = reader.get_variable_to_shape_map() + + parameters_dict = {} + for key in sorted(var_to_shape_map): + parameters_dict[key] = reader.get_tensor(key) + np.savez(save_name, **parameters_dict) + parameters_dict = None + del parameters_dict + logging.info("[*] Ckpt weights saved in npz_dict %s" % save_name) diff --git a/tensorlayer/initializers/__init__.py b/tensorlayer/initializers/__init__.py new file mode 100644 index 0000000..80557bd --- /dev/null +++ b/tensorlayer/initializers/__init__.py @@ -0,0 +1,25 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +# __all__ = [ +# 'Initializer', 'Zeros', 'Ones', 'Constant', 'RandomUniform', 'RandomNormal', 'TruncatedNormal', +# 'deconv2d_bilinear_upsampling_initializer', 'He_Normal' +# ] + +from .load_initializers_backend import Zeros +from .load_initializers_backend import Ones +from .load_initializers_backend import Constant +from .load_initializers_backend import RandomUniform +from .load_initializers_backend import RandomNormal +from .load_initializers_backend import TruncatedNormal +from .load_initializers_backend import deconv2d_bilinear_upsampling_initializer +from .load_initializers_backend import HeNormal + +# Alias +zeros = Zeros +ones = Ones +constant = Constant +random_uniform = RandomUniform +random_normal = RandomNormal +truncated_normal = TruncatedNormal +he_normal = HeNormal \ No newline at end of file diff --git a/tensorlayer/initializers/load_initializers_backend.py b/tensorlayer/initializers/load_initializers_backend.py new file mode 100644 index 0000000..fc65bab --- /dev/null +++ b/tensorlayer/initializers/load_initializers_backend.py @@ -0,0 +1,16 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, print_function +from tensorlayer.backend.ops.load_backend import BACKEND + +if BACKEND == 'tensorflow': + from .tensorflow_initializers import * +elif BACKEND == 'mindspore': + from .tensorflow_initializers import * +elif BACKEND == 'dragon': + from .tensorflow_initializers import * +elif BACKEND == 'paddle': + from .paddle_initializers import * +else: + raise NotImplementedError("This backend is not supported") diff --git a/tensorlayer/initializers/paddle_initializers.py b/tensorlayer/initializers/paddle_initializers.py new file mode 100644 index 0000000..22ffa7a --- /dev/null +++ b/tensorlayer/initializers/paddle_initializers.py @@ -0,0 +1,178 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +from paddle.fluid.initializer import ConstantInitializer +from paddle.fluid.initializer import UniformInitializer +from paddle.fluid.initializer import NormalInitializer +from paddle.fluid.initializer import TruncatedNormalInitializer +from paddle.fluid.initializer import MSRAInitializer +import paddle + +__all__ = [ + 'Zeros', 'Ones', 'Constant', 'RandomUniform', 'RandomNormal', 'TruncatedNormal', + 'deconv2d_bilinear_upsampling_initializer', 'HeNormal' +] + + +class Zeros(ConstantInitializer): + """Initializer that generates tensors initialized to 0. + """ + + def __init__(self): + super(Zeros, self).__init__(value=0.0, force_cpu=False) + + +class Ones(object): + """Initializer that generates tensors initialized to 1. + """ + + def __init__(self): + # super(Ones, self).__init__(value=1.0, force_cpu=False) + pass + + def __call__(self, shape, dtype): + return paddle.ones(shape=shape, dtype=dtype) + + +class Constant(ConstantInitializer): + """Initializer that generates tensors initialized to a constant value. + + Parameters + ---------- + value : A python scalar or a numpy array. + The assigned value. + + """ + + def __init__(self, value=0.0): + if value is None: + raise ValueError("value must not be none.") + super(Constant, self).__init__(value=value, force_cpu=False) + self.value = value + + def get_config(self): + return {"value": self.value} + + +class RandomUniform(UniformInitializer): + """Initializer that generates tensors with a uniform distribution. + + Parameters + ---------- + minval : A python scalar or a scalar tensor. + Lower bound of the range of random values to generate. + maxval : A python scalar or a scalar tensor. + Upper bound of the range of random values to generate. + seed : A Python integer. + Used to seed the random generator. + + """ + + def __init__(self, minval=-0.05, maxval=0.05, seed=0): + assert minval is not None, 'low should not be None' + assert maxval is not None, 'high should not be None' + assert maxval >= minval, 'high should greater or equal than low' + super(RandomUniform, self).__init__( + low=minval, high=maxval, seed=seed, diag_num=0, diag_step=0, diag_val=1.0) + self.minval = minval + self.maxval = maxval + self.seed = seed + + def get_config(self): + return {"minval": self.minval, "maxval": self.maxval, "seed": self.seed} + + +class RandomNormal(NormalInitializer): + """Initializer that generates tensors with a normal distribution. + + Parameters + ---------- + mean : A python scalar or a scalar tensor. + Mean of the random values to generate. + stddev : A python scalar or a scalar tensor. + Standard deviation of the random values to generate. + seed : A Python integer. + Used to seed the random generator. + """ + + def __init__(self, mean=0.0, stddev=0.05, seed=0): + assert mean is not None, 'mean should not be None' + assert stddev is not None, 'std should not be None' + super(RandomNormal, self).__init__(loc=mean, scale=stddev, seed=seed) + self.mean = mean + self.stddev = stddev + self.seed = seed + + def get_config(self): + return {"mean": self.mean, "stddev": self.stddev, "seed": self.seed} + + +class TruncatedNormal(TruncatedNormalInitializer): + """Initializer that generates a truncated normal distribution. + + These values are similar to values from a `RandomNormal` + except that values more than two standard deviations from the mean + are discarded and re-drawn. This is the recommended initializer for + neural network weights and filters. + + + Parameters + ---------- + mean : A python scalar or a scalar tensor. + Mean of the random values to generate. + stddev : A python scalar or a scalar tensor. + Standard deviation of the andom values to generate. + seed : A Python integer. + Used to seed the random generator. + """ + + def __init__(self, mean=0.0, stddev=0.05, seed=0): + assert mean is not None, 'mean should not be None' + assert stddev is not None, 'std should not be None' + super(TruncatedNormal, self).__init__(loc=mean, scale=stddev, seed=seed) + self.mean = mean + self.stddev = stddev + self.seed = seed + + def get_config(self): + return {"mean": self.mean, "stddev": self.stddev, "seed": self.seed} + + +class HeNormal(MSRAInitializer): + """He normal initializer. + + Parameters + ---------- + seed : A Python integer. + Used to seed the random generator. + + """ + + def __init__(self, seed=0): + super(HeNormal, self).__init__( + uniform=False, fan_in=None, seed=seed) + self.seed = seed + + def get_config(self): + return {"seed", self.seed} + + +def deconv2d_bilinear_upsampling_initializer(shape): + """Returns the initializer that can be passed to DeConv2dLayer for initializing the + weights in correspondence to channel-wise bilinear up-sampling. + Used in segmentation approaches such as [FCN](https://arxiv.org/abs/1605.06211) + + Parameters + ---------- + shape : tuple of int + The shape of the filters, [height, width, output_channels, in_channels]. + It must match the shape passed to DeConv2dLayer. + + Returns + ------- + ``tf.constant_initializer`` + A constant initializer with weights set to correspond to per channel bilinear upsampling + when passed as W_int in DeConv2dLayer + + """ + raise NotImplementedError diff --git a/tensorlayer/initializers/tensorflow_initializers.py b/tensorlayer/initializers/tensorflow_initializers.py new file mode 100644 index 0000000..8865216 --- /dev/null +++ b/tensorlayer/initializers/tensorflow_initializers.py @@ -0,0 +1,243 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import numpy as np +import tensorlayer as tl + +__all__ = [ + 'Initializer', 'Zeros', 'Ones', 'Constant', 'RandomUniform', 'RandomNormal', 'TruncatedNormal', + 'deconv2d_bilinear_upsampling_initializer', 'HeNormal' +] + + +class Initializer(object): + """Initializer base class: all initializers inherit from this class. + """ + + def __call__(self, shape, dtype=None): + """Returns a tensor object initialized as specified by the initializer. + + Parameters + ---------- + shape : tuple of int. + The shape of the tensor. + dtype : Optional dtype of the tensor. + If not provided will return tensor of `tl.float32`. + + Returns + ------- + + """ + raise NotImplementedError + + def get_config(self): + """Returns the configuration of the initializer as a JSON-serializable dict. + + Returns + ------- + A JSON-serializable Python dict. + """ + return {} + + @classmethod + def from_config(cls, config): + """Instantiates an initializer from a configuration dictionary. + + Parameters + ---------- + config : A python dictionary. + It will typically be the output of `get_config`. + + Returns + ------- + An Initializer instance. + """ + if 'dtype' in config: + config.pop('dtype') + return cls(**config) + + +class Zeros(Initializer): + """Initializer that generates tensors initialized to 0. + """ + + def __call__(self, shape, dtype=tl.float32): + return tl.zeros(shape, dtype=dtype) + + +class Ones(Initializer): + """Initializer that generates tensors initialized to 1. + """ + + def __call__(self, shape, dtype=tl.float32): + return tl.ones(shape, dtype=dtype) + + +class Constant(Initializer): + """Initializer that generates tensors initialized to a constant value. + + Parameters + ---------- + value : A python scalar or a numpy array. + The assigned value. + + """ + + def __init__(self, value=0): + self.value = value + + def __call__(self, shape, dtype=tl.float32): + return tl.constant(self.value, shape=shape, dtype=dtype) + + def get_config(self): + return {"value": self.value} + + +class RandomUniform(Initializer): + """Initializer that generates tensors with a uniform distribution. + + Parameters + ---------- + minval : A python scalar or a scalar tensor. + Lower bound of the range of random values to generate. + maxval : A python scalar or a scalar tensor. + Upper bound of the range of random values to generate. + seed : A Python integer. + Used to seed the random generator. + + """ + + def __init__(self, minval=-0.05, maxval=0.05, seed=None): + self.minval = minval + self.maxval = maxval + self.seed = seed + + def __call__(self, shape, dtype=tl.float32): + return tl.random_uniform(shape, self.minval, self.maxval, dtype=dtype, seed=self.seed) + + def get_config(self): + return {"minval": self.minval, "maxval": self.maxval, "seed": self.seed} + + +class RandomNormal(Initializer): + """Initializer that generates tensors with a normal distribution. + + Parameters + ---------- + mean : A python scalar or a scalar tensor. + Mean of the random values to generate. + stddev : A python scalar or a scalar tensor. + Standard deviation of the random values to generate. + seed : A Python integer. + Used to seed the random generator. + """ + + def __init__(self, mean=0.0, stddev=0.05, seed=None): + self.mean = mean + self.stddev = stddev + self.seed = seed + + def __call__(self, shape, dtype=tl.float32): + return tl.random_normal(shape, self.mean, self.stddev, dtype=dtype, seed=self.seed) + + def get_config(self): + return {"mean": self.mean, "stddev": self.stddev, "seed": self.seed} + + +class TruncatedNormal(Initializer): + """Initializer that generates a truncated normal distribution. + + These values are similar to values from a `RandomNormal` + except that values more than two standard deviations from the mean + are discarded and re-drawn. This is the recommended initializer for + neural network weights and filters. + + + Parameters + ---------- + mean : A python scalar or a scalar tensor. + Mean of the random values to generate. + stddev : A python scalar or a scalar tensor. + Standard deviation of the andom values to generate. + seed : A Python integer. + Used to seed the random generator. + """ + + def __init__(self, mean=0.0, stddev=0.05, seed=None): + self.mean = mean + self.stddev = stddev + self.seed = seed + + def __call__(self, shape, dtype=tl.float32): + return tl.truncated_normal(shape, self.mean, self.stddev, dtype=dtype, seed=self.seed) + + def get_config(self): + return {"mean": self.mean, "stddev": self.stddev, "seed": self.seed} + + +class HeNormal(Initializer): + """He normal initializer. + + Parameters + ---------- + seed : A Python integer. + Used to seed the random generator. + + """ + + def __init__(self, seed=None): + self.seed = seed + + def __call__(self, shape, dtype=tl.float32): + return tl.he_normal(seed=self.seed, shape=shape, dtype=dtype) + + def get_config(self): + return {"seed", self.seed} + + +def deconv2d_bilinear_upsampling_initializer(shape): + """Returns the initializer that can be passed to DeConv2dLayer for initializing the + weights in correspondence to channel-wise bilinear up-sampling. + Used in segmentation approaches such as [FCN](https://arxiv.org/abs/1605.06211) + + Parameters + ---------- + shape : tuple of int + The shape of the filters, [height, width, output_channels, in_channels]. + It must match the shape passed to DeConv2dLayer. + + Returns + ------- + ``tf.constant_initializer`` + A constant initializer with weights set to correspond to per channel bilinear upsampling + when passed as W_int in DeConv2dLayer + + """ + if shape[0] != shape[1]: + raise Exception('deconv2d_bilinear_upsampling_initializer only supports symmetrical filter sizes') + + if shape[3] < shape[2]: + raise Exception( + 'deconv2d_bilinear_upsampling_initializer behaviour is not defined for num_in_channels < num_out_channels ' + ) + + filter_size = shape[0] + num_out_channels = shape[2] + num_in_channels = shape[3] + + # Create bilinear filter kernel as numpy array + bilinear_kernel = np.zeros([filter_size, filter_size], dtype=np.float32) + scale_factor = (filter_size + 1) // 2 + if filter_size % 2 == 1: + center = scale_factor - 1 + else: + center = scale_factor - 0.5 + for x in range(filter_size): + for y in range(filter_size): + bilinear_kernel[x, y] = (1 - abs(x - center) / scale_factor) * (1 - abs(y - center) / scale_factor) + weights = np.zeros((filter_size, filter_size, num_out_channels, num_in_channels), dtype=np.float32) + for i in range(num_out_channels): + weights[:, :, i, i] = bilinear_kernel + + # assign numpy array to constant_initalizer and pass to get_variable + return Constant(value=weights) diff --git a/tensorlayer/iterate.py b/tensorlayer/iterate.py new file mode 100644 index 0000000..804bbb3 --- /dev/null +++ b/tensorlayer/iterate.py @@ -0,0 +1,283 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import numpy as np +from six.moves import xrange + +__all__ = [ + 'minibatches', + 'seq_minibatches', + 'seq_minibatches2', + 'ptb_iterator', +] + + +def minibatches(inputs=None, targets=None, batch_size=None, allow_dynamic_batch_size=False, shuffle=False): + """Generate a generator that input a group of example in numpy.array and + their labels, return the examples and labels by the given batch size. + + Parameters + ---------- + inputs : numpy.array + The input features, every row is a example. + targets : numpy.array + The labels of inputs, every row is a example. + batch_size : int + The batch size. + allow_dynamic_batch_size: boolean + Allow the use of the last data batch in case the number of examples is not a multiple of batch_size, this may result in unexpected behaviour if other functions expect a fixed-sized batch-size. + shuffle : boolean + Indicating whether to use a shuffling queue, shuffle the dataset before return. + + Examples + -------- + >>> X = np.asarray([['a','a'], ['b','b'], ['c','c'], ['d','d'], ['e','e'], ['f','f']]) + >>> y = np.asarray([0,1,2,3,4,5]) + >>> for batch in tl.iterate.minibatches(inputs=X, targets=y, batch_size=2, shuffle=False): + >>> print(batch) + ... (array([['a', 'a'], ['b', 'b']], dtype=' len(inputs): + if allow_dynamic_batch_size: + end_idx = len(inputs) + else: + break + if shuffle: + excerpt = indices[start_idx:end_idx] + else: + excerpt = slice(start_idx, end_idx) + if (isinstance(inputs, list) or isinstance(targets, list)) and (shuffle ==True): + # zsdonghao: for list indexing when shuffle==True + yield [inputs[i] for i in excerpt], [targets[i] for i in excerpt] + else: + yield inputs[excerpt], targets[excerpt] + + +def seq_minibatches(inputs, targets, batch_size, seq_length, stride=1): + """Generate a generator that return a batch of sequence inputs and targets. + If `batch_size=100` and `seq_length=5`, one return will have 500 rows (examples). + + Parameters + ---------- + inputs : numpy.array + The input features, every row is a example. + targets : numpy.array + The labels of inputs, every element is a example. + batch_size : int + The batch size. + seq_length : int + The sequence length. + stride : int + The stride step, default is 1. + + Examples + -------- + Synced sequence input and output. + + >>> X = np.asarray([['a','a'], ['b','b'], ['c','c'], ['d','d'], ['e','e'], ['f','f']]) + >>> y = np.asarray([0, 1, 2, 3, 4, 5]) + >>> for batch in tl.iterate.seq_minibatches(inputs=X, targets=y, batch_size=2, seq_length=2, stride=1): + >>> print(batch) + ... (array([['a', 'a'], ['b', 'b'], ['b', 'b'], ['c', 'c']], dtype='>> return_last = True + >>> num_steps = 2 + >>> X = np.asarray([['a','a'], ['b','b'], ['c','c'], ['d','d'], ['e','e'], ['f','f']]) + >>> Y = np.asarray([0,1,2,3,4,5]) + >>> for batch in tl.iterate.seq_minibatches(inputs=X, targets=Y, batch_size=2, seq_length=num_steps, stride=1): + >>> x, y = batch + >>> if return_last: + >>> tmp_y = y.reshape((-1, num_steps) + y.shape[1:]) + >>> y = tmp_y[:, -1] + >>> print(x, y) + ... [['a' 'a'] + ... ['b' 'b'] + ... ['b' 'b'] + ... ['c' 'c']] [1 2] + ... [['c' 'c'] + ... ['d' 'd'] + ... ['d' 'd'] + ... ['e' 'e']] [3 4] + + """ + if len(inputs) != len(targets): + raise AssertionError("The length of inputs and targets should be equal") + + n_loads = (batch_size * stride) + (seq_length - stride) + + for start_idx in range(0, len(inputs) - n_loads + 1, (batch_size * stride)): + seq_inputs = np.zeros((batch_size, seq_length) + inputs.shape[1:], dtype=inputs.dtype) + seq_targets = np.zeros((batch_size, seq_length) + targets.shape[1:], dtype=targets.dtype) + for b_idx in xrange(batch_size): + start_seq_idx = start_idx + (b_idx * stride) + end_seq_idx = start_seq_idx + seq_length + seq_inputs[b_idx] = inputs[start_seq_idx:end_seq_idx] + seq_targets[b_idx] = targets[start_seq_idx:end_seq_idx] + flatten_inputs = seq_inputs.reshape((-1, ) + inputs.shape[1:]) + flatten_targets = seq_targets.reshape((-1, ) + targets.shape[1:]) + yield flatten_inputs, flatten_targets + + +def seq_minibatches2(inputs, targets, batch_size, num_steps): + """Generate a generator that iterates on two list of words. Yields (Returns) the source contexts and + the target context by the given batch_size and num_steps (sequence_length). + In TensorFlow's tutorial, this generates the `batch_size` pointers into the raw PTB data, and allows minibatch iteration along these pointers. + + Parameters + ---------- + inputs : list of data + The context in list format; note that context usually be represented by splitting by space, and then convert to unique word IDs. + targets : list of data + The context in list format; note that context usually be represented by splitting by space, and then convert to unique word IDs. + batch_size : int + The batch size. + num_steps : int + The number of unrolls. i.e. sequence length + + Yields + ------ + Pairs of the batched data, each a matrix of shape [batch_size, num_steps]. + + Raises + ------ + ValueError : if batch_size or num_steps are too high. + + Examples + -------- + >>> X = [i for i in range(20)] + >>> Y = [i for i in range(20,40)] + >>> for batch in tl.iterate.seq_minibatches2(X, Y, batch_size=2, num_steps=3): + ... x, y = batch + ... print(x, y) + ... + ... [[ 0. 1. 2.] + ... [ 10. 11. 12.]] + ... [[ 20. 21. 22.] + ... [ 30. 31. 32.]] + ... + ... [[ 3. 4. 5.] + ... [ 13. 14. 15.]] + ... [[ 23. 24. 25.] + ... [ 33. 34. 35.]] + ... + ... [[ 6. 7. 8.] + ... [ 16. 17. 18.]] + ... [[ 26. 27. 28.] + ... [ 36. 37. 38.]] + + Notes + ----- + - Hint, if the input data are images, you can modify the source code `data = np.zeros([batch_size, batch_len)` to `data = np.zeros([batch_size, batch_len, inputs.shape[1], inputs.shape[2], inputs.shape[3]])`. + """ + if len(inputs) != len(targets): + raise AssertionError("The length of inputs and targets should be equal") + + data_len = len(inputs) + batch_len = data_len // batch_size + # data = np.zeros([batch_size, batch_len]) + data = np.zeros((batch_size, batch_len) + inputs.shape[1:], dtype=inputs.dtype) + data2 = np.zeros([batch_size, batch_len]) + + for i in range(batch_size): + data[i] = inputs[batch_len * i:batch_len * (i + 1)] + data2[i] = targets[batch_len * i:batch_len * (i + 1)] + + epoch_size = (batch_len - 1) // num_steps + + if epoch_size == 0: + raise ValueError("epoch_size == 0, decrease batch_size or num_steps") + + for i in range(epoch_size): + x = data[:, i * num_steps:(i + 1) * num_steps] + x2 = data2[:, i * num_steps:(i + 1) * num_steps] + yield (x, x2) + + +def ptb_iterator(raw_data, batch_size, num_steps): + """Generate a generator that iterates on a list of words, see `PTB example `__. + Yields the source contexts and the target context by the given batch_size and num_steps (sequence_length). + + In TensorFlow's tutorial, this generates `batch_size` pointers into the raw + PTB data, and allows minibatch iteration along these pointers. + + Parameters + ---------- + raw_data : a list + the context in list format; note that context usually be + represented by splitting by space, and then convert to unique + word IDs. + batch_size : int + the batch size. + num_steps : int + the number of unrolls. i.e. sequence_length + + Yields + ------ + Pairs of the batched data, each a matrix of shape [batch_size, num_steps]. + The second element of the tuple is the same data time-shifted to the + right by one. + + Raises + ------ + ValueError : if batch_size or num_steps are too high. + + Examples + -------- + >>> train_data = [i for i in range(20)] + >>> for batch in tl.iterate.ptb_iterator(train_data, batch_size=2, num_steps=3): + >>> x, y = batch + >>> print(x, y) + ... [[ 0 1 2] <---x 1st subset/ iteration + ... [10 11 12]] + ... [[ 1 2 3] <---y + ... [11 12 13]] + ... + ... [[ 3 4 5] <--- 1st batch input 2nd subset/ iteration + ... [13 14 15]] <--- 2nd batch input + ... [[ 4 5 6] <--- 1st batch target + ... [14 15 16]] <--- 2nd batch target + ... + ... [[ 6 7 8] 3rd subset/ iteration + ... [16 17 18]] + ... [[ 7 8 9] + ... [17 18 19]] + """ + raw_data = np.array(raw_data, dtype=np.int32) + + data_len = len(raw_data) + batch_len = data_len // batch_size + data = np.zeros([batch_size, batch_len], dtype=np.int32) + for i in range(batch_size): + data[i] = raw_data[batch_len * i:batch_len * (i + 1)] + + epoch_size = (batch_len - 1) // num_steps + + if epoch_size == 0: + raise ValueError("epoch_size == 0, decrease batch_size or num_steps") + + for i in range(epoch_size): + x = data[:, i * num_steps:(i + 1) * num_steps] + y = data[:, i * num_steps + 1:(i + 1) * num_steps + 1] + yield (x, y) diff --git a/tensorlayer/layers/__init__.py b/tensorlayer/layers/__init__.py new file mode 100644 index 0000000..309d586 --- /dev/null +++ b/tensorlayer/layers/__init__.py @@ -0,0 +1,26 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +from .activation import * +from .convolution import * +from .core import * +from .dense import * +from .deprecated import * +from .dropout import * +from .embedding import * +from .extend import * +from .image_resampling import * +from .inputs import * +from .lambda_layers import * +from .merge import * +from .noise import * +from .normalization import * +from .padding import * +from .pooling import * +from .quantize import * +# from .recurrent import * +from .scale import * +from .shape import * +from .spatial_transformer import * +from .stack import * +from .utils import * diff --git a/tensorlayer/layers/activation.py b/tensorlayer/layers/activation.py new file mode 100644 index 0000000..c5a0de3 --- /dev/null +++ b/tensorlayer/layers/activation.py @@ -0,0 +1,605 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +from tensorlayer import logging +import tensorlayer as tl +from tensorlayer.initializers import truncated_normal +from tensorlayer.layers.core import Module + +__all__ = [ + 'PRelu', + 'PRelu6', + 'PTRelu6', + 'LeakyReLU', + 'LeakyReLU6', + 'LeakyTwiceRelu6', + 'Ramp', + 'Swish', + 'HardTanh', + 'Mish' +] + + +class PRelu(Module): + """ + The :class:`PRelu` class is Parametric Rectified Linear layer. + It follows f(x) = alpha * x for x < 0, f(x) = x for x >= 0, + where alpha is a learned array with the same shape as x. + + Parameters + ---------- + channel_shared : boolean + If True, single weight is shared by all channels. + in_channels: int + The number of channels of the previous layer. + If None, it will be automatically detected when the layer is forwarded for the first time. + a_init : initializer + The initializer for initializing the alpha(s). + name : None or str + A unique layer name. + + Examples + ----------- + >>> inputs = tl.layers.Input([10, 5]) + >>> prelulayer = tl.layers.PRelu(channel_shared=True) + + References + ----------- + - `Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification `__ + - `Convolutional Deep Belief Networks on CIFAR-10 [A. Krizhevsky, 2010] `__ + + """ + + def __init__( + self, channel_shared=False, in_channels=None, a_init=truncated_normal(mean=0.0, stddev=0.05), name=None, + data_format='channels_last', dim=2 + ): + + super(PRelu, self).__init__(name) + self.channel_shared = channel_shared + self.in_channels = in_channels + self.a_init = a_init + self.data_format = data_format + self.dim = dim + + if self.channel_shared: + self.build((None, )) + self._built = True + elif self.in_channels is not None: + self.build((None, self.in_channels)) + self._built = True + + logging.info("PRelu %s: channel_shared: %s" % (self.name, self.channel_shared)) + + def __repr__(self): + s = ('{classname}(') + s += 'channel_shared={channel_shared},' + s += 'in_channels={in_channels},' + s += 'name={name}' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + if self.channel_shared: + w_shape = (1, ) + elif self.data_format == 'channels_last': + w_shape = (self.in_channels, ) + elif self.data_format == 'channels_first': + if self.dim == 2: + w_shape = (1, self.in_channels, 1, 1) + elif self.dim == 1: + w_shape = (1, self.in_channels, 1) + elif self.dim == 3: + w_shape = (1, self.in_channels, 1, 1, 1) + else: + raise Exception("Dim should be equal to 1, 2 or 3") + self.alpha_var = self._get_weights("alpha", shape=w_shape, init=self.a_init) + self.relu = tl.ops.ReLU() + self.sigmoid = tl.ops.Sigmoid() + + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + pos = self.relu(inputs) + self.alpha_var_constrained = self.sigmoid(self.alpha_var) + neg = -self.alpha_var_constrained * self.relu(-inputs) + return pos + neg + + +class PRelu6(Module): + """ + The :class:`PRelu6` class is Parametric Rectified Linear layer integrating ReLU6 behaviour. + + This Layer is a modified version of the :class:`PRelu`. + + This activation layer use a modified version :func:`tl.act.leaky_relu` introduced by the following paper: + `Rectifier Nonlinearities Improve Neural Network Acoustic Models [A. L. Maas et al., 2013] `__ + + This activation function also use a modified version of the activation function :func:`tf.nn.relu6` introduced by the following paper: + `Convolutional Deep Belief Networks on CIFAR-10 [A. Krizhevsky, 2010] `__ + + This activation layer push further the logic by adding `leaky` behaviour both below zero and above six. + + The function return the following results: + - When x < 0: ``f(x) = alpha_low * x``. + - When x in [0, 6]: ``f(x) = x``. + - When x > 6: ``f(x) = 6``. + + Parameters + ---------- + channel_shared : boolean + If True, single weight is shared by all channels. + in_channels: int + The number of channels of the previous layer. + If None, it will be automatically detected when the layer is forwarded for the first time. + a_init : initializer + The initializer for initializing the alpha(s). + name : None or str + A unique layer name. + + References + ----------- + - `Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification `__ + - `Rectifier Nonlinearities Improve Neural Network Acoustic Models [A. L. Maas et al., 2013] `__ + - `Convolutional Deep Belief Networks on CIFAR-10 [A. Krizhevsky, 2010] `__ + + """ + + def __init__( + self, + channel_shared=False, + in_channels=None, + a_init=truncated_normal(mean=0.0, stddev=0.05), + name=None, # "prelu6" + data_format='channels_last', + dim=2 + ): + + super(PRelu6, self).__init__(name) + self.channel_shared = channel_shared + self.in_channels = in_channels + self.a_init = a_init + self.data_format = data_format + self.dim = dim + + if self.channel_shared: + self.build((None, )) + self._built = True + elif self.in_channels is not None: + self.build((None, self.in_channels)) + self._built = True + + logging.info("PRelu6 %s: channel_shared: %s" % (self.name, self.channel_shared)) + + def __repr__(self): + s = ('{classname}(') + s += 'channel_shared={channel_shared},' + s += 'in_channels={in_channels},' + s += 'name={name}' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + if self.channel_shared: + w_shape = (1, ) + elif self.data_format == 'channels_last': + w_shape = (self.in_channels, ) + elif self.data_format == 'channels_first': + if self.dim == 2: + w_shape = (1, self.in_channels, 1, 1) + elif self.dim == 1: + w_shape = (1, self.in_channels, 1) + elif self.dim == 3: + w_shape = (1, self.in_channels, 1, 1, 1) + else: + raise Exception("Dim should be equal to 1, 2 or 3") + self.alpha_var = self._get_weights("alpha", shape=w_shape, init=self.a_init) + self.sigmoid = tl.ops.Sigmoid() + self.relu = tl.ops.ReLU() + + # @tf.function + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + alpha_var_constrained = self.sigmoid(self.alpha_var) + pos = self.relu(inputs) + pos_6 = -self.relu(inputs - 6) + neg = -alpha_var_constrained * self.relu(-inputs) + return pos + pos_6 + neg + + +class PTRelu6(Module): + """ + The :class:`PTRelu6` class is Parametric Rectified Linear layer integrating ReLU6 behaviour. + + This Layer is a modified version of the :class:`PRelu`. + + This activation layer use a modified version :func:`tl.act.leaky_relu` introduced by the following paper: + `Rectifier Nonlinearities Improve Neural Network Acoustic Models [A. L. Maas et al., 2013] `__ + + This activation function also use a modified version of the activation function :func:`tf.nn.relu6` introduced by the following paper: + `Convolutional Deep Belief Networks on CIFAR-10 [A. Krizhevsky, 2010] `__ + + This activation layer push further the logic by adding `leaky` behaviour both below zero and above six. + + The function return the following results: + - When x < 0: ``f(x) = alpha_low * x``. + - When x in [0, 6]: ``f(x) = x``. + - When x > 6: ``f(x) = 6 + (alpha_high * (x-6))``. + + This version goes one step beyond :class:`PRelu6` by introducing leaky behaviour on the positive side when x > 6. + + Parameters + ---------- + channel_shared : boolean + If True, single weight is shared by all channels. + in_channels: int + The number of channels of the previous layer. + If None, it will be automatically detected when the layer is forwarded for the first time. + a_init : initializer + The initializer for initializing the alpha(s). + name : None or str + A unique layer name. + + References + ----------- + - `Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification `__ + - `Convolutional Deep Belief Networks on CIFAR-10 [A. Krizhevsky, 2010] `__ + - `Rectifier Nonlinearities Improve Neural Network Acoustic Models [A. L. Maas et al., 2013] `__ + + """ + + def __init__( + self, + channel_shared=False, + in_channels=None, + data_format='channels_last', + a_init=truncated_normal(mean=0.0, stddev=0.05), + name=None # "ptrelu6" + ): + + super(PTRelu6, self).__init__(name) + self.channel_shared = channel_shared + self.in_channels = in_channels + self.data_format = data_format + self.a_init = a_init + + if self.channel_shared: + self.build((None, )) + self._built = True + elif self.in_channels: + self.build((None, self.in_channels)) + self._built = True + + logging.info("PTRelu6 %s: channel_shared: %s" % (self.name, self.channel_shared)) + + def __repr__(self): + s = ('{classname}(') + s += 'channel_shared={channel_shared},' + s += 'in_channels={in_channels},' + s += 'name={name}' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + if self.channel_shared: + w_shape = (1, ) + elif self.data_format == 'channels_last': + w_shape = (self.in_channels, ) + elif self.data_format == 'channels_first': + if self.dim == 2: + w_shape = (1, self.in_channels, 1, 1) + elif self.dim == 1: + w_shape = (1, self.in_channels, 1) + elif self.dim == 3: + w_shape = (1, self.in_channels, 1, 1, 1) + else: + raise Exception("Dim should be equal to 1, 2 or 3") + + # Alpha for outputs lower than zeros + self.alpha_low = self._get_weights("alpha_low", shape=w_shape, init=self.a_init) + self.sigmoid = tl.ops.Sigmoid() + self.relu = tl.ops.ReLU() + # Alpha for outputs higher than 6 + self.alpha_high = self._get_weights("alpha_high", shape=w_shape, init=self.a_init) + + # @tf.function + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + alpha_low_constrained = self.sigmoid(self.alpha_low) + alpha_high_constrained = self.sigmoid(self.alpha_high) + pos = self.relu(inputs) + pos_6 = -self.relu(inputs - 6) + alpha_high_constrained * self.relu(inputs - 6) + neg = -alpha_low_constrained * self.relu(-inputs) + + return pos + pos_6 + neg + + +class Ramp(Module): + """Ramp activation function. + + Reference: [tf.clip_by_value] + + Parameters + ---------- + x : Tensor + input. + v_min : float + cap input to v_min as a lower bound. + v_max : float + cap input to v_max as a upper bound. + + Returns + ------- + Tensor + A ``Tensor`` in the same type as ``x``. + + """ + + def __init__(self, v_min=0, v_max=1): + super(Ramp, self).__init__() + self._built = True + self.v_min = v_min + self.v_max = v_max + + def forward(self, x): + return tl.ops.clip_by_value(x, clip_value_min=self.v_min, clip_value_max=self.v_max) + + +class LeakyReLU(Module): + """ + + This function is a modified version of ReLU, introducing a nonzero gradient for negative input. Introduced by the paper: + `Rectifier Nonlinearities Improve Neural Network Acoustic Models [A. L. Maas et al., 2013] `__ + + The function return the following results: + - When x < 0: ``f(x) = alpha_low * x``. + - When x >= 0: ``f(x) = x``. + + Parameters + ---------- + x : Tensor + Support input type ``float``, ``double``, ``int32``, ``int64``, ``uint8``, ``int16``, or ``int8``. + alpha : float + Slope. + name : str + The function name (optional). + + Examples + -------- + >>> import tensorlayer as tl + >>> net = tl.layers.Input([10, 200]) + >>> net = tl.layers.LeakyReLU(alpha=0.5)(net) + + Returns + ------- + Tensor + A ``Tensor`` in the same type as ``x``. + + References + ---------- + - `Rectifier Nonlinearities Improve Neural Network Acoustic Models [A. L. Maas et al., 2013] `__ + + """ + + def __init__(self, alpha=0.2): + super(LeakyReLU, self).__init__() + self._built = True + self.alpha = alpha + self._leakyrelu = tl.ops.LeakyReLU(alpha=alpha) + + def forward(self, x): + return self._leakyrelu(x) + + +class LeakyReLU6(Module): + """ + This activation function is a modified version :func:`leaky_relu` introduced by the following paper: + `Rectifier Nonlinearities Improve Neural Network Acoustic Models [A. L. Maas et al., 2013] `__ + + This activation function also follows the behaviour of the activation function :func:`tf.ops.relu6` introduced by the following paper: + `Convolutional Deep Belief Networks on CIFAR-10 [A. Krizhevsky, 2010] `__ + + The function return the following results: + - When x < 0: ``f(x) = alpha_low * x``. + - When x in [0, 6]: ``f(x) = x``. + - When x > 6: ``f(x) = 6``. + + Parameters + ---------- + x : Tensor + Support input type ``float``, ``double``, ``int32``, ``int64``, ``uint8``, ``int16``, or ``int8``. + alpha : float + Slope. + name : str + The function name (optional). + + Examples + -------- + >>> import tensorlayer as tl + >>> net = tl.layers.Input([10, 200]) + >>> net = tl.layers.LeakyReLU6(alpha=0.5)(net) + + Returns + ------- + Tensor + A ``Tensor`` in the same type as ``x``. + + References + ---------- + - `Rectifier Nonlinearities Improve Neural Network Acoustic Models [A. L. Maas et al., 2013] `__ + - `Convolutional Deep Belief Networks on CIFAR-10 [A. Krizhevsky, 2010] `__ + """ + + def __init__(self, alpha=0.2): + super(LeakyReLU6, self).__init__() + self._built = True + if not (0 < alpha <= 1): + raise ValueError("`alpha` value must be in [0, 1]`") + + self.alpha = alpha + self.minimum = tl.ops.Minimum() + self.maximum = tl.ops.Maximum() + + def forward(self, x): + return self.minimum(self.maximum(x, self.alpha * x), 6) + + +class LeakyTwiceRelu6(Module): + """ + + This activation function is a modified version :func:`leaky_relu` introduced by the following paper: + `Rectifier Nonlinearities Improve Neural Network Acoustic Models [A. L. Maas et al., 2013] `__ + + This activation function also follows the behaviour of the activation function :func:`tf.ops.relu6` introduced by the following paper: + `Convolutional Deep Belief Networks on CIFAR-10 [A. Krizhevsky, 2010] `__ + + This function push further the logic by adding `leaky` behaviour both below zero and above six. + + The function return the following results: + - When x < 0: ``f(x) = alpha_low * x``. + - When x in [0, 6]: ``f(x) = x``. + - When x > 6: ``f(x) = 6 + (alpha_high * (x-6))``. + + Parameters + ---------- + x : Tensor + Support input type ``float``, ``double``, ``int32``, ``int64``, ``uint8``, ``int16``, or ``int8``. + alpha_low : float + Slope for x < 0: ``f(x) = alpha_low * x``. + alpha_high : float + Slope for x < 6: ``f(x) = 6 (alpha_high * (x-6))``. + name : str + The function name (optional). + + Examples + -------- + >>> import tensorlayer as tl + >>> net = tl.layers.Input([10, 200]) + >>> net = tl.layers.LeakyTwiceRelu6(alpha_low=0.5, alpha_high=0.2)(net) + + Returns + ------- + Tensor + A ``Tensor`` in the same type as ``x``. + + References + ---------- + - `Rectifier Nonlinearities Improve Neural Network Acoustic Models [A. L. Maas et al., 2013] `__ + - `Convolutional Deep Belief Networks on CIFAR-10 [A. Krizhevsky, 2010] `__ + + """ + + def __init__(self, alpha_low=0.2, alpha_high=0.2): + super(LeakyTwiceRelu6, self).__init__() + self._built = True + if not (0 < alpha_high <= 1): + raise ValueError("`alpha_high` value must be in [0, 1]`") + + if not (0 < alpha_low <= 1): + raise ValueError("`alpha_low` value must be in [0, 1]`") + + self.alpha_low = alpha_low + self.alpha_high = alpha_high + self.minimum = tl.ops.Minimum() + self.maximum = tl.ops.Maximum() + + def forward(self, x): + x_is_above_0 = self.minimum(x, 6 * (1 - self.alpha_high) + self.alpha_high * x) + x_is_below_0 = self.minimum(self.alpha_low * x, 0) + return self.maximum(x_is_above_0, x_is_below_0) + + +class Swish(Module): + """Swish function. + + See `Swish: a Self-Gated Activation Function `__. + + Parameters + ---------- + x : Tensor + input. + name: str + function name (optional). + + Returns + ------- + Tensor + A ``Tensor`` in the same type as ``x``. + + """ + + def __init__(self): + super(Swish, self).__init__() + self.sigmoid = tl.ops.Sigmoid() + self._built = True + + def forward(self, x): + return self.sigmoid(x) * x + + +class HardTanh(Module): + """Hard tanh activation function. + + Which is a ramp function with low bound of -1 and upper bound of 1, shortcut is `htanh`. + + Parameters + ---------- + x : Tensor + input. + name : str + The function name (optional). + + Returns + ------- + Tensor + A ``Tensor`` in the same type as ``x``. + + """ + + def __init__(self): + super(HardTanh, self).__init__() + self._built = True + + def forward(self, x): + return tl.ops.clip_by_value(x, -1, 1) + + +class Mish(Module): + """Mish activation function. + + Reference: [Mish: A Self Regularized Non-Monotonic Neural Activation Function .Diganta Misra, 2019] + + Parameters + ---------- + x : Tensor + input. + + Returns + ------- + Tensor + A ``Tensor`` in the same type as ``x``. + + """ + + def __init__(self): + super(Mish, self).__init__() + self._tanh = tl.ops.Tanh() + self._softplus = tl.ops.Softplus() + self._built = True + + def forward(self, x): + return x * self._tanh(self._softplus(x)) diff --git a/tensorlayer/layers/convolution/__init__.py b/tensorlayer/layers/convolution/__init__.py new file mode 100644 index 0000000..668736e --- /dev/null +++ b/tensorlayer/layers/convolution/__init__.py @@ -0,0 +1,83 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +""" +TensorLayer provides rich layer implementations trailed for +various benchmarks and domain-specific problems. In addition, we also +support transparent access to native TensorFlow parameters. +For example, we provide not only layers for local response normalization, but also +layers that allow user to apply ``tf.ops.lrn`` on ``network.outputs``. +More functions can be found in `TensorFlow API `__. +""" + +# from .binary_conv import * +from .deformable_conv import * +from .depthwise_conv import * +# from .dorefa_conv import * +# from .expert_conv import * +# from .expert_deconv import * +# from .group_conv import * +from .quan_conv import * +from .quan_conv_bn import * +# from .separable_conv import * +from .simplified_conv import * +# from .simplified_deconv import * +from .super_resolution import * +from .ternary_conv import * + +__all__ = [ + + # simplified conv + 'Conv1d', + 'Conv2d', + 'Conv3d', + + # simplified deconv + 'DeConv1d', + 'DeConv2d', + 'DeConv3d', + + # expert conv + # 'Conv1dLayer', + # 'Conv2dLayer', + # 'Conv3dLayer', + + # expert conv + # 'DeConv1dLayer', + # 'DeConv2dLayer', + # 'DeConv3dLayer', + + # atrous + # 'AtrousConv1dLayer', + # 'AtrousConv2dLayer', + # 'AtrousDeConv2d', + + # binary + # 'BinaryConv2d', + + # deformable + 'DeformableConv2d', + + # depthwise + 'DepthwiseConv2d', + + # dorefa + # 'DorefaConv2d', + + # group + # 'GroupConv2d', + + # separable + # 'SeparableConv1d', + # 'SeparableConv2d', + + # subpixel + 'SubpixelConv1d', + 'SubpixelConv2d', + + # ternary + 'TernaryConv2d', + + #quan_conv + 'QuanConv2d', + 'QuanConv2dWithBN', +] diff --git a/tensorlayer/layers/convolution/deformable_conv.py b/tensorlayer/layers/convolution/deformable_conv.py new file mode 100644 index 0000000..8a2cba0 --- /dev/null +++ b/tensorlayer/layers/convolution/deformable_conv.py @@ -0,0 +1,324 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module + +__all__ = [ + 'DeformableConv2d', +] + + +class DeformableConv2d(Module): + """The :class:`DeformableConv2d` class is a 2D + `Deformable Convolutional Networks `__. + + Parameters + ---------- + offset_layer : tf.Tensor + To predict the offset of convolution operations. + The shape is (batchsize, input height, input width, 2*(number of element in the convolution kernel)) + e.g. if apply a 3*3 kernel, the number of the last dimension should be 18 (2*3*3) + n_filter : int + The number of filters. + filter_size : tuple of int + The filter size (height, width). + act : activation function + The activation function of this layer. + padding : str + The padding algorithm type: "SAME" or "VALID". + W_init : initializer + The initializer for the weight matrix. + b_init : initializer or None + The initializer for the bias vector. If None, skip biases. + in_channels : int + The number of in channels. + name : str + A unique layer name. + + Examples + -------- + With TensorLayer + + >>> net = tl.layers.Input([5, 10, 10, 16], name='input') + >>> offset1 = tl.layers.Conv2d( + ... n_filter=18, filter_size=(3, 3), strides=(1, 1), padding='SAME', name='offset1' + ... )(net) + >>> deformconv1 = tl.layers.DeformableConv2d( + ... offset_layer=offset1, n_filter=32, filter_size=(3, 3), name='deformable1' + ... )(net) + >>> offset2 = tl.layers.Conv2d( + ... n_filter=18, filter_size=(3, 3), strides=(1, 1), padding='SAME', name='offset2' + ... )(deformconv1) + >>> deformconv2 = tl.layers.DeformableConv2d( + ... offset_layer=offset2, n_filter=64, filter_size=(3, 3), name='deformable2' + ... )(deformconv1) + + References + ---------- + - The deformation operation was adapted from the implementation in `here `__ + + Notes + ----- + - The padding is fixed to 'SAME'. + - The current implementation is not optimized for memory usgae. Please use it carefully. + + """ + + def __init__( + self, + offset_layer=None, + n_filter=32, + filter_size=(3, 3), + act=None, + padding='SAME', + W_init=tl.initializers.truncated_normal(stddev=0.02), + b_init=tl.initializers.constant(value=0.0), + in_channels=None, + name=None + ): + super().__init__(name, act=act) + + self.offset_layer = offset_layer + self.n_filter = n_filter + self.filter_size = filter_size + self.padding = padding + self.W_init = W_init + self.b_init = b_init + self.in_channels = in_channels + + # layer forward state + self._forward_state = False + + self.kernel_n = filter_size[0] * filter_size[1] + if self.offset_layer.get_shape()[-1] != 2 * self.kernel_n: + raise AssertionError("offset.get_shape()[-1] is not equal to: %d" % 2 * self.kernel_n) + + if self.in_channels is not None: + self.build(None) + self._built = True + + logging.info( + "DeformableConv2d %s: n_filter: %d, filter_size: %s act: %s" % ( + self.name, self.n_filter, str(self.filter_size + ), self.act.__class__.__name__ if self.act is not None else 'No Activation' + ) + ) + + + def __repr__(self): + actstr = self.act.__class__.__name__ if self.act is not None else 'No Activation' + s = ( + '{classname}(in_channels={in_channels}, out_channels={n_filter}, kernel_size={filter_size}' + ', padding={padding}' + ) + if self.b_init is None: + s += ', bias=False' + s += (', ' + actstr) + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + if self.in_channels is None: + self.in_channels = inputs_shape[-1] + + self.input_h = int(inputs_shape[1]) + self.input_w = int(inputs_shape[2]) + initial_offsets = tl.ops.stack(tl.ops.meshgrid(tl.ops.range(self.filter_size[0]), + tl.ops.range(self.filter_size[1]), indexing='ij')) # initial_offsets --> (kh, kw, 2) + initial_offsets = tl.ops.reshape(initial_offsets, (-1, 2)) # initial_offsets --> (n, 2) + initial_offsets = tl.ops.expand_dims(initial_offsets, 0) # initial_offsets --> (1, n, 2) + initial_offsets = tl.ops.expand_dims(initial_offsets, 0) # initial_offsets --> (1, 1, n, 2) + initial_offsets = tl.ops.tile( + initial_offsets, [self.input_h, self.input_w, 1, 1] + ) # initial_offsets --> (h, w, n, 2) + initial_offsets = tl.ops.cast(initial_offsets, 'float32') + grid = tl.ops.meshgrid( + tl.ops.range( + -int((self.filter_size[0] - 1) / 2.0), int(self.input_h - int((self.filter_size[0] - 1) / 2.0)), 1 + ), + tl.ops.range( + -int((self.filter_size[1] - 1) / 2.0), int(self.input_w - int((self.filter_size[1] - 1) / 2.0)), 1 + ), indexing='ij' + ) + + grid = tl.ops.stack(grid, axis=-1) + grid = tl.ops.cast(grid, 'float32') # grid --> (h, w, 2) + grid = tl.ops.expand_dims(grid, 2) # grid --> (h, w, 1, 2) + grid = tl.ops.tile(grid, [1, 1, self.kernel_n, 1]) # grid --> (h, w, n, 2) + self.grid_offset = grid + initial_offsets # grid_offset --> (h, w, n, 2) + + self.filter_shape = (1, 1, self.kernel_n, self.in_channels, self.n_filter) + + self.W = self._get_weights("W_deformableconv2d", shape=self.filter_shape, init=self.W_init) + + if self.b_init: + self.b = self._get_weights("b_deformableconv2d", shape=(self.n_filter, ), init=self.b_init) + + self.conv3d = tl.ops.Conv3D(strides=[1, 1, 1, 1, 1], padding='VALID') + self.bias_add = tl.ops.BiasAdd() + + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + offset = self.offset_layer + grid_offset = self.grid_offset + + input_deform = self._tf_batch_map_offsets(inputs, offset, grid_offset) + outputs = self.conv3d(input=input_deform, filters=self.W) + outputs = tl.ops.reshape(tensor=outputs, shape=[outputs.get_shape()[0], self.input_h, self.input_w, self.n_filter]) + if self.b_init: + outputs = self.bias_add(outputs, self.b) + if self.act: + outputs = self.act(outputs) + return outputs + + def _to_bc_h_w(self, x, x_shape): + """(b, h, w, c) -> (b*c, h, w)""" + x = tl.ops.transpose(a=x, perm=[0, 3, 1, 2]) + x = tl.ops.reshape(x, (-1, x_shape[1], x_shape[2])) + return x + + def _to_b_h_w_n_c(self, x, x_shape): + """(b*c, h, w, n) -> (b, h, w, n, c)""" + x = tl.ops.reshape(x, (-1, x_shape[4], x_shape[1], x_shape[2], x_shape[3])) + x = tl.ops.transpose(a=x, perm=[0, 2, 3, 4, 1]) + return x + + def tf_flatten(self, a): + """Flatten tensor""" + return tl.ops.reshape(a, [-1]) + + def _get_vals_by_coords(self, inputs, coords, idx, out_shape): + indices = tl.ops.stack( + [idx, self.tf_flatten(coords[:, :, :, :, 0]), + self.tf_flatten(coords[:, :, :, :, 1])], axis=-1 + ) + vals = tl.ops.gather_nd(inputs, indices) + vals = tl.ops.reshape(vals, out_shape) + return vals + + def _tf_repeat(self, a, repeats): + """Tensorflow version of np.repeat for 1D""" + # https://github.com/tensorflow/tensorflow/issues/8521 + + if len(a.get_shape()) != 1: + raise AssertionError("This is not a 1D Tensor") + + a = tl.ops.expand_dims(a, -1) + a = tl.ops.tile(a, [1, repeats]) + a = self.tf_flatten(a) + return a + + def _tf_batch_map_coordinates(self, inputs, coords): + """Batch version of tf_map_coordinates + + Only supports 2D feature maps + + Parameters + ---------- + inputs : ``tf.Tensor`` + shape = (b*c, h, w) + coords : ``tf.Tensor`` + shape = (b*c, h, w, n, 2) + + Returns + ------- + ``tf.Tensor`` + A Tensor with the shape as (b*c, h, w, n) + + """ + inputs_shape = inputs.get_shape() + coords_shape = coords.get_shape() + batch_channel = tl.get_tensor_shape(inputs)[0] + input_h = int(inputs_shape[1]) + input_w = int(inputs_shape[2]) + kernel_n = int(coords_shape[3]) + n_coords = input_h * input_w * kernel_n + + coords_lt = tl.ops.cast(tl.ops.floor(coords), 'int32') + coords_rb = tl.ops.cast(tl.ops.ceil(coords), 'int32') + coords_lb = tl.ops.stack([coords_lt[:, :, :, :, 0], coords_rb[:, :, :, :, 1]], axis=-1) + coords_rt = tl.ops.stack([coords_rb[:, :, :, :, 0], coords_lt[:, :, :, :, 1]], axis=-1) + + idx = self._tf_repeat(tl.ops.range(batch_channel), n_coords) + + vals_lt = self._get_vals_by_coords(inputs, coords_lt, idx, (batch_channel, input_h, input_w, kernel_n)) + vals_rb = self._get_vals_by_coords(inputs, coords_rb, idx, (batch_channel, input_h, input_w, kernel_n)) + vals_lb = self._get_vals_by_coords(inputs, coords_lb, idx, (batch_channel, input_h, input_w, kernel_n)) + vals_rt = self._get_vals_by_coords(inputs, coords_rt, idx, (batch_channel, input_h, input_w, kernel_n)) + + coords_offset_lt = coords - tl.ops.cast(coords_lt, 'float32') + + vals_t = vals_lt + (vals_rt - vals_lt) * coords_offset_lt[:, :, :, :, 0] + vals_b = vals_lb + (vals_rb - vals_lb) * coords_offset_lt[:, :, :, :, 0] + mapped_vals = vals_t + (vals_b - vals_t) * coords_offset_lt[:, :, :, :, 1] + + return mapped_vals + + def _tf_batch_map_offsets(self, inputs, offsets, grid_offset): + """Batch map offsets into input + + Parameters + ------------ + inputs : ``tf.Tensor`` + shape = (b, h, w, c) + offsets: ``tf.Tensor`` + shape = (b, h, w, 2*n) + grid_offset: `tf.Tensor`` + Offset grids shape = (h, w, n, 2) + + Returns + ------- + ``tf.Tensor`` + A Tensor with the shape as (b, h, w, c) + + """ + inputs_shape = inputs.get_shape() + batch_size = tl.get_tensor_shape(inputs)[0] + kernel_n = int(int(offsets.get_shape()[3]) / 2) + input_h = inputs_shape[1] + input_w = inputs_shape[2] + channel = inputs_shape[3] + + # inputs (b, h, w, c) --> (b*c, h, w) + inputs = self._to_bc_h_w(inputs, inputs_shape) + + # offsets (b, h, w, 2*n) --> (b, h, w, n, 2) + offsets = tl.ops.reshape(offsets, (batch_size, input_h, input_w, kernel_n, 2)) + # offsets (b, h, w, n, 2) --> (b*c, h, w, n, 2) + # offsets = tf.tile(offsets, [channel, 1, 1, 1, 1]) + + coords = tl.ops.expand_dims(grid_offset, 0) # grid_offset --> (1, h, w, n, 2) + coords = tl.ops.tile(coords, [batch_size, 1, 1, 1, 1]) + offsets # grid_offset --> (b, h, w, n, 2) + + # clip out of bound + coords = tl.ops.stack( + [ + tl.ops.clip_by_value(coords[:, :, :, :, 0], 0.0, tl.ops.cast(input_h - 1, 'float32')), + tl.ops.clip_by_value(coords[:, :, :, :, 1], 0.0, tl.ops.cast(input_w - 1, 'float32')) + ], axis=-1 + ) + coords = tl.ops.tile(coords, [channel, 1, 1, 1, 1]) + + mapped_vals = self._tf_batch_map_coordinates(inputs, coords) + # (b*c, h, w, n) --> (b, h, w, n, c) + mapped_vals = self._to_b_h_w_n_c(mapped_vals, [batch_size, input_h, input_w, kernel_n, channel]) + + return mapped_vals + +if __name__ == '__main__': + net = tl.layers.Input([5, 10, 10, 16], name='input') + offset1 = tl.layers.Conv2d(n_filter=18, filter_size=(3, 3), strides=(1, 1), padding='SAME', name='offset1', in_channels=16)(net) + deformconv1 = DeformableConv2d(offset_layer=offset1, n_filter=32, filter_size=(3, 3), name='deformable1')(net) + offset2 = tl.layers.Conv2d(n_filter=18, filter_size=(3, 3), strides=(1, 1), padding='SAME', name='offset2', in_channels=32)(deformconv1) + deformconv2 = DeformableConv2d(offset_layer=offset2, n_filter=64, filter_size=(3, 3), name='deformable2')(deformconv1) + print(deformconv2) + diff --git a/tensorlayer/layers/convolution/depthwise_conv.py b/tensorlayer/layers/convolution/depthwise_conv.py new file mode 100644 index 0000000..bac18de --- /dev/null +++ b/tensorlayer/layers/convolution/depthwise_conv.py @@ -0,0 +1,170 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module +from tensorlayer.backend import BACKEND + +__all__ = [ + 'DepthwiseConv2d', +] + + +class DepthwiseConv2d(Module): + """Separable/Depthwise Convolutional 2D layer, see `tf.nn.depthwise_conv2d `__. + + Input: + 4-D Tensor (batch, height, width, in_channels). + Output: + 4-D Tensor (batch, new height, new width, in_channels * depth_multiplier). + + Parameters + ------------ + filter_size : tuple of 2 int + The filter size (height, width). + strides : tuple of 2 int + The stride step (height, width). + act : activation function + The activation function of this layer. + padding : str + The padding algorithm type: "SAME" or "VALID". + data_format : str + "channels_last" (NHWC, default) or "channels_first" (NCHW). + dilation_rate: tuple of 2 int + The dilation rate in which we sample input values across the height and width dimensions in atrous convolution. If it is greater than 1, then all values of strides must be 1. + depth_multiplier : int + The number of channels to expand to. + W_init : initializer + The initializer for the weight matrix. + b_init : initializer or None + The initializer for the bias vector. If None, skip bias. + in_channels : int + The number of in channels. + name : str + A unique layer name. + + Examples + --------- + With TensorLayer + + >>> net = tl.layers.Input([8, 200, 200, 32], name='input') + >>> depthwiseconv2d = tl.layers.DepthwiseConv2d( + ... filter_size=(3, 3), strides=(1, 1), dilation_rate=(2, 2), act=tl.ReLU, depth_multiplier=2, name='depthwise' + ... )(net) + >>> print(depthwiseconv2d) + >>> output shape : (8, 200, 200, 64) + + + References + ----------- + - tflearn's `grouped_conv_2d `__ + - keras's `separableconv2d `__ + + """ + + # https://zhuanlan.zhihu.com/p/31551004 https://github.com/xiaohu2015/DeepLearning_tutorials/blob/master/CNNs/MobileNet.py + def __init__( + self, + filter_size=(3, 3), + strides=(1, 1), + act=None, + padding='SAME', + data_format='channels_last', + dilation_rate=(1, 1), + depth_multiplier=1, + W_init=tl.initializers.truncated_normal(stddev=0.02), + b_init=tl.initializers.constant(value=0.0), + in_channels=None, + name=None # 'depthwise_conv2d' + ): + super().__init__(name, act=act) + self.filter_size = filter_size + self.strides = self._strides = strides + self.padding = padding + self.dilation_rate = self._dilation_rate = dilation_rate + self.data_format = data_format + self.depth_multiplier = depth_multiplier + self.W_init = W_init + self.b_init = b_init + self.in_channels = in_channels + + if self.in_channels: + self.build(None) + self._built = True + + logging.info( + "DepthwiseConv2d %s: filter_size: %s strides: %s pad: %s act: %s" % ( + self.name, str(filter_size), str(strides), padding, + self.act.__class__.__name__ if self.act is not None else 'No Activation' + ) + ) + + def __repr__(self): + actstr = self.act.__class__.__name__ if self.act is not None else 'No Activation' + s = ( + '{classname}(in_channels={in_channels}, out_channels={n_filter}, kernel_size={filter_size}' + ', strides={strides}, padding={padding}' + ) + if self.dilation_rate != (1, ) * len(self.dilation_rate): + s += ', dilation={dilation_rate}' + if self.b_init is None: + s += ', bias=False' + s += (', ' + actstr) + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format( + classname=self.__class__.__name__, n_filter=self.in_channels * self.depth_multiplier, **self.__dict__ + ) + + def build(self, inputs_shape): + if self.data_format == 'channels_last': + self.data_format = 'NHWC' + if self.in_channels is None: + self.in_channels = inputs_shape[-1] + self._strides = [1, self._strides[0], self._strides[1], 1] + elif self.data_format == 'channels_first': + self.data_format = 'NCHW' + if self.in_channels is None: + self.in_channels = inputs_shape[1] + self._strides = [1, 1, self._strides[0], self._strides[1]] + else: + raise Exception("data_format should be either channels_last or channels_first") + + self.filter_shape = (self.filter_size[0], self.filter_size[1], self.in_channels, self.depth_multiplier) + + # Set the size of kernel as (K1,K2), then the shape is (K,Cin,K1,K2), K must be 1. + if BACKEND == 'mindspore': + self.filter_shape = (self.filter_size[0], self.filter_size[1], self.in_channels, 1) + + self.W = self._get_weights("filters", shape=self.filter_shape, init=self.W_init) + + self.depthwise_conv2d = tl.ops.DepthwiseConv2d( + strides=self._strides, padding=self.padding, data_format=self.data_format, dilations=self._dilation_rate, + ksize=self.filter_size, channel_multiplier=self.depth_multiplier + ) + + self.b_init_flag = False + if self.b_init: + self.b = self._get_weights("biases", shape=(self.in_channels * self.depth_multiplier, ), init=self.b_init) + self.bias_add = tl.ops.BiasAdd(self.data_format) + self.b_init_flag = True + + self.act_init_flag = False + if self.act: + self.act_init_flag = True + + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + outputs = self.depthwise_conv2d(input=inputs, filter=self.W) + if self.b_init_flag: + outputs = self.bias_add(outputs, self.b) + if self.act_init_flag: + outputs = self.act(outputs) + return outputs diff --git a/tensorlayer/layers/convolution/quan_conv.py b/tensorlayer/layers/convolution/quan_conv.py new file mode 100644 index 0000000..87f4f52 --- /dev/null +++ b/tensorlayer/layers/convolution/quan_conv.py @@ -0,0 +1,173 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module +from tensorlayer.layers.utils import (quantize_active_overflow, quantize_weight_overflow) + +__all__ = ['QuanConv2d'] + + +class QuanConv2d(Module): + """The :class:`QuanConv2d` class is a quantized convolutional layer without BN, which weights are 'bitW' bits and the output of the previous layer + are 'bitA' bits while inferencing. + Note that, the bias vector would not be binarized. + + Parameters + ---------- + With TensorLayer + + n_filter : int + The number of filters. + filter_size : tuple of int + The filter size (height, width). + strides : tuple of int + The sliding window strides of corresponding input dimensions. + It must be in the same order as the ``shape`` parameter. + act : activation function + The activation function of this layer. + padding : str + The padding algorithm type: "SAME" or "VALID". + bitW : int + The bits of this layer's parameter + bitA : int + The bits of the output of previous layer + use_gemm : boolean + If True, use gemm instead of ``tf.matmul`` for inference. + TODO: support gemm + data_format : str + "channels_last" (NHWC, default) or "channels_first" (NCHW). + dilation_rate : tuple of int + Specifying the dilation rate to use for dilated convolution. + W_init : initializer + The initializer for the the weight matrix. + b_init : initializer or None + The initializer for the the bias vector. If None, skip biases. + in_channels : int + The number of in channels. + name : None or str + A unique layer name. + + Examples + --------- + With TensorLayer + + >>> net = tl.layers.Input([8, 12, 12, 64], name='input') + >>> quanconv2d = tl.layers.QuanConv2d( + ... n_filter=32, filter_size=(5, 5), strides=(1, 1), act=tf.nn.relu, padding='SAME', name='quancnn2d' + ... )(net) + >>> print(quanconv2d) + >>> output shape : (8, 12, 12, 32) + + """ + + def __init__( + self, + bitW=8, + bitA=8, + n_filter=32, + filter_size=(3, 3), + strides=(1, 1), + act=None, + padding='SAME', + use_gemm=False, + data_format="channels_last", + dilation_rate=(1, 1), + W_init=tl.initializers.truncated_normal(stddev=0.02), + b_init=tl.initializers.constant(value=0.0), + in_channels=None, + name=None # 'quan_cnn2d', + ): + super().__init__(name, act=act) + self.bitW = bitW + self.bitA = bitA + self.n_filter = n_filter + self.filter_size = filter_size + self.strides = self._strides = strides + self.padding = padding + self.use_gemm = use_gemm + self.data_format = data_format + self.dilation_rate = self._dilation_rate = dilation_rate + self.W_init = W_init + self.b_init = b_init + self.in_channels = in_channels + + if self.in_channels: + self.build(None) + self._built = True + + logging.info( + "QuanConv2d %s: n_filter: %d filter_size: %s strides: %s pad: %s act: %s" % ( + self.name, n_filter, str(filter_size), str(strides), padding, + self.act.__class__.__name__ if self.act is not None else 'No Activation' + ) + ) + + if self.use_gemm: + raise Exception("TODO. The current version use tf.matmul for inferencing.") + + if len(self.strides) != 2: + raise ValueError("len(strides) should be 2.") + + def __repr__(self): + actstr = self.act.__name__ if self.act is not None else 'No Activation' + s = ( + '{classname}(in_channels={in_channels}, out_channels={n_filter}, kernel_size={filter_size}' + ', strides={strides}, padding={padding}' + ) + if self.dilation_rate != (1, ) * len(self.dilation_rate): + s += ', dilation={dilation_rate}' + if self.b_init is None: + s += ', bias=False' + s += (', ' + actstr) + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + if self.data_format == 'channels_last': + self.data_format = 'NHWC' + if self.in_channels is None: + self.in_channels = inputs_shape[-1] + self._strides = [1, self._strides[0], self._strides[1], 1] + self._dilation_rate = [1, self._dilation_rate[0], self._dilation_rate[1], 1] + elif self.data_format == 'channels_first': + self.data_format = 'NCHW' + if self.in_channels is None: + self.in_channels = inputs_shape[1] + self._strides = [1, 1, self._strides[0], self._strides[1]] + self._dilation_rate = [1, 1, self._dilation_rate[0], self._dilation_rate[1]] + else: + raise Exception("data_format should be either channels_last or channels_first") + + self.filter_shape = (self.filter_size[0], self.filter_size[1], self.in_channels, self.n_filter) + + self.W = self._get_weights("filters", shape=self.filter_shape, init=self.W_init) + if self.b_init: + self.b = self._get_weights("biases", shape=(self.n_filter, ), init=self.b_init) + self.bias_add = tl.ops.BiasAdd(data_format=self.data_format) + + self.conv2d = tl.ops.Conv2D(strides=self.strides, padding=self.padding, data_format=self.data_format, + dilations=self._dilation_rate) + + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + inputs = quantize_active_overflow(inputs, self.bitA) + + W_ = quantize_weight_overflow(self.W, self.bitW) + + outputs = self.conv2d(inputs, W_) + + if self.b_init: + outputs = self.bias_add(outputs, self.b) + if self.act: + outputs = self.act(outputs) + + return outputs diff --git a/tensorlayer/layers/convolution/quan_conv_bn.py b/tensorlayer/layers/convolution/quan_conv_bn.py new file mode 100644 index 0000000..335742b --- /dev/null +++ b/tensorlayer/layers/convolution/quan_conv_bn.py @@ -0,0 +1,240 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorflow as tf +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module +from tensorflow.python.training import moving_averages +from tensorlayer.layers.utils import (quantize_active_overflow, quantize_weight_overflow) +from tensorlayer.backend import BACKEND + +__all__ = ['QuanConv2dWithBN'] + + +class QuanConv2dWithBN(Module): + """The :class:`QuanConv2dWithBN` class is a quantized convolutional layer with BN, which weights are 'bitW' bits and the output of the previous layer + are 'bitA' bits while inferencing. + + Note that, the bias vector would keep the same. + + Parameters + ---------- + n_filter : int + The number of filters. + filter_size : tuple of int + The filter size (height, width). + strides : tuple of int + The sliding window strides of corresponding input dimensions. + It must be in the same order as the ``shape`` parameter. + padding : str + The padding algorithm type: "SAME" or "VALID". + act : activation function + The activation function of this layer. + decay : float + A decay factor for `ExponentialMovingAverage`. + Suggest to use a large value for large dataset. + epsilon : float + Eplison. + is_train : boolean + Is being used for training or inference. + beta_init : initializer or None + The initializer for initializing beta, if None, skip beta. + Usually you should not skip beta unless you know what happened. + gamma_init : initializer or None + The initializer for initializing gamma, if None, skip gamma. + bitW : int + The bits of this layer's parameter + bitA : int + The bits of the output of previous layer + use_gemm : boolean + If True, use gemm instead of ``tf.matmul`` for inferencing. (TODO). + W_init : initializer + The initializer for the the weight matrix. + W_init_args : dictionary + The arguments for the weight matrix initializer. + data_format : str + "NHWC" or "NCHW", default is "NHWC". + dilation_rate : tuple of int + Specifying the dilation rate to use for dilated convolution. + in_channels : int + The number of in channels. + name : str + A unique layer name. + + Examples + --------- + >>> import tensorlayer as tl + >>> net = tl.layers.Input([50, 256, 256, 3]) + >>> layer = tl.layers.QuanConv2dWithBN(n_filter=64, filter_size=(5,5),strides=(1,1),padding='SAME',name='qcnnbn1') + >>> print(layer) + >>> net = tl.layers.QuanConv2dWithBN(n_filter=64, filter_size=(5,5),strides=(1,1),padding='SAME',name='qcnnbn1')(net) + >>> print(net) + """ + + def __init__( + self, + n_filter=32, + filter_size=(3, 3), + strides=(1, 1), + padding='SAME', + act=None, + decay=0.9, + epsilon=1e-5, + is_train=False, + gamma_init=tl.initializers.truncated_normal(stddev=0.02), + beta_init=tl.initializers.truncated_normal(stddev=0.02), + bitW=8, + bitA=8, + use_gemm=False, + W_init=tl.initializers.truncated_normal(stddev=0.02), + W_init_args=None, + data_format="channels_last", + dilation_rate=(1, 1), + in_channels=None, + name='quan_cnn2d_bn', + ): + super(QuanConv2dWithBN, self).__init__(act=act, name=name) + self.n_filter = n_filter + self.filter_size = filter_size + self.strides = strides + self.padding = padding + self.decay = decay + self.epsilon = epsilon + self.is_train = is_train + self.gamma_init = gamma_init + self.beta_init = beta_init + self.bitW = bitW + self.bitA = bitA + self.use_gemm = use_gemm + self.W_init = W_init + self.W_init_args = W_init_args + self.data_format = data_format + self.dilation_rate = dilation_rate + self.in_channels = in_channels + logging.info( + "QuanConv2dWithBN %s: n_filter: %d filter_size: %s strides: %s pad: %s act: %s " % ( + self.name, n_filter, filter_size, str(strides), padding, + self.act.__class__.__name__ if self.act is not None else 'No Activation' + ) + ) + + if BACKEND == 'mindspore': + raise NotImplementedError("MindSpore backend does not implement this method") + + if self.in_channels: + self.build(None) + self._built = True + + if use_gemm: + raise Exception("TODO. The current version use tf.matmul for inferencing.") + + if len(strides) != 2: + raise ValueError("len(strides) should be 2.") + + def __repr__(self): + actstr = self.act.__class__.__name__ if self.act is not None else 'No Activation' + s = ( + '{classname}(in_channels={in_channels}, out_channels={n_filter}, kernel_size={filter_size}' + ', strides={strides}, padding={padding}' + actstr + ) + if self.dilation_rate != (1, ) * len(self.dilation_rate): + s += ', dilation={dilation_rate}' + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + if self.data_format == 'channels_last': + self.data_format = 'NHWC' + if self.in_channels is None: + self.in_channels = inputs_shape[-1] + self._strides = [1, self.strides[0], self.strides[1], 1] + self._dilation_rate = [1, self.dilation_rate[0], self.dilation_rate[1], 1] + elif self.data_format == 'channels_first': + self.data_format = 'NCHW' + if self.in_channels is None: + self.in_channels = inputs_shape[1] + self._strides = [1, 1, self.strides[0], self.strides[1]] + self._dilation_rate = [1, 1, self.dilation_rate[0], self.dilation_rate[1]] + else: + raise Exception("data_format should be either channels_last or channels_first") + + self.filter_shape = (self.filter_size[0], self.filter_size[1], self.in_channels, self.n_filter) + self.W = self._get_weights("filters", shape=self.filter_shape, init=self.W_init) + + para_bn_shape = (self.n_filter, ) + if self.gamma_init: + self.scale_para = self._get_weights( + "scale_para", shape=para_bn_shape, init=self.gamma_init, trainable=self.is_train + ) + else: + self.scale_para = None + + if self.beta_init: + self.offset_para = self._get_weights( + "offset_para", shape=para_bn_shape, init=self.beta_init, trainable=self.is_train + ) + else: + self.offset_para = None + + self.moving_mean = self._get_weights( + "moving_mean", shape=para_bn_shape, init=tl.initializers.constant(1.0), trainable=False + ) + self.moving_variance = self._get_weights( + "moving_variance", shape=para_bn_shape, init=tl.initializers.constant(1.0), trainable=False + ) + + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + x = inputs + inputs = quantize_active_overflow(inputs, self.bitA) # Do not remove + outputs = tf.nn.conv2d( + input=x, filters=self.W, strides=self._strides, padding=self.padding, data_format=self.data_format, + dilations=self._dilation_rate, name=self.name + ) + + mean, variance = tf.nn.moments(outputs, axes=list(range(len(outputs.get_shape()) - 1))) + + update_moving_mean = moving_averages.assign_moving_average( + self.moving_mean, mean, self.decay, zero_debias=False + ) # if zero_debias=True, has bias + update_moving_variance = moving_averages.assign_moving_average( + self.moving_variance, mean, self.decay, zero_debias=False + ) # if zero_debias=True, has bias + + if self.is_train: + mean, var = self.mean_var_with_update(update_moving_mean, update_moving_variance, mean, variance) + else: + mean, var = self.moving_mean, self.moving_variance + + w_fold = self._w_fold(self.W, self.scale_para, var, self.epsilon) + + W_ = quantize_weight_overflow(w_fold, self.bitW) + + conv_fold = tf.nn.conv2d(inputs, W_, strides=self.strides, padding=self.padding, data_format=self.data_format) + + if self.beta_init: + bias_fold = self._bias_fold(self.offset_para, self.scale_para, mean, var, self.epsilon) + conv_fold = tf.nn.bias_add(conv_fold, bias_fold, name='bn_bias_add') + + if self.act: + conv_fold = self.act(conv_fold) + + return conv_fold + + def mean_var_with_update(self, update_moving_mean, update_moving_variance, mean, variance): + with tf.control_dependencies([update_moving_mean, update_moving_variance]): + return tf.identity(mean), tf.identity(variance) + + def _w_fold(self, w, gama, var, epsilon): + return tf.compat.v1.div(tf.multiply(gama, w), tf.sqrt(var + epsilon)) + + def _bias_fold(self, beta, gama, mean, var, epsilon): + return tf.subtract(beta, tf.compat.v1.div(tf.multiply(gama, mean), tf.sqrt(var + epsilon))) \ No newline at end of file diff --git a/tensorlayer/layers/convolution/simplified_conv.py b/tensorlayer/layers/convolution/simplified_conv.py new file mode 100644 index 0000000..e78dfbd --- /dev/null +++ b/tensorlayer/layers/convolution/simplified_conv.py @@ -0,0 +1,893 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +from tensorlayer.layers.core import Module +import tensorlayer as tl +from tensorlayer import logging + + +__all__ = [ + 'Conv1d', + 'Conv2d', + 'Conv3d', + 'DeConv1d', + 'DeConv2d', + 'DeConv3d', +] + + +class Conv1d(Module): + """Simplified version of :class:`Conv1dLayer`. + + Parameters + ---------- + n_filter : int + The number of filters + filter_size : int + The filter size + stride : int + The stride step + dilation_rate : int + Specifying the dilation rate to use for dilated convolution. + act : activation function + The function that is applied to the layer activations + padding : str + The padding algorithm type: "SAME" or "VALID". + data_format : str + "channel_last" (NWC, default) or "channels_first" (NCW). + W_init : initializer + The initializer for the weight matrix. + b_init : initializer or None + The initializer for the bias vector. If None, skip biases. + in_channels : int + The number of in channels. + name : None or str + A unique layer name + + Examples + -------- + With TensorLayer + + >>> net = tl.layers.Input([8, 100, 1], name='input') + >>> conv1d = tl.layers.Conv1d(n_filter=32, filter_size=5, stride=2, b_init=None, in_channels=1, name='conv1d_1') + >>> print(conv1d) + >>> tensor = tl.layers.Conv1d(n_filter=32, filter_size=5, stride=2, act=tl.ops.relu, name='conv1d_2')(net) + >>> print(tensor) + + """ + + def __init__( + self, + n_filter=32, + filter_size=5, + stride=1, + act=None, + padding='SAME', + data_format="channels_last", + dilation_rate=1, + W_init=tl.initializers.truncated_normal(stddev=0.02), + b_init=tl.initializers.constant(value=0.0), + in_channels=None, + name=None # 'conv1d' + ): + super().__init__(name, act=act) + self.n_filter = n_filter + self.filter_size = filter_size + self.stride = stride + self.padding = padding + self.data_format = data_format + self.dilation_rate = dilation_rate + self.W_init = W_init + self.b_init = b_init + self.in_channels = in_channels + + if self.in_channels: + self.build(None) + self._built = True + + logging.info( + "Conv1d %s: n_filter: %d filter_size: %s stride: %d pad: %s act: %s" % ( + self.name, n_filter, filter_size, stride, padding, + self.act.__class__.__name__ if self.act is not None else 'No Activation' + ) + ) + + def __repr__(self): + actstr = self.act.__class__.__name__ if self.act is not None else 'No Activation' + s = ( + '{classname}(in_channels={in_channels}, out_channels={n_filter}, kernel_size={filter_size}' + ', stride={stride}, padding={padding}' + ) + if self.dilation_rate != 1: + s += ', dilation={dilation_rate}' + if self.b_init is None: + s += ', bias=False' + s += (', ' + actstr) + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + if self.data_format == 'channels_last': + self.data_format = 'NWC' + if self.in_channels is None: + self.in_channels = inputs_shape[-1] + elif self.data_format == 'channels_first': + self.data_format = 'NCW' + if self.in_channels is None: + self.in_channels = inputs_shape[1] + else: + raise Exception("data_format should be either channels_last or channels_first") + + self.filter_shape = (self.filter_size, self.in_channels, self.n_filter) + + # TODO : check + self.W = self._get_weights("filters", shape=self.filter_shape, init=self.W_init) + + self.b_init_flag = False + if self.b_init: + self.b = self._get_weights("biases", shape=(self.n_filter, ), init=self.b_init) + self.bias_add = tl.ops.BiasAdd(self.data_format) + self.b_init_flag = True + + self.conv1d = tl.ops.Conv1D( + stride=self.stride, padding=self.padding, data_format=self.data_format, dilations=self.dilation_rate, + out_channel=self.n_filter, k_size=self.filter_size + ) + + self.act_init_flag = False + if self.act: + self.act_init_flag = True + + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + outputs = self.conv1d(inputs, self.W) + if self.b_init_flag: + outputs = self.bias_add(outputs, self.b) + if self.act_init_flag: + outputs = self.act(outputs) + + return outputs + + +class Conv2d(Module): + """Simplified version of :class:`Conv2dLayer`. + + Parameters + ---------- + n_filter : int + The number of filters. + filter_size : tuple of int + The filter size (height, width). + strides : tuple of int + The sliding window strides of corresponding input dimensions. + It must be in the same order as the ``shape`` parameter. + dilation_rate : tuple of int + Specifying the dilation rate to use for dilated convolution. + act : activation function + The activation function of this layer. + padding : str + The padding algorithm type: "SAME" or "VALID". + data_format : str + "channels_last" (NHWC, default) or "channels_first" (NCHW). + W_init : initializer + The initializer for the the weight matrix. + b_init : initializer or None + The initializer for the the bias vector. If None, skip biases. + in_channels : int + The number of in channels. + name : None or str + A unique layer name. + + Examples + -------- + With TensorLayer + + >>> net = tl.layers.Input([8, 3, 400, 400], name='input') + >>> conv2d = tl.layers.Conv2d(n_filter=32, filter_size=(3, 3), strides=(2, 2), b_init=None, in_channels=3, name='conv2d_1') + >>> print(conv2d) + >>> tensor = tl.layers.Conv2d(n_filter=32, filter_size=(3, 3), strides=(2, 2), act=tl.ops.relu, name='conv2d_2')(net) + >>> print(tensor) + + """ + + def __init__( + self, + n_filter=32, + filter_size=(3, 3), + strides=(1, 1), + act=None, + padding='SAME', + data_format='channels_last', + dilation_rate=(1, 1), + W_init=tl.initializers.truncated_normal(stddev=0.02), + b_init=tl.initializers.constant(value=0.0), + in_channels=None, + name=None, # 'conv2d', + ): + super(Conv2d, self).__init__(name, act=act) + self.n_filter = n_filter + self.filter_size = filter_size + self._strides = self.strides = strides + self.padding = padding + self.data_format = data_format + self._dilation_rate = self.dilation_rate = dilation_rate + self.W_init = W_init + self.b_init = b_init + self.in_channels = in_channels + + if self.in_channels: + self.build(None) + self._built = True + + logging.info( + "Conv2d %s: n_filter: %d filter_size: %s strides: %s pad: %s act: %s" % ( + self.name, n_filter, str(filter_size), str(strides), padding, + self.act.__class__.__name__ if self.act is not None else 'No Activation' + ) + ) + + def __repr__(self): + actstr = self.act.__class__.__name__ if self.act is not None else 'No Activation' + s = ( + '{classname}(in_channels={in_channels}, out_channels={n_filter}, kernel_size={filter_size}' + ', strides={strides}, padding={padding}' + ) + if self.dilation_rate != (1, ) * len(self.dilation_rate): + s += ', dilation={dilation_rate}' + if self.b_init is None: + s += ', bias=False' + s += (', ' + actstr) + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + if self.data_format == 'channels_last': + self.data_format = 'NHWC' + if self.in_channels is None: + self.in_channels = inputs_shape[-1] + self._strides = [1, self._strides[0], self._strides[1], 1] + self._dilation_rate = [1, self._dilation_rate[0], self._dilation_rate[1], 1] + elif self.data_format == 'channels_first': + self.data_format = 'NCHW' + if self.in_channels is None: + self.in_channels = inputs_shape[1] + self._strides = [1, 1, self._strides[0], self._strides[1]] + self._dilation_rate = [1, 1, self._dilation_rate[0], self._dilation_rate[1]] + else: + raise Exception("data_format should be either channels_last or channels_first") + + #TODO channels first filter shape [out_channel, in_channel, filter_h, filter_w] + self.filter_shape = (self.filter_size[0], self.filter_size[1], self.in_channels, self.n_filter) + self.W = self._get_weights("filters", shape=self.filter_shape, init=self.W_init) + + self.b_init_flag = False + if self.b_init: + self.b = self._get_weights("biases", shape=(self.n_filter, ), init=self.b_init) + self.bias_add = tl.ops.BiasAdd(self.data_format) + self.b_init_flag = True + + self.conv2d = tl.ops.Conv2D( + strides=self._strides, padding=self.padding, data_format=self.data_format, dilations=self._dilation_rate, + out_channel=self.n_filter, k_size=(self.filter_size[0], self.filter_size[1]) + ) + + self.act_init_flag = False + if self.act: + self.act_init_flag = True + + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + outputs = self.conv2d(inputs, self.W) + if self.b_init_flag: + outputs = self.bias_add(outputs, self.b) + if self.act_init_flag: + outputs = self.act(outputs) + return outputs + + +class Conv3d(Module): + """Simplified version of :class:`Conv3dLayer`. + + Parameters + ---AppData\Local\Continuum\anaconda3\envs\ms_tf\lib\site-packages\mindspore\common\api.py", line 412, in compile + result = self._executor.compile(obj, args_list, phase, use_vm) +RuntimeError: Unable to cast from non-held to held instance (T& to Holder) of type 'std:------- + n_filter : int + The number of filters. + filter_size : tuple of int + The filter size (height, width). + strides : tuple of int + The sliding window strides of corresponding input dimensions. + It must be in the same order as the ``shape`` parameter. + dilation_rate : tuple of int + Specifying the dilation rate to use for dilated convolution. + act : activation function + The activation function of this layer. + padding : str + The padding algorithm type: "SAME" or "VALID". + data_format : str + "channels_last" (NDHWC, default) or "channels_first" (NCDHW). + W_init : initializer + The initializer for the the weight matrix. + b_init : initializer or None + The initializer for the the bias vector. If None, skip biases. + in_channels : int + The number of in channels. + name : None or str + A unique layer name. + + Examples + -------- + With TensorLayer + + >>> net = tl.layers.Input([8, 20, 20, 20, 3], name='input') + >>> conv3d = tl.layers.Conv3d(n_filter=32, filter_size=(3, 3, 3), strides=(2, 2, 2), b_init=None, in_channels=3, name='conv3d_1') + >>> print(conv3d) + >>> tensor = tl.layers.Conv3d(n_filter=32, filter_size=(3, 3, 3), strides=(2, 2, 2), act=tl.ops.relu, name='conv3d_2')(net) + >>> print(tensor) + + """ + + def __init__( + self, + n_filter=32, + filter_size=(3, 3, 3), + strides=(1, 1, 1), + act=None, + padding='SAME', + data_format='channels_last', + dilation_rate=(1, 1, 1), + W_init=tl.initializers.truncated_normal(stddev=0.02), + b_init=tl.initializers.constant(value=0.0), + in_channels=None, + name=None # 'conv3d', + ): + super().__init__(name, act=act) + self.n_filter = n_filter + self.filter_size = filter_size + self._strides = self.strides = strides + self.padding = padding + self.data_format = data_format + self._dilation_rate = self.dilation_rate = dilation_rate + self.W_init = W_init + self.b_init = b_init + self.in_channels = in_channels + + if self.in_channels: + self.build(None) + self._built = True + + logging.info( + "Conv3d %s: n_filter: %d filter_size: %s strides: %s pad: %s act: %s" % ( + self.name, n_filter, str(filter_size), str(strides), padding, + self.act.__class__.__name__ if self.act is not None else 'No Activation' + ) + ) + + def __repr__(self): + actstr = self.act.__class__.__name__ if self.act is not None else 'No Activation' + s = ( + '{classname}(in_channels={in_channels}, out_channels={n_filter}, kernel_size={filter_size}' + ', strides={strides}, padding={padding}' + ) + if self.dilation_rate != (1, ) * len(self.dilation_rate): + s += ', dilation={dilation_rate}' + if self.b_init is None: + s += ', bias=False' + s += (', ' + actstr) + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + if self.data_format == 'channels_last': + self.data_format = 'NDHWC' + if self.in_channels is None: + self.in_channels = inputs_shape[-1] + self._strides = [1, self._strides[0], self._strides[1], self._strides[2], 1] + self._dilation_rate = [1, self.dilation_rate[0], self.dilation_rate[1], self.dilation_rate[2], 1] + elif self.data_format == 'channels_first': + self.data_format = 'NCDHW' + if self.in_channels is None: + self.in_channels = inputs_shape[1] + self._strides = [1, 1, self._strides[0], self._strides[1], self._strides[2]] + self._dilation_rate = [1, 1, self._dilation_rate[0], self._dilation_rate[1], self._dilation_rate[2]] + else: + raise Exception("data_format should be either channels_last or channels_first") + + self.filter_shape = ( + self.filter_size[0], self.filter_size[1], self.filter_size[2], self.in_channels, self.n_filter + ) + + self.W = self._get_weights("filters", shape=self.filter_shape, init=self.W_init) + + if self.b_init: + self.b = self._get_weights("biases", shape=(self.n_filter, ), init=self.b_init) + + self.b_init_flag = False + if self.b_init: + self.b = self._get_weights("biases", shape=(self.n_filter, ), init=self.b_init) + self.bias_add = tl.ops.BiasAdd(self.data_format) + self.b_init_flag = True + + self.conv3d = tl.ops.Conv3D( + strides=self._strides, padding=self.padding, data_format=self.data_format, dilations=self._dilation_rate, + out_channel=self.n_filter, k_size=(self.filter_size[0], self.filter_size[1]) + ) + + self.act_init_flag = False + if self.act: + self.act_init_flag = True + + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + outputs = self.conv3d(inputs, self.W) + if self.b_init_flag: + outputs = self.bias_add(outputs, self.b) + if self.act_init_flag: + outputs = self.act(outputs) + return outputs + + +class DeConv1d(Module): + """Simplified version of :class:`Deconv1dlayer`. + + Parameters + ---------- + n_filter : int + The number of filters + filter_size : int + The filter size + strides : int or list + An int or list of `ints` that has length `1` or `3`. The number of entries by which the filter is moved right at each step. + output_shape : a 1-D Tensor + containing three elements, representing the output shape of the deconvolution op. + dilation_rate : int or list + Specifying the dilation rate to use for dilated convolution. + act : activation function + The function that is applied to the layer activations + padding : str + The padding algorithm type: "SAME" or "VALID". + data_format : str + "channel_last" (NWC, default) or "channels_first" (NCW). + W_init : initializer + The initializer for the weight matrix. + b_init : initializer or None + The initializer for the bias vector. If None, skip biases. + in_channels : int + The number of in channels. + name : None or str + A unique layer name + + Examples + -------- + With TensorLayer + + >>> net = tl.layers.Input([8, 100, 1], name='input') + >>> conv1d = tl.layers.DeConv1d(n_filter=32, filter_size=5, stride=2, b_init=None, in_channels=1, name='Deonv1d_1') + >>> print(conv1d) + >>> tensor = tl.layers.DeConv1d(n_filter=32, filter_size=5, stride=2, act=tl.ops.relu, name='Deconv1d_2')(net) + >>> print(tensor) + + """ + + def __init__( + self, + n_filter=32, + filter_size=15, + strides=1, + act=None, + padding='SAME', + data_format="channels_last", + dilation_rate=1, + W_init=tl.initializers.truncated_normal(stddev=0.02), + b_init=tl.initializers.constant(value=0.0), + in_channels=None, + name=None # 'conv1d_transpose' + ): + super(DeConv1d, self).__init__(name, act=act) + self.n_filter = n_filter + self.filter_size = filter_size + self.strides = strides + self.padding = padding + self.data_format = data_format + self.dilation_rate = dilation_rate + self.W_init = W_init + self.b_init = b_init + self.in_channels = in_channels + + if self.in_channels: + self.build(None) + self._built = True + + logging.info( + "DeConv1d %s: n_filter: %d filter_size: %s stride: %d pad: %s act: %s" % ( + self.name, n_filter, filter_size, strides, padding, + self.act.__class__.__name__ if self.act is not None else 'No Activation' + ) + ) + + def __repr__(self): + actstr = self.act.__class__.__name__ if self.act is not None else 'No Activation' + s = ( + '{classname}(in_channels={in_channels}, out_channels={n_filter}, kernel_size={filter_size}' + ', strides={strides}, padding={padding}' + ) + if self.dilation_rate != 1: + s += ', dilation={dilation_rate}' + if self.b_init is None: + s += ', bias=False' + s += (', ' + actstr) + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + if self.data_format == 'channels_last': + self.data_format = 'NWC' + if self.in_channels is None: + self.in_channels = inputs_shape[-1] + elif self.data_format == 'channels_first': + self.data_format = 'NCW' + if self.in_channels is None: + self.in_channels = inputs_shape[1] + else: + raise Exception("data_format should be either channels_last or channels_first") + + self.filter_shape = (self.filter_size, self.n_filter, self.in_channels) + + # TODO : check + self.W = self._get_weights("filters", shape=self.filter_shape, init=self.W_init) + + self.b_init_flag = False + if self.b_init: + self.b = self._get_weights("biases", shape=(self.n_filter, ), init=self.b_init) + self.bias_add = tl.ops.BiasAdd(self.data_format) + self.b_init_flag = True + + self.conv1d_transpose = tl.ops.Conv1d_transpose( + strides=self.strides, + padding=self.padding, + data_format=self.data_format, + dilations=self.dilation_rate, + out_channel=self.n_filter, + k_size=self.filter_size, + in_channels=self.in_channels, + ) + + self.act_init_flag = False + if self.act: + self.act_init_flag = True + + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + outputs = self.conv1d_transpose(inputs, self.W) + if self.b_init_flag: + outputs = self.bias_add(outputs, self.b) + if self.act_init_flag: + outputs = self.act(outputs) + return outputs + + +class DeConv2d(Module): + """Simplified version of :class:`Deconv2dLayer`. + + Parameters + ---------- + + n_filter : int + The number of filters. + filter_size : tuple of int + The filter size. + strides : tuple of int + The sliding window strides of corresponding input dimensions. + It must be in the same order as the ``shape`` parameter. + output_shape : A 1-D Tensor + representing the output shape of the deconvolution op. + dilation_rate : tuple of int + Specifying the dilation rate to use for dilated convolution. + act : activation function + The activation function of this layer. + padding : str + The padding algorithm type: "SAME" or "VALID". + data_format : str + "channels_last" (NHWC, default) or "channels_first" (NCHW). + W_init : initializer + The initializer for the the weight matrix. + b_init : initializer or None + The initializer for the the bias vector. If None, skip biases. + in_channels : int + The number of in channels. + name : None or str + A unique layer name. + + Examples + -------- + With TensorLayer + + >>> net = tl.layers.Input([8, 3, 400, 400], name='input') + >>> conv2d_transpose = tl.layers.DeConv2d(n_filter=32, filter_size=(3, 3), strides=(2, 2), b_init=None, in_channels=3, name='conv2d_transpose_1') + >>> print(conv2d_transpose) + >>> tensor = tl.layers.DeConv2d(n_filter=32, filter_size=(3, 3), strides=(2, 2), act=tl.ops.relu, name='conv2d_transpose_2')(net) + >>> print(tensor) + + """ + + def __init__( + self, + n_filter=32, + filter_size=(3, 3), + strides=(1, 1), + act=None, + padding='SAME', + data_format='channels_last', + dilation_rate=(1, 1), + W_init=tl.initializers.truncated_normal(stddev=0.02), + b_init=tl.initializers.constant(value=0.0), + in_channels=None, + name=None, # 'conv2d_transpose', + ): + super(DeConv2d, self).__init__(name, act=act) + self.n_filter = n_filter + self.filter_size = filter_size + self._strides = self.strides = strides + self.padding = padding + self.data_format = data_format + self._dilation_rate = self.dilation_rate = dilation_rate + self.W_init = W_init + self.b_init = b_init + self.in_channels = in_channels + + if self.in_channels: + self.build(None) + self._built = True + + logging.info( + "DeConv2d %s: n_filter: %d filter_size: %s strides: %s pad: %s act: %s" % ( + self.name, n_filter, str(filter_size), str(strides), padding, + self.act.__class__.__name__ if self.act is not None else 'No Activation' + ) + ) + + def __repr__(self): + actstr = self.act.__class__.__name__ if self.act is not None else 'No Activation' + s = ( + '{classname}(in_channels={in_channels}, out_channels={n_filter}, kernel_size={filter_size}' + ', strides={strides}, padding={padding}' + ) + if self.dilation_rate != (1, ) * len(self.dilation_rate): + s += ', dilation={dilation_rate}' + if self.b_init is None: + s += ', bias=False' + s += (', ' + actstr) + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + if self.data_format == 'channels_last': + self.data_format = 'NHWC' + if self.in_channels is None: + self.in_channels = inputs_shape[-1] + self._strides = [1, self._strides[0], self._strides[1], 1] + self._dilation_rate = [1, self._dilation_rate[0], self._dilation_rate[1], 1] + elif self.data_format == 'channels_first': + self.data_format = 'NCHW' + if self.in_channels is None: + self.in_channels = inputs_shape[1] + self._strides = [1, 1, self._strides[0], self._strides[1]] + self._dilation_rate = [1, 1, self._dilation_rate[0], self._dilation_rate[1]] + else: + raise Exception("data_format should be either channels_last or channels_first") + + #TODO channels first filter shape [out_channel, in_channel, filter_h, filter_w] + self.filter_shape = (self.filter_size[0], self.filter_size[1], self.n_filter, self.in_channels) + self.W = self._get_weights("filters", shape=self.filter_shape, init=self.W_init) + + self.b_init_flag = False + if self.b_init: + self.b = self._get_weights("biases", shape=(self.n_filter, ), init=self.b_init) + self.bias_add = tl.ops.BiasAdd(self.data_format) + self.b_init_flag = True + + self.conv2d_transpose = tl.ops.Conv2d_transpose( + strides=self._strides, padding=self.padding, data_format=self.data_format, dilations=self._dilation_rate, + out_channel=self.n_filter, k_size=(self.filter_size[0], self.filter_size[1]), in_channels=self.in_channels + ) + + self.act_init_flag = False + if self.act: + self.act_init_flag = True + + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + outputs = self.conv2d_transpose(inputs, self.W) + if self.b_init_flag: + outputs = self.bias_add(outputs, self.b) + if self.act_init_flag: + outputs = self.act(outputs) + return outputs + + +class DeConv3d(Module): + """Simplified version of :class:`Deconv3dLayer`. + + Parameters + ---AppData\Local\Continuum\anaconda3\envs\ms_tf\lib\site-packages\mindspore\common\api.py", line 412, in compile + result = self._executor.compile(obj, args_list, phase, use_vm) + RuntimeError: Unable to cast from non-held to held instance (T& to Holder) of type 'std:------- + n_filter : int + The number of filters. + filter_size : tuple of int + The filter size (depth, height, width). + output_shape: + A 1-D Tensor representing the output shape of the deconvolution op. + strides : tuple of int + The sliding window strides of corresponding input dimensions. + It must be in the same order as the ``shape`` parameter. + dilation_rate : tuple of int + Specifying the dilation rate to use for dilated convolution. + act : activation function + The activation function of this layer. + padding : str + The padding algorithm type: "SAME" or "VALID". + data_format : str + "channels_last" (NDHWC, default) or "channels_first" (NCDHW). + W_init : initializer + The initializer for the the weight matrix. + b_init : initializer or None + The initializer for the the bias vector. If None, skip biases. + in_channels : int + The number of in channels. + name : None or str + A unique layer name. + + Examples + -------- + With TensorLayer + + >>> net = tl.layers.Input([8, 20, 20, 20, 3], name='input') + >>> deconv3d = tl.layers.DeConv3d(n_filter=32, filter_size=(3, 3, 3), strides=(2, 2, 2), b_init=None, in_channels=3, name='deconv3d_1') + >>> print(deconv3d) + >>> tensor = tl.layers.DeConv3d(n_filter=32, filter_size=(3, 3, 3), strides=(2, 2, 2), act=tl.ops.relu, name='deconv3d_2')(net) + >>> print(tensor) + + """ + + def __init__( + self, + n_filter=32, + filter_size=(3, 3, 3), + strides=(1, 1, 1), + act=None, + padding='SAME', + data_format='channels_last', + dilation_rate=(1, 1, 1), + W_init=tl.initializers.truncated_normal(stddev=0.02), + b_init=tl.initializers.constant(value=0.0), + in_channels=None, + name=None # 'deconv3d', + ): + super(DeConv3d, self).__init__(name, act=act) + self.n_filter = n_filter + self.filter_size = filter_size + self._strides = self.strides = strides + self.padding = padding + self.data_format = data_format + self._dilation_rate = self.dilation_rate = dilation_rate + self.W_init = W_init + self.b_init = b_init + self.in_channels = in_channels + + if self.in_channels: + self.build(None) + self._built = True + + logging.info( + "DeConv3d %s: n_filter: %d filter_size: %s strides: %s pad: %s act: %s" % ( + self.name, n_filter, str(filter_size), str(strides), padding, + self.act.__class__.__name__ if self.act is not None else 'No Activation' + ) + ) + + def __repr__(self): + actstr = self.act.__class__.__name__ if self.act is not None else 'No Activation' + s = ( + '{classname}(in_channels={in_channels}, out_channels={n_filter}, kernel_size={filter_size}' + ', strides={strides}, padding={padding}' + ) + if self.dilation_rate != (1, ) * len(self.dilation_rate): + s += ', dilation={dilation_rate}' + if self.b_init is None: + s += ', bias=False' + s += (', ' + actstr) + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + if self.data_format == 'channels_last': + self.data_format = 'NDHWC' + if self.in_channels is None: + self.in_channels = inputs_shape[-1] + self._strides = [1, self._strides[0], self._strides[1], self._strides[2], 1] + self._dilation_rate = [1, self.dilation_rate[0], self.dilation_rate[1], self.dilation_rate[2], 1] + elif self.data_format == 'channels_first': + self.data_format = 'NCDHW' + if self.in_channels is None: + self.in_channels = inputs_shape[1] + self._strides = [1, 1, self._strides[0], self._strides[1], self._strides[2]] + self._dilation_rate = [1, 1, self._dilation_rate[0], self._dilation_rate[1], self._dilation_rate[2]] + else: + raise Exception("data_format should be either channels_last or channels_first") + + self.filter_shape = ( + self.filter_size[0], self.filter_size[1], self.filter_size[2], self.n_filter, self.in_channels + ) + + self.W = self._get_weights("filters", shape=self.filter_shape, init=self.W_init) + + if self.b_init: + self.b = self._get_weights("biases", shape=(self.n_filter, ), init=self.b_init) + + self.b_init_flag = False + if self.b_init: + self.b = self._get_weights("biases", shape=(self.n_filter, ), init=self.b_init) + self.bias_add = tl.ops.BiasAdd(self.data_format) + self.b_init_flag = True + + self.conv3d_transpose = tl.ops.Conv3d_transpose( + strides=self._strides, padding=self.padding, data_format=self.data_format, dilations=self._dilation_rate, + out_channel=self.n_filter, k_size=(self.filter_size[0], self.filter_size[1], self.filter_size[2]) + ) + + self.act_init_flag = False + if self.act: + self.act_init_flag = True + + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + outputs = self.conv3d_transpose(inputs, self.W) + if self.b_init_flag: + outputs = self.bias_add(outputs, self.b) + if self.act_init_flag: + outputs = self.act(outputs) + return outputs diff --git a/tensorlayer/layers/convolution/super_resolution.py b/tensorlayer/layers/convolution/super_resolution.py new file mode 100644 index 0000000..102ef52 --- /dev/null +++ b/tensorlayer/layers/convolution/super_resolution.py @@ -0,0 +1,214 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module + +__all__ = [ + 'SubpixelConv1d', + 'SubpixelConv2d', +] + + +class SubpixelConv1d(Module): + """It is a 1D sub-pixel up-sampling layer. + + Calls a TensorFlow function that directly implements this functionality. + We assume input has dim (batch, width, r) + + Parameters + ------------ + scale : int + The up-scaling ratio, a wrong setting will lead to Dimension size error. + act : activation function + The activation function of this layer. + in_channels : int + The number of in channels. + name : str + A unique layer name. + + Examples + ---------- + With TensorLayer + + >>> net = tl.layers.Input([8, 25, 32], name='input') + >>> subpixelconv1d = tl.layers.SubpixelConv1d(scale=2, name='subpixelconv1d')(net) + >>> print(subpixelconv1d) + >>> output shape : (8, 50, 16) + + References + ----------- + `Audio Super Resolution Implementation `__. + + """ + + def __init__( + self, + scale=2, + act=None, + in_channels=None, + name=None # 'subpixel_conv1d' + ): + super().__init__(name, act=act) + self.scale = scale + self.in_channels = in_channels + # self.out_channels = int(self.in_channels / self.scale) + + if self.in_channels is not None: + self.build(None) + self._built = True + + logging.info( + "SubpixelConv1d %s: scale: %d act: %s" % + (self.name, scale, self.act.__name__ if self.act is not None else 'No Activation') + ) + + def __repr__(self): + actstr = self.act.__name__ if self.act is not None else 'No Activation' + s = ('{classname}(in_channels={in_channels}, out_channels={out_channels}') + s += (', ' + actstr) + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + if inputs_shape is not None: + self.in_channels = inputs_shape[-1] + self.out_channels = int(self.in_channels / self.scale) + self.transpose = tl.ops.Transpose(perm=[2, 1, 0]) + self.batch_to_space = tl.ops.BatchToSpace(block_size=[self.scale], crops=[[0, 0]]) + + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + outputs = self._PS(inputs) + if self.act is not None: + outputs = self.act(outputs) + return outputs + + def _PS(self, I): + X = self.transpose(I) # (r, w, b) + X = self.batch_to_space(X) # (1, r*w, b) + X = self.transpose(X) + return X + + +class SubpixelConv2d(Module): + """It is a 2D sub-pixel up-sampling layer, usually be used + for Super-Resolution applications, see `SRGAN `__ for example. + + Parameters + ------------ + scale : int + The up-scaling ratio, a wrong setting will lead to dimension size error. + n_out_channel : int or None + The number of output channels. + - If None, automatically set n_out_channel == the number of input channels / (scale x scale). + - The number of input channels == (scale x scale) x The number of output channels. + act : activation function + The activation function of this layer. + in_channels : int + The number of in channels. + name : str + A unique layer name. + + Examples + --------- + With TensorLayer + + >>> # examples here just want to tell you how to set the n_out_channel. + >>> net = tl.layers.Input([2, 16, 16, 4], name='input1') + >>> subpixelconv2d = tl.layers.SubpixelConv2d(scale=2, n_out_channels=1, name='subpixel_conv2d1')(net) + >>> print(subpixelconv2d) + >>> output shape : (2, 32, 32, 1) + + >>> net = tl.layers.Input([2, 16, 16, 4*10], name='input2') + >>> subpixelconv2d = tl.layers.SubpixelConv2d(scale=2, n_out_channels=10, name='subpixel_conv2d2')(net) + >>> print(subpixelconv2d) + >>> output shape : (2, 32, 32, 10) + + >>> net = tl.layers.Input([2, 16, 16, 25*10], name='input3') + >>> subpixelconv2d = tl.layers.SubpixelConv2d(scale=5, n_out_channels=10, name='subpixel_conv2d3')(net) + >>> print(subpixelconv2d) + >>> output shape : (2, 80, 80, 10) + + References + ------------ + - `Real-Time Single Image and Video Super-Resolution Using an Efficient Sub-Pixel Convolutional Neural Network `__ + + """ + + # github/Tetrachrome/subpixel https://github.com/Tetrachrome/subpixel/blob/master/subpixel.py + def __init__( + self, + scale=2, + n_out_channels=None, + act=None, + in_channels=None, + name=None # 'subpixel_conv2d' + ): + super().__init__(name, act=act) + self.scale = scale + self.n_out_channels = n_out_channels + self.in_channels = in_channels + + if self.in_channels is not None: + self.build(None) + self._built = True + logging.info( + "SubpixelConv2d %s: scale: %d act: %s" % + (self.name, scale, self.act.__name__ if self.act is not None else 'No Activation') + ) + + def __repr__(self): + actstr = self.act.__name__ if self.act is not None else 'No Activation' + s = ('{classname}(in_channels={in_channels}, out_channels={n_out_channels}') + s += (', ' + actstr) + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + + if inputs_shape is not None: + self.in_channels = inputs_shape[-1] + + if self.in_channels / (self.scale**2) % 1 != 0: + raise Exception( + "SubpixelConv2d: The number of input channels == (scale x scale) x The number of output channels" + ) + self.n_out_channels = int(self.in_channels / (self.scale**2)) + self.depth_to_space = tl.ops.DepthToSpace(block_size=self.scale) + + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + outputs = self._PS(X=inputs, r=self.scale, n_out_channels=self.n_out_channels) + if self.act is not None: + outputs = self.act(outputs) + return outputs + + def _PS(self, X, r, n_out_channels): + + _err_log = "SubpixelConv2d: The number of input channels == (scale x scale) x The number of output channels" + + if n_out_channels >= 1: + if int(X.get_shape()[-1]) != (r**2) * n_out_channels: + raise Exception(_err_log) + + X = self.depth_to_space(input=X) + else: + raise RuntimeError(_err_log) + + return X diff --git a/tensorlayer/layers/convolution/ternary_conv.py b/tensorlayer/layers/convolution/ternary_conv.py new file mode 100644 index 0000000..5b60ae0 --- /dev/null +++ b/tensorlayer/layers/convolution/ternary_conv.py @@ -0,0 +1,166 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module +from tensorlayer.layers.utils import compute_alpha, ternary_operation + +__all__ = ['TernaryConv2d'] + + +class TernaryConv2d(Module): + """ + The :class:`TernaryConv2d` class is a 2D ternary CNN layer, which weights are either -1 or 1 or 0 while inference. + + Note that, the bias vector would not be tenarized. + + Parameters + ---------- + n_filter : int + The number of filters. + filter_size : tuple of int + The filter size (height, width). + strides : tuple of int + The sliding window strides of corresponding input dimensions. + It must be in the same order as the ``shape`` parameter. + act : activation function + The activation function of this layer. + padding : str + The padding algorithm type: "SAME" or "VALID". + use_gemm : boolean + If True, use gemm instead of ``tf.matmul`` for inference. + TODO: support gemm + data_format : str + "channels_last" (NHWC, default) or "channels_first" (NCHW). + dilation_rate : tuple of int + Specifying the dilation rate to use for dilated convolution. + W_init : initializer + The initializer for the the weight matrix. + b_init : initializer or None + The initializer for the the bias vector. If None, skip biases. + in_channels : int + The number of in channels. + name : None or str + A unique layer name. + + Examples + --------- + With TensorLayer + + >>> net = tl.layers.Input([8, 12, 12, 32], name='input') + >>> ternaryconv2d = tl.layers.TernaryConv2d( + ... n_filter=64, filter_size=(5, 5), strides=(1, 1), act=tf.nn.relu, padding='SAME', name='ternaryconv2d' + ... )(net) + >>> print(ternaryconv2d) + >>> output shape : (8, 12, 12, 64) + + """ + + def __init__( + self, + n_filter=32, + filter_size=(3, 3), + strides=(1, 1), + act=None, + padding='SAME', + use_gemm=False, + data_format="channels_last", + dilation_rate=(1, 1), + W_init=tl.initializers.truncated_normal(stddev=0.02), + b_init=tl.initializers.constant(value=0.0), + in_channels=None, + name=None # 'ternary_cnn2d', + ): + super().__init__(name, act=act) + self.n_filter = n_filter + self.filter_size = filter_size + self.strides = self._strides = strides + self.padding = padding + self.use_gemm = use_gemm + self.data_format = data_format + self.dilation_rate = self._dilation_rate = dilation_rate + self.W_init = W_init + self.b_init = b_init + self.in_channels = in_channels + + if self.in_channels: + self.build(None) + self._built = True + + logging.info( + "TernaryConv2d %s: n_filter: %d filter_size: %s strides: %s pad: %s act: %s" % ( + self.name, n_filter, str(filter_size), str(strides), padding, + self.act.__class__.__name__ if self.act is not None else 'No Activation' + ) + ) + + if use_gemm: + raise Exception("TODO. The current version use tf.matmul for inferencing.") + + if len(self.strides) != 2: + raise ValueError("len(strides) should be 2.") + + def __repr__(self): + actstr = self.act.__name__ if self.act is not None else 'No Activation' + s = ( + '{classname}(in_channels={in_channels}, out_channels={n_filter}, kernel_size={filter_size}' + ', strides={strides}, padding={padding}' + ) + if self.dilation_rate != (1, ) * len(self.dilation_rate): + s += ', dilation={dilation_rate}' + if self.b_init is None: + s += ', bias=False' + s += (', ' + actstr) + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + if self.data_format == 'channels_last': + self.data_format = 'NHWC' + if self.in_channels is None: + self.in_channels = inputs_shape[-1] + self._strides = [1, self._strides[0], self._strides[1], 1] + self._dilation_rate = [1, self._dilation_rate[0], self._dilation_rate[1], 1] + elif self.data_format == 'channels_first': + self.data_format = 'NCHW' + if self.in_channels is None: + self.in_channels = inputs_shape[1] + self._strides = [1, 1, self._strides[0], self._strides[1]] + self._dilation_rate = [1, 1, self._dilation_rate[0], self._dilation_rate[1]] + else: + raise Exception("data_format should be either channels_last or channels_first") + + self.filter_shape = (self.filter_size[0], self.filter_size[1], self.in_channels, self.n_filter) + + self.W = self._get_weights("filters", shape=self.filter_shape, init=self.W_init) + if self.b_init: + self.b = self._get_weights("biases", shape=(self.n_filter, ), init=self.b_init) + self.bias_add = tl.ops.BiasAdd(data_format=self.data_format) + + self.conv2d = tl.ops.Conv2D(strides=self._strides, padding=self.padding, data_format=self.data_format, + dilations=self._dilation_rate) + + + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + alpha = compute_alpha(self.W) + + W_ = ternary_operation(self.W) + W_ = tl.ops.multiply(alpha, W_) + + outputs = self.conv2d(inputs, W_) + + if self.b_init: + outputs = self.bias_add(outputs, self.b) + if self.act: + outputs = self.act(outputs) + + return outputs diff --git a/tensorlayer/layers/core/__init__.py b/tensorlayer/layers/core/__init__.py new file mode 100644 index 0000000..a3b3f95 --- /dev/null +++ b/tensorlayer/layers/core/__init__.py @@ -0,0 +1,15 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +from tensorlayer.backend import BACKEND + +if BACKEND == 'mindspore': + from .core_mindspore import * +elif BACKEND == 'tensorflow': + from .core_tensorflow import * +elif BACKEND == 'paddle': + from .core_paddle import * +elif BACKEND == 'dragon': + from .core_dragon import * +else: + raise ("Unsupported backend:", BACKEND) diff --git a/tensorlayer/layers/core/common.py b/tensorlayer/layers/core/common.py new file mode 100644 index 0000000..1af257f --- /dev/null +++ b/tensorlayer/layers/core/common.py @@ -0,0 +1,174 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import os +import tensorlayer as tl +from tensorlayer.files import utils +from tensorlayer import logging + +_act_dict = { + "relu": tl.ops.ReLU, + "relu6": tl.ops.ReLU6, + "leaky_relu": tl.ops.LeakyReLU, + "lrelu": tl.ops.LeakyReLU, + "softplus": tl.ops.Softplus, + "tanh": tl.ops.Tanh, + "sigmoid": tl.ops.Sigmoid, + "softmax": tl.ops.Softmax +} + + +def str2act(act): + if len(act) > 5 and act[0:5] == "lrelu": + try: + alpha = float(act[5:]) + return tl.ops.LeakyReLU(alpha=alpha) + except Exception as e: + raise Exception("{} can not be parsed as a float".format(act[5:])) + + if len(act) > 10 and act[0:10] == "leaky_relu": + try: + alpha = float(act[10:]) + return tl.ops.LeakyReLU(alpha=alpha) + except Exception as e: + raise Exception("{} can not be parsed as a float".format(act[10:])) + + if act not in _act_dict.keys(): + raise Exception("Unsupported act: {}".format(act)) + return _act_dict[act] + +def _save_weights(self, file_path, format=None): + """Input file_path, save model weights into a file of given format. + Use self.load_weights() to restore. + + Parameters + ---------- + file_path : str + Filename to which the model weights will be saved. + format : str or None + Saved file format. + Value should be None, 'hdf5', 'npz', 'npz_dict' or 'ckpt'. Other format is not supported now. + 1) If this is set to None, then the postfix of file_path will be used to decide saved format. + If the postfix is not in ['h5', 'hdf5', 'npz', 'ckpt'], then file will be saved in hdf5 format by default. + 2) 'hdf5' will save model weights name in a list and each layer has its weights stored in a group of + the hdf5 file. + 3) 'npz' will save model weights sequentially into a npz file. + 4) 'npz_dict' will save model weights along with its name as a dict into a npz file. + 5) 'ckpt' will save model weights into a tensorflow ckpt file. + + Default None. + + Examples + -------- + 1) Save model weights in hdf5 format by default. + >>> net = vgg16() + >>> net.save_weights('./model.h5') + ... + >>> net.load_weights('./model.h5') + + 2) Save model weights in npz/npz_dict format + >>> net = vgg16() + >>> net.save_weights('./model.npz') + >>> net.save_weights('./model.npz', format='npz_dict') + + """ + + # self.all_weights = self.network.all_weights + if self.all_weights is None or len(self.all_weights) == 0: + logging.warning("Model contains no weights or layers haven't been built, nothing will be saved") + return + + if format is None: + postfix = file_path.split('.')[-1] + if postfix in ['h5', 'hdf5', 'npz', 'ckpt']: + format = postfix + else: + format = 'hdf5' + + if format == 'hdf5' or format == 'h5': + utils.save_weights_to_hdf5(file_path, self) + elif format == 'npz': + utils.save_npz(self.all_weights, file_path) + elif format == 'npz_dict': + utils.save_npz_dict(self.all_weights, file_path) + elif format == 'ckpt': + # TODO: enable this when tf save ckpt is enabled + raise NotImplementedError("ckpt load/save is not supported now.") + else: + raise ValueError( + "Save format must be 'hdf5', 'npz', 'npz_dict' or 'ckpt'." + "Other format is not supported now." + ) + +def _load_weights(self, file_path, format=None, in_order=True, skip=False): + """Load model weights from a given file, which should be previously saved by self.save_weights(). + + Parameters + ---------- + file_path : str + Filename from which the model weights will be loaded. + format : str or None + If not specified (None), the postfix of the file_path will be used to decide its format. If specified, + value should be 'hdf5', 'npz', 'npz_dict' or 'ckpt'. Other format is not supported now. + In addition, it should be the same format when you saved the file using self.save_weights(). + Default is None. + in_order : bool + Allow loading weights into model in a sequential way or by name. Only useful when 'format' is 'hdf5'. + If 'in_order' is True, weights from the file will be loaded into model in a sequential way. + If 'in_order' is False, weights from the file will be loaded into model by matching the name + with the weights of the model, particularly useful when trying to restore model in eager(graph) mode from + a weights file which is saved in graph(eager) mode. + Default is True. + skip : bool + Allow skipping weights whose name is mismatched between the file and model. Only useful when 'format' is + 'hdf5' or 'npz_dict'. If 'skip' is True, 'in_order' argument will be ignored and those loaded weights + whose name is not found in model weights (self.all_weights) will be skipped. If 'skip' is False, error will + occur when mismatch is found. + Default is False. + + Examples + -------- + 1) load model from a hdf5 file. + >>> net = vgg16() + >>> net.load_weights('./model_graph.h5', in_order=False, skip=True) # load weights by name, skipping mismatch + >>> net.load_weights('./model_eager.h5') # load sequentially + + 2) load model from a npz file + >>> net.load_weights('./model.npz') + + 2) load model from a npz file, which is saved as npz_dict previously + >>> net.load_weights('./model.npz', format='npz_dict') + + Notes + ------- + 1) 'in_order' is only useful when 'format' is 'hdf5'. If you are trying to load a weights file which is + saved in a different mode, it is recommended to set 'in_order' be True. + 2) 'skip' is useful when 'format' is 'hdf5' or 'npz_dict'. If 'skip' is True, + 'in_order' argument will be ignored. + + """ + if not os.path.exists(file_path): + raise FileNotFoundError("file {} doesn't exist.".format(file_path)) + + if format is None: + format = file_path.split('.')[-1] + + if format == 'hdf5' or format == 'h5': + if skip ==True or in_order == False: + # load by weights name + utils.load_hdf5_to_weights(file_path, self, skip) + else: + # load in order + utils.load_hdf5_to_weights_in_order(file_path, self) + elif format == 'npz': + utils.load_and_assign_npz(file_path, self) + elif format == 'npz_dict': + utils.load_and_assign_npz_dict(file_path, self, skip) + elif format == 'ckpt': + # TODO: enable this when tf save ckpt is enabled + raise NotImplementedError("ckpt load/save is not supported now.") + else: + raise ValueError( + "File format must be 'hdf5', 'npz', 'npz_dict' or 'ckpt'. " + "Other format is not supported now." + ) diff --git a/tensorlayer/layers/core/core_dragon.py b/tensorlayer/layers/core/core_dragon.py new file mode 100644 index 0000000..f07772c --- /dev/null +++ b/tensorlayer/layers/core/core_dragon.py @@ -0,0 +1,765 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +#TODO Dragon Module needs a better implementation + +import time +import dragon as dg +import tensorlayer as tl +from tensorlayer.layers.utils import (get_variable_with_initializer) +from .common import str2act, _save_weights, _load_weights +from collections import OrderedDict +from tensorlayer import logging + +__all__ = ['Module', 'SequentialLayer', 'LayerList'] + +_global_layer_name_dict = {} +Parameter_ = dg.Tensor + +class Module(object): + + def __init__(self, name=None, act=None, *args, **kwargs): + self._params = OrderedDict() + self._layers = OrderedDict() + self._params_status = OrderedDict() + self._parameter_layout_dict = {} + self._create_time = int(time.time() * 1e9) + + global _global_layer_name_dict + if name is None: + prefix = self.__class__.__name__.lower() + + if _global_layer_name_dict.get(prefix) is not None: + _global_layer_name_dict[prefix] += 1 + name = prefix + '_' + str(_global_layer_name_dict[prefix]) + else: + _global_layer_name_dict[prefix] = 0 + name = prefix + while True: + if _global_layer_name_dict.get(name) is None: + break + _global_layer_name_dict[prefix] += 1 + name = prefix + '_' + str(_global_layer_name_dict[prefix]) + else: + if _global_layer_name_dict.get(name) is not None: + pass + else: + _global_layer_name_dict[name] = 0 + + self.name = name + + if isinstance(act, str): + str_act = str2act(act) + + if act: + if isinstance(act, str) and (len(act) > 5 and act[0:5] == "lrelu" or len(act) > 10 and act[0:10] == "leaky_relu"): + self.act = str_act + elif isinstance(act, str): + self.act = str_act() + else: + self.act = act() + else: + self.act = act + + # Layer building state + self._built = False + + # Layer nodes state + self._nodes = [] + self._nodes_fixed = False + + # Layer weight state + self._all_weights = [] + self._trainable_weights = [] + self._nontrainable_weights = [] + + # layer forward state + self._forward_state = False + + # Layer training state + self.is_train = True + + def extend_repr(self): + """ + Sets the extended representation of the Module. + + To print customized extended information, re-implement this method in your own Layers. + """ + return '' + + def __repr__(self): + extra_str = self.extend_repr() + info_str = self.__class__.__name__ + '<' + if self._layers: + sub_str = '\n' + if extra_str: + sub_str += '{}\n'.format(self.extend_repr()) + for key, value in self._layers.items(): + sub_str += '({}): {}\n'.format(key, repr(value)) + sub_str = sub_str.replace('\n', '\n ') + '>' + info_str += sub_str + else: + info_str += extra_str + '>' + return info_str + + def __setattr__(self, name, value): + layers = self.__dict__.get('_layers') + params = self.__dict__.get('_params') + + if isinstance(value, Parameter_): + if params is None: + raise AttributeError("Can not assign params before Module.__init__() call.") + if name in self.__dict__: + if self.__dict__[name] is not None: + raise TypeError("Expected type is not in (Parameter, Module), but got Parameter.") + del self.__dict__[name] + if layers and name in layers: + raise TypeError("Expected type is Module, but got Parameter.") + self.insert_param_to_layer(name, value) + + elif isinstance(value, Module): + if layers is None: + raise AttributeError("Can not assign layers before Module.__init__() call.") + if name in self.__dict__: + del self.__dict__[name] + if params and name in params: + raise TypeError("Expected type is Parameter, but got Module.") + # TODO How to prompt the user, enter the in_channels. + # TODO Automatic shape inference when the user does not enter inchannels. + # if value._built is False: + # raise AttributeError( + # "The registered layer `{}` should be built in advance. " + # "Do you forget to pass the keyword argument 'in_channels'? ".format(value.name) + # ) + layers[name] = value + else: + object.__setattr__(self, name, value) + + def __call__(self, inputs, *args, **kwargs): + + output = self.forward(inputs, *args, **kwargs) + + return output + + def forward(self, *inputs, **kwargs): + raise Exception("The forward method must be implemented by inherited class") + + def build(self, inputs_shape): + raise Exception("The build(self, inputs_shape) method must be implemented by inherited class") + + def _get_weights(self, var_name, shape, init=tl.initializers.random_normal(), trainable=True): + """ Get trainable variables. """ + weight = get_variable_with_initializer( + scope_name=self.name, var_name=var_name, shape=shape, init=init, trainable=trainable + ) + self.trainable = trainable + return weight + + def save_weights(self, file_path, format=None): + """Input file_path, save model weights into a file of given format.""" + _save_weights(self, file_path, format) + + def load_weights(self, file_path, format=None, in_order=True, skip=False): + """Load model weights from a given file, which should be previously saved by self.save_weights().""" + _load_weights(self, file_path, format, in_order, skip) + + def _set_mode_for_layers(self, is_train): + """Set all layers of this network to a given mode. + + Parameters + ---------- + is_train : boolean + Network's mode. True means training mode while False means evaluation mode. + + """ + layers = self.layers_and_names(name_prefix='') + for layer_name, layer in layers: + if isinstance(layer, Module): + layer.is_train = is_train + + + def set_train(self): + """Set this network in training mode. After calling this method, + all layers in network are in training mode, in particular, BatchNorm, Dropout, etc. + TODO It is not possible to modify the parameter state after initialization, and a better way needs to be found. + Examples + -------- + >>> import tensorlayer as tl + >>> net = tl.vgg16() + >>> net.set_train() + + """ + if self.is_train !=True: + self.is_train = True + self._set_mode_for_layers(True) + + def set_eval(self): + """Set this network in evaluation mode. After calling this method, + all layers in network are in evaluation mode, in particular, BatchNorm, Dropout, etc. + TODO It is not possible to modify the parameter state after initialization, and a better way needs to be found. + Examples + -------- + >>> import tensorlayer as tl + >>> net = tl.vgg16() + >>> net.eval() + # do evaluation + + """ + if self.is_train != False: + self.is_train = False + self._set_mode_for_layers(False) + + def test(self): + """Set this network in evaluation mode.""" + self.eval() + + def infer(self): + """Set this network in evaluation mode.""" + self.eval() + + @staticmethod + def _compute_shape(tensors): + if isinstance(tensors, list): + shape_mem = [tl.get_tensor_shape(t) for t in tensors] + else: + shape_mem = tl.get_tensor_shape(tensors) + return shape_mem + + def insert_param_to_layer(self, param_name, param, check_name=True): + """ + Adds a parameter to the current layer. + + Inserts a parameter with given name to the layer. Please refer to the usage in + source code of `tensorlayer.layer.Module.__setattr__`. + + Args: + param_name (str): Name of the parameter. + param (Parameter): Parameter to be inserted to the layer. + check_name (bool): Determines whether the name input is compatible. Default: True. + + Raises: + KeyError: If the name of parameter is null or contains dot. + AttributeError: If user did not call init() first. + TypeError: If the type of parameter is not Parameter_. + """ + if not param_name: + raise KeyError("The name of parameter should not be null.") + if check_name and '.' in param_name: + raise KeyError("The name of parameter should not contain \".\"") + if '_params' not in self.__dict__: + raise AttributeError("You need call init() first.") + if hasattr(self, param_name) and param_name not in self._params: + raise KeyError("Duplicated parameter name '{}'.".format(param_name)) + if not isinstance(param, Parameter_) and param is not None: + raise TypeError("The type of parameter should be 'Parameter' if not None.") + self._params[param_name] = param + try: + self._params_status[param_name] = self.trainable + except: + pass + + def _add_node(self, input_tensors, output_tensors): + """Add a LayerNode for this layer given input_tensors, output_tensors. + + WARINING: This function should not be called from outside, it should only be called + in layer.__call__ when building static model. + + Parameters + ---------- + input_tensors : Tensor or a list of tensors + Input tensors to this layer. + output_tensors : Tensor or a list of tensors + Output tensors to this layer. + + """ + raise NotImplementedError + + @property + def create_time(self): + return self._create_time + + def __getattr__(self, name): + if '_params' in self.__dict__: + params = self.__dict__['_params'] + if name in params: + return params[name] + if '_layers' in self.__dict__: + layers = self.__dict__['_layers'] + if name in layers: + return layers[name] + if '_params_status' in self.__dict__: + params_status = self.__dict__['_params_status'] + if name in params_status: + return params_status[name] + raise AttributeError("'{}' object has no attribute '{}'.".format(type(self).__name__, name)) + + def __delattr__(self, name): + if name in self._params: + del self._params[name] + elif name in self._layers: + del self._layers[name] + else: + object.__delattr__(self, name) + + @property + def trainable_weights(self): + """ + Returns all trainable weights. + + Returns a list of all trainable parmeters. + + Args: + recurse (bool): Whether contains the trainable weights of sublayers. Default: True. + + Returns: + List, the list of trainable weights. + """ + self.get_weights() + layers = self.layers_and_names(name_prefix='') + for layer_name, layer in layers: + params = layer._params.items() + params_status = layer._params_status.items() + params_zip = zip(params, params_status) + for params, params_status in params_zip: + if params_status[1] ==True: + self._trainable_weights.append(params[1]) + return self._trainable_weights + + @property + def nontrainable_weights(self): + """ + Returns all untrainable weights. + + Returns a list of all untrainable weights. + + Args: + recurse (bool): Whether contains the untrainable weights of sublayers. Default: True. + + Returns: + List, the list of untrainable weights. + """ + layers = self.layers_and_names(name_prefix='') + for layer_name, layer in layers: + params = layer._params.items() + params_status = layer._params_status.items() + params_zip = zip(params, params_status) + for params, params_status in params_zip: + if params_status[1] == False: + self._nontrainable_weights.append(params[1]) + return self._nontrainable_weights + + @property + def all_weights(self): + layers = self.layers_and_names(name_prefix='') + for layer_name, layer in layers: + params = layer._params.items() + for par, val in params: + self._all_weights.append(val) + return self._all_weights + + def get_weights(self, expand=True): + """ + Returns an iterator over layer weights. + + Yields weights of this layer. If `expand` is True, yield parameters of this layer and all sublayers. + + Args: + expand (bool): If True, yields parameters of this layer and all sublayers. Otherwise, yields only parameters + that are direct members of this layer. Default: True. + + Examples: + >>> net = Net() + >>> for item in net.get_weights(): + >>> print(item) + """ + for _, param in self.parameters_and_names(expand=expand): + yield param + + def check_names(self): + names = set("") + for value, param in self.parameters_and_names(): + if param.name in names: + raise ValueError( + "The value of {} is {}, its name '{}' already exists.".format(value, param, param.name) + ) + names.add(param.name) + + def insert_child_to_layer(self, child_name, child): + """ + Adds a child layer to the current layer. + + Args: + child_name (str): Name of the child layer. + child (Module): The child layer to be inserted. + + Raises: + KeyError: Child Module's name is incorrect or duplicated with the other child name. + TypeError: Child Module's type is incorrect. + """ + if not child_name or '.' in child_name: + raise KeyError("Child layer name is incorrect.") + if hasattr(self, child_name) and child_name not in self._layers: + raise KeyError("Duplicate child name '{}'.".format(child_name)) + if not isinstance(child, Module) and child is not None: + raise TypeError("Child layer type is incorrect.") + self._layers[child_name] = child + + def parameters_and_names(self, name_prefix='', expand=True): + """ + Returns an iterator over layer parameters. + + Includes the parameter's name and itself. + + Args: + name_prefix (str): Namespace. Default: ''. + expand (bool): If True, yields parameters of this layer and all sublayers. Otherwise, yields only parameters + that are direct members of this layer. Default: True. + + Examples: + >>> n = Net() + >>> names = [] + >>> for m in n.parameters_and_names(): + >>> if m[0]: + >>> names.append(m[0]) + """ + layers = [] + if expand: + layers = self.layers_and_names(name_prefix=name_prefix) + else: + layers.append((name_prefix, self)) + + params_set = set() + for layer_name, layer in layers: + params = layer._params.items() + for par_name, par in params: + if par.inited_param is not None: + par = par.inited_param + if par is not None and id(par) not in params_set: + params_set.add(id(par)) + par_new_name = par_name + if layer_name: + par_new_name = layer_name + '.' + par_new_name + + yield par_new_name, par + + def layers_and_names(self, layers=None, name_prefix=''): + """ + Returns an iterator over all layers in the network. + + Includes the layer's name and itself. + + Args: + layers (str): layers to iterate over. Default: None. + name_prefix (str): Namespace. Default: ''. + + Examples: + >>> n = Net() + >>> names = [] + >>> for m in n.layers_and_names(): + >>> if m[0]: + >>> names.append(m[0]) + """ + t_layers = layers if layers else set() + if self in t_layers: + return + + t_layers.add(self) + yield name_prefix, self + + for name, layer in self._layers.items(): + if layer: + layers_name_prefix = name + if name_prefix: + layers_name_prefix = name_prefix + '.' + layers_name_prefix + for ele in layer.layers_and_names(t_layers, layers_name_prefix): + yield ele + + def layers(self): + """Returns an iterator over immediate layers.""" + return self.name_layers().values() + + def name_layers(self): + """ + Returns an iterator over all layers in the network. + + Include name of the layer and layer itself. + """ + value_set = set() + layers = OrderedDict() + for name, layer in self._layers.items(): + if layer is not None and layer not in value_set: + value_set.add(layer) + layers[name] = layer + return layers + + def init_build(self, *inputs, **kwargs): + """ + (1) This method must be called when the Layer has no input in_channels. + (2) Automatic shape inference when the user does not enter inchannels. + """ + + self.forward(*inputs, **kwargs) + + +class SequentialLayer(Module): + """ + Sequential layer container. + + A list of Layers will be added to it in the order they are passed in the constructor. + Alternatively, an ordered dict of layers can also be passed in. + + Args: + args (list, OrderedDict): List of subclass of Module. + + Raises: + TypeError: If the type of the argument is not list or OrderedDict. + + Inputs: + - **input** (Tensor) - Tensor with shape according to the first Module in the sequence. + + Outputs: + Tensor, the output Tensor with shape depending on the input and defined sequence of Layers. + + Examples: + >>> conv = tl.layers.Conv2d(3, 2, 3, pad_mode='valid') + >>> bn = tl.layers.BatchNorm2d(2) + >>> seq = tl.layers.SequentialLayer([conv, bn]) + >>> + >>> x = tl.layers.Input((1, 3, 4, 4)) + >>> seq(x) + """ + def __init__(self, *args): + super(SequentialLayer, self).__init__() + self._built = True + if len(args) == 1: + layers = args[0] + if isinstance(layers, list): + for index, layer in enumerate(layers): + self.insert_child_to_layer(str(index), layer) + elif isinstance(layers, OrderedDict): + for name, layer in layers.items(): + self.insert_child_to_layer(name, layer) + else: + raise TypeError('Layers must be list or orderedDict') + else: + for index, layer in enumerate(args): + self.insert_child_to_layer(str(index), layer) + self.layer_list = list(self._layers.values()) + + def __getitem__(self, index): + if isinstance(index, slice): + return self.__class__( + OrderedDict(list(self._layers.items())[index])) + index = self._valid_index(len(self), index) + return list(self._layers.values())[index] + + def __setitem__(self, index, layer): + if self._valid_module(layer): + index = self._valid_index(len(self), index) + key = list(self._layers.keys())[index] + self._layers[key] = layer + self.layer_list = list(self._layers.values()) + + def __delitem__(self, index): + if isinstance(index, int): + index = self._valid_index(len(self), index) + key = list(self._layers.keys())[index] + del self._layers[key] + elif isinstance(index, slice): + keys = list(self._layers.keys())[index] + for key in keys: + del self._layers[key] + else: + raise TypeError('Index {} is not int type or slice type'.format(index)) + self.layer_list = list(self._layers.values()) + + def __len__(self): + return len(self._layers) + + + def append(self, layer): + if self._valid_module(layer): + self._layers[str(len(self))] = layer + self.layer_list = list(self._layers.values()) + return self + + def build(self, inputs_shape): + pass + + def forward(self, input_data): + for layer in self.layer_list: + input_data = layer(input_data) + return input_data + + def _valid_index(self, layer_num, index): + if not isinstance(index, int): + raise TypeError("Index {} is not int type") + if not -layer_num <= index < layer_num: + raise IndexError("Index should be a number in range [{}, {}), but got {}" + .format(-layer_num, layer_num, index)) + return index % layer_num + + def _valid_module(self, layer): + if issubclass(layer.__class__, Module): + return True + raise TypeError('Module {} is not subclass of Module'.format(layer)) + + +class LayerList(Module): + """ + The class :class:`LayerList` is a linear stack of layers. + + The :class:`LayerList` can be created by passing a list of layer instances. + The given layer instances will be automatically connected one by one. + + Parameters + ---------- + layers: list of Layer + A list of layers. + name : str or None + A unique layer name. If None, a unique name will be automatically assigned. + + Methods + --------- + __init__() + Initializing the LayerList. + weights() + A collection of weights of all the layer instances. + build() + Build the LayerList. The layer instances will be connected automatically one by one. + forward() + Forward the computation. The computation will go through all layer instances. + """ + + def __init__(self, layers, name=None): + """ + Initializing the LayerList given a list of Layer. + + :param layers: list of Layer + :param name: str or None + """ + + super(LayerList, self).__init__(name=name) + self.layers = layers + is_built = True + for layer in self.layers: + self._trainable_weights.extend(layer.trainable_weights) + self._nontrainable_weights.extend(layer.nontrainable_weights) + if layer._built is False: + is_built = False + # if layer._built and layer.all_weights is not None: + # # some layers in the list passed in have already been built + # # e.g. using input shape to construct layers in dynamic eager + # if self._all_weights is None: + # self._all_weights = list() + # self._all_weights.extend(layer.all_weights) + if is_built: + self._built = True + + logging.info( + "LayerList %s including layers [%s]" % (self.name, ', '.join([layer.name for layer in self.layers])) + ) + + # check layer name uniqueness in LayerList + local_layer_name_set = set() + for layer in self.layers: + if layer.name not in local_layer_name_set: + local_layer_name_set.add(layer.name) + else: + raise ValueError( + 'Layer name \'%s\' has already been used by another layer. Please change the layer name.' % + layer.name + ) + + def __getitem__(self, idx): + if isinstance(idx, slice): + return LayerList(list(self.layers)[idx]) + else: + return self.layers[idx] + + def __len__(self): + return len(self.layers) + + def __repr__(self): + tmpstr = 'LayerList' + '(\n' + for idx, layer in enumerate(self.layers): + modstr = layer.__repr__() + modstr = _addindent(modstr, 2) + tmpstr = tmpstr + ' (' + str(idx) + '): ' + modstr + '\n' + + tmpstr = tmpstr + ')' + return tmpstr + + @property + def trainable_weights(self): + return self._trainable_weights + + @property + def nontrainable_weights(self): + return self._nontrainable_weights + + @property + def all_weights(self): + return self._trainable_weights + self._nontrainable_weights + + # def build(self, inputs_shape): + # """ + # Build the LayerList. The layer instances will be connected automatically one by one. + # """ + # in_tensor = self._input_tensors + # # in_layer = self._input_layer + # for layer in self.layers: + # is_build = layer._built + # out_tensor = layer(in_tensor) + # # nlayer = layer(in_layer) + # if is_build is False and layer.all_weights is not None: + # if self._all_weights is None: + # self._all_weights = list() + # self._all_weights.extend(layer.all_weights) + # layer._built = True + # in_tensor = out_tensor + # # in_layer = nlayer + + def forward(self, inputs): + """ + Forward the computation. The computation will go through all layer instances. + """ + z = inputs + for layer in self.layers: + z = layer.forward(z) + return z + + def _set_mode_for_layers(self, is_train): + """Set training/evaluation mode for all layer instances.""" + self.is_train = is_train + for layer in self.layers: + if isinstance(layer, LayerList): + layer._set_mode_for_layers(is_train) + else: + layer.is_train = is_train + + def get_args(self): + init_args = {} + layers = self.layer_args["layers"] + init_args["layers"] = [layer.config for layer in layers] + init_args.update({"layer_type": "layerlist"}) + return init_args + +def tolist(tensors): + if isinstance(tensors, list) or isinstance(tensors, tuple): + ntensors = list() + for t in tensors: + ntensors += tolist(t) + return ntensors + else: + return [tensors] + +def _addindent(s_, numSpaces): + s = s_.split('\n') + # don't do anything for single-line stuff + if len(s) == 1: + return s_ + first = s.pop(0) + s = [(numSpaces * ' ') + line for line in s] + s = '\n'.join(s) + s = first + '\n' + s + return s \ No newline at end of file diff --git a/tensorlayer/layers/core/core_mindspore.py b/tensorlayer/layers/core/core_mindspore.py new file mode 100644 index 0000000..b8bfe0d --- /dev/null +++ b/tensorlayer/layers/core/core_mindspore.py @@ -0,0 +1,355 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +from .common import str2act, _save_weights, _load_weights +from mindspore.nn import Cell +import tensorlayer as tl +from tensorlayer.layers.utils import (get_variable_with_initializer) +from collections import OrderedDict + +__all__ = ['Module', 'SequentialLayer', 'LayerList'] + +_global_layer_name_dict = {} # TODO: better implementation? + + +class Module(Cell): + + def __init__(self, name=None, act=None, *args, **kwargs): + super().__init__(*args, **kwargs) + + global _global_layer_name_dict + if name is None: + prefix = self.__class__.__name__.lower() + + if _global_layer_name_dict.get(prefix) is not None: + _global_layer_name_dict[prefix] += 1 + name = prefix + '_' + str(_global_layer_name_dict[prefix]) + else: + _global_layer_name_dict[prefix] = 0 + name = prefix + while True: + if _global_layer_name_dict.get(name) is None: + break + _global_layer_name_dict[prefix] += 1 + name = prefix + '_' + str(_global_layer_name_dict[prefix]) + else: + if _global_layer_name_dict.get(name) is not None: + pass + else: + _global_layer_name_dict[name] = 0 + + self.name = name + + if isinstance(act, str): + str_act = str2act(act) + + if act: + if isinstance(act, str) and (len(act) > 5 and act[0:5] == "lrelu" or len(act) > 10 and act[0:10] == "leaky_relu"): + self.act = str_act + elif isinstance(act, str): + self.act = str_act() + else: + self.act = act() + else: + self.act = act + + # Layer building state + self._built = False + + # Layer nodes state + self._nodes = [] + self._nodes_fixed = False + + # Layer weight state + self._all_weights = [] + self._trainable_weights = [] + self._nontrainable_weights = [] + + # Layer training state + self.is_train = True + + + # layer forward state + self._forward_state = False + + def forward(self, *inputs, **kwargs): + raise Exception("The forward method must be implemented by inherited class") + + def construct(self, *inputs, **kwargs): + return self.forward(*inputs, **kwargs) + + def build(self, inputs_shape): + raise Exception("The build(self, inputs_shape) method must be implemented by inherited class") + + def _get_weights(self, var_name, shape, init=tl.initializers.random_normal(), trainable=True): + """ Get trainable variables. """ + weight = get_variable_with_initializer( + scope_name=self.name, var_name=var_name, shape=shape, init=init, trainable=trainable + ) + self.trainable = trainable + return weight + + def save_weights(self, file_path, format=None): + """Input file_path, save model weights into a file of given format.""" + _save_weights(self, file_path, format) + + def load_weights(self, file_path, format=None, in_order=True, skip=False): + """Load model weights from a given file, which should be previously saved by self.save_weights().""" + _load_weights(self, file_path, format, in_order, skip) + + @staticmethod + def _compute_shape(tensors): + if isinstance(tensors, list): + shape_mem = [tl.get_tensor_shape(t) for t in tensors] + else: + shape_mem = tl.get_tensor_shape(tensors) + return shape_mem + + def _add_node(self, input_tensors, output_tensors): + """Add a LayerNode for this layer given input_tensors, output_tensors. + + WARINING: This function should not be called from outside, it should only be called + in layer.__call__ when building static model. + + Parameters + ---------- + input_tensors : Tensor or a list of tensors + Input tensors to this layer. + output_tensors : Tensor or a list of tensors + Output tensors to this layer. + + """ + raise NotImplementedError + + def set_train(self): + """ + Sets the cell to training mode. + + The cell itself and all children cells will be set to training mode. + + Args: + mode (bool): Specifies whether the model is training. Default: True. + """ + self._phase = 'train' + self.add_flags_recursive(training=True) + return self + + def set_eval(self): + """Set this network in evaluation mode. After calling this method, + all layers in network are in evaluation mode, in particular, BatchNorm, Dropout, etc. + + Examples + -------- + >>> import tensorlayer as tl + >>> net = tl.models.vgg16() + >>> net.eval() + # do evaluation + + """ + self._phase = 'predict' + self.add_flags_recursive(training=False) + return self + + def test(self): + """Set this network in evaluation mode.""" + self.eval() + + def infer(self): + """Set this network in evaluation mode.""" + self.eval() + + @property + def trainable_weights(self): + """ + Returns all trainable weights. + + Returns a list of all trainable parmeters. + + Args: + recurse (bool): Whether contains the trainable weights of sublayers. Default: True. + + Returns: + List, the list of trainable weights. + """ + self._trainable_weights = list(filter(lambda x: x.requires_grad, self.get_parameters(expand=True))) + return self._trainable_weights + + @property + def nontrainable_weights(self): + """ + Returns all untrainable weights. + + Returns a list of all untrainable weights. + + Args: + recurse (bool): Whether contains the untrainable weights of sublayers. Default: True. + + Returns: + List, the list of untrainable weights. + """ + return list(filter(lambda x: not x.requires_grad, self.get_parameters(expand=True))) + + @property + def all_weights(self): + return list(filter(lambda x: x.requires_grad, self.get_parameters(expand=True))) \ + + list(filter(lambda x: not x.requires_grad, self.get_parameters(expand=True))) + + +class SequentialLayer(Module): + """ + Sequential layer container. + + A list of Layers will be added to it in the order they are passed in the constructor. + Alternatively, an ordered dict of layers can also be passed in. + + Args: + args (list, OrderedDict): List of subclass of Module. + + Raises: + TypeError: If the type of the argument is not list or OrderedDict. + + Inputs: + - **input** (Tensor) - Tensor with shape according to the first Module in the sequence. + + Outputs: + Tensor, the output Tensor with shape depending on the input and defined sequence of Layers. + + Examples: + >>> conv = tl.layers.Conv2d(3, 2, 3, pad_mode='valid') + >>> bn = tl.layers.BatchNorm2d(2) + >>> relu = tl.ReLU() + >>> seq = tl.layers.SequentialLayer([conv, bn, relu]) + >>> + >>> x = tl.layers.Input((1, 3, 4, 4)) + >>> seq(x) + """ + def __init__(self, *args): + super(SequentialLayer, self).__init__() + # self._built = True + if len(args) == 1: + layers = args[0] + if isinstance(layers, list): + for index, layer in enumerate(layers): + self.insert_child_to_layer(str(index), layer) + elif isinstance(layers, OrderedDict): + for name, layer in layers.items(): + self.insert_child_to_layer(name, layer) + else: + raise TypeError('Layers must be list or orderedDict') + else: + for index, layer in enumerate(args): + self.insert_child_to_layer(str(index), layer) + self.layer_list = list(self._layers.values()) + + def __getitem__(self, index): + if isinstance(index, slice): + return self.__class__( + OrderedDict(list(self._layers.items())[index])) + index = self._valid_index(len(self), index) + return list(self._layers.values())[index] + + def __setitem__(self, index, layer): + if self._valid_module(layer): + index = self._valid_index(len(self), index) + key = list(self._layers.keys())[index] + self._layers[key] = layer + self.layer_list = list(self._layers.values()) + + def __delitem__(self, index): + if isinstance(index, int): + index = self._valid_index(len(self), index) + key = list(self._layers.keys())[index] + del self._layers[key] + elif isinstance(index, slice): + keys = list(self._layers.keys())[index] + for key in keys: + del self._layers[key] + else: + raise TypeError('Index {} is not int type or slice type'.format(index)) + self.layer_list = list(self._layers.values()) + + def __len__(self): + return len(self._layers) + + def set_grad(self, flag=True): + self.requires_grad = flag + for layer in self._layers.values(): + layer.set_grad(flag) + + def append(self, layer): + if self._valid_module(layer): + self._layers[str(len(self))] = layer + self.layer_list = list(self._layers.values()) + return self + + def build(self, inputs_shape): + pass + + def forward(self, input_data): + for layer in self.layer_list: + input_data = layer(input_data) + return input_data + + def _valid_index(self, layer_num, index): + if not isinstance(index, int): + raise TypeError("Index {} is not int type") + if not -layer_num <= index < layer_num: + raise IndexError("Index should be a number in range [{}, {}), but got {}" + .format(-layer_num, layer_num, index)) + return index % layer_num + + def _valid_module(self, layer): + if issubclass(layer.__class__, Module): + return True + raise TypeError('Module {} is not subclass of Module'.format(layer)) + + +class LayerList(Module): + """ + The class :class:`LayerList` is a linear stack of layers. + + The :class:`LayerList` can be created by passing a list of layer instances. + The given layer instances will be automatically connected one by one. + + Parameters + ---------- + layers: list of Layer + A list of layers. + name : str or None + A unique layer name. If None, a unique name will be automatically assigned. + + Methods + --------- + __init__() + Initializing the LayerList. + weights() + A collection of weights of all the layer instances. + build() + Build the LayerList. The layer instances will be connected automatically one by one. + forward() + Forward the computation. The computation will go through all layer instances. + """ + + def __init__(self, layers, name=None): + """ + Initializing the LayerList given a list of Layer. + + :param layers: list of Layer + :param name: str or None + """ + + super(LayerList, self).__init__(name=name) + pass + + def __getitem__(self, idx): + pass + + def __len__(self): + return len(self.layers) + + def __repr__(self): + pass + + def forward(self, inputs): + pass + diff --git a/tensorlayer/layers/core/core_paddle.py b/tensorlayer/layers/core/core_paddle.py new file mode 100644 index 0000000..19b56ee --- /dev/null +++ b/tensorlayer/layers/core/core_paddle.py @@ -0,0 +1,206 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import copy, six +import tensorlayer as tl +from .common import str2act +from paddle.fluid import framework +from paddle.fluid.dygraph import Layer +from paddle.fluid.framework import in_dygraph_mode + + +_global_layer_name_dict = {} # TODO: better implementation? + + +class Module(Layer): + + def __init__(self, name=None, act=None, *args, **kwargs): + super().__init__(*args, **kwargs) + + global _global_layer_name_dict + if name is None: + prefix = self.__class__.__name__.lower() + + if _global_layer_name_dict.get(prefix) is not None: + _global_layer_name_dict[prefix] += 1 + name = prefix + '_' + str(_global_layer_name_dict[prefix]) + else: + _global_layer_name_dict[prefix] = 0 + name = prefix + while True: + if _global_layer_name_dict.get(name) is None: + break + _global_layer_name_dict[prefix] += 1 + name = prefix + '_' + str(_global_layer_name_dict[prefix]) + else: + if _global_layer_name_dict.get(name) is not None: + pass + else: + _global_layer_name_dict[name] = 0 + + self.name = name + + if isinstance(act, str): + str_act = str2act(act) + + if act: + if isinstance(act, str) and (len(act) > 5 and act[0:5] == "lrelu" or len(act) > 10 and act[0:10] == "leaky_relu"): + self.act = str_act + elif isinstance(act, str): + self.act = str_act() + else: + self.act = act() + else: + self.act = act + + # Layer building state + # self._built = False + + # Layer nodes state + self._nodes = [] + self._nodes_fixed = False + + # Layer weight state + self._all_weights = [] + self._trainable_weights = [] + self._nontrainable_weights = [] + + # Layer training state + self.is_train = True + + # layer forward state + self._forward_state = False + + def set_train(self): + """ + Sets this Layer and all its sublayers to training mode. + This only effects certain modules like `Dropout` and `BatchNorm`. + + Returns: + None + + Example:: + .. code-block:: python + + import paddle + + class MyLayer(paddle.nn.Layer): + def __init__(self): + super(MyLayer, self).__init__() + self._linear = paddle.nn.Linear(1, 1) + self._dropout = paddle.nn.Dropout(p=0.5) + + def forward(self, input): + temp = self._linear(input) + temp = self._dropout(temp) + return temp + + x = paddle.randn([10, 1], 'float32') + mylayer = MyLayer() + mylayer.eval() # set mylayer._dropout to eval mode + out = mylayer(x) + mylayer.train() # set mylayer._dropout to train mode + out = mylayer(x) + + """ + # global setting in dygraph + # NOTE(chenweihang): nn.Layer also can be used in static mode, + # but _dygraph_tracer() can not be called in static mode + if in_dygraph_mode(): + framework._dygraph_tracer().train_mode() + # Layer-level setting + self.training = True + for layer in self.sublayers(): + layer.training = True + + def set_eval(self): + """ + Sets this Layer and all its sublayers to evaluation mode. + This only effects certain modules like `Dropout` and `BatchNorm`. + + Returns: + None + + Example:: + .. code-block:: python + + import paddle + + class MyLayer(paddle.nn.Layer): + def __init__(self): + super(MyLayer, self).__init__() + self._linear = paddle.nn.Linear(1, 1) + self._dropout = paddle.nn.Dropout(p=0.5) + + def forward(self, input): + temp = self._linear(input) + temp = self._dropout(temp) + return temp + + x = paddle.randn([10, 1], 'float32') + mylayer = MyLayer() + mylayer.eval() # set mylayer._dropout to eval mode + out = mylayer(x) + print(out) + + """ + # global setting in dygraph + # NOTE(chenweihang): nn.Layer also can be used in static mode, + # but _dygraph_tracer() can not be called in static mode + if in_dygraph_mode(): + framework._dygraph_tracer().eval_mode() + # Layer-level setting + self.training = False + for layer in self.sublayers(): + layer.training = False + + def build(self, inputs_shape): + raise Exception("The build(self, inputs_shape) method must be implemented by inherited class") + + def forward(self, *inputs, **kwargs): + raise Exception("The forward method must be implemented by inherited class") + + + def _get_weights(self, var_name, shape, init=None, trainable=True): + if var_name in ["filters", "weights"]: + w_tmp = self.create_parameter(shape=shape, attr=init, is_bias=False) + elif var_name in ["biases"]: + w_tmp = self.create_parameter(shape=shape, attr=init, is_bias=True) + else: + w_tmp = self.create_parameter(shape=shape, attr=init) + self.trainable = trainable + return w_tmp + + def create_parameter(self, + shape, + attr=None, + dtype=None, + is_bias=False, + default_initializer=None): + """Create parameters for this layer.""" + temp_attr = copy.deepcopy(attr) + if isinstance(temp_attr, six.string_types) and temp_attr == "": + temp_attr = None + return self._helper.create_parameter(temp_attr, shape, dtype, is_bias, + default_initializer) + + @property + def all_weights(self): + ret = [ + param + for _, param in self.named_parameters( + include_sublayers=True) + ] + return ret + + @property + def trainable_weights(self): + return self.parameters() + + def init_build(self, *inputs, **kwargs): + """ + (1) This method must be called when the Layer has no input in_channels. + (2) Automatic shape inference when the user does not enter inchannels. + """ + + self.forward(*inputs, **kwargs) \ No newline at end of file diff --git a/tensorlayer/layers/core/core_tensorflow.py b/tensorlayer/layers/core/core_tensorflow.py new file mode 100644 index 0000000..0f70388 --- /dev/null +++ b/tensorlayer/layers/core/core_tensorflow.py @@ -0,0 +1,765 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +from .common import str2act, _save_weights, _load_weights +from collections import OrderedDict +import time +import tensorlayer as tl +import tensorflow as tf +from tensorlayer.layers.utils import (get_variable_with_initializer) +from tensorlayer import logging + +__all__ = ['Module', 'SequentialLayer', 'LayerList'] + +_global_layer_name_dict = {} +Parameter_ = tf.Variable + + +class Module(object): + + def __init__(self, name=None, act=None, *args, **kwargs): + self._params = OrderedDict() + self._layers = OrderedDict() + self._params_status = OrderedDict() + self._parameter_layout_dict = {} + self._create_time = int(time.time() * 1e9) + + global _global_layer_name_dict + if name is None: + prefix = self.__class__.__name__.lower() + + if _global_layer_name_dict.get(prefix) is not None: + _global_layer_name_dict[prefix] += 1 + name = prefix + '_' + str(_global_layer_name_dict[prefix]) + else: + _global_layer_name_dict[prefix] = 0 + name = prefix + while True: + if _global_layer_name_dict.get(name) is None: + break + _global_layer_name_dict[prefix] += 1 + name = prefix + '_' + str(_global_layer_name_dict[prefix]) + else: + if _global_layer_name_dict.get(name) is not None: + pass + else: + _global_layer_name_dict[name] = 0 + + self.name = name + + if isinstance(act, str): + str_act = str2act(act) + + if act: + if isinstance(act, str) and (len(act) > 5 and act[0:5] == "lrelu" or len(act) > 10 and act[0:10] == "leaky_relu"): + self.act = str_act + elif isinstance(act, str): + self.act = str_act() + else: + self.act = act() + else: + self.act = act + + # Layer building state + self._built = False + + # Layer nodes state + self._nodes = [] + self._nodes_fixed = False + + # Layer weight state + self._all_weights = [] + self._trainable_weights = [] + self._nontrainable_weights = [] + + # layer forward state + self._forward_state = False + + # Layer training state + self.is_train = True + + def extend_repr(self): + """ + Sets the extended representation of the Module. + + To print customized extended information, re-implement this method in your own Layers. + """ + return '' + + def __repr__(self): + extra_str = self.extend_repr() + info_str = self.__class__.__name__ + '<' + if self._layers: + sub_str = '\n' + if extra_str: + sub_str += '{}\n'.format(self.extend_repr()) + for key, value in self._layers.items(): + sub_str += '({}): {}\n'.format(key, repr(value)) + sub_str = sub_str.replace('\n', '\n ') + '>' + info_str += sub_str + else: + info_str += extra_str + '>' + return info_str + + def __setattr__(self, name, value): + layers = self.__dict__.get('_layers') + params = self.__dict__.get('_params') + + if isinstance(value, Parameter_): + if params is None: + raise AttributeError("Can not assign params before Module.__init__() call.") + if name in self.__dict__: + if self.__dict__[name] is not None: + raise TypeError("Expected type is not in (Parameter, Module), but got Parameter.") + del self.__dict__[name] + if layers and name in layers: + raise TypeError("Expected type is Module, but got Parameter.") + self.insert_param_to_layer(name, value) + + elif isinstance(value, Module): + if layers is None: + raise AttributeError("Can not assign layers before Module.__init__() call.") + if name in self.__dict__: + del self.__dict__[name] + if params and name in params: + raise TypeError("Expected type is Parameter, but got Module.") + # TODO How to prompt the user, enter the in_channels. + # TODO Automatic shape inference when the user does not enter inchannels. + # if value._built is False: + # raise AttributeError( + # "The registered layer `{}` should be built in advance. " + # "Do you forget to pass the keyword argument 'in_channels'? ".format(value.name) + # ) + layers[name] = value + else: + object.__setattr__(self, name, value) + + def __call__(self, inputs, *args, **kwargs): + + output = self.forward(inputs, *args, **kwargs) + + return output + + def forward(self, *inputs, **kwargs): + raise Exception("The forward method must be implemented by inherited class") + + def build(self, inputs_shape): + raise Exception("The build(self, inputs_shape) method must be implemented by inherited class") + + def _get_weights(self, var_name, shape, init=tl.initializers.random_normal(), trainable=True): + """ Get trainable variables. """ + weight = get_variable_with_initializer( + scope_name=self.name, var_name=var_name, shape=shape, init=init, trainable=trainable + ) + self.trainable = trainable + return weight + + def save_weights(self, file_path, format=None): + """Input file_path, save model weights into a file of given format.""" + _save_weights(self, file_path, format) + + def load_weights(self, file_path, format=None, in_order=True, skip=False): + """Load model weights from a given file, which should be previously saved by self.save_weights().""" + _load_weights(self, file_path, format, in_order, skip) + + def _set_mode_for_layers(self, is_train): + """Set all layers of this network to a given mode. + + Parameters + ---------- + is_train : boolean + Network's mode. True means training mode while False means evaluation mode. + + """ + layers = self.layers_and_names(name_prefix='') + for layer_name, layer in layers: + if isinstance(layer, Module): + layer.is_train = is_train + + + def set_train(self): + """Set this network in training mode. After calling this method, + all layers in network are in training mode, in particular, BatchNorm, Dropout, etc. + TODO It is not possible to modify the parameter state after initialization, and a better way needs to be found. + Examples + -------- + >>> import tensorlayer as tl + >>> net = tl.vgg16() + >>> net.set_train() + + """ + if self.is_train !=True: + self.is_train = True + self._set_mode_for_layers(True) + + def set_eval(self): + """Set this network in evaluation mode. After calling this method, + all layers in network are in evaluation mode, in particular, BatchNorm, Dropout, etc. + TODO It is not possible to modify the parameter state after initialization, and a better way needs to be found. + Examples + -------- + >>> import tensorlayer as tl + >>> net = tl.vgg16() + >>> net.eval() + # do evaluation + + """ + if self.is_train != False: + self.is_train = False + self._set_mode_for_layers(False) + + def test(self): + """Set this network in evaluation mode.""" + self.eval() + + def infer(self): + """Set this network in evaluation mode.""" + self.eval() + + @staticmethod + def _compute_shape(tensors): + if isinstance(tensors, list): + shape_mem = [tl.get_tensor_shape(t) for t in tensors] + else: + shape_mem = tl.get_tensor_shape(tensors) + return shape_mem + + def insert_param_to_layer(self, param_name, param, check_name=True): + """ + Adds a parameter to the current layer. + + Inserts a parameter with given name to the layer. Please refer to the usage in + source code of `tensorlayer.layer.Module.__setattr__`. + + Args: + param_name (str): Name of the parameter. + param (Parameter): Parameter to be inserted to the layer. + check_name (bool): Determines whether the name input is compatible. Default: True. + + Raises: + KeyError: If the name of parameter is null or contains dot. + AttributeError: If user did not call init() first. + TypeError: If the type of parameter is not Parameter_. + """ + if not param_name: + raise KeyError("The name of parameter should not be null.") + if check_name and '.' in param_name: + raise KeyError("The name of parameter should not contain \".\"") + if '_params' not in self.__dict__: + raise AttributeError("You need call init() first.") + if hasattr(self, param_name) and param_name not in self._params: + raise KeyError("Duplicated parameter name '{}'.".format(param_name)) + if not isinstance(param, Parameter_) and param is not None: + raise TypeError("The type of parameter should be 'Parameter' if not None.") + self._params[param_name] = param + try: + self._params_status[param_name] = self.trainable + except: + pass + + def _add_node(self, input_tensors, output_tensors): + """Add a LayerNode for this layer given input_tensors, output_tensors. + + WARINING: This function should not be called from outside, it should only be called + in layer.__call__ when building static model. + + Parameters + ---------- + input_tensors : Tensor or a list of tensors + Input tensors to this layer. + output_tensors : Tensor or a list of tensors + Output tensors to this layer. + + """ + raise NotImplementedError + + @property + def create_time(self): + return self._create_time + + def __getattr__(self, name): + if '_params' in self.__dict__: + params = self.__dict__['_params'] + if name in params: + return params[name] + if '_layers' in self.__dict__: + layers = self.__dict__['_layers'] + if name in layers: + return layers[name] + if '_params_status' in self.__dict__: + params_status = self.__dict__['_params_status'] + if name in params_status: + return params_status[name] + raise AttributeError("'{}' object has no attribute '{}'.".format(type(self).__name__, name)) + + def __delattr__(self, name): + if name in self._params: + del self._params[name] + elif name in self._layers: + del self._layers[name] + else: + object.__delattr__(self, name) + + @property + def trainable_weights(self): + """ + Returns all trainable weights. + + Returns a list of all trainable parmeters. + + Args: + recurse (bool): Whether contains the trainable weights of sublayers. Default: True. + + Returns: + List, the list of trainable weights. + """ + self.get_weights() + layers = self.layers_and_names(name_prefix='') + for layer_name, layer in layers: + params = layer._params.items() + params_status = layer._params_status.items() + params_zip = zip(params, params_status) + for params, params_status in params_zip: + if params_status[1] ==True: + self._trainable_weights.append(params[1]) + return self._trainable_weights + + @property + def nontrainable_weights(self): + """ + Returns all untrainable weights. + + Returns a list of all untrainable weights. + + Args: + recurse (bool): Whether contains the untrainable weights of sublayers. Default: True. + + Returns: + List, the list of untrainable weights. + """ + layers = self.layers_and_names(name_prefix='') + for layer_name, layer in layers: + params = layer._params.items() + params_status = layer._params_status.items() + params_zip = zip(params, params_status) + for params, params_status in params_zip: + if params_status[1] == False: + self._nontrainable_weights.append(params[1]) + return self._nontrainable_weights + + @property + def all_weights(self): + layers = self.layers_and_names(name_prefix='') + for layer_name, layer in layers: + params = layer._params.items() + for par, val in params: + self._all_weights.append(val) + return self._all_weights + + def get_weights(self, expand=True): + """ + Returns an iterator over layer weights. + + Yields weights of this layer. If `expand` is True, yield parameters of this layer and all sublayers. + + Args: + expand (bool): If True, yields parameters of this layer and all sublayers. Otherwise, yields only parameters + that are direct members of this layer. Default: True. + + Examples: + >>> net = Net() + >>> for item in net.get_weights(): + >>> print(item) + """ + for _, param in self.parameters_and_names(expand=expand): + yield param + + def check_names(self): + names = set("") + for value, param in self.parameters_and_names(): + if param.name in names: + raise ValueError( + "The value of {} is {}, its name '{}' already exists.".format(value, param, param.name) + ) + names.add(param.name) + + def insert_child_to_layer(self, child_name, child): + """ + Adds a child layer to the current layer. + + Args: + child_name (str): Name of the child layer. + child (Module): The child layer to be inserted. + + Raises: + KeyError: Child Module's name is incorrect or duplicated with the other child name. + TypeError: Child Module's type is incorrect. + """ + if not child_name or '.' in child_name: + raise KeyError("Child layer name is incorrect.") + if hasattr(self, child_name) and child_name not in self._layers: + raise KeyError("Duplicate child name '{}'.".format(child_name)) + if not isinstance(child, Module) and child is not None: + raise TypeError("Child layer type is incorrect.") + self._layers[child_name] = child + + def parameters_and_names(self, name_prefix='', expand=True): + """ + Returns an iterator over layer parameters. + + Includes the parameter's name and itself. + + Args: + name_prefix (str): Namespace. Default: ''. + expand (bool): If True, yields parameters of this layer and all sublayers. Otherwise, yields only parameters + that are direct members of this layer. Default: True. + + Examples: + >>> n = Net() + >>> names = [] + >>> for m in n.parameters_and_names(): + >>> if m[0]: + >>> names.append(m[0]) + """ + layers = [] + if expand: + layers = self.layers_and_names(name_prefix=name_prefix) + else: + layers.append((name_prefix, self)) + + params_set = set() + for layer_name, layer in layers: + params = layer._params.items() + for par_name, par in params: + if par.inited_param is not None: + par = par.inited_param + if par is not None and id(par) not in params_set: + params_set.add(id(par)) + par_new_name = par_name + if layer_name: + par_new_name = layer_name + '.' + par_new_name + + yield par_new_name, par + + def layers_and_names(self, layers=None, name_prefix=''): + """ + Returns an iterator over all layers in the network. + + Includes the layer's name and itself. + + Args: + layers (str): layers to iterate over. Default: None. + name_prefix (str): Namespace. Default: ''. + + Examples: + >>> n = Net() + >>> names = [] + >>> for m in n.layers_and_names(): + >>> if m[0]: + >>> names.append(m[0]) + """ + t_layers = layers if layers else set() + if self in t_layers: + return + + t_layers.add(self) + yield name_prefix, self + + for name, layer in self._layers.items(): + if layer: + layers_name_prefix = name + if name_prefix: + layers_name_prefix = name_prefix + '.' + layers_name_prefix + for ele in layer.layers_and_names(t_layers, layers_name_prefix): + yield ele + + def layers(self): + """Returns an iterator over immediate layers.""" + return self.name_layers().values() + + def name_layers(self): + """ + Returns an iterator over all layers in the network. + + Include name of the layer and layer itself. + """ + value_set = set() + layers = OrderedDict() + for name, layer in self._layers.items(): + if layer is not None and layer not in value_set: + value_set.add(layer) + layers[name] = layer + return layers + + def init_build(self, *inputs, **kwargs): + """ + (1) This method must be called when the Layer has no input in_channels. + (2) Automatic shape inference when the user does not enter inchannels. + """ + + self.forward(*inputs, **kwargs) + + +class SequentialLayer(Module): + """ + Sequential layer container. + + A list of Layers will be added to it in the order they are passed in the constructor. + Alternatively, an ordered dict of layers can also be passed in. + + Args: + args (list, OrderedDict): List of subclass of Module. + + Raises: + TypeError: If the type of the argument is not list or OrderedDict. + + Inputs: + - **input** (Tensor) - Tensor with shape according to the first Module in the sequence. + + Outputs: + Tensor, the output Tensor with shape depending on the input and defined sequence of Layers. + + Examples: + >>> conv = tl.layers.Conv2d(3, 2, 3, pad_mode='valid') + >>> bn = tl.layers.BatchNorm2d(2) + >>> seq = tl.layers.SequentialLayer([conv, bn]) + >>> + >>> x = tl.layers.Input((1, 3, 4, 4)) + >>> seq(x) + """ + def __init__(self, *args): + super(SequentialLayer, self).__init__() + self._built = True + if len(args) == 1: + layers = args[0] + if isinstance(layers, list): + for index, layer in enumerate(layers): + self.insert_child_to_layer(str(index), layer) + elif isinstance(layers, OrderedDict): + for name, layer in layers.items(): + self.insert_child_to_layer(name, layer) + else: + raise TypeError('Layers must be list or orderedDict') + else: + for index, layer in enumerate(args): + self.insert_child_to_layer(str(index), layer) + self.layer_list = list(self._layers.values()) + + def __getitem__(self, index): + if isinstance(index, slice): + return self.__class__( + OrderedDict(list(self._layers.items())[index])) + index = self._valid_index(len(self), index) + return list(self._layers.values())[index] + + def __setitem__(self, index, layer): + if self._valid_module(layer): + index = self._valid_index(len(self), index) + key = list(self._layers.keys())[index] + self._layers[key] = layer + self.layer_list = list(self._layers.values()) + + def __delitem__(self, index): + if isinstance(index, int): + index = self._valid_index(len(self), index) + key = list(self._layers.keys())[index] + del self._layers[key] + elif isinstance(index, slice): + keys = list(self._layers.keys())[index] + for key in keys: + del self._layers[key] + else: + raise TypeError('Index {} is not int type or slice type'.format(index)) + self.layer_list = list(self._layers.values()) + + def __len__(self): + return len(self._layers) + + + def append(self, layer): + if self._valid_module(layer): + self._layers[str(len(self))] = layer + self.layer_list = list(self._layers.values()) + return self + + def build(self, inputs_shape): + pass + + def forward(self, input_data): + for layer in self.layer_list: + input_data = layer(input_data) + return input_data + + def _valid_index(self, layer_num, index): + if not isinstance(index, int): + raise TypeError("Index {} is not int type") + if not -layer_num <= index < layer_num: + raise IndexError("Index should be a number in range [{}, {}), but got {}" + .format(-layer_num, layer_num, index)) + return index % layer_num + + def _valid_module(self, layer): + if issubclass(layer.__class__, Module): + return True + raise TypeError('Module {} is not subclass of Module'.format(layer)) + + +class LayerList(Module): + """ + The class :class:`LayerList` is a linear stack of layers. + + The :class:`LayerList` can be created by passing a list of layer instances. + The given layer instances will be automatically connected one by one. + + Parameters + ---------- + layers: list of Layer + A list of layers. + name : str or None + A unique layer name. If None, a unique name will be automatically assigned. + + Methods + --------- + __init__() + Initializing the LayerList. + weights() + A collection of weights of all the layer instances. + build() + Build the LayerList. The layer instances will be connected automatically one by one. + forward() + Forward the computation. The computation will go through all layer instances. + """ + + def __init__(self, layers, name=None): + """ + Initializing the LayerList given a list of Layer. + + :param layers: list of Layer + :param name: str or None + """ + + super(LayerList, self).__init__(name=name) + self.layers = layers + is_built = True + for layer in self.layers: + self._trainable_weights.extend(layer.trainable_weights) + self._nontrainable_weights.extend(layer.nontrainable_weights) + if layer._built is False: + is_built = False + # if layer._built and layer.all_weights is not None: + # # some layers in the list passed in have already been built + # # e.g. using input shape to construct layers in dynamic eager + # if self._all_weights is None: + # self._all_weights = list() + # self._all_weights.extend(layer.all_weights) + if is_built: + self._built = True + + logging.info( + "LayerList %s including layers [%s]" % (self.name, ', '.join([layer.name for layer in self.layers])) + ) + + # check layer name uniqueness in LayerList + local_layer_name_set = set() + for layer in self.layers: + if layer.name not in local_layer_name_set: + local_layer_name_set.add(layer.name) + else: + raise ValueError( + 'Layer name \'%s\' has already been used by another layer. Please change the layer name.' % + layer.name + ) + + def __getitem__(self, idx): + if isinstance(idx, slice): + return LayerList(list(self.layers)[idx]) + else: + return self.layers[idx] + + def __len__(self): + return len(self.layers) + + def __repr__(self): + tmpstr = 'LayerList' + '(\n' + for idx, layer in enumerate(self.layers): + modstr = layer.__repr__() + modstr = _addindent(modstr, 2) + tmpstr = tmpstr + ' (' + str(idx) + '): ' + modstr + '\n' + + tmpstr = tmpstr + ')' + return tmpstr + + @property + def trainable_weights(self): + return self._trainable_weights + + @property + def nontrainable_weights(self): + return self._nontrainable_weights + + @property + def all_weights(self): + return self._trainable_weights + self._nontrainable_weights + + # def build(self, inputs_shape): + # """ + # Build the LayerList. The layer instances will be connected automatically one by one. + # """ + # in_tensor = self._input_tensors + # # in_layer = self._input_layer + # for layer in self.layers: + # is_build = layer._built + # out_tensor = layer(in_tensor) + # # nlayer = layer(in_layer) + # if is_build is False and layer.all_weights is not None: + # if self._all_weights is None: + # self._all_weights = list() + # self._all_weights.extend(layer.all_weights) + # layer._built = True + # in_tensor = out_tensor + # # in_layer = nlayer + + def forward(self, inputs): + """ + Forward the computation. The computation will go through all layer instances. + """ + z = inputs + for layer in self.layers: + z = layer.forward(z) + return z + + def _set_mode_for_layers(self, is_train): + """Set training/evaluation mode for all layer instances.""" + self.is_train = is_train + for layer in self.layers: + if isinstance(layer, LayerList): + layer._set_mode_for_layers(is_train) + else: + layer.is_train = is_train + + def get_args(self): + init_args = {} + layers = self.layer_args["layers"] + init_args["layers"] = [layer.config for layer in layers] + init_args.update({"layer_type": "layerlist"}) + return init_args + +def tolist(tensors): + if isinstance(tensors, list) or isinstance(tensors, tuple): + ntensors = list() + for t in tensors: + ntensors += tolist(t) + return ntensors + else: + return [tensors] + +def _addindent(s_, numSpaces): + s = s_.split('\n') + # don't do anything for single-line stuff + if len(s) == 1: + return s_ + first = s.pop(0) + s = [(numSpaces * ' ') + line for line in s] + s = '\n'.join(s) + s = first + '\n' + s + return s \ No newline at end of file diff --git a/tensorlayer/layers/dense/__init__.py b/tensorlayer/layers/dense/__init__.py new file mode 100644 index 0000000..2291b94 --- /dev/null +++ b/tensorlayer/layers/dense/__init__.py @@ -0,0 +1,28 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +""" +TensorLayer provides rich layer implementations trailed for +various benchmarks and domain-specific problems. In addition, we also +support transparent access to native TensorFlow parameters. +For example, we provide not only layers for local response normalization, but also +layers that allow user to apply ``tf.ops.lrn`` on ``network.outputs``. +More functions can be found in `TensorFlow API `__. +""" + +from .base_dense import * +from .binary_dense import * +from .dorefa_dense import * +from .dropconnect import * +from .quan_dense import * +from .quan_dense_bn import * +from .ternary_dense import * + +__all__ = [ + 'BinaryDense', + 'Dense', + 'DorefaDense', + 'DropconnectDense', + 'TernaryDense', + 'QuanDense', + 'QuanDenseWithBN', +] diff --git a/tensorlayer/layers/dense/base_dense.py b/tensorlayer/layers/dense/base_dense.py new file mode 100644 index 0000000..a047030 --- /dev/null +++ b/tensorlayer/layers/dense/base_dense.py @@ -0,0 +1,122 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module + +__all__ = [ + 'Dense', +] + + +class Dense(Module): + """The :class:`Dense` class is a fully connected layer. + + Parameters + ---------- + n_units : int + The number of units of this layer. + act : activation function + The activation function of this layer. + W_init : initializer + The initializer for the weight matrix. + b_init : initializer or None + The initializer for the bias vector. If None, skip biases. + in_channels: int + The number of channels of the previous layer. + If None, it will be automatically detected when the layer is forwarded for the first time. + name : None or str + A unique layer name. If None, a unique name will be automatically generated. + + Examples + -------- + With TensorLayer + + >>> net = tl.layers.Input([100, 50], name='input') + >>> dense = tl.layers.Dense(n_units=800, act=tl.ops.relu, in_channels=50, name='dense_1') + >>> print(dense) + Dense(n_units=800, relu, in_channels='50', name='dense_1') + >>> tensor = tl.layers.Dense(n_units=800, act=tl.ops.relu, name='dense_2')(net) + >>> print(tensor) + tf.Tensor([...], shape=(100, 800), dtype=float32) + + Notes + ----- + If the layer input has more than two axes, it needs to be flatten by using :class:`Flatten`. + + """ + + # @cell_attr_register + def __init__( + self, + n_units, + act=None, + W_init=tl.initializers.truncated_normal(stddev=0.05), + b_init=tl.initializers.constant(value=0.0), + in_channels=None, + name=None, # 'dense', + ): + + super(Dense, self).__init__(name, act=act) + + self.n_units = n_units + self.W_init = W_init + self.b_init = b_init + self.in_channels = in_channels + + if self.in_channels is not None: + self.build(self.in_channels) + self._built = True + + logging.info( + "Dense %s: %d %s" % + (self.name, self.n_units, self.act.__class__.__name__ if self.act is not None else 'No Activation') + ) + + def __repr__(self): + actstr = self.act.__class__.__name__ if self.act is not None else 'No Activation' + s = ('{classname}(n_units={n_units}, ' + actstr) + if self.in_channels is not None: + s += ', in_channels=\'{in_channels}\'' + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + if self.in_channels is None and len(inputs_shape) != 2: + raise AssertionError("The input dimension must be rank 2, please reshape or flatten it") + if self.in_channels: + shape = [self.in_channels, self.n_units] + else: + self.in_channels = inputs_shape[1] + shape = [inputs_shape[1], self.n_units] + + self.W = self._get_weights("weights", shape=tuple(shape), init=self.W_init) + + self.b_init_flag = False + if self.b_init: + self.b = self._get_weights("biases", shape=(self.n_units, ), init=self.b_init) + self.b_init_flag = True + self.bias_add = tl.ops.BiasAdd() + + self.act_init_flag = False + if self.act: + self.act_init_flag = True + + self.matmul = tl.ops.MatMul() + + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + z = self.matmul(inputs, self.W) + if self.b_init_flag: + z = self.bias_add(z, self.b) + if self.act_init_flag: + z = self.act(z) + return z diff --git a/tensorlayer/layers/dense/binary_dense.py b/tensorlayer/layers/dense/binary_dense.py new file mode 100644 index 0000000..90c6e2b --- /dev/null +++ b/tensorlayer/layers/dense/binary_dense.py @@ -0,0 +1,109 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module +from tensorlayer.layers.utils import quantize + +__all__ = [ + 'BinaryDense', +] + + +class BinaryDense(Module): + """The :class:`BinaryDense` class is a binary fully connected layer, which weights are either -1 or 1 while inferencing. + + Note that, the bias vector would not be binarized. + + Parameters + ---------- + n_units : int + The number of units of this layer. + act : activation function + The activation function of this layer, usually set to ``tf.act.sign`` or apply :class:`Sign` after :class:`BatchNorm`. + use_gemm : boolean + If True, use gemm instead of ``tf.matmul`` for inference. (TODO). + W_init : initializer + The initializer for the weight matrix. + b_init : initializer or None + The initializer for the bias vector. If None, skip biases. + in_channels: int + The number of channels of the previous layer. + If None, it will be automatically detected when the layer is forwarded for the first time. + name : None or str + A unique layer name. + + """ + + def __init__( + self, + n_units=100, + act=None, + use_gemm=False, + W_init=tl.initializers.truncated_normal(stddev=0.05), + b_init=tl.initializers.constant(value=0.0), + in_channels=None, + name=None, + ): + super().__init__(name, act=act) + self.n_units = n_units + self.use_gemm = use_gemm + self.W_init = W_init + self.b_init = b_init + self.in_channels = in_channels + + if self.in_channels is not None: + self.build((None, self.in_channels)) + self._built = True + + logging.info( + "BinaryDense %s: %d %s" % + (self.name, n_units, self.act.__class__.__name__ if self.act is not None else 'No Activation') + ) + + def __repr__(self): + actstr = self.act.__class__.__name__ if self.act is not None else 'No Activation' + s = ('{classname}(n_units={n_units}, ' + actstr) + if self.in_channels is not None: + s += ', in_channels=\'{in_channels}\'' + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + if len(inputs_shape) != 2: + raise Exception("The input dimension must be rank 2, please reshape or flatten it") + + if self.in_channels is None: + self.in_channels = inputs_shape[1] + + if self.use_gemm: + raise Exception("TODO. The current version use tf.matmul for inferencing.") + + n_in = inputs_shape[-1] + self.W = self._get_weights("weights", shape=(n_in, self.n_units), init=self.W_init) + if self.b_init is not None: + self.b = self._get_weights("biases", shape=(self.n_units), init=self.b_init) + self.bias_add = tl.ops.BiasAdd() + + self.matmul = tl.ops.MatMul() + + + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + W_ = quantize(self.W) + outputs = self.matmul(inputs, W_) + + if self.b_init is not None: + outputs = self.bias_add(outputs, self.b) + + if self.act: + outputs = self.act(outputs) + return outputs diff --git a/tensorlayer/layers/dense/dorefa_dense.py b/tensorlayer/layers/dense/dorefa_dense.py new file mode 100644 index 0000000..bf35c14 --- /dev/null +++ b/tensorlayer/layers/dense/dorefa_dense.py @@ -0,0 +1,116 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module +from tensorlayer.layers.utils import cabs, quantize_active, quantize_weight + +__all__ = [ + 'DorefaDense', +] + + +class DorefaDense(Module): + """The :class:`DorefaDense` class is a binary fully connected layer, which weights are 'bitW' bits and the output of the previous layer + are 'bitA' bits while inferencing. + + Note that, the bias vector would not be binarized. + + Parameters + ---------- + bitW : int + The bits of this layer's parameter + bitA : int + The bits of the output of previous layer + n_units : int + The number of units of this layer. + act : activation function + The activation function of this layer, usually set to ``tf.act.sign`` or apply :class:`Sign` after :class:`BatchNorm`. + use_gemm : boolean + If True, use gemm instead of ``tf.matmul`` for inferencing. (TODO). + W_init : initializer + The initializer for the weight matrix. + b_init : initializer or None + The initializer for the bias vector. If None, skip biases. + in_channels: int + The number of channels of the previous layer. + If None, it will be automatically detected when the layer is forwarded for the first time. + name : a str + A unique layer name. + + """ + + def __init__( + self, + bitW=1, + bitA=3, + n_units=100, + act=None, + use_gemm=False, + W_init=tl.initializers.truncated_normal(stddev=0.05), + b_init=tl.initializers.constant(value=0.0), + in_channels=None, + name=None, #'dorefa_dense', + ): + super().__init__(name, act=act) + self.bitW = bitW + self.bitA = bitA + self.n_units = n_units + self.use_gemm = use_gemm + self.W_init = W_init + self.b_init = b_init + self.in_channels = in_channels + + if self.in_channels is not None: + self.build((None, self.in_channels)) + self._built = True + + logging.info( + "DorefaDense %s: %d %s" % + (self.name, n_units, self.act.__name__ if self.act is not None else 'No Activation') + ) + + def __repr__(self): + actstr = self.act.__name__ if self.act is not None else 'No Activation' + s = ('{classname}(n_units={n_units}, ' + actstr) + s += ', bitW={bitW}, bitA={bitA}' + if self.in_channels is not None: + s += ', in_channels=\'{in_channels}\'' + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + if len(inputs_shape) != 2: + raise Exception("The input dimension must be rank 2, please reshape or flatten it") + + if self.in_channels is None: + self.in_channels = inputs_shape[1] + + if self.use_gemm: + raise Exception("TODO. The current version use tf.matmul for inferencing.") + + n_in = inputs_shape[-1] + self.W = self._get_weights("weights", shape=(n_in, self.n_units), init=self.W_init) + if self.b_init is not None: + self.b = self._get_weights("biases", shape=(self.n_units), init=self.b_init) + self.bias_add = tl.ops.BiasAdd() + self.matmul = tl.ops.MatMul() + + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + inputs = quantize_active(cabs(inputs), self.bitA) + W_ = quantize_weight(self.W, self.bitW) + outputs = self.matmul(inputs, W_) + if self.b_init is not None: + outputs = self.bias_add(outputs, self.b) + if self.act: + outputs = self.act(outputs) + return outputs \ No newline at end of file diff --git a/tensorlayer/layers/dense/dropconnect.py b/tensorlayer/layers/dense/dropconnect.py new file mode 100644 index 0000000..178ea2c --- /dev/null +++ b/tensorlayer/layers/dense/dropconnect.py @@ -0,0 +1,125 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import numbers +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module + +__all__ = [ + 'DropconnectDense', +] + + +class DropconnectDense(Module): + """ + The :class:`DropconnectDense` class is :class:`Dense` with DropConnect + behaviour which randomly removes connections between this layer and the previous + layer according to a keeping probability. + + Parameters + ---------- + keep : float + The keeping probability. + The lower the probability it is, the more activations are set to zero. + n_units : int + The number of units of this layer. + act : activation function + The activation function of this layer. + W_init : weights initializer + The initializer for the weight matrix. + b_init : biases initializer + The initializer for the bias vector. + in_channels: int + The number of channels of the previous layer. + If None, it will be automatically detected when the layer is forwarded for the first time. + name : str + A unique layer name. + + Examples + -------- + >>> net = tl.layers.Input([None, 784], name='input') + >>> net = tl.layers.DropconnectDense(keep=0.8, + ... n_units=800, act=tl.ReLU, name='relu1')(net) + >>> net = tl.layers.DropconnectDense(keep=0.5, + ... n_units=800, act=tl.ReLU, name='relu2')(net) + >>> net = tl.layers.DropconnectDense(keep=0.5, + ... n_units=10, name='output')(net) + + References + ---------- + - `Wan, L. (2013). Regularization of neural networks using dropconnect `__ + + """ + + def __init__( + self, + keep=0.5, + n_units=100, + act=None, + W_init=tl.initializers.truncated_normal(stddev=0.05), + b_init=tl.initializers.constant(value=0.0), + in_channels=None, + name=None, # 'dropconnect', + ): + super().__init__(name, act=act) + + if isinstance(keep, numbers.Real) and not (keep > 0 and keep <= 1): + raise ValueError("keep must be a scalar tensor or a float in the " "range (0, 1], got %g" % keep) + + self.keep = keep + self.n_units = n_units + self.W_init = W_init + self.b_init = b_init + self.in_channels = in_channels + + if self.in_channels is not None: + self.build((None, self.in_channels)) + self._built = True + + logging.info( + "DropconnectDense %s: %d %s" % + (self.name, n_units, self.act.__class__.__name__ if self.act is not None else 'No Activation') + ) + + def __repr__(self): + actstr = self.act.__name__ if self.act is not None else 'No Activation' + s = ('{classname}(n_units={n_units}, ' + actstr) + s += ', keep={keep}' + if self.in_channels is not None: + s += ', in_channels=\'{in_channels}\'' + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + if len(inputs_shape) != 2: + raise Exception("The input dimension must be rank 2") + + if self.in_channels is None: + self.in_channels = inputs_shape[1] + + n_in = inputs_shape[-1] + self.W = self._get_weights("weights", shape=(n_in, self.n_units), init=self.W_init) + if self.b_init: + self.b = self._get_weights("biases", shape=(self.n_units), init=self.b_init) + + self.dropout = tl.ops.Dropout(keep=self.keep) + self.matmul = tl.ops.MatMul() + self.bias_add = tl.ops.BiasAdd() + + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + W_dropcon = self.dropout(self.W) + outputs = self.matmul(inputs, W_dropcon) + if self.b_init: + outputs = self.bias_add(outputs, self.b) + if self.act: + outputs = self.act(outputs) + return outputs diff --git a/tensorlayer/layers/dense/quan_dense.py b/tensorlayer/layers/dense/quan_dense.py new file mode 100644 index 0000000..4604023 --- /dev/null +++ b/tensorlayer/layers/dense/quan_dense.py @@ -0,0 +1,119 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module +from tensorlayer.layers.utils import (quantize_active_overflow, quantize_weight_overflow) + +__all__ = [ + 'QuanDense', +] + + +class QuanDense(Module): + """The :class:`QuanDense` class is a quantized fully connected layer with BN, which weights are 'bitW' bits and the output of the previous layer + are 'bitA' bits while inferencing. + + Parameters + ---------- + n_units : int + The number of units of this layer. + act : activation function + The activation function of this layer. + bitW : int + The bits of this layer's parameter + bitA : int + The bits of the output of previous layer + use_gemm : boolean + If True, use gemm instead of ``tf.matmul`` for inference. (TODO). + W_init : initializer + The initializer for the weight matrix. + b_init : initializer or None + The initializer for the bias vector. If None, skip biases. + in_channels: int + The number of channels of the previous layer. + If None, it will be automatically detected when the layer is forwarded for the first time. + name : None or str + A unique layer name. + + """ + + def __init__( + self, + n_units=100, + act=None, + bitW=8, + bitA=8, + use_gemm=False, + W_init=tl.initializers.truncated_normal(stddev=0.05), + b_init=tl.initializers.constant(value=0.0), + in_channels=None, + name=None, #'quan_dense', + ): + super().__init__(name, act=act) + self.n_units = n_units + self.bitW = bitW + self.bitA = bitA + self.use_gemm = use_gemm + self.W_init = W_init + self.b_init = b_init + self.in_channels = in_channels + + if self.in_channels is not None: + self.build((None, self.in_channels)) + self._built = True + + logging.info( + "QuanDense %s: %d %s" % + (self.name, n_units, self.act.__name__ if self.act is not None else 'No Activation') + ) + + def __repr__(self): + actstr = self.act.__name__ if self.act is not None else 'No Activation' + s = ('{classname}(n_units={n_units}, ' + actstr) + s += ', bitW={bitW}, bitA={bitA}' + if self.in_channels is not None: + s += ', in_channels=\'{in_channels}\'' + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + if len(inputs_shape) != 2: + raise Exception("The input dimension must be rank 2, please reshape or flatten it") + + if self.in_channels is None: + self.in_channels = inputs_shape[1] + + if self.use_gemm: + raise Exception("TODO. The current version use tf.matmul for inferencing.") + + n_in = inputs_shape[-1] + self.W = self._get_weights("weights", shape=(n_in, self.n_units), init=self.W_init) + if self.b_init is not None: + self.b = self._get_weights("biases", shape=int(self.n_units), init=self.b_init) + self.bias_add = tl.ops.BiasAdd() + + self.matmul = tl.ops.MatMul() + + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + inputs = quantize_active_overflow(inputs, self.bitA) + + W_ = quantize_weight_overflow(self.W, self.bitW) + + # outputs = tf.matmul(inputs, self.W) + outputs = self.matmul(inputs, W_) # hao dong change to this + + if self.b_init is not None: + outputs = self.bias_add(outputs, self.b) + if self.act: + outputs = self.act(outputs) + return outputs diff --git a/tensorlayer/layers/dense/quan_dense_bn.py b/tensorlayer/layers/dense/quan_dense_bn.py new file mode 100644 index 0000000..3f811a2 --- /dev/null +++ b/tensorlayer/layers/dense/quan_dense_bn.py @@ -0,0 +1,188 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + + + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module +from tensorflow.python.training import moving_averages +from tensorlayer.layers.utils import (quantize_active_overflow, quantize_weight_overflow, + mean_var_with_update, w_fold, bias_fold) + +__all__ = [ + 'QuanDenseWithBN', +] + + +class QuanDenseWithBN(Module): + """The :class:`QuanDenseWithBN` class is a quantized fully connected layer with BN, which weights are 'bitW' bits and the output of the previous layer + are 'bitA' bits while inferencing. + # TODO The QuanDenseWithBN only supports TensorFlow backend. + Parameters + ---------- + n_units : int + The number of units of this layer. + act : activation function + The activation function of this layer. + decay : float + A decay factor for `ExponentialMovingAverage`. + Suggest to use a large value for large dataset. + epsilon : float + Eplison. + is_train : boolean + Is being used for training or inference. + beta_init : initializer or None + The initializer for initializing beta, if None, skip beta. + Usually you should not skip beta unless you know what happened. + gamma_init : initializer or None + The initializer for initializing gamma, if None, skip gamma. + bitW : int + The bits of this layer's parameter + bitA : int + The bits of the output of previous layer + use_gemm : boolean + If True, use gemm instead of ``tf.matmul`` for inferencing. (TODO). + W_init : initializer + The initializer for the the weight matrix. + W_init_args : dictionary + The arguments for the weight matrix initializer. + in_channels: int + The number of channels of the previous layer. + If None, it will be automatically detected when the layer is forwarded for the first time. + name : a str + A unique layer name. + + Examples + --------- + >>> import tensorlayer as tl + >>> net = tl.layers.Input([50, 256]) + >>> layer = tl.layers.QuanDenseWithBN(128, act='relu', name='qdbn1')(net) + >>> net = tl.layers.QuanDenseWithBN(256, act='relu', name='qdbn2')(net) + """ + + def __init__( + self, + n_units=100, + act=None, + decay=0.9, + epsilon=1e-5, + is_train=False, + bitW=8, + bitA=8, + gamma_init=tl.initializers.truncated_normal(stddev=0.05), + beta_init=tl.initializers.truncated_normal(stddev=0.05), + use_gemm=False, + W_init=tl.initializers.truncated_normal(stddev=0.05), + W_init_args=None, + in_channels=None, + name=None, # 'quan_dense_with_bn', + ): + super(QuanDenseWithBN, self).__init__(act=act, W_init_args=W_init_args, name=name) + self.n_units = n_units + self.decay = decay + self.epsilon = epsilon + self.is_train = is_train + self.bitW = bitW + self.bitA = bitA + self.gamma_init = gamma_init + self.beta_init = beta_init + self.use_gemm = use_gemm + self.W_init = W_init + self.in_channels = in_channels + + if self.in_channels is not None: + self.build((None, self.in_channels)) + self._built = True + + logging.info( + "QuanDenseLayerWithBN %s: %d %s" % + (self.name, n_units, self.act.__class__.__name__ if self.act is not None else 'No Activation') + ) + + def __repr__(self): + actstr = self.act.__class__.__name__ if self.act is not None else 'No Activation' + s = ('{classname}(n_units={n_units}, ' + actstr) + s += ', bitW={bitW}, bitA={bitA}' + if self.in_channels is not None: + s += ', in_channels=\'{in_channels}\'' + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + if self.in_channels is None and len(inputs_shape) != 2: + raise Exception("The input dimension must be rank 2, please reshape or flatten it") + + if self.in_channels is None: + self.in_channels = inputs_shape[1] + + if self.use_gemm: + raise Exception("TODO. The current version use tf.matmul for inferencing.") + + n_in = inputs_shape[-1] + self.W = self._get_weights("weights", shape=(n_in, self.n_units), init=self.W_init) + + para_bn_shape = (self.n_units, ) + if self.gamma_init: + self.scale_para = self._get_weights("gamm_weights", shape=para_bn_shape, init=self.gamma_init) + else: + self.scale_para = None + + if self.beta_init: + self.offset_para = self._get_weights("beta_weights", shape=para_bn_shape, init=self.beta_init) + else: + self.offset_para = None + + self.moving_mean = self._get_weights( + "moving_mean", shape=para_bn_shape, init=tl.initializers.constant(1.0), trainable=False + ) + self.moving_variance = self._get_weights( + "moving_variacne", shape=para_bn_shape, init=tl.initializers.constant(1.0), trainable=False + ) + + + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + x = inputs + inputs = quantize_active_overflow(inputs, self.bitA) + mid_out = tl.ops.matmul(x, self.W) + + mean, variance = tl.ops.moments(x=mid_out, axes=list(range(len(mid_out.get_shape()) - 1))) + + update_moving_mean = moving_averages.assign_moving_average( + self.moving_mean, mean, self.decay, zero_debias=False + ) # if zero_debias=True, has bias + + update_moving_variance = moving_averages.assign_moving_average( + self.moving_variance, variance, self.decay, zero_debias=False + ) # if zero_debias=True, has bias + + if self.is_train: + mean, var = mean_var_with_update(update_moving_mean, update_moving_variance, mean, variance) + else: + mean, var = self.moving_mean, self.moving_variance + + _w_fold = w_fold(self.W, self.scale_para, var, self.epsilon) + + W = quantize_weight_overflow(_w_fold, self.bitW) + + outputs = tl.ops.matmul(inputs, W) + + if self.beta_init: + _bias_fold = bias_fold(self.offset_para, self.scale_para, mean, var, self.epsilon) + outputs = tl.ops.bias_add(outputs, _bias_fold) + else: + outputs = outputs + + if self.act: + outputs = self.act(outputs) + else: + outputs = outputs + return outputs diff --git a/tensorlayer/layers/dense/ternary_dense.py b/tensorlayer/layers/dense/ternary_dense.py new file mode 100644 index 0000000..5cf4457 --- /dev/null +++ b/tensorlayer/layers/dense/ternary_dense.py @@ -0,0 +1,109 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module +from tensorlayer.layers.utils import compute_alpha, ternary_operation + +__all__ = [ + 'TernaryDense', +] + + +class TernaryDense(Module): + """The :class:`TernaryDense` class is a ternary fully connected layer, which weights are either -1 or 1 or 0 while inference. + # TODO The TernaryDense only supports TensorFlow backend. + + Note that, the bias vector would not be tenaried. + + Parameters + ---------- + n_units : int + The number of units of this layer. + act : activation function + The activation function of this layer, usually set to ``tf.act.sign`` or apply :class:`SignLayer` after :class:`BatchNormLayer`. + use_gemm : boolean + If True, use gemm instead of ``tf.matmul`` for inference. (TODO). + W_init : initializer + The initializer for the weight matrix. + b_init : initializer or None + The initializer for the bias vector. If None, skip biases. + in_channels: int + The number of channels of the previous layer. + If None, it will be automatically detected when the layer is forwarded for the first time. + name : None or str + A unique layer name. + + """ + + def __init__( + self, + n_units=100, + act=None, + use_gemm=False, + W_init=tl.initializers.truncated_normal(stddev=0.05), + b_init=tl.initializers.constant(value=0.0), + in_channels=None, + name=None, #'ternary_dense', + ): + super().__init__(name, act=act) + self.n_units = n_units + self.use_gemm = use_gemm + self.W_init = W_init + self.b_init = b_init + self.in_channels = in_channels + + if self.in_channels is not None: + self.build((None, self.in_channels)) + self._built = True + + logging.info( + "TernaryDense %s: %d %s" % + (self.name, n_units, self.act.__name__ if self.act is not None else 'No Activation') + ) + + def __repr__(self): + actstr = self.act.__name__ if self.act is not None else 'No Activation' + s = ('{classname}(n_units={n_units}, ' + actstr) + if self.in_channels is not None: + s += ', in_channels=\'{in_channels}\'' + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + if len(inputs_shape) != 2: + raise Exception("The input dimension must be rank 2, please reshape or flatten it") + + if self.in_channels is None: + self.in_channels = inputs_shape[1] + + if self.use_gemm: + raise Exception("TODO. The current version use tf.matmul for inferencing.") + + n_in = inputs_shape[-1] + + self.W = self._get_weights(var_name="weights", shape=(n_in, self.n_units), init=self.W_init) + if self.b_init is not None: + self.b = self._get_weights(var_name="biases", shape=(self.n_units), init=self.b_init) + + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + alpha = compute_alpha(self.W) + W_ = ternary_operation(self.W) + W_ = tl.ops.multiply(alpha, W_) + + outputs = tl.ops.matmul(inputs, W_) + + if self.b_init is not None: + outputs = tl.ops.bias_add(outputs, self.b, name='bias_add') + if self.act: + outputs = self.act(outputs) + return outputs diff --git a/tensorlayer/layers/deprecated.py b/tensorlayer/layers/deprecated.py new file mode 100644 index 0000000..2cb6699 --- /dev/null +++ b/tensorlayer/layers/deprecated.py @@ -0,0 +1,416 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +__all__ = [] + + +class NonExistingLayerError(Exception): + pass + + +# activation.py +__all__ += [ + 'PReluLayer', + 'PRelu6Layer', + 'PTRelu6Layer', +] + +__log__ = '\n Hint: 1) downgrade TF and TL from version 2.x to 1.x. 2) check the documentation of TF and TL version 2.x' + + +def PReluLayer(*args, **kwargs): + raise NonExistingLayerError("PReluLayer(net, name='a') --> PRelu(name='a')(net))" + __log__) + + +def PRelu6Layer(*args, **kwargs): + raise NonExistingLayerError("PRelu6Layer(net, name='a') --> PRelu6(name='a')(net))" + __log__) + + +def PTRelu6Layer(*args, **kwargs): + raise NonExistingLayerError("PTRelu6Layer(net, name='a') --> PTRelu(name='a')(net))" + __log__) + + +# convolution/atrous_conv.py +__all__ += [ + 'AtrousConv1dLayer', + 'AtrousConv2dLayer', + 'AtrousDeConv2dLayer', +] + + +def AtrousConv1dLayer(*args, **kwargs): + raise NonExistingLayerError("use `tl.layers.Conv1d` with dilation instead" + __log__) + + +def AtrousConv2dLayer(*args, **kwargs): + raise NonExistingLayerError("use `tl.layers.Conv2d` with dilation instead" + __log__) + + +def AtrousDeConv2dLayer(*args, **kwargs): + # raise NonExistingLayerError("AtrousDeConv2dLayer(net, name='a') --> AtrousDeConv2d(name='a')(net)") + raise NonExistingLayerError("use `tl.layers.DeConv2d` with dilation instead" + __log__) + + +# dense/base_dense.py +__all__ += [ + 'DenseLayer', +] + + +def DenseLayer(*args, **kwargs): + raise NonExistingLayerError("DenseLayer(net, name='a') --> Dense(name='a')(net)" + __log__) + + +# dense/binary_dense.py +__all__ += [ + 'BinaryDenseLayer', +] + + +def BinaryDenseLayer(*args, **kwargs): + raise NonExistingLayerError("BinaryDenseLayer(net, name='a') --> BinaryDense(name='a')(net)" + __log__) + + +# dense/dorefa_dense.py +__all__ += [ + 'DorefaDenseLayer', +] + + +def DorefaDenseLayer(*args, **kwargs): + raise NonExistingLayerError("DorefaDenseLayer(net, name='a') --> DorefaDense(name='a')(net)" + __log__) + + +# dense/dropconnect.py +__all__ += [ + 'DropconnectDenseLayer', +] + + +def DropconnectDenseLayer(*args, **kwargs): + raise NonExistingLayerError("DropconnectDenseLayer(net, name='a') --> DropconnectDense(name='a')(net)" + __log__) + + +# dense/quan_dense_bn.py +__all__ += [ + 'QuanDenseLayerWithBN', +] + + +def QuanDenseLayerWithBN(*args, **kwargs): + raise NonExistingLayerError("QuanDenseLayerWithBN(net, name='a') --> QuanDenseWithBN(name='a')(net)" + __log__) + + +# dense/ternary_dense.py +__all__ += [ + 'TernaryDenseLayer', +] + + +def TernaryDenseLayer(*args, **kwargs): + raise NonExistingLayerError("TernaryDenseLayer(net, name='a') --> TernaryDense(name='a')(net)" + __log__) + + +# dropout.py +__all__ += [ + 'DropoutLayer', +] + + +def DropoutLayer(*args, **kwargs): + raise NonExistingLayerError( + "DropoutLayer(net, is_train=True, name='a') --> Dropout(name='a')(net, is_train=True)" + __log__ + ) + + +# extend.py +__all__ += [ + 'ExpandDimsLayer', + 'TileLayer', +] + + +def ExpandDimsLayer(*args, **kwargs): + raise NonExistingLayerError("ExpandDimsLayer(net, name='a') --> ExpandDims(name='a')(net)" + __log__) + + +def TileLayer(*args, **kwargs): + raise NonExistingLayerError("TileLayer(net, name='a') --> Tile(name='a')(net)" + __log__) + + +# image_resampling.py +__all__ += [ + 'UpSampling2dLayer', + 'DownSampling2dLayer', +] + + +def UpSampling2dLayer(*args, **kwargs): + raise NonExistingLayerError("UpSampling2dLayer(net, name='a') --> UpSampling2d(name='a')(net)" + __log__) + + +def DownSampling2dLayer(*args, **kwargs): + raise NonExistingLayerError("DownSampling2dLayer(net, name='a') --> DownSampling2d(name='a')(net)" + __log__) + + +# importer.py +__all__ += [ + 'SlimNetsLayer', + 'KerasLayer', +] + + +def SlimNetsLayer(*args, **kwargs): + raise NonExistingLayerError("SlimNetsLayer(net, name='a') --> SlimNets(name='a')(net)" + __log__) + + +def KerasLayer(*args, **kwargs): + raise NonExistingLayerError("KerasLayer(net, name='a') --> Keras(name='a')(net)" + __log__) + + +# inputs.py +__all__ += [ + 'InputLayer', +] + + +def InputLayer(*args, **kwargs): + raise NonExistingLayerError("InputLayer(x, name='a') --> Input(name='a')(x)" + __log__) + + +# embedding.py +__all__ += [ + 'OneHotInputLayer', + 'Word2vecEmbeddingInputlayer', + 'EmbeddingInputlayer', + 'AverageEmbeddingInputlayer', +] + + +def OneHotInputLayer(*args, **kwargs): + raise NonExistingLayerError( + "Not longer Input layer: OneHotInputLayer(x, name='a') --> OneHot(name='a')(layer)" + __log__ + ) + + +def Word2vecEmbeddingInputlayer(*args, **kwargs): + raise NonExistingLayerError( + "Not longer Input layer: Word2vecEmbeddingInputlayer(x, name='a') --> Word2vecEmbedding(name='a')(layer)" + + __log__ + ) + + +def EmbeddingInputlayer(*args, **kwargs): + raise NonExistingLayerError( + "Not longer Input layer: EmbeddingInputlayer(x, name='a') --> Embedding(name='a')(layer)" + __log__ + ) + + +def AverageEmbeddingInputlayer(*args, **kwargs): + raise NonExistingLayerError( + "Not longer Input layer: AverageEmbeddingInputlayer(x, name='a') --> AverageEmbedding(name='a')(layer)" + + __log__ + ) + + +# lambda.py +__all__ += [ + 'LambdaLayer', + 'ElementwiseLambdaLayer', +] + + +def LambdaLayer(*args, **kwargs): + raise NonExistingLayerError( + "LambdaLayer(x, lambda x: 2*x, name='a') --> Lambda(lambda x: 2*x, name='a')(x)" + __log__ + ) + + +def ElementwiseLambdaLayer(*args, **kwargs): + raise NonExistingLayerError( + "ElementwiseLambdaLayer(x, ..., name='a') --> ElementwiseLambda(..., name='a')(x)" + __log__ + ) + + +# merge.py +__all__ += [ + 'ConcatLayer', + 'ElementwiseLayer', +] + + +def ConcatLayer(*args, **kwargs): + raise NonExistingLayerError("ConcatLayer(x, ..., name='a') --> Concat(..., name='a')(x)" + __log__) + + +def ElementwiseLayer(*args, **kwargs): + raise NonExistingLayerError("ElementwiseLayer(x, ..., name='a') --> Elementwise(..., name='a')(x)" + __log__) + + +# noise.py +__all__ += [ + 'GaussianNoiseLayer', +] + + +def GaussianNoiseLayer(*args, **kwargs): + raise NonExistingLayerError("GaussianNoiseLayer(x, ..., name='a') --> GaussianNoise(..., name='a')(x)" + __log__) + + +# normalization.py +__all__ += [ + 'BatchNormLayer', + 'InstanceNormLayer', + 'LayerNormLayer', + 'LocalResponseNormLayer', + 'GroupNormLayer', + 'SwitchNormLayer', +] + + +def BatchNormLayer(*args, **kwargs): + raise NonExistingLayerError( + "BatchNormLayer(x, is_train=True, name='a') --> BatchNorm(name='a')(x, is_train=True)" + __log__ + ) + + +def InstanceNormLayer(*args, **kwargs): + raise NonExistingLayerError("InstanceNormLayer(x, name='a') --> InstanceNorm(name='a')(x)" + __log__) + + +def LayerNormLayer(*args, **kwargs): + raise NonExistingLayerError("LayerNormLayer(x, name='a') --> LayerNorm(name='a')(x)" + __log__) + + +def LocalResponseNormLayer(*args, **kwargs): + raise NonExistingLayerError("LocalResponseNormLayer(x, name='a') --> LocalResponseNorm(name='a')(x)" + __log__) + + +def GroupNormLayer(*args, **kwargs): + raise NonExistingLayerError("GroupNormLayer(x, name='a') --> GroupNorm(name='a')(x)" + __log__) + + +def SwitchNormLayer(*args, **kwargs): + raise NonExistingLayerError("SwitchNormLayer(x, name='a') --> SwitchNorm(name='a')(x)" + __log__) + + +# quantize_layer.py +__all__ += [ + 'SignLayer', +] + + +def SignLayer(*args, **kwargs): + raise NonExistingLayerError("SignLayer(x, name='a') --> Sign(name='a')(x)" + __log__) + + +# recurrent/lstm_layers.py +__all__ += [ + 'ConvLSTMLayer', +] + + +def ConvLSTMLayer(*args, **kwargs): + raise NonExistingLayerError("ConvLSTMLayer(x, name='a') --> ConvLSTM(name='a')(x)" + __log__) + + +# recurrent/rnn_dynamic_layers.py +__all__ += [ + 'DynamicRNNLayer', + 'BiDynamicRNNLayer', +] + + +def DynamicRNNLayer(*args, **kwargs): + raise NonExistingLayerError( + "DynamicRNNLayer(x, is_train=True, name='a') --> DynamicRNN(name='a')(x, is_train=True)" + __log__ + ) + + +def BiDynamicRNNLayer(*args, **kwargs): + raise NonExistingLayerError( + "BiDynamicRNNLayer(x, is_train=True, name='a') --> BiDynamicRNN(name='a')(x, is_train=True)" + __log__ + ) + + +# recurrent/rnn_layers.py +__all__ += [ + 'RNNLayer', + 'BiRNNLayer', +] + + +def RNNLayer(*args, **kwargs): + raise NonExistingLayerError("RNNLayer(x, name='a') --> RNN(name='a')(x)" + __log__) + + +def BiRNNLayer(*args, **kwargs): + raise NonExistingLayerError( + "BiRNNLayer(x, is_train=True, name='a') --> BiRNN(name='a')(x, is_train=True)" + __log__ + ) + + +# reshape.py +__all__ += [ + 'FlattenLayer', + 'ReshapeLayer', + 'TransposeLayer', +] + + +def FlattenLayer(*args, **kwargs): + raise NonExistingLayerError("FlattenLayer(x, name='a') --> Flatten(name='a')(x)" + __log__) + + +def ReshapeLayer(*args, **kwargs): + raise NonExistingLayerError("ReshapeLayer(x, name='a') --> Reshape(name='a')(x)" + __log__) + + +def TransposeLayer(*args, **kwargs): + raise NonExistingLayerError("TransposeLayer(x, name='a') --> Transpose(name='a')(x)" + __log__) + + +# scale.py +__all__ += [ + 'ScaleLayer', +] + + +def ScaleLayer(*args, **kwargs): + raise NonExistingLayerError("ScaleLayer(x, name='a') --> Scale(name='a')(x)" + __log__) + + +# spatial_transformer.py +__all__ += ['SpatialTransformer2dAffineLayer'] + + +def SpatialTransformer2dAffineLayer(*args, **kwargs): + raise NonExistingLayerError( + "SpatialTransformer2dAffineLayer(x1, x2, name='a') --> SpatialTransformer2dAffine(name='a')(x1, x2)" + __log__ + ) + + +# stack.py +__all__ += [ + 'StackLayer', + 'UnStackLayer', +] + + +def StackLayer(*args, **kwargs): + raise NonExistingLayerError("StackLayer(x1, x2, name='a') --> Stack(name='a')(x1, x2)" + __log__) + + +def UnStackLayer(*args, **kwargs): + raise NonExistingLayerError("UnStackLayer(x1, x2, name='a') --> UnStack(name='a')(x1, x2)" + __log__) + + +# time_distributed.py +__all__ += [ + 'TimeDistributedLayer', +] + + +def TimeDistributedLayer(*args, **kwargs): + # raise NonExistingLayerError("TimeDistributedLayer(x1, x2, name='a') --> TimeDistributed(name='a')(x1, x2)") + raise NonExistingLayerError("TimeDistributedLayer is removed for TF 2.0, please use eager mode instead." + __log__) diff --git a/tensorlayer/layers/dropout.py b/tensorlayer/layers/dropout.py new file mode 100644 index 0000000..8dccda6 --- /dev/null +++ b/tensorlayer/layers/dropout.py @@ -0,0 +1,56 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module + +__all__ = [ + 'Dropout', +] + + +class Dropout(Module): + """ + The :class:`Dropout` class is a noise layer which randomly set some + activations to zero according to a keeping probability. + + Parameters + ---------- + keep : float + The keeping probability. + The lower the probability it is, the more activations are set to zero. + seed : int or None + The seed for random dropout. + name : None or str + A unique layer name. + + """ + + def __init__(self, keep, seed=0, name=None): #"dropout"): + super(Dropout, self).__init__(name) + self.keep = keep + self.seed = seed + + self.build() + self._built = True + + logging.info("Dropout %s: keep: %f " % (self.name, self.keep)) + + def __repr__(self): + s = ('{classname}(keep={keep}') + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + self.dropout = tl.ops.Dropout(keep=self.keep, seed=self.seed) + + # @tf.function + def forward(self, inputs): + if self.is_train: + outputs = self.dropout(inputs) + else: + outputs = inputs + return outputs diff --git a/tensorlayer/layers/embedding.py b/tensorlayer/layers/embedding.py new file mode 100644 index 0000000..a6b4313 --- /dev/null +++ b/tensorlayer/layers/embedding.py @@ -0,0 +1,517 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module +# from tensorlayer.layers.core import LayersConfig + +__all__ = ['OneHot', 'Word2vecEmbedding', 'Embedding', 'AverageEmbedding'] + + +class OneHot(Module): + """ + The :class:`OneHot` class is the starting layer of a neural network, see ``tf.one_hot``. + Useful link: `https://www.tensorflow.org/api_docs/python/tf/one_hot`. + + Parameters + ---------- + depth : None or int + If the input indices is rank N, the output will have rank N+1. The new axis is created at dimension `axis` (default: the new axis is appended at the end). + on_value : None or number + The value to represnt `ON`. If None, it will default to the value 1. + off_value : None or number + The value to represnt `OFF`. If None, it will default to the value 0. + axis : None or int + The axis. + dtype : None or TensorFlow dtype + The data type, None means tf.float32. + name : str + A unique layer name. + + Examples + --------- + >>> import tensorflow as tf + >>> import tensorlayer as tl + >>> net = tl.layers.Input([32], dtype=tl.int32) + >>> onehot = tl.layers.OneHot(depth=8) + >>> print(onehot) + OneHot(depth=8, name='onehot') + >>> tensor = tl.layers.OneHot(depth=8)(net) + >>> print(tensor) + tf.Tensor([...], shape=(32, 8), dtype=float32) + + """ + + def __init__(self, depth=None, on_value=1.0, off_value=0.0, axis=-1, dtype=tl.float32, name=None): + super(OneHot, self).__init__(name) + self.depth = depth + self.on_value = on_value + self.off_value = off_value + self.axis = axis + self.dtype = dtype + logging.info("OneHotInput %s" % (self.name)) + + self.build() + self._built = True + + if self.depth is None: + raise RuntimeError(self.__class__.__name__ + ": depth == None the number of output units is undefined") + + def __repr__(self): + s = ('{classname}(depth={depth}') + if self.on_value is not None: + s += ', on_value={on_value}' + if self.off_value is not None: + s += ', off_value={off_value}' + if self.axis is not None: + s += ', axis={axis}' + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + self.onehot = tl.ops.OneHot( + depth=self.depth, on_value=self.on_value, off_value=self.off_value, axis=self.axis, dtype=self.dtype + ) + + def forward(self, inputs): + """ + Parameters + ---------- + inputs : input tensor + The inputs are indices. The locations represented by indices in indices take value on_value, while all other locations take value off_value. + """ + outputs = self.onehot(inputs) + return outputs + + +class Word2vecEmbedding(Module): + """ + The :class:`Word2vecEmbedding` class is a fully connected layer. + For Word Embedding, words are input as integer index. + The output is the embedded word vector. + + The layer integrates NCE loss by default (activate_nce_loss=True). + If the NCE loss is activated, in a dynamic model, + the computation of nce loss can be turned off in customised forward feeding + by setting use_nce_loss=False when the layer is called. + The NCE loss can be deactivated by setting activate_nce_loss=False. + + Parameters + ---------- + vocabulary_size : int + The size of vocabulary, number of words + embedding_size : int + The number of embedding dimensions + num_sampled : int + The number of negative examples for NCE loss + activate_nce_loss : boolean + Whether activate nce loss or not. By default, True + If True, the layer will return both outputs of embedding and nce_cost in forward feeding. + If False, the layer will only return outputs of embedding. + In a dynamic model, the computation of nce loss can be turned off in forward feeding + by setting use_nce_loss=False when the layer is called. + In a static model, once the model is constructed, the computation of nce loss + cannot be changed (always computed or not computed). + nce_loss_args : dictionary + The arguments for tf.ops.nce_loss() + E_init : initializer + The initializer for initializing the embedding matrix + nce_W_init : initializer + The initializer for initializing the nce decoder weight matrix + nce_b_init : initializer + The initializer for initializing of the nce decoder bias vector + name : str + A unique layer name + + Attributes + ---------- + outputs : Tensor + The embedding layer outputs. + normalized_embeddings : Tensor + Normalized embedding matrix. + nce_weights : Tensor + The NCE weights only when activate_nce_loss is True. + nce_biases: Tensor + The NCE biases only when activate_nce_loss is True. + + Examples + -------- + Word2Vec With TensorLayer (Example in `examples/text_word_embedding/tutorial_word2vec_basic.py`) + + >>> import tensorflow as tf + >>> import tensorlayer as tl + >>> batch_size = 8 + >>> embedding_size = 50 + >>> inputs = tl.layers.Input([batch_size], dtype=tf.int32) + >>> labels = tl.layers.Input([batch_size, 1], dtype=tf.int32) + >>> emb_net = tl.layers.Word2vecEmbedding( + >>> vocabulary_size=10000, + >>> embedding_size=embedding_size, + >>> num_sampled=100, + >>> activate_nce_loss=True, # the nce loss is activated + >>> nce_loss_args={}, + >>> E_init=tl.initializers.random_uniform(minval=-1.0, maxval=1.0), + >>> nce_W_init=tl.initializers.truncated_normal(stddev=float(1.0 / np.sqrt(embedding_size))), + >>> nce_b_init=tl.initializers.constant(value=0.0), + >>> name='word2vec_layer', + >>> ) + >>> print(emb_net) + Word2vecEmbedding(vocabulary_size=10000, embedding_size=50, num_sampled=100, activate_nce_loss=True, nce_loss_args={}) + >>> embed_tensor = emb_net(inputs, use_nce_loss=False) # the nce loss is turned off and no need to provide labels + >>> embed_tensor = emb_net([inputs, labels], use_nce_loss=False) # the nce loss is turned off and the labels will be ignored + >>> embed_tensor, embed_nce_loss = emb_net([inputs, labels]) # the nce loss is calculated + >>> outputs = tl.layers.Dense(n_units=10, name="dense")(embed_tensor) + >>> model = tl.models.Model(inputs=[inputs, labels], outputs=[outputs, embed_nce_loss], name="word2vec_model") # a static model + >>> out = model([data_x, data_y], is_train=True) # where data_x is inputs and data_y is labels + + References + ---------- + `https://www.tensorflow.org/tutorials/representation/word2vec` + + """ + + def __init__( + self, + vocabulary_size, + embedding_size, + num_sampled=64, + activate_nce_loss=True, + nce_loss_args=None, + E_init=tl.initializers.random_uniform(minval=-1.0, maxval=1.0), + nce_W_init=tl.initializers.truncated_normal(stddev=0.03), + nce_b_init=tl.initializers.constant(value=0.0), + name=None, #'word2vec', + ): + + super(Word2vecEmbedding, self).__init__(name) + self.vocabulary_size = vocabulary_size + self.embedding_size = embedding_size + self.num_sampled = num_sampled + self.E_init = E_init + self.activate_nce_loss = activate_nce_loss + + if self.activate_nce_loss: + self.nce_loss_args = nce_loss_args + self.nce_W_init = nce_W_init + self.nce_b_init = nce_b_init + + if not self._built: + self.build(tuple()) + self._built = True + + logging.info("Word2vecEmbedding %s: (%d, %d)" % (self.name, self.vocabulary_size, self.embedding_size)) + + def __repr__(self): + s = ('{classname}(') + s += 'vocabulary_size={vocabulary_size}' + s += ', embedding_size={embedding_size}' + s += ', num_sampled={num_sampled}' + s += ', activate_nce_loss={activate_nce_loss}' + if self.activate_nce_loss: + s += ', nce_loss_args={nce_loss_args}' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + """ + Parameters + ---------- + inputs_shape : tuple + the shape of inputs tensor + """ + # Look up embeddings for inputs. + # Note: a row of 'embeddings' is the vector representation of a word. + # for the sake of speed, it is better to slice the embedding matrix + # instead of transferring a word id to one-hot-format vector and then + # multiply by the embedding matrix. + # embed is the outputs of the hidden layer (embedding layer), it is a + # row vector with 'embedding_size' values. + + self.embeddings = self._get_weights( + "embeddings", + shape=(self.vocabulary_size, self.embedding_size), + init=self.E_init, + ) + + self.normalized_embeddings = tl.L2Normalize(axis=1)(self.embeddings) + + if self.activate_nce_loss: + # Construct the variables for the NCE loss (i.e. negative sampling) + self.nce_weights = self._get_weights( + "nce_weights", + shape=(self.vocabulary_size, self.embedding_size), + init=self.nce_W_init, + ) + + self.nce_biases = self._get_weights( + "nce_biases", + shape=(self.vocabulary_size, ), + init=self.nce_b_init, + ) + + self.embedding_lookup = tl.EmbeddingLookup() + + if self.activate_nce_loss: + self.nce_loss = tl.NCELoss(**self.nce_loss_args) + + def forward(self, inputs, use_nce_loss=None): + """ + Parameters + ---------- + inputs : tensor or list + If the nce loss is activated and is used, the argument should be a list of two tensors [inputs, labels]. + Otherwise, the argument should be a single tensor which is inputs. + use_nce_loss: boolean + Whether use NCE loss in this run. + If the nce loss is used, the activate_nce_loss should be True when the layer is initialized. + By default, same as activate_nce_loss. + + Outputs: + ---------- + outputs: tensor + nce_cost: tensor + The nce_cost is returned only if the nce_loss is used. + """ + + if isinstance(inputs, list): + outputs = self.embedding_lookup(params=self.embeddings, ids=inputs[0]) + else: + outputs = self.embedding_lookup(params=self.embeddings, ids=inputs) + + if use_nce_loss is True and not self.activate_nce_loss: + raise AttributeError( + "The nce loss is not activated when the %s is initialized. Please set activate_nce_loss=True." % + self.__class__.__name__ + ) + + if self.activate_nce_loss and (use_nce_loss is True or use_nce_loss is None): + if not isinstance(inputs, list): + raise ValueError("If nce loss is used, the labels of inputs must be provided.") + + nce_cost = tl.reduce_mean( + input_tensor=self.nce_loss( + weights=self.nce_weights, biases=self.nce_biases, inputs=outputs, labels=inputs[1], + num_sampled=self.num_sampled, num_classes=self.vocabulary_size + ) + ) + + return outputs, nce_cost + + return outputs + + +class Embedding(Module): + """ + The :class:`Embedding` class is a look-up table for word embedding. + + Word content are accessed using integer indexes, then the output is the embedded word vector. + To train a word embedding matrix, you can used :class:`Word2vecEmbedding`. + If you have a pre-trained matrix, you can assign the parameters into it. + + Parameters + ---------- + vocabulary_size : int + The size of vocabulary, number of words. + embedding_size : int + The number of embedding dimensions. + E_init : initializer + The initializer for the embedding matrix. + E_init_args : dictionary + The arguments for embedding matrix initializer. + name : str + A unique layer name. + + Attributes + ---------- + outputs : tensor + The embedding layer output is a 3D tensor in the shape: (batch_size, num_steps(num_words), embedding_size). + + Examples + -------- + >>> import tensorflow as tf + >>> import tensorlayer as tl + >>> input = tl.layers.Input([8, 100], dtype=tf.int32) + >>> embed = tl.layers.Embedding(vocabulary_size=1000, embedding_size=50, name='embed') + >>> print(embed) + Embedding(vocabulary_size=1000, embedding_size=50) + >>> tensor = embed(input) + >>> print(tensor) + tf.Tensor([...], shape=(8, 100, 50), dtype=float32) + + """ + + def __init__( + self, + vocabulary_size, + embedding_size, + E_init=tl.initializers.random_uniform(-0.1, 0.1), + name=None, #'embedding', + ): + super(Embedding, self).__init__(name) + self.vocabulary_size = vocabulary_size + self.embedding_size = embedding_size + self.E_init = E_init + + if not self._built: + self.build(tuple()) + self._built = True + + logging.info("Embedding %s: (%d, %d)" % (self.name, self.vocabulary_size, self.embedding_size)) + + def __repr__(self): + s = ('{classname}(') + s += 'vocabulary_size={vocabulary_size}' + s += ', embedding_size={embedding_size}' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + """ + Parameters + ---------- + inputs_shape : tuple + the shape of inputs tensor + """ + + self.embeddings = self._get_weights( + "embeddings", + shape=(self.vocabulary_size, self.embedding_size), + init=self.E_init, + ) + self.embedding_lookup = tl.EmbeddingLookup() + + def forward(self, inputs): + """ + Parameters + ---------- + inputs : Tensor + The input of a network. + """ + outputs = self.embedding_lookup(params=self.embeddings, ids=inputs) + return outputs + + +class AverageEmbedding(Module): + """The :class:`AverageEmbedding` averages over embeddings of inputs. + This is often used as the input layer for models like DAN[1] and FastText[2]. + + Parameters + ---------- + vocabulary_size : int + The size of vocabulary. + embedding_size : int + The dimension of the embedding vectors. + pad_value : int + The scalar padding value used in inputs, 0 as default. + E_init : initializer + The initializer of the embedding matrix. + name : str + A unique layer name. + + Attributes + ---------- + outputs : tensor + The embedding layer output is a 2D tensor in the shape: (batch_size, embedding_size). + + References + ---------- + - [1] Iyyer, M., Manjunatha, V., Boyd-Graber, J., & Daum’e III, H. (2015). Deep Unordered Composition Rivals Syntactic Methods for Text Classification. In Association for Computational Linguistics. + - [2] Joulin, A., Grave, E., Bojanowski, P., & Mikolov, T. (2016). `Bag of Tricks for Efficient Text Classification. `__ + + Examples + --------- + >>> import tensorflow as tf + >>> import tensorlayer as tl + >>> batch_size = 8 + >>> length = 5 + >>> input = tl.layers.Input([batch_size, length], dtype=tf.int32) + >>> avgembed = tl.layers.AverageEmbedding(vocabulary_size=1000, embedding_size=50, name='avg') + >>> print(avgembed) + AverageEmbedding(vocabulary_size=1000, embedding_size=50, pad_value=0) + >>> tensor = avgembed(input) + >>> print(tensor) + tf.Tensor([...], shape=(8, 50), dtype=float32) + + """ + + def __init__( + self, + vocabulary_size, + embedding_size, + pad_value=0, + E_init=tl.initializers.random_uniform(-0.1, 0.1), + name=None, # 'average_embedding', + ): + + super(AverageEmbedding, self).__init__(name) + self.vocabulary_size = vocabulary_size + self.embedding_size = embedding_size + self.pad_value = pad_value + self.E_init = E_init + + if not self._built: + self.build(tuple()) + self._built = True + + logging.info("AverageEmbedding %s: (%d, %d)" % (self.name, self.vocabulary_size, self.embedding_size)) + + def __repr__(self): + s = ('{classname}(') + s += 'vocabulary_size={vocabulary_size}' + s += ', embedding_size={embedding_size}' + s += ', pad_value={pad_value}' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + """ + Parameters + ---------- + inputs_shape : tuple + the shape of inputs tensor. + """ + # if len(inputs_shape) != 2: + # raise ValueError('inputs must be of size (batch_size, sentence_length)') + + self.embeddings = self._get_weights( + "embeddings", + shape=(self.vocabulary_size, self.embedding_size), + init=self.E_init, + ) + self.embedding_lookup = tl.EmbeddingLookup() + self.not_equal = tl.Not_equal() + self.cast = tl.Cast(tl.float32) + self.expand_dims = tl.ExpandDims(axis=-1) + self.reduce_sum = tl.ReduceSum(axis=1) + self.count_nonzero = tl.Count_nonzero(keepdims=True, dtype=tl.float32) + + def forward(self, inputs): + """ + Parameters + ---------- + inputs : tensor + The network input. + For word inputs, please use integer index format, 2D tensor: (batch_size, sentence_length). + """ + word_embeddings = self.embedding_lookup(params=self.embeddings, ids=inputs) + + # Zero out embeddings of pad value + masks = self.not_equal(inputs, self.pad_value) + word_embeddings *= self.cast(self.expand_dims(masks)) + sum_word_embeddings = self.reduce_sum(input=word_embeddings) + + # Count number of non-padding words in each sentence + sentence_lengths = self.count_nonzero(masks, axis=1) + + sentence_embeddings = tl.ops.divide( + sum_word_embeddings, + sentence_lengths + 1e-8, # Add epsilon to avoid dividing by 0 + ) + + outputs = sentence_embeddings + + return outputs + diff --git a/tensorlayer/layers/extend.py b/tensorlayer/layers/extend.py new file mode 100644 index 0000000..9f765c5 --- /dev/null +++ b/tensorlayer/layers/extend.py @@ -0,0 +1,105 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorlayer as tl + +from tensorlayer import logging +from tensorlayer.layers.core import Module + +__all__ = [ + 'ExpandDims', + 'Tile', +] + + +class ExpandDims(Module): + """ + The :class:`ExpandDims` class inserts a dimension of 1 into a tensor's shape, + see `tf.expand_dims() `__ . + + Parameters + ---------- + axis : int + The dimension index at which to expand the shape of input. + name : str + A unique layer name. If None, a unique name will be automatically assigned. + + Examples + -------- + >>> x = tl.layers.Input([10, 3], name='in') + >>> y = tl.layers.ExpandDims(axis=-1)(x) + [10, 3, 1] + """ + + def __init__( + self, + axis, + name=None # 'expand_dims', + ): + super(ExpandDims, self).__init__(name) + self.axis = axis + + self.build((None, )) + self._built = True + + logging.info("ExpandDims %s: axis: %d" % (self.name, self.axis)) + + def __repr__(self): + s = '{classname}(' + s += 'axis={axis},' + s += 'name={name}' + s += ")" + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + self.expand_dims = tl.ops.ExpandDims(axis=self.axis) + + # @tf.function + def forward(self, inputs): + outputs = self.expand_dims(inputs) + return outputs + + +class Tile(Module): + """ + The :class:`Tile` class constructs a tensor by tiling a given tensor, + see `tf.tile() `__ . + + Parameters + ---------- + multiples: tensor + Must be one of the following types: int32, int64. + 1-D Length must be the same as the number of dimensions in input. + name : None or str + A unique layer name. + + Examples + -------- + >>> x = tl.layers.Input([10, 3], name='in') + >>> y = tl.layers.Tile(multiples=[2, 3])(x) + """ + + def __init__(self, multiples=None, name=None): #'tile'): + + super(Tile, self).__init__(name) + self.multiples = multiples + + self.build((None, )) + self._built = True + + logging.info("Tile %s: multiples: %s" % (self.name, self.multiples)) + + def __repr__(self): + s = '{classname}(' + s += 'multiples={multiples},' + s += 'name={name}' + s += ")" + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + self.tile = tl.ops.Tile() + + # @tf.function + def forward(self, inputs): + outputs = self.tile(inputs, multiples=self.multiples) + return outputs diff --git a/tensorlayer/layers/image_resampling.py b/tensorlayer/layers/image_resampling.py new file mode 100644 index 0000000..a676a34 --- /dev/null +++ b/tensorlayer/layers/image_resampling.py @@ -0,0 +1,170 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module + +__all__ = [ + 'UpSampling2d', + 'DownSampling2d', +] + + +class UpSampling2d(Module): + """The :class:`UpSampling2d` class is a up-sampling 2D layer. + + See `tf.image.resize_images `__. + + Parameters + ---------- + scale : int/float or tuple of int/float + (height, width) scale factor. + method : str + The resize method selected through the given string. Default 'bilinear'. + - 'bilinear', Bilinear interpolation. + - 'nearest', Nearest neighbor interpolation. + - 'bicubic', Bicubic interpolation. + - 'area', Area interpolation. + antialias : boolean + Whether to use an anti-aliasing filter when downsampling an image. + data_format : str + channels_last 'channel_last' (default) or channels_first. + name : None or str + A unique layer name. + + Examples + --------- + With TensorLayer + + >>> ni = tl.layers.Input([None, 50, 50, 32], name='input') + >>> ni = tl.layers.UpSampling2d(scale=(2, 2))(ni) + >>> output shape : [None, 100, 100, 32] + + """ + + def __init__(self, scale, method='bilinear', antialias=False, data_format='channels_last', name=None, ksize=None): + super(UpSampling2d, self).__init__(name) + self.method = method + self.antialias = antialias + self.data_format = data_format + self.ksize = ksize + + logging.info( + "UpSampling2d %s: scale: %s method: %s antialias: %s" % (self.name, scale, self.method, self.antialias) + ) + + if isinstance(scale, (list, tuple)) and len(scale) != 2: + raise ValueError("scale must be int or tuple/list of length 2") + + self.scale = (scale, scale) if isinstance(scale, int) else scale + self.build(None) + self._built = True + + def __repr__(self): + s = '{classname}(scale={scale}, method={method}' + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, scale=self.scale, method=self.method, name=self.name) + + def build(self, inputs_shape): + self.resize = tl.ops.Resize( + scale=self.scale, method=self.method, antialias=self.antialias, data_format=self.data_format, + ksize=self.ksize + ) + + def forward(self, inputs): + """ + + Parameters + ------------ + inputs : :class:`Tensor` + Inputs tensors with 4-D Tensor of the shape (batch, height, width, channels) + """ + outputs = self.resize(inputs) + return outputs + +class DownSampling2d(Module): + """The :class:`DownSampling2d` class is down-sampling 2D layer. + + See `tf.image.resize_images `__. + + Parameters + ---------- + scale : int/float or tuple of int/float + (height, width) scale factor. + method : str + The resize method selected through the given string. Default 'bilinear'. + - 'bilinear', Bilinear interpolation. + - 'nearest', Nearest neighbor interpolation. + - 'bicubic', Bicubic interpolation. + - 'area', Area interpolation. + antialias : boolean + Whether to use an anti-aliasing filter when downsampling an image. + data_format : str + channels_last 'channel_last' (default) or channels_first. + name : None or str + A unique layer name. + + Examples + --------- + With TensorLayer + + >>> ni = tl.layers.Input([None, 50, 50, 32], name='input') + >>> ni = tl.layers.DownSampling2d(scale=(2, 2))(ni) + >>> output shape : [None, 25, 25, 32] + + """ + + def __init__( + self, + scale, + method='bilinear', + antialias=False, + data_format='channels_last', + name=None, + ksize=None + ): + super(DownSampling2d, self).__init__(name) + self.method = method + self.antialias = antialias + self.data_format = data_format + self.ksize = ksize + logging.info( + "DownSampling2d %s: scale: %s method: %s antialias: %s" % (self.name, scale, self.method, self.antialias) + ) + + if isinstance(scale, (list, tuple)) and len(scale) != 2: + raise ValueError("scale must be int or tuple/list of length 2") + + self.scale = (scale, scale) if isinstance(scale, int) else scale + + self.build(None) + self._built = True + + def __repr__(self): + s = '{classname}(scale={scale}, method={method}' + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, scale=self.scale, method=self.method, name=self.name) + + def build(self, inputs_shape): + scale = [1.0 / self.scale[0], 1.0 / self.scale[1]] + self.resize = tl.ops.Resize( + scale=scale, method=self.method, antialias=self.antialias, data_format=self.data_format, + ksize=self.ksize + ) + + def forward(self, inputs): + """ + + Parameters + ------------ + inputs : :class:`Tensor` + Inputs tensors with 4-D Tensor of the shape (batch, height, width, channels) + """ + + outputs = self.resize(inputs) + return outputs \ No newline at end of file diff --git a/tensorlayer/layers/inputs.py b/tensorlayer/layers/inputs.py new file mode 100644 index 0000000..34a8778 --- /dev/null +++ b/tensorlayer/layers/inputs.py @@ -0,0 +1,70 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module + +__all__ = ['Input', '_InputLayer'] + + +class _InputLayer(Module): + """ + The :class:`Input` class is the starting layer of a neural network. + + Parameters + ---------- + shape : tuple (int) + Including batch size. + dtype: dtype + The type of input values. By default, tf.float32. + name : None or str + A unique layer name. + + """ + + def __init__(self, shape, dtype=tl.float32, name=None, init=None): + super(_InputLayer, self).__init__(name) + + logging.info("Input %s: %s" % (self.name, str(shape))) + self.shape = shape + self.dtype = dtype + self.shape_without_none = [_ if _ is not None else 1 for _ in shape] + if init is None: + self.outputs = tl.initializers.ones()(self.shape_without_none, dtype=self.dtype) + else: + self.outputs = init(self.shape_without_none, dtype=self.dtype) + self._built = True + + def __repr__(self): + s = 'Input(shape=%s' % str(self.shape) + if self.name is not None: + s += (', name=\'%s\'' % self.name) + s += ')' + return s + + def __call__(self, *args, **kwargs): + return self.outputs + + def build(self, inputs_shape): + pass + + def forward(self): + return self.outputs + + +def Input(shape, init=tl.initializers.ones(), dtype=tl.float32, name=None): + """ + The :class:`Input` class is the starting layer of a neural network. + + Parameters + ---------- + shape : tuple (int) + Including batch size. + name : None or str + A unique layer name. + + """ + input_layer = _InputLayer(shape, dtype=dtype, name=name, init=init) + outputs = input_layer() + return outputs diff --git a/tensorlayer/layers/lambda_layers.py b/tensorlayer/layers/lambda_layers.py new file mode 100644 index 0000000..1184f29 --- /dev/null +++ b/tensorlayer/layers/lambda_layers.py @@ -0,0 +1,280 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorflow as tf + +from tensorlayer import logging +from tensorlayer.files import utils +from tensorlayer.layers.core import Module + +__all__ = [ + 'Lambda', + 'ElementwiseLambda', +] + + +class Lambda(Module): + """A layer that takes a user-defined function using Lambda. + If the function has trainable weights, the weights should be provided. + Remember to make sure the weights provided when the layer is constructed are SAME as + the weights used when the layer is forwarded. + For multiple inputs see :class:`ElementwiseLambda`. + + Parameters + ---------- + fn : function + The function that applies to the inputs (e.g. tensor from the previous layer). + fn_weights : list + The trainable weights for the function if any. Optional. + fn_args : dict + The arguments for the function if any. Optional. + name : str or None + A unique layer name. + + Examples + --------- + Non-parametric and non-args case: + This case is supported in the Model.save() / Model.load() to save / load the whole model architecture and weights(optional). + + >>> x = tl.layers.Input([8, 3], name='input') + >>> y = tl.layers.Lambda(lambda x: 2*x, name='lambda')(x) + + + Non-parametric and with args case: + This case is supported in the Model.save() / Model.load() to save / load the whole model architecture and weights(optional). + + >>> def customize_func(x, foo=42): # x is the inputs, foo is an argument + >>> return foo * x + >>> x = tl.layers.Input([8, 3], name='input') + >>> lambdalayer = tl.layers.Lambda(customize_func, fn_args={'foo': 2}, name='lambda')(x) + + + Any function with outside variables: + This case has not been supported in Model.save() / Model.load() yet. + Please avoid using Model.save() / Model.load() to save / load models that contain such Lambda layer. Instead, you may use Model.save_weights() / Model.load_weights() to save / load model weights. + Note: In this case, fn_weights should be a list, and then the trainable weights in this Lambda layer can be added into the weights of the whole model. + + >>> a = tf.Variable(1.0) + >>> def func(x): + >>> return x + a + >>> x = tl.layers.Input([8, 3], name='input') + >>> y = tl.layers.Lambda(func, fn_weights=[a], name='lambda')(x) + + + Parametric case, merge other wrappers into TensorLayer: + This case is supported in the Model.save() / Model.load() to save / load the whole model architecture and weights(optional). + + >>> layers = [ + >>> tf.keras.layers.Dense(10, activation=tf.nn.relu), + >>> tf.keras.layers.Dense(5, activation=tf.nn.sigmoid), + >>> tf.keras.layers.Dense(1, activation=tf.identity) + >>> ] + >>> perceptron = tf.keras.Sequential(layers) + >>> # in order to compile keras model and get trainable_variables of the keras model + >>> _ = perceptron(np.random.random([100, 5]).astype(np.float32)) + >>> + >>> class CustomizeModel(tl.models.Model): + >>> def __init__(self): + >>> super(CustomizeModel, self).__init__() + >>> self.dense = tl.layers.Dense(in_channels=1, n_units=5) + >>> self.lambdalayer = tl.layers.Lambda(perceptron, perceptron.trainable_variables) + >>> + >>> def forward(self, x): + >>> z = self.dense(x) + >>> z = self.lambdalayer(z) + >>> return z + >>> + >>> optimizer = tl.optimizers.Adam(learning_rate=0.1) + >>> model = CustomizeModel() + >>> model.train() + >>> + >>> for epoch in range(50): + >>> with tf.GradientTape() as tape: + >>> pred_y = model(data_x) + >>> loss = tl.cost.mean_squared_error(pred_y, data_y) + >>> + >>> gradients = tape.gradient(loss, model.trainable_weights) + >>> optimizer.apply_gradients(zip(gradients, model.trainable_weights)) + + """ + + def __init__( + self, + fn, + fn_weights=None, + fn_args=None, + name=None, + ): + + super(Lambda, self).__init__(name=name) + self.fn = fn + self._trainable_weights = fn_weights if fn_weights is not None else [] + self.fn_args = fn_args if fn_args is not None else {} + + try: + fn_name = repr(self.fn) + except: + fn_name = 'name not available' + logging.info("Lambda %s: func: %s, len_weights: %s" % (self.name, fn_name, len(self._trainable_weights))) + + self.build() + self._built = True + + def __repr__(self): + s = '{classname}(' + s += 'fn={fn_name},' + s += 'len_weights={len_weights},' + s += 'name=\'{name}\'' + s += ')' + try: + fn_name = repr(self.fn) + except: + fn_name = 'name not available' + return s.format( + classname=self.__class__.__name__, fn_name=fn_name, len_weights=len(self._trainable_weights), + **self.__dict__ + ) + + def build(self, inputs_shape=None): + pass + + def forward(self, inputs, **kwargs): + + if len(kwargs) == 0: + outputs = self.fn(inputs, **self.fn_args) + else: + outputs = self.fn(inputs, **kwargs) + + return outputs + + def get_args(self): + init_args = {} + if isinstance(self.fn, tf.keras.layers.Layer) or isinstance(self.fn, tf.keras.Model): + init_args.update({"layer_type": "keraslayer"}) + init_args["fn"] = utils.save_keras_model(self.fn) + init_args["fn_weights"] = None + if len(self._nodes) == 0: + init_args["keras_input_shape"] = [] + else: + init_args["keras_input_shape"] = self._nodes[0].in_tensors[0].get_shape().as_list() + else: + init_args = {"layer_type": "normal"} + return init_args + + +class ElementwiseLambda(Module): + """A layer that use a custom function to combine multiple :class:`Layer` inputs. + If the function has trainable weights, the weights should be provided. + Remember to make sure the weights provided when the layer is constructed are SAME as + the weights used when the layer is forwarded. + + Parameters + ---------- + fn : function + The function that applies to the inputs (e.g. tensor from the previous layer). + fn_weights : list + The trainable weights for the function if any. Optional. + fn_args : dict + The arguments for the function if any. Optional. + name : str or None + A unique layer name. + + Examples + -------- + + Non-parametric and with args case + This case is supported in the Model.save() / Model.load() to save / load the whole model architecture and weights(optional). + + >>> # z = mean + noise * tf.exp(std * 0.5) + foo + >>> def func(noise, mean, std, foo=42): + >>> return mean + noise * tf.exp(std * 0.5) + foo + >>> noise = tl.layers.Input([100, 1]) + >>> mean = tl.layers.Input([100, 1]) + >>> std = tl.layers.Input([100, 1]) + >>> out = tl.layers.ElementwiseLambda(fn=func, fn_args={'foo': 84}, name='elementwiselambda')([noise, mean, std]) + + + Non-parametric and non-args case + This case is supported in the Model.save() / Model.load() to save / load the whole model architecture and weights(optional). + + >>> # z = mean + noise * tf.exp(std * 0.5) + >>> noise = tl.layers.Input([100, 1]) + >>> mean = tl.layers.Input([100, 1]) + >>> std = tl.layers.Input([100, 1]) + >>> out = tl.layers.ElementwiseLambda(fn=lambda x, y, z: x + y * tf.exp(z * 0.5), name='elementwiselambda')([noise, mean, std]) + + + Any function with outside variables + This case has not been supported in Model.save() / Model.load() yet. + Please avoid using Model.save() / Model.load() to save / load models that contain such ElementwiseLambda layer. Instead, you may use Model.save_weights() / Model.load_weights() to save / load model weights. + Note: In this case, fn_weights should be a list, and then the trainable weights in this ElementwiseLambda layer can be added into the weights of the whole model. + + >>> # z = mean + noise * tf.exp(std * 0.5) + vara + >>> vara = [tf.Variable(1.0)] + >>> def func(noise, mean, std): + >>> return mean + noise * tf.exp(std * 0.5) + vara + >>> noise = tl.layers.Input([100, 1]) + >>> mean = tl.layers.Input([100, 1]) + >>> std = tl.layers.Input([100, 1]) + >>> out = tl.layers.ElementwiseLambda(fn=func, fn_weights=vara, name='elementwiselambda')([noise, mean, std]) + + """ + + def __init__( + self, + fn, + fn_weights=None, + fn_args=None, + name=None, #'elementwiselambda', + ): + + super(ElementwiseLambda, self).__init__(name=name) + self.fn = fn + self._trainable_weights = fn_weights if fn_weights is not None else [] + self.fn_args = fn_args if fn_args is not None else {} + + try: + fn_name = repr(self.fn) + except: + fn_name = 'name not available' + logging.info( + "ElementwiseLambda %s: func: %s, len_weights: %s" % (self.name, fn_name, len(self._trainable_weights)) + ) + + self.build() + self._built = True + + def __repr__(self): + s = '{classname}(' + s += 'fn={fn_name},' + s += 'len_weights={len_weights},' + s += 'name=\'{name}\'' + s += ')' + try: + fn_name = repr(self.fn) + except: + fn_name = 'name not available' + return s.format( + classname=self.__class__.__name__, fn_name=fn_name, len_weights=len(self._trainable_weights), + **self.__dict__ + ) + + def build(self, inputs_shape=None): + # do nothing + # the weights of the function are provided when the Lambda layer is constructed + pass + + # @tf.function + def forward(self, inputs, **kwargs): + + if not isinstance(inputs, list): + raise TypeError( + "The inputs should be a list of values which corresponds with the customised lambda function." + ) + + if len(kwargs) == 0: + outputs = self.fn(*inputs, **self.fn_args) + else: + outputs = self.fn(*inputs, **kwargs) + + return outputs \ No newline at end of file diff --git a/tensorlayer/layers/merge.py b/tensorlayer/layers/merge.py new file mode 100644 index 0000000..8e41d5a --- /dev/null +++ b/tensorlayer/layers/merge.py @@ -0,0 +1,142 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorlayer as tl + +from tensorlayer import logging +from tensorlayer.layers.core import Module + +__all__ = [ + 'Concat', + 'Elementwise', +] + + +class Concat(Module): + """A layer that concats multiple tensors according to given axis. + + Parameters + ---------- + concat_dim : int + The dimension to concatenate. + name : None or str + A unique layer name. + + Examples + ---------- + >>> class CustomModel(Module): + >>> def __init__(self): + >>> super(CustomModel, self).__init__(name="custom") + >>> self.dense1 = tl.layers.Dense(in_channels=20, n_units=10, act=tf.nn.relu, name='relu1_1') + >>> self.dense2 = tl.layers.Dense(in_channels=20, n_units=10, act=tf.nn.relu, name='relu2_1') + >>> self.concat = tl.layers.Concat(concat_dim=1, name='concat_layer') + + >>> def forward(self, inputs): + >>> d1 = self.dense1(inputs) + >>> d2 = self.dense2(inputs) + >>> outputs = self.concat([d1, d2]) + >>> return outputs + + """ + + def __init__( + self, + concat_dim=-1, + name=None, #'concat', + ): + + super(Concat, self).__init__(name) + self.concat_dim = concat_dim + + self.build(None) + self._built = True + + logging.info("Concat %s: concat_dim: %d" % (self.name, concat_dim)) + + def __repr__(self): + s = ('{classname}(concat_dim={concat_dim})') + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + self.concat = tl.ops.Concat(self.concat_dim) + + # @tf.function + def forward(self, inputs): + """ + + prev_layer : list of :class:`Layer` + List of layers to concatenate. + """ + outputs = self.concat(inputs) + return outputs + +class Elementwise(Module): + """A layer that combines multiple :class:`Layer` that have the same output shapes + according to an element-wise operation. + If the element-wise operation is complicated, please consider to use :class:`ElementwiseLambda`. + + Parameters + ---------- + combine_fn : a TensorFlow element-wise combine function + e.g. AND is ``tf.minimum`` ; OR is ``tf.maximum`` ; ADD is ``tf.add`` ; MUL is ``tf.multiply`` and so on. + See `TensorFlow Math API `__ . + If the combine function is more complicated, please consider to use :class:`ElementwiseLambda`. + act : activation function + The activation function of this layer. + name : None or str + A unique layer name. + + Examples + -------- + >>> class CustomModel(tl.models.Model): + >>> def __init__(self): + >>> super(CustomModel, self).__init__(name="custom") + >>> self.dense1 = tl.layers.Dense(in_channels=20, n_units=10, act=tf.nn.relu, name='relu1_1') + >>> self.dense2 = tl.layers.Dense(in_channels=20, n_units=10, act=tf.nn.relu, name='relu2_1') + >>> self.element = tl.layers.Elementwise(combine_fn=tf.minimum, name='minimum', act=tf.identity) + + >>> def forward(self, inputs): + >>> d1 = self.dense1(inputs) + >>> d2 = self.dense2(inputs) + >>> outputs = self.element([d1, d2]) + >>> return outputs + """ + + def __init__( + self, + combine_fn=tl.ops.minimum, + act=None, + name=None, #'elementwise', + ): + + super(Elementwise, self).__init__(name, act=act) + self.combine_fn = combine_fn + self.combine_fn_str = str(combine_fn).split(' ')[1] + + self.build(None) + self._built = True + + logging.info( + "Elementwise %s: fn: %s act: %s" % + (self.name, combine_fn.__name__, ('No Activation' if self.act is None else self.act.__class__.__name__)) + ) + + def __repr__(self): + actstr = self.act.__class__.__name__ if self.act is not None else 'No Activation' + s = ('{classname}(combine_fn={combine_fn_str}, ' + actstr) + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + pass + + # @tf.function + def forward(self, inputs): + outputs = inputs[0] + for input in inputs[1:]: + outputs = self.combine_fn(outputs, input) + if self.act: + outputs = self.act(outputs) + return outputs \ No newline at end of file diff --git a/tensorlayer/layers/noise.py b/tensorlayer/layers/noise.py new file mode 100644 index 0000000..65e12fc --- /dev/null +++ b/tensorlayer/layers/noise.py @@ -0,0 +1,80 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module + +__all__ = [ + 'GaussianNoise', +] + + +class GaussianNoise(Module): + """ + The :class:`GaussianNoise` class is noise layer that adding noise with + gaussian distribution to the activation. + + Parameters + ------------ + mean : float + The mean. Default is 0.0. + stddev : float + The standard deviation. Default is 1.0. + is_always : boolean + Is True, add noise for train and eval mode. If False, skip this layer in eval mode. + seed : int or None + The seed for random noise. + name : str + A unique layer name. + + Examples + -------- + With TensorLayer + + >>> net = tl.layers.Input([64, 200], name='input') + >>> net = tl.layers.Dense(in_channels=200, n_units=100, act=tl.ReLU, name='dense')(net) + >>> gaussianlayer = tl.layers.GaussianNoise(name='gaussian')(net) + >>> print(gaussianlayer) + >>> output shape : (64, 100) + + """ + + def __init__( + self, + mean=0.0, + stddev=1.0, + is_always=True, + seed=None, + name=None, # 'gaussian_noise', + ): + super().__init__(name) + self.mean = mean + self.stddev = stddev + self.seed = seed + self.is_always = is_always + + self.build() + self._built = True + + logging.info("GaussianNoise %s: mean: %f stddev: %f" % (self.name, self.mean, self.stddev)) + + def __repr__(self): + s = '{classname}(mean={mean}, stddev={stddev}' + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs=None): + pass + + def forward(self, inputs): + if (self.is_train or self.is_always) is False: + return inputs + else: + shapes = tl.get_tensor_shape(inputs) + noise = tl.ops.random_normal(shape=shapes, mean=self.mean, stddev=self.stddev, seed=self.seed) + print(noise) + outputs = inputs + noise + return outputs diff --git a/tensorlayer/layers/normalization.py b/tensorlayer/layers/normalization.py new file mode 100644 index 0000000..5ab2e89 --- /dev/null +++ b/tensorlayer/layers/normalization.py @@ -0,0 +1,258 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module + +__all__ = [ + 'BatchNorm', + 'BatchNorm1d', + 'BatchNorm2d', + 'BatchNorm3d', +] + + +class BatchNorm(Module): + """ + The :class:`BatchNorm` is a batch normalization layer for both fully-connected and convolution outputs. + See ``tf.nn.batch_normalization`` and ``tf.nn.moments``. + + Parameters + ---------- + decay : float + A decay factor for `ExponentialMovingAverage`. + Suggest to use a large value for large dataset. + epsilon : float + Eplison. + act : activation function + The activation function of this layer. + is_train : boolean + Is being used for training or inference. + beta_init : initializer or None + The initializer for initializing beta, if None, skip beta. + Usually you should not skip beta unless you know what happened. + gamma_init : initializer or None + The initializer for initializing gamma, if None, skip gamma. + When the batch normalization layer is use instead of 'biases', or the next layer is linear, this can be + disabled since the scaling can be done by the next layer. see `Inception-ResNet-v2 `__ + moving_mean_init : initializer or None + The initializer for initializing moving mean, if None, skip moving mean. + moving_var_init : initializer or None + The initializer for initializing moving var, if None, skip moving var. + num_features: int + Number of features for input tensor. Useful to build layer if using BatchNorm1d, BatchNorm2d or BatchNorm3d, + but should be left as None if using BatchNorm. Default None. + data_format : str + channels_last 'channel_last' (default) or channels_first. + name : None or str + A unique layer name. + + Examples + --------- + With TensorLayer + + >>> net = tl.layers.Input([None, 50, 50, 32], name='input') + >>> net = tl.layers.BatchNorm()(net) + + Notes + ----- + The :class:`BatchNorm` is universally suitable for 3D/4D/5D input in static model, but should not be used + in dynamic model where layer is built upon class initialization. So the argument 'num_features' should only be used + for subclasses :class:`BatchNorm1d`, :class:`BatchNorm2d` and :class:`BatchNorm3d`. All the three subclasses are + suitable under all kinds of conditions. + + References + ---------- + - `Source `__ + - `stackoverflow `__ + + """ + + def __init__( + self, + decay=0.9, + epsilon=0.00001, + act=None, + is_train=True, + beta_init=tl.initializers.zeros(), + gamma_init=tl.initializers.random_normal(mean=1.0, stddev=0.002), + moving_mean_init=tl.initializers.zeros(), + moving_var_init=tl.initializers.zeros(), + num_features=None, + data_format='channels_last', + name=None, + ): + super(BatchNorm, self).__init__(name=name, act=act) + self.decay = decay + self.epsilon = epsilon + self.data_format = data_format + self.beta_init = beta_init + self.gamma_init = gamma_init + self.moving_mean_init = moving_mean_init + self.moving_var_init = moving_var_init + self.num_features = num_features + self.is_train = is_train + + self.axes = None + + # if self.num_features is None: + # raise AttributeError( + # "The registered layer `{}` should be built in advance. " + # "Do you forget to pass the keyword argument 'num_feature'? " + # ) + + if self.num_features: + self.build(None) + self._built = True + + + if self.decay < 0.0 or 1.0 < self.decay: + raise ValueError("decay should be between 0 to 1") + + logging.info( + "BatchNorm %s: decay: %f epsilon: %f act: %s is_train: %s" % ( + self.name, decay, epsilon, self.act.__class__.__name__ if self.act is not None else 'No Activation', + is_train + ) + ) + + def __repr__(self): + actstr = self.act.__class__.__name__ if self.act is not None else 'No Activation' + s = ('{classname}(num_features={num_features}, decay={decay}' ', epsilon={epsilon}') + s += (', ' + actstr) + if self.name is not None: + s += ', name="{name}"' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def _get_param_shape(self, inputs_shape): + if self.data_format == 'channels_last': + axis = -1 + elif self.data_format == 'channels_first': + axis = 1 + else: + raise ValueError('data_format should be either %s or %s' % ('channels_last', 'channels_first')) + + channels = inputs_shape[axis] + params_shape = [channels] + + return params_shape + + def _check_input_shape(self, inputs): + if inputs.ndim <= 1: + raise ValueError('expected input at least 2D, but got {}D input'.format(inputs.ndim)) + + def build(self, inputs_shape): + params_shape = [self.num_features] if self.num_features is not None else self._get_param_shape(inputs_shape) + + self.beta, self.gamma = None, None + if self.beta_init: + self.beta = self._get_weights(var_name="beta", shape=params_shape, init=self.beta_init) + + if self.gamma_init: + self.gamma = self._get_weights(var_name="gamma", shape=params_shape, init=self.gamma_init) + + self.moving_mean = self._get_weights( + var_name="moving_mean", shape=params_shape, init=self.moving_mean_init, trainable=False + ) + self.moving_var = self._get_weights( + var_name="moving_var", shape=params_shape, init=self.moving_var_init, trainable=False + ) + + self.batchnorm = tl.ops.BatchNorm( + decay=self.decay, epsilon=self.epsilon, beta=self.beta, gamma=self.gamma, moving_mean=self.moving_mean, + moving_var=self.moving_var, num_features=self.num_features, data_format=self.data_format, + is_train=self.is_train + ) + + self.act_init_flag = False + if self.act: + self.act_init_flag = True + + def forward(self, inputs): + if self._forward_state == False: + if self._built == False: + self.build(tl.get_tensor_shape(inputs)) + self._built = True + self._forward_state = True + + if not self.is_train: + self.batchnorm = tl.ops.BatchNorm( + decay=self.decay, epsilon=self.epsilon, beta=self.beta, gamma=self.gamma, moving_mean=self.moving_mean, + moving_var=self.moving_var, num_features=self.num_features, data_format=self.data_format, + is_train=False + ) + outputs = self.batchnorm(inputs=inputs) + if self.act_init_flag: + outputs = self.act(outputs) + return outputs + + +class BatchNorm1d(BatchNorm): + """The :class:`BatchNorm1d` applies Batch Normalization over 2D/3D input (a mini-batch of 1D + inputs (optional) with additional channel dimension), of shape (N, C) or (N, L, C) or (N, C, L). + See more details in :class:`BatchNorm`. + + Examples + --------- + With TensorLayer + + >>> # in static model, no need to specify num_features + >>> net = tl.layers.Input([None, 50, 32], name='input') + >>> net = tl.layers.BatchNorm1d()(net) + >>> # in dynamic model, build by specifying num_features + >>> conv = tl.layers.Conv1d(32, 5, 1, in_channels=3) + >>> bn = tl.layers.BatchNorm1d(num_features=32) + + """ + + def _check_input_shape(self, inputs): + if inputs.ndim != 2 and inputs.ndim != 3: + raise ValueError('expected input to be 2D or 3D, but got {}D input'.format(inputs.ndim)) + + +class BatchNorm2d(BatchNorm): + """The :class:`BatchNorm2d` applies Batch Normalization over 4D input (a mini-batch of 2D + inputs with additional channel dimension) of shape (N, H, W, C) or (N, C, H, W). + See more details in :class:`BatchNorm`. + + Examples + --------- + With TensorLayer + + >>> # in static model, no need to specify num_features + >>> net = tl.layers.Input([None, 50, 50, 32], name='input') + >>> net = tl.layers.BatchNorm2d()(net) + >>> # in dynamic model, build by specifying num_features + >>> conv = tl.layers.Conv2d(32, (5, 5), (1, 1), in_channels=3) + >>> bn = tl.layers.BatchNorm2d(num_features=32) + + """ + + def _check_input_shape(self, inputs): + if inputs.ndim != 4: + raise ValueError('expected input to be 4D, but got {}D input'.format(inputs.ndim)) + + +class BatchNorm3d(BatchNorm): + """The :class:`BatchNorm3d` applies Batch Normalization over 5D input (a mini-batch of 3D + inputs with additional channel dimension) with shape (N, D, H, W, C) or (N, C, D, H, W). + See more details in :class:`BatchNorm`. + + Examples + --------- + With TensorLayer + + >>> # in static model, no need to specify num_features + >>> net = tl.layers.Input([None, 50, 50, 50, 32], name='input') + >>> net = tl.layers.BatchNorm3d()(net) + >>> # in dynamic model, build by specifying num_features + >>> conv = tl.layers.Conv3d(32, (5, 5, 5), (1, 1), in_channels=3) + >>> bn = tl.layers.BatchNorm3d(num_features=32) + + """ + + def _check_input_shape(self, inputs): + if inputs.ndim != 5: + raise ValueError('expected input to be 5D, but got {}D input'.format(inputs.ndim)) diff --git a/tensorlayer/layers/padding.py b/tensorlayer/layers/padding.py new file mode 100644 index 0000000..5a21b50 --- /dev/null +++ b/tensorlayer/layers/padding.py @@ -0,0 +1,235 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module + +__all__ = [ + 'PadLayer', + 'ZeroPad1d', + 'ZeroPad2d', + 'ZeroPad3d', +] + + +class PadLayer(Module): + """The :class:`PadLayer` class is a padding layer for any mode and dimension. + Please see `tf.pad `__ for usage. + + Parameters + ---------- + padding : list of lists of 2 ints, or a Tensor of type int32. + The int32 values to pad. + mode : str + "CONSTANT", "REFLECT", or "SYMMETRIC" (case-insensitive). + name : None or str + A unique layer name. + + Examples + -------- + With TensorLayer + + >>> net = tl.layers.Input([None, 224, 224, 3], name='input') + >>> padlayer = tl.layers.PadLayer([[0, 0], [3, 3], [3, 3], [0, 0]], "REFLECT", name='inpad')(net) + >>> print(padlayer) + >>> output shape : (None, 230, 230, 3) + + """ + + def __init__( + self, + padding=None, + mode='CONSTANT', + name=None, # 'pad_layer', + ): + super().__init__(name) + self.padding = padding + self.mode = mode + + logging.info("PadLayer %s: padding: %s mode: %s" % (self.name, self.padding, self.mode)) + + if self.padding is None: + raise Exception( + "padding should be a Tensor of type int32. see https://www.tensorflow.org/versions/r2.0/api_docs/python/tf/pad" + ) + + self.build() + self._built = True + + def __repr__(self): + s = '{classname}(padding={padding}, mode={mode}' + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + self.pad = tl.ops.Pad(paddings=self.padding, mode=self.mode) + + def forward(self, inputs): + outputs = self.pad(inputs) + return outputs + + +class ZeroPad1d(Module): + """ + The :class:`ZeroPad1d` class is a 1D padding layer for signal [batch, length, channel]. + + Parameters + ---------- + padding : int, or tuple of 2 ints + - If int, zeros to add at the beginning and end of the padding dimension (axis 1). + - If tuple of 2 ints, zeros to add at the beginning and at the end of the padding dimension. + name : None or str + A unique layer name. + + Examples + -------- + With TensorLayer + + >>> net = tl.layers.Input([None, 100, 1], name='input') + >>> pad1d = tl.layers.ZeroPad1d(padding=(3, 3))(net) + >>> print(pad1d) + >>> output shape : (None, 106, 1) + + """ + + def __init__( + self, + padding, + name=None, # 'zeropad1d', + ): + super().__init__(name) + self.padding = padding + logging.info("ZeroPad1d %s: padding: %s" % (self.name, str(padding))) + + if not isinstance(self.padding, (int, tuple, dict)): + raise AssertionError() + + self.build() + self._built = True + + def __repr__(self): + s = '{classname}(padding={padding}' + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + self.layer = tl.ops.ZeroPadding1D(padding=self.padding) + + def forward(self, inputs): + outputs = self.layer(inputs) + return outputs + + +class ZeroPad2d(Module): + """ + The :class:`ZeroPad2d` class is a 2D padding layer for image [batch, height, width, channel]. + + Parameters + ---------- + padding : tuple of 2 ints or int, or tuple of 2 tuples of 2 ints. + - If int, the same symmetric padding is applied to width and height. + - If tuple of 2 ints, interpreted as two different symmetric padding values for height and width as ``(symmetric_height_pad, symmetric_width_pad)``. + - If tuple of 2 tuples of 2 ints, interpreted as ``((top_pad, bottom_pad), (left_pad, right_pad))``. + name : None or str + A unique layer name. + + Examples + -------- + With TensorLayer + + >>> net = tl.layers.Input([None, 100, 100, 3], name='input') + >>> pad2d = tl.layers.ZeroPad2d(padding=((3, 3), (4, 4)))(net) + >>> print(pad2d) + >>> output shape : (None, 106, 108, 3) + + """ + + def __init__( + self, + padding, + name=None, # 'zeropad2d', + ): + super().__init__(name) + + self.padding = padding + logging.info("ZeroPad2d %s: padding: %s" % (self.name, str(self.padding))) + + if not isinstance(self.padding, (int, tuple)): + raise AssertionError("Padding should be of type `int` or `tuple`") + + self.build() + self._built = True + + def __repr__(self): + s = '{classname}(padding={padding}' + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + self.layer = tl.ops.ZeroPadding2D(padding=self.padding) + + def forward(self, inputs): + outputs = self.layer(inputs) + return outputs + + +class ZeroPad3d(Module): + """ + The :class:`ZeroPad3d` class is a 3D padding layer for volume [batch, depth, height, width, channel]. + + Parameters + ---------- + padding : int, or tuple of 2 ints, or tuple of 2 tuples of 2 ints. + - If int, the same symmetric padding is applied to width and height. + - If tuple of 2 ints, interpreted as two different symmetric padding values for height and width as ``(symmetric_dim1_pad, symmetric_dim2_pad, symmetric_dim3_pad)``. + - If tuple of 2 tuples of 2 ints, interpreted as ``((left_dim1_pad, right_dim1_pad), (left_dim2_pad, right_dim2_pad), (left_dim3_pad, right_dim3_pad))``. + name : None or str + A unique layer name. + + Examples + -------- + With TensorLayer + + >>> net = tl.layers.Input([None, 100, 100, 100, 3], name='input') + >>> pad3d = tl.layers.ZeroPad3d(padding=((3, 3), (4, 4), (5, 5)))(net) + >>> print(pad3d) + >>> output shape : (None, 106, 108, 110, 3) + + """ + + def __init__( + self, + padding, + name=None, # 'zeropad3d', + ): + super().__init__(name) + self.padding = padding + + logging.info("ZeroPad3d %s: padding: %s" % (self.name, str(self.padding))) + + if not isinstance(self.padding, (int, tuple)): + raise AssertionError() + + self.build() + self._built = True + + def __repr__(self): + s = '{classname}(padding={padding}' + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + self.layer = tl.ops.ZeroPadding3D(padding=self.padding) + + def forward(self, inputs): + outputs = self.layer(inputs) + return outputs diff --git a/tensorlayer/layers/pooling.py b/tensorlayer/layers/pooling.py new file mode 100644 index 0000000..51cc9a7 --- /dev/null +++ b/tensorlayer/layers/pooling.py @@ -0,0 +1,979 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module + + +__all__ = [ + 'PoolLayer', + 'MaxPool1d', + 'MeanPool1d', + 'MaxPool2d', + 'MeanPool2d', + 'MaxPool3d', + 'MeanPool3d', + 'GlobalMaxPool1d', + 'GlobalMeanPool1d', + 'GlobalMaxPool2d', + 'GlobalMeanPool2d', + 'GlobalMaxPool3d', + 'GlobalMeanPool3d', + 'CornerPool2d', +] + + +class PoolLayer(Module): + """ + The :class:`PoolLayer` class is a Pooling layer. + You can choose ``tl.ops.max_pool`` and ``tl.ops.avg_pool`` for 2D input or + ``tl.ops.max_pool3d`` and ``tl.ops.avg_pool3d`` for 3D input. + + Parameters + ---------- + filter_size : tuple of int + The size of the window for each dimension of the input tensor. + Note that: len(filter_size) >= 4. + strides : tuple of int + The stride of the sliding window for each dimension of the input tensor. + Note that: len(strides) >= 4. + padding : str + The padding algorithm type: "SAME" or "VALID". + pool : pooling function + One of ``tl.ops.max_pool``, ``tl.ops.avg_pool``, ``tl.ops.max_pool3d`` and ``f.ops.avg_pool3d``. + See `TensorFlow pooling APIs `__ + name : None or str + A unique layer name. + + Examples + --------- + With TensorLayer + + >>> net = tl.layers.Input([None, 50, 50, 32], name='input') + >>> net = tl.layers.PoolLayer()(net) + >>> output shape : [None, 25, 25, 32] + + """ + + def __init__( + self, + filter_size=(1, 2, 2, 1), + strides=(1, 2, 2, 1), + padding='SAME', + pool=tl.ops.MaxPool, + name=None # 'pool_pro', + ): + super().__init__(name) + self.filter_size = filter_size + self.strides = strides + self.padding = padding + self.pool = pool + + self.build() + self._built = True + + logging.info( + "PoolLayer %s: filter_size: %s strides: %s padding: %s pool: %s" % + (self.name, str(self.filter_size), str(self.strides), self.padding, pool.__name__) + ) + + def __repr__(self): + s = '{classname}(pool={poolname}, filter_size={strides}, padding={padding}' + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, poolname=self.pool.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + self._pool = self.pool(ksize=self.filter_size, strides=self.strides, padding=self.padding) + + def forward(self, inputs): + outputs = self._pool(inputs) + return outputs + + +class MaxPool1d(Module): + """Max pooling for 1D signal. + + Parameters + ---------- + filter_size : int + Pooling window size. + strides : int + Stride of the pooling operation. + padding : str + The padding method: 'VALID' or 'SAME'. + data_format : str + One of channels_last (default, [batch, length, channel]) or channels_first. The ordering of the dimensions in the inputs. + name : None or str + A unique layer name. + + Examples + --------- + With TensorLayer + + >>> net = tl.layers.Input([None, 50, 32], name='input') + >>> net = tl.layers.MaxPool1d(filter_size=3, strides=2, padding='SAME', name='maxpool1d')(net) + >>> output shape : [None, 25, 32] + + """ + + def __init__( + self, + filter_size=3, + strides=2, + padding='SAME', + data_format='channels_last', + dilation_rate=1, + name=None # 'maxpool1d' + ): + super().__init__(name) + self.filter_size = self._filter_size = filter_size + self.strides = self._strides = strides + self.padding = padding + self.data_format = data_format + self.dilation_rate = self._dilation_rate = dilation_rate + + self.build() + self._built = True + + logging.info( + "MaxPool1d %s: filter_size: %s strides: %s padding: %s" % + (self.name, str(filter_size), str(strides), str(padding)) + ) + + def __repr__(self): + s = ('{classname}(filter_size={filter_size}' ', strides={strides}, padding={padding}') + if self.dilation_rate != 1: + s += ', dilation={dilation_rate}' + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + # https://tensorflow.google.cn/versions/r2.0/api_docs/python/tf/nn/pool + if self.data_format == 'channels_last': + self.data_format = 'NWC' + elif self.data_format == 'channels_first': + self.data_format = 'NCW' + else: + raise Exception("unsupported data format") + self._filter_size = [self.filter_size] + self._strides = [self.strides] + self._dilation_rate = [self.dilation_rate] + + def forward(self, inputs): + outputs = tl.ops.pool( + input=inputs, + window_shape=self._filter_size, + pooling_type="MAX", + strides=self._strides, + padding=self.padding, + data_format=self.data_format, + dilations=self._dilation_rate, + ) + return outputs + + +class MeanPool1d(Module): + """Mean pooling for 1D signal. + + Parameters + ------------ + filter_size : int + Pooling window size. + strides : int + Strides of the pooling operation. + padding : str + The padding method: 'VALID' or 'SAME'. + data_format : str + One of channels_last (default, [batch, length, channel]) or channels_first. The ordering of the dimensions in the inputs. + name : None or str + A unique layer name. + + Examples + --------- + With TensorLayer + + >>> net = tl.layers.Input([None, 50, 32], name='input') + >>> net = tl.layers.MeanPool1d(filter_size=3, strides=2, padding='SAME')(net) + >>> output shape : [None, 25, 32] + + """ + + def __init__( + self, + filter_size=3, + strides=2, + padding='SAME', + data_format='channels_last', + dilation_rate=1, + name=None # 'meanpool1d' + ): + super().__init__(name) + self.filter_size = self._filter_size = filter_size + self.strides = self._strides = strides + self.padding = padding + self.data_format = data_format + self.dilation_rate = self._dilation_rate = dilation_rate + + self.build() + self._built = True + + logging.info( + "MeanPool1d %s: filter_size: %s strides: %s padding: %s" % + (self.name, str(filter_size), str(strides), str(padding)) + ) + + def __repr__(self): + s = ('{classname}(filter_size={filter_size}' ', strides={strides}, padding={padding}') + if self.dilation_rate != 1: + s += ', dilation={dilation_rate}' + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + # https://tensorflow.google.cn/versions/r2.0/api_docs/python/tf/nn/pool + if self.data_format == 'channels_last': + self.data_format = 'NWC' + elif self.data_format == 'channels_first': + self.data_format = 'NCW' + else: + raise Exception("unsupported data format") + self._filter_size = [self.filter_size] + self._strides = [self.strides] + self._dilation_rate = [self.dilation_rate] + + def forward(self, inputs): + outputs = tl.ops.pool( + input=inputs, window_shape=self._filter_size, pooling_type="AVG", padding=self.padding, + dilations=self._dilation_rate, strides=self._strides, data_format=self.data_format + ) + return outputs + + +class MaxPool2d(Module): + """Max pooling for 2D image. + + Parameters + ----------- + filter_size : tuple of int + (height, width) for filter size. + strides : tuple of int + (height, width) for strides. + padding : str + The padding method: 'VALID' or 'SAME'. + data_format : str + One of channels_last (default, [batch, height, width, channel]) or channels_first. The ordering of the dimensions in the inputs. + name : None or str + A unique layer name. + + Examples + --------- + With TensorLayer + + >>> net = tl.layers.Input([None, 50, 50, 32], name='input') + >>> net = tl.layers.MaxPool2d(filter_size=(3, 3), strides=(2, 2), padding='SAME')(net) + >>> output shape : [None, 25, 25, 32] + + """ + + def __init__( + self, + filter_size=(3, 3), + strides=(2, 2), + padding='SAME', + data_format='channels_last', + name=None # 'maxpool2d' + ): + super().__init__(name) + self.filter_size = filter_size + if strides is None: + strides = filter_size + self.strides = self._strides = strides + self.padding = padding + self.data_format = data_format + + self.build() + self._built = True + + logging.info( + "MaxPool2d %s: filter_size: %s strides: %s padding: %s" % + (self.name, str(filter_size), str(strides), str(padding)) + ) + + def __repr__(self): + s = ('{classname}(filter_size={filter_size}' ', strides={strides}, padding={padding}') + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + if self.data_format == 'channels_last': + self.data_format = 'NHWC' + self._strides = [1, self.strides[0], self.strides[1], 1] + elif self.data_format == 'channels_first': + self.data_format = 'NCHW' + self._strides = [1, 1, self.strides[0], self.strides[1]] + else: + raise Exception("unsupported data format") + + self.max_pool = tl.ops.MaxPool( + ksize=self.filter_size, strides=self._strides, padding=self.padding, data_format=self.data_format + ) + + def forward(self, inputs): + outputs = self.max_pool(inputs) + return outputs + + +class MeanPool2d(Module): + """Mean pooling for 2D image [batch, height, width, channel]. + + Parameters + ----------- + filter_size : tuple of int + (height, width) for filter size. + strides : tuple of int + (height, width) for strides. + padding : str + The padding method: 'VALID' or 'SAME'. + data_format : str + One of channels_last (default, [batch, height, width, channel]) or channels_first. The ordering of the dimensions in the inputs. + name : None or str + A unique layer name. + + Examples + --------- + With TensorLayer + + >>> net = tl.layers.Input([None, 50, 50, 32], name='input') + >>> net = tl.layers.MeanPool2d(filter_size=(3, 3), strides=(2, 2), padding='SAME')(net) + >>> output shape : [None, 25, 25, 32] + + """ + + def __init__( + self, + filter_size=(3, 3), + strides=(2, 2), + padding='SAME', + data_format='channels_last', + name=None # 'meanpool2d' + ): + super().__init__(name) + self.filter_size = filter_size + if strides is None: + strides = filter_size + self.strides = self._strides = strides + self.padding = padding + self.data_format = data_format + + self.build() + self._built = True + + logging.info( + "MeanPool2d %s: filter_size: %s strides: %s padding: %s" % + (self.name, str(filter_size), str(strides), str(padding)) + ) + + def __repr__(self): + s = ('{classname}(filter_size={filter_size}' ', strides={strides}, padding={padding}') + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + if self.data_format == 'channels_last': + self.data_format = 'NHWC' + self._strides = [1, self.strides[0], self.strides[1], 1] + elif self.data_format == 'channels_first': + self.data_format = 'NCHW' + self._strides = [1, 1, self.strides[0], self.strides[1]] + else: + raise Exception("unsupported data format") + self.avg_pool = tl.ops.AvgPool( + ksize=self.filter_size, strides=self._strides, padding=self.padding, data_format=self.data_format + ) + + def forward(self, inputs): + outputs = self.avg_pool(inputs) + return outputs + + +class MaxPool3d(Module): + """Max pooling for 3D volume. + + Parameters + ------------ + filter_size : tuple of int + Pooling window size. + strides : tuple of int + Strides of the pooling operation. + padding : str + The padding method: 'VALID' or 'SAME'. + data_format : str + One of channels_last (default, [batch, depth, height, width, channel]) or channels_first. The ordering of the dimensions in the inputs. + name : None or str + A unique layer name. + + Returns + ------- + :class:`tf.Tensor` + A max pooling 3-D layer with a output rank as 5. + + Examples + --------- + With TensorLayer + + >>> net = tl.layers.Input([None, 50, 50, 50, 32], name='input') + >>> net = tl.layers.MaxPool3d(filter_size=(3, 3, 3), strides=(2, 2, 2), padding='SAME')(net) + >>> output shape : [None, 25, 25, 25, 32] + + """ + + def __init__( + self, + filter_size=(3, 3, 3), + strides=(2, 2, 2), + padding='VALID', + data_format='channels_last', + name=None # 'maxpool3d' + ): + super().__init__(name) + self.filter_size = filter_size + self.strides = self._strides = strides + self.padding = padding + self.data_format = data_format + + self.build() + self._built = True + + logging.info( + "MaxPool3d %s: filter_size: %s strides: %s padding: %s" % + (self.name, str(filter_size), str(strides), str(padding)) + ) + + def __repr__(self): + s = ('{classname}(filter_size={filter_size}' ', strides={strides}, padding={padding}') + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + if self.data_format == 'channels_last': + self.data_format = 'NDHWC' + self._strides = [1, self.strides[0], self.strides[1], self.strides[2], 1] + elif self.data_format == 'channels_first': + self.data_format = 'NCDHW' + self._strides = [1, 1, self.strides[0], self.strides[1], self.strides[2]] + else: + raise Exception("unsupported data format") + + def forward(self, inputs): + outputs = tl.ops.max_pool3d( + input=inputs, + ksize=self.filter_size, + strides=self._strides, + padding=self.padding, + data_format=self.data_format, + ) + return outputs + + +class MeanPool3d(Module): + """Mean pooling for 3D volume. + + Parameters + ------------ + filter_size : tuple of int + Pooling window size. + strides : tuple of int + Strides of the pooling operation. + padding : str + The padding method: 'VALID' or 'SAME'. + data_format : str + One of channels_last (default, [batch, depth, height, width, channel]) or channels_first. The ordering of the dimensions in the inputs. + name : None or str + A unique layer name. + + Returns + ------- + :class:`tf.Tensor` + A mean pooling 3-D layer with a output rank as 5. + + Examples + --------- + With TensorLayer + + >>> net = tl.layers.Input([None, 50, 50, 50, 32], name='input') + >>> net = tl.layers.MeanPool3d(filter_size=(3, 3, 3), strides=(2, 2, 2), padding='SAME')(net) + >>> output shape : [None, 25, 25, 25, 32] + + """ + + def __init__( + self, + filter_size=(3, 3, 3), + strides=(2, 2, 2), + padding='VALID', + data_format='channels_last', + name=None # 'meanpool3d' + ): + super().__init__(name) + self.filter_size = filter_size + self.strides = self._strides = strides + self.padding = padding + self.data_format = data_format + + self.build() + self._built = True + + logging.info( + "MeanPool3d %s: filter_size: %s strides: %s padding: %s" % + (self.name, str(filter_size), str(strides), str(padding)) + ) + + def __repr__(self): + s = ('{classname}(filter_size={filter_size}' ', strides={strides}, padding={padding}') + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + self._strides = [1, self.strides[0], self.strides[1], self.strides[2], 1] + if self.data_format == 'channels_last': + self.data_format = 'NDHWC' + elif self.data_format == 'channels_first': + self.data_format = 'NCDHW' + else: + raise Exception("unsupported data format") + + def forward(self, inputs): + outputs = tl.ops.avg_pool3d( + input=inputs, + ksize=self.filter_size, + strides=self._strides, + padding=self.padding, + data_format=self.data_format, + ) + return outputs + + +class GlobalMaxPool1d(Module): + """The :class:`GlobalMaxPool1d` class is a 1D Global Max Pooling layer. + + Parameters + ------------ + data_format : str + One of channels_last (default, [batch, length, channel]) or channels_first. The ordering of the dimensions in the inputs. + name : None or str + A unique layer name. + + Examples + --------- + With TensorLayer + + >>> net = tl.layers.Input([None, 100, 30], name='input') + >>> net = tl.layers.GlobalMaxPool1d()(net) + >>> output shape : [None, 30] + + """ + + def __init__( + self, + data_format="channels_last", + name=None # 'globalmaxpool1d' + ): + super().__init__(name) + + self.data_format = data_format + + self.build() + self._built = True + + logging.info("GlobalMaxPool1d %s" % self.name) + + def __repr__(self): + s = '{classname}(' + if self.name is not None: + s += 'name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + if self.data_format == 'channels_last': + self.reduce_max = tl.ReduceMax(axis=1) + elif self.data_format == 'channels_first': + self.reduce_max = tl.ReduceMax(axis=2) + else: + raise ValueError( + "`data_format` should have one of the following values: [`channels_last`, `channels_first`]" + ) + + def forward(self, inputs): + outputs = self.reduce_max(inputs) + return outputs + + +class GlobalMeanPool1d(Module): + """The :class:`GlobalMeanPool1d` class is a 1D Global Mean Pooling layer. + + Parameters + ------------ + data_format : str + One of channels_last (default, [batch, length, channel]) or channels_first. The ordering of the dimensions in the inputs. + name : None or str + A unique layer name. + + Examples + --------- + With TensorLayer + + >>> net = tl.layers.Input([None, 100, 30], name='input') + >>> net = tl.layers.GlobalMeanPool1d()(net) + >>> output shape : [None, 30] + + """ + + def __init__( + self, + data_format='channels_last', + name=None # 'globalmeanpool1d' + ): + super().__init__(name) + self.data_format = data_format + + self.build() + self._built = True + + logging.info("GlobalMeanPool1d %s" % self.name) + + def __repr__(self): + s = '{classname}(' + if self.name is not None: + s += 'name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + if self.data_format == 'channels_last': + self.reduce_mean = tl.ReduceMean(axis=1) + elif self.data_format == 'channels_first': + self.reduce_mean = tl.ReduceMean(axis=2) + else: + raise ValueError( + "`data_format` should have one of the following values: [`channels_last`, `channels_first`]" + ) + + def forward(self, inputs): + outputs = self.reduce_mean(inputs) + return outputs + + +class GlobalMaxPool2d(Module): + """The :class:`GlobalMaxPool2d` class is a 2D Global Max Pooling layer. + + Parameters + ------------ + data_format : str + One of channels_last (default, [batch, height, width, channel]) or channels_first. The ordering of the dimensions in the inputs. + name : None or str + A unique layer name. + + Examples + --------- + With TensorLayer + + >>> net = tl.layers.Input([None, 100, 100, 30], name='input') + >>> net = tl.layers.GlobalMaxPool2d()(net) + >>> output shape : [None, 30] + + """ + + def __init__( + self, + data_format='channels_last', + name=None # 'globalmaxpool2d' + ): + super().__init__(name) + self.data_format = data_format + + self.build() + self._built = True + + logging.info("GlobalMaxPool2d %s" % self.name) + + def __repr__(self): + s = '{classname}(' + if self.name is not None: + s += 'name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + if self.data_format == 'channels_last': + self.reduce_max = tl.ReduceMax(axis=[1, 2]) + elif self.data_format == 'channels_first': + self.reduce_max = tl.ReduceMax(axis=[2, 3]) + else: + raise ValueError( + "`data_format` should have one of the following values: [`channels_last`, `channels_first`]" + ) + + def forward(self, inputs): + outputs = self.reduce_max(inputs) + return outputs + + +class GlobalMeanPool2d(Module): + """The :class:`GlobalMeanPool2d` class is a 2D Global Mean Pooling layer. + + Parameters + ------------ + data_format : str + One of channels_last (default, [batch, height, width, channel]) or channels_first. The ordering of the dimensions in the inputs. + name : None or str + A unique layer name. + + Examples + --------- + With TensorLayer + + >>> net = tl.layers.Input([None, 100, 100, 30], name='input') + >>> net = tl.layers.GlobalMeanPool2d()(net) + >>> output shape : [None, 30] + + """ + + def __init__( + self, + data_format='channels_last', + name=None # 'globalmeanpool2d' + ): + super().__init__(name) + + self.data_format = data_format + + self.build() + self._built = True + + logging.info("GlobalMeanPool2d %s" % self.name) + + def __repr__(self): + s = '{classname}(' + if self.name is not None: + s += 'name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + if self.data_format == 'channels_last': + self.reduce_mean = tl.ReduceMean(axis=[1, 2]) + elif self.data_format == 'channels_first': + self.reduce_mean = tl.ReduceMean(axis=[2, 3]) + else: + raise ValueError( + "`data_format` should have one of the following values: [`channels_last`, `channels_first`]" + ) + + def forward(self, inputs): + outputs = self.reduce_mean(inputs) + return outputs + + +class GlobalMaxPool3d(Module): + """The :class:`GlobalMaxPool3d` class is a 3D Global Max Pooling layer. + + Parameters + ------------ + data_format : str + One of channels_last (default, [batch, depth, height, width, channel]) or channels_first. The ordering of the dimensions in the inputs. + name : None or str + A unique layer name. + + Examples + --------- + With TensorLayer + + >>> net = tl.layers.Input([None, 100, 100, 100, 30], name='input') + >>> net = tl.layers.GlobalMaxPool3d()(net) + >>> output shape : [None, 30] + + """ + + def __init__( + self, + data_format='channels_last', + name=None # 'globalmaxpool3d' + ): + super().__init__(name) + + self.data_format = data_format + + self.build() + self._built = True + + logging.info("GlobalMaxPool3d %s" % self.name) + + def __repr__(self): + s = '{classname}(' + if self.name is not None: + s += 'name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + if self.data_format == 'channels_last': + self.reduce_max = tl.ReduceMax(axis=[1, 2, 3]) + elif self.data_format == 'channels_first': + self.reduce_max = tl.ReduceMax(axis=[2, 3, 4]) + else: + raise ValueError( + "`data_format` should have one of the following values: [`channels_last`, `channels_first`]" + ) + + def forward(self, inputs): + outputs = self.reduce_max(inputs) + return outputs + + +class GlobalMeanPool3d(Module): + """The :class:`GlobalMeanPool3d` class is a 3D Global Mean Pooling layer. + + Parameters + ------------ + data_format : str + One of channels_last (default, [batch, depth, height, width, channel]) or channels_first. The ordering of the dimensions in the inputs. + name : None or str + A unique layer name. + + Examples + --------- + With TensorLayer + + >>> net = tl.layers.Input([None, 100, 100, 100, 30], name='input') + >>> net = tl.layers.GlobalMeanPool3d()(net) + >>> output shape : [None, 30] + + """ + + def __init__( + self, + data_format='channels_last', + name=None # 'globalmeanpool3d' + ): + super().__init__(name) + self.data_format = data_format + + self.build() + self._built = True + + logging.info("GlobalMeanPool3d %s" % self.name) + + def __repr__(self): + s = '{classname}(' + if self.name is not None: + s += 'name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + pass + + def forward(self, inputs): + if self.data_format == 'channels_last': + outputs = tl.reduce_mean(input_tensor=inputs, axis=[1, 2, 3]) + elif self.data_format == 'channels_first': + outputs = tl.reduce_mean(input_tensor=inputs, axis=[2, 3, 4]) + else: + raise ValueError( + "`data_format` should have one of the following values: [`channels_last`, `channels_first`]" + ) + return outputs + + +class CornerPool2d(Module): + """Corner pooling for 2D image [batch, height, width, channel], see `here `__. + + Parameters + ---------- + mode : str + TopLeft for the top left corner, + Bottomright for the bottom right corner. + name : None or str + A unique layer name. + + Examples + --------- + With TensorLayer + + >>> net = tl.layers.Input([None, 32, 32, 8], name='input') + >>> net = tl.layers.CornerPool2d(mode='TopLeft',name='cornerpool2d')(net) + >>> output shape : [None, 32, 32, 8] + + """ + + def __init__( + self, + mode='TopLeft', + name=None # 'cornerpool2d' + ): + super().__init__(name) + self.mode = mode + self.build() + self._built = True + + logging.info("CornerPool2d %s : mode: %s" % (self.name, str(mode))) + + def __repr__(self): + s = ('{classname}(mode={mode}') + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + pass + + def forward(self, inputs): + _, input_width, input_height, _ = tl.get_tensor_shape(inputs) + # input_width = inputs.shape[2] + # input_height = inputs.shape[1] + batch_min = tl.reduce_min(inputs) + if self.mode == 'TopLeft': + temp_bottom = tl.pad( + inputs, tl.constant([[0, 0], [0, input_height - 1], [0, 0], [0, 0]]), constant_values=batch_min + ) + temp_right = tl.pad( + inputs, tl.constant([[0, 0], [0, 0], [0, input_width - 1], [0, 0]]), constant_values=batch_min + ) + temp_bottom = tl.ops.max_pool(temp_bottom, ksize=(input_height, 1), strides=(1, 1), padding='VALID') + temp_right = tl.ops.max_pool(temp_right, ksize=(1, input_width), strides=(1, 1), padding='VALID') + outputs = tl.add(temp_bottom, temp_right)#, name=self.name) + elif self.mode == 'BottomRight': + temp_top = tl.pad( + inputs, tl.constant([[0, 0], [input_height - 1, 0], [0, 0], [0, 0]]), constant_values=batch_min + ) + temp_left = tl.pad( + inputs, tl.constant([[0, 0], [0, 0], [input_width - 1, 0], [0, 0]]), constant_values=batch_min + ) + temp_top = tl.ops.max_pool(temp_top, ksize=(input_height, 1), strides=(1, 1), padding='VALID') + temp_left = tl.ops.max_pool(temp_left, ksize=(1, input_width), strides=(1, 1), padding='VALID') + outputs = tl.add(temp_top, temp_left) + else: + outputs = tl.identity(inputs) + return outputs + +if __name__ == '__main__': + net = tl.layers.Input([None, 32, 32, 8], name='input') + net = CornerPool2d(mode='TopLeft',name='cornerpool2d')(net) + print(net) \ No newline at end of file diff --git a/tensorlayer/layers/quantize.py b/tensorlayer/layers/quantize.py new file mode 100644 index 0000000..1a64f63 --- /dev/null +++ b/tensorlayer/layers/quantize.py @@ -0,0 +1,49 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +import os +os.environ['TL_BACKEND'] = 'tensorflow' + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module +from tensorlayer.layers.utils import quantize + +__all__ = [ + 'Sign', +] + + +class Sign(Module): + """The :class:`SignLayer` class is for quantizing the layer outputs to -1 or 1 while inferencing. + + Parameters + ---------- + name : a str + A unique layer name. + + """ + + # @deprecated_alias(layer='prev_layer', end_support_version=1.9) # TODO remove this line for the 1.9 release + def __init__( + self, + name=None # 'sign', + ): + super().__init__(name) + logging.info("Sign %s" % self.name) + + self.build() + self._built = True + + def build(self, inputs_shape=None): + pass + + def __repr__(self): + s = ('{classname}(') + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def forward(self, inputs): + outputs = quantize(inputs) + return outputs diff --git a/tensorlayer/layers/recurrent.py b/tensorlayer/layers/recurrent.py new file mode 100644 index 0000000..23d611a --- /dev/null +++ b/tensorlayer/layers/recurrent.py @@ -0,0 +1,8 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import numpy as np + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module diff --git a/tensorlayer/layers/scale.py b/tensorlayer/layers/scale.py new file mode 100644 index 0000000..b86dcae --- /dev/null +++ b/tensorlayer/layers/scale.py @@ -0,0 +1,57 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module + +__all__ = [ + 'Scale', +] + + +class Scale(Module): + """The :class:`Scale` class is to multiple a trainable scale value to the layer outputs. Usually be used on the output of binary net. + + Parameters + ---------- + init_scale : float + The initial value for the scale factor. + name : a str + A unique layer name. + + Examples + ---------- + >>> inputs = tl.layers.Input([8, 3]) + >>> dense = tl.layers.Dense(n_units=10, in_channels=3)(inputs) + >>> outputs = tl.layers.Scale(init_scale=0.5)(dense) + + """ + + def __init__( + self, + init_scale=0.05, + name='scale', + ): + super(Scale, self).__init__(name) + self.init_scale = init_scale + + self.build((None, )) + self._built = True + + logging.info("Scale %s: init_scale: %f" % (self.name, self.init_scale)) + + def __repr__(self): + s = '{classname}(' + s += 'init_scale={init_scale},' + s += 'name={name}' + s += ")" + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + self.scale = self._get_weights("scale", shape=[1], init=tl.initializers.constant(value=self.init_scale)) + + # @tf.function + def forward(self, inputs): + outputs = inputs * self.scale + return outputs diff --git a/tensorlayer/layers/shape.py b/tensorlayer/layers/shape.py new file mode 100644 index 0000000..477847d --- /dev/null +++ b/tensorlayer/layers/shape.py @@ -0,0 +1,219 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +from tensorlayer import logging +from tensorlayer.layers.core import Module +import tensorlayer as tl + +__all__ = [ + 'Flatten', + 'Reshape', + 'Transpose', + 'Shuffle', +] + + +class Flatten(Module): + """A layer that reshapes high-dimension input into a vector. + + Then we often apply Dense, RNN, Concat and etc on the top of a flatten layer. + [batch_size, mask_row, mask_col, n_mask] ---> [batch_size, mask_row * mask_col * n_mask] + + Parameters + ---------- + name : None or str + A unique layer name. + + Examples + -------- + >>> x = tl.layers.Input([8, 4, 3], name='input') + >>> y = tl.layers.Flatten(name='flatten')(x) + [8, 12] + + """ + + def __init__(self, name=None): #'flatten'): + super(Flatten, self).__init__(name) + + self.build() + self._built = True + + logging.info("Flatten %s:" % (self.name)) + + def __repr__(self): + s = '{classname}(' + s += 'name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + self.flatten_reshape = tl.ops.FlattenReshape() + + # @tf.function + def forward(self, inputs): + outputs = self.flatten_reshape(inputs) + return outputs + + +class Reshape(Module): + """A layer that reshapes a given tensor. + + Parameters + ---------- + shape : tuple of int + The output shape, see ``tf.reshape``. + name : str + A unique layer name. + + Examples + -------- + >>> x = tl.layers.Input([8, 4, 3], name='input') + >>> y = tl.layers.Reshape(shape=[-1, 12], name='reshape')(x) + (8, 12) + + """ + + def __init__(self, shape, name=None): #'reshape'): + super(Reshape, self).__init__(name) + self.shape = shape + + logging.info("Reshape %s" % (self.name)) + + self.build() + self._built = True + + def __repr__(self): + s = '{classname}(' + s += 'shape={shape},' + s += 'name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + self.reshape = tl.ops.Reshape(self.shape) + + def forward(self, inputs): + outputs = self.reshape(inputs) + return outputs + + +class Transpose(Module): + """A layer that transposes the dimension of a tensor. + + See `tf.transpose() `__ . + + Parameters + ---------- + perm: list of int + The permutation of the dimensions, similar with ``numpy.transpose``. + If None, it is set to (n-1...0), where n is the rank of the input tensor. + conjugate: bool + By default False. If True, returns the complex conjugate of complex numbers (and transposed) + For example [[1+1j, 2+2j]] --> [[1-1j], [2-2j]] + name : str + A unique layer name. + + Examples + ---------- + >>> x = tl.layers.Input([8, 4, 3], name='input') + >>> y = tl.layers.Transpose(perm=[0, 2, 1], conjugate=False, name='trans')(x) + (8, 3, 4) + + """ + + def __init__(self, perm=None, conjugate=False, name=None): #'transpose'): + super(Transpose, self).__init__(name) + self.perm = perm + self.conjugate = conjugate + + logging.info("Transpose %s: perm: %s, conjugate: %s" % (self.name, self.perm, self.conjugate)) + + self.build() + self._built = True + + def __repr__(self): + s = '{classname}(' + s += 'perm={perm},' + s += 'conjugate={conjugate},' + s += 'name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + self.transpose = tl.ops.Transpose(perm=self.perm, conjugate=self.conjugate) + + # @tf.function + def forward(self, inputs): + outputs = self.transpose(a=inputs) + return outputs + + +class Shuffle(Module): + """A layer that shuffle a 2D image [batch, height, width, channel], see `here `__. + + Parameters + ---------- + group: int + The number of groups. + name : str + A unique layer name. + + Examples + -------- + >>> x = tl.layers.Input([1, 16, 16, 8], name='input') + >>> y = tl.layers.Shuffle(group=2, name='shuffle')(x) + (1, 16, 16, 8) + + """ + + def __init__(self, group, in_channels=None, name=None): #'reshape'): + super(Shuffle, self).__init__(name) + self.group = group + self.inchannels = in_channels + + logging.info("Shuffle %s" % (self.name)) + + self.build() + self._built = True + + def __repr__(self): + s = '{classname}(' + s += 'group={group},' + s += 'name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape=None): + self.transpose = tl.ops.Transpose([0, 1, 2, 4, 3]) + inputs_shape = self.inchannels + if tl.BACKEND == 'mindspore' and inputs_shape == None: + raise ValueError("Do you forget to pass the keyword argument 'in_channels") + if tl.BACKEND == 'mindspore': + h, w, in_channel = inputs_shape[1:] + if in_channel % self.group != 0: + raise ValueError( + "The in_channel must be a multiple of the number of groups. The in_channel got %d and the number of groups is %d." + % (in_channel, self.group) + ) + self.reshape1 = tl.ops.Reshape([-1, h, w, in_channel // self.group, self.group]) + self.reshape2 = tl.ops.Reshape([-1, h, w, in_channel]) + + def forward(self, inputs): + if tl.BACKEND == 'tensorflow': + in_shape = tl.get_tensor_shape(inputs) + h, w, in_channel = in_shape[1:] + # if in_channel % self.group != 0: + # raise ValueError( + # "The in_channel must be a multiple of the number of groups. The in_channel got %d and the number of groups is %d." + # % (in_channel, self.group) + # ) + reshape1 = tl.ops.Reshape([-1, h, w, in_channel // self.group, self.group]) + temp = reshape1(inputs) + temp = self.transpose(temp) + reshape2 = tl.ops.Reshape([-1, h, w, in_channel]) + outputs = reshape2(temp) + else: + temp = self.reshape1(inputs) + temp = self.transpose(temp) + outputs = self.reshape2(temp) + return outputs diff --git a/tensorlayer/layers/spatial_transformer.py b/tensorlayer/layers/spatial_transformer.py new file mode 100644 index 0000000..23c94eb --- /dev/null +++ b/tensorlayer/layers/spatial_transformer.py @@ -0,0 +1,280 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import numpy as np +from six.moves import xrange +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module + +__all__ = [ + 'transformer', + 'batch_transformer', + 'SpatialTransformer2dAffine', +] + + +def transformer(U, theta, out_size, name='SpatialTransformer2dAffine'): + """Spatial Transformer Layer for `2D Affine Transformation `__ + , see :class:`SpatialTransformer2dAffine` class. + + Parameters + ---------- + U : list of float + The output of a convolutional net should have the + shape [num_batch, height, width, num_channels]. + theta: float + The output of the localisation network should be [num_batch, 6], value range should be [0, 1] (via tanh). + out_size: tuple of int + The size of the output of the network (height, width) + name: str + Optional function name + + Returns + ------- + Tensor + The transformed tensor. + + References + ---------- + - `Spatial Transformer Networks `__ + - `TensorFlow/Models `__ + + Notes + ----- + To initialize the network to the identity transform init. + + >>> import tensorflow as tf + >>> # ``theta`` to + >>> identity = np.array([[1., 0., 0.], [0., 1., 0.]]) + >>> identity = identity.flatten() + >>> theta = tf.Variable(initial_value=identity) + + """ + + def _repeat(x, n_repeats): + rep = tl.transpose(a=tl.expand_dims(tl.ones(shape=tl.stack([ + n_repeats, + ])), axis=1), perm=[1, 0]) + rep = tl.cast(rep, 'int32') + x = tl.matmul(tl.reshape(x, (-1, 1)), rep) + return tl.reshape(x, [-1]) + + def _interpolate(im, x, y, out_size): + # constants + num_batch, height, width, channels = tl.get_tensor_shape(im) + x = tl.cast(x, 'float32') + y = tl.cast(y, 'float32') + height_f = tl.cast(height, 'float32') + width_f = tl.cast(width, 'float32') + out_height = out_size[0] + out_width = out_size[1] + zero = tl.zeros([], dtype='int32') + max_y = tl.cast(height - 1, 'int32') + max_x = tl.cast(width - 1, 'int32') + + # scale indices from [-1, 1] to [0, width/height] + x = (x + 1.0) * (width_f) / 2.0 + y = (y + 1.0) * (height_f) / 2.0 + + # do sampling + x0 = tl.cast(tl.floor(x), 'int32') + x1 = x0 + 1 + y0 = tl.cast(tl.floor(y), 'int32') + y1 = y0 + 1 + + x0 = tl.clip_by_value(x0, zero, max_x) + x1 = tl.clip_by_value(x1, zero, max_x) + y0 = tl.clip_by_value(y0, zero, max_y) + y1 = tl.clip_by_value(y1, zero, max_y) + dim2 = width + dim1 = width * height + base = _repeat(tl.range(num_batch) * dim1, out_height * out_width) + base_y0 = base + y0 * dim2 + base_y1 = base + y1 * dim2 + idx_a = base_y0 + x0 + idx_b = base_y1 + x0 + idx_c = base_y0 + x1 + idx_d = base_y1 + x1 + + # use indices to lookup pixels in the flat image and restore + # channels dim + im_flat = tl.reshape(im, tl.stack([-1, channels])) + im_flat = tl.cast(im_flat, 'float32') + Ia = tl.gather(im_flat, idx_a) + Ib = tl.gather(im_flat, idx_b) + Ic = tl.gather(im_flat, idx_c) + Id = tl.gather(im_flat, idx_d) + + # and finally calculate interpolated values + x0_f = tl.cast(x0, 'float32') + x1_f = tl.cast(x1, 'float32') + y0_f = tl.cast(y0, 'float32') + y1_f = tl.cast(y1, 'float32') + wa = tl.expand_dims(((x1_f - x) * (y1_f - y)), 1) + wb = tl.expand_dims(((x1_f - x) * (y - y0_f)), 1) + wc = tl.expand_dims(((x - x0_f) * (y1_f - y)), 1) + wd = tl.expand_dims(((x - x0_f) * (y - y0_f)), 1) + output = tl.add_n([wa * Ia, wb * Ib, wc * Ic, wd * Id]) + return output + + def _meshgrid(height, width): + # This should be equivalent to: + # x_t, y_t = np.meshgrid(np.linspace(-1, 1, width), + # np.linspace(-1, 1, height)) + # ones = np.ones(np.prod(x_t.shape)) + # grid = np.vstack([x_t.flatten(), y_t.flatten(), ones]) + x_t = tl.matmul( + tl.ones(shape=tl.stack([height, 1])), + tl.transpose(a=tl.expand_dims(tl.linspace(-1.0, 1.0, width), 1), perm=[1, 0]) + ) + y_t = tl.matmul(tl.expand_dims(tl.linspace(-1.0, 1.0, height), 1), tl.ones(shape=tl.stack([1, width]))) + + x_t_flat = tl.reshape(x_t, (1, -1)) + y_t_flat = tl.reshape(y_t, (1, -1)) + + ones = tl.ones(shape=tl.get_tensor_shape(x_t_flat)) + grid = tl.concat(axis=0, values=[x_t_flat, y_t_flat, ones]) + return grid + + def _transform(theta, input_dim, out_size): + num_batch, _, _, num_channels = tl.get_tensor_shape(input_dim) + theta = tl.reshape(theta, (-1, 2, 3)) + theta = tl.cast(theta, 'float32') + + # grid of (x_t, y_t, 1), eq (1) in ref [1] + out_height = out_size[0] + out_width = out_size[1] + grid = _meshgrid(out_height, out_width) + grid = tl.expand_dims(grid, 0) + grid = tl.reshape(grid, [-1]) + grid = tl.tile(grid, tl.stack([num_batch])) + grid = tl.reshape(grid, tl.stack([num_batch, 3, -1])) + + # Transform A x (x_t, y_t, 1)^T -> (x_s, y_s) + T_g = tl.matmul(theta, grid) + x_s = tl.slice(T_g, [0, 0, 0], [-1, 1, -1]) + y_s = tl.slice(T_g, [0, 1, 0], [-1, 1, -1]) + x_s_flat = tl.reshape(x_s, [-1]) + y_s_flat = tl.reshape(y_s, [-1]) + + input_transformed = _interpolate(input_dim, x_s_flat, y_s_flat, out_size) + + output = tl.reshape(input_transformed, tl.stack([num_batch, out_height, out_width, num_channels])) + return output + + output = _transform(theta, U, out_size) + return output + + +def batch_transformer(U, thetas, out_size, name='BatchSpatialTransformer2dAffine'): + """Batch Spatial Transformer function for `2D Affine Transformation `__. + + Parameters + ---------- + U : list of float + tensor of inputs [batch, height, width, num_channels] + thetas : list of float + a set of transformations for each input [batch, num_transforms, 6] + out_size : list of int + the size of the output [out_height, out_width] + name : str + optional function name + + Returns + ------ + float + Tensor of size [batch * num_transforms, out_height, out_width, num_channels] + + """ + # with tf.compat.v1.variable_scope(name): + num_batch, num_transforms = map(int, thetas.get_shape().as_list()[:2]) + indices = [[i] * num_transforms for i in xrange(num_batch)] + input_repeated = tl.gather(U, tl.reshape(indices, [-1])) + return transformer(input_repeated, thetas, out_size) + + +class SpatialTransformer2dAffine(Module): + """The :class:`SpatialTransformer2dAffine` class is a 2D `Spatial Transformer Layer `__ for + `2D Affine Transformation `__. + + Parameters + ----------- + out_size : tuple of int or None + - The size of the output of the network (height, width), the feature maps will be resized by this. + in_channels : int + The number of in channels. + data_format : str + "channel_last" (NHWC, default) or "channels_first" (NCHW). + name : str + - A unique layer name. + + References + ----------- + - `Spatial Transformer Networks `__ + - `TensorFlow/Models `__ + + """ + + def __init__( + self, + out_size=(40, 40), + in_channels=None, + data_format='channel_last', + name=None, + ): + super(SpatialTransformer2dAffine, self).__init__(name) + + self.in_channels = in_channels + self.out_size = out_size + self.data_format = data_format + if self.in_channels is not None: + self.build(self.in_channels) + self._built = True + + logging.info("SpatialTransformer2dAffine %s" % self.name) + + def __repr__(self): + s = '{classname}(out_size={out_size}, ' + if self.in_channels is not None: + s += 'in_channels=\'{in_channels}\'' + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + if self.in_channels is None and len(inputs_shape) != 2: + raise AssertionError("The dimension of theta layer input must be rank 2, please reshape or flatten it") + if self.in_channels: + shape = [self.in_channels, 6] + else: + # self.in_channels = inputs_shape[1] # BUG + # shape = [inputs_shape[1], 6] + self.in_channels = inputs_shape[0][-1] # zsdonghao + shape = [self.in_channels, 6] + self.W = self._get_weights("weights", shape=tuple(shape), init=tl.initializers.Zeros()) + identity = np.reshape(np.array([[1, 0, 0], [0, 1, 0]], dtype=np.float32), newshape=(6, )) + self.b = self._get_weights("biases", shape=(6, ), init=tl.initializers.Constant(identity)) + + def forward(self, inputs): + """ + :param inputs: a tuple (theta_input, U). + - theta_input is of size [batch, in_channels]. We will use a :class:`Dense` to + make the theta size to [batch, 6], value range to [0, 1] (via tanh). + - U is the previous layer, which the affine transformation is applied to. + :return: tensor of size [batch, out_size[0], out_size[1], n_channels] after affine transformation, + n_channels is identical to that of U. + """ + theta_input, U = inputs + theta = tl.tanh(tl.matmul(theta_input, self.W) + self.b) + outputs = transformer(U, theta, out_size=self.out_size) + # automatically set batch_size and channels + # e.g. [?, 40, 40, ?] --> [64, 40, 40, 1] or [64, 20, 20, 4] + batch_size = theta_input.shape[0] + n_channels = U.shape[-1] + if self.data_format == 'channel_last': + outputs = tl.reshape(outputs, shape=[batch_size, self.out_size[0], self.out_size[1], n_channels]) + else: + raise Exception("unimplement data_format {}".format(self.data_format)) + return outputs diff --git a/tensorlayer/layers/stack.py b/tensorlayer/layers/stack.py new file mode 100644 index 0000000..570f805 --- /dev/null +++ b/tensorlayer/layers/stack.py @@ -0,0 +1,114 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.layers.core import Module + +__all__ = [ + 'Stack', + 'UnStack', +] + + +class Stack(Module): + """ + The :class:`Stack` class is a layer for stacking a list of rank-R tensors into one rank-(R+1) tensor, see `tf.stack() `__. + + Parameters + ---------- + axis : int + New dimension along which to stack. + name : str + A unique layer name. + + Examples + --------- + >>> import tensorflow as tf + >>> import tensorlayer as tl + >>> ni = tl.layers.Input([None, 784], name='input') + >>> net1 = tl.layers.Dense(10, name='dense1')(ni) + >>> net2 = tl.layers.Dense(10, name='dense2')(ni) + >>> net3 = tl.layers.Dense(10, name='dense3')(ni) + >>> net = tl.layers.Stack(axis=1, name='stack')([net1, net2, net3]) + (?, 3, 10) + + """ + + def __init__( + self, + axis=1, + name=None, #'stack', + ): + super().__init__(name) + self.axis = axis + + self.build(None) + self._built = True + logging.info("Stack %s: axis: %d" % (self.name, self.axis)) + + def __repr__(self): + s = '{classname}(axis={axis}' + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + self.stack = tl.ops.Stack(axis=self.axis) + + def forward(self, inputs): + outputs = self.stack(inputs) + return outputs + + +class UnStack(Module): + """ + The :class:`UnStack` class is a layer for unstacking the given dimension of a rank-R tensor into rank-(R-1) tensors., see `tf.unstack() `__. + + Parameters + ---------- + num : int or None + The length of the dimension axis. Automatically inferred if None (the default). + axis : int + Dimension along which axis to concatenate. + name : str + A unique layer name. + + Returns + ------- + list of :class:`Layer` + The list of layer objects unstacked from the input. + + Examples + -------- + >>> ni = Input([4, 10], name='input') + >>> nn = Dense(n_units=5)(ni) + >>> nn = UnStack(axis=1)(nn) # unstack in channel axis + >>> len(nn) # 5 + >>> nn[0].shape # (4,) + + """ + + def __init__(self, num=None, axis=0, name=None): #'unstack'): + super().__init__(name) + self.num = num + self.axis = axis + + self.build(None) + self._built = True + logging.info("UnStack %s: num: %s axis: %d" % (self.name, self.num, self.axis)) + + def __repr__(self): + s = '{classname}(num={num}, axis={axis}' + if self.name is not None: + s += ', name=\'{name}\'' + s += ')' + return s.format(classname=self.__class__.__name__, **self.__dict__) + + def build(self, inputs_shape): + self.unstack = tl.ops.Unstack(num=self.num, axis=self.axis) + + def forward(self, inputs): + outputs = self.unstack(inputs) + return outputs diff --git a/tensorlayer/layers/utils.py b/tensorlayer/layers/utils.py new file mode 100644 index 0000000..b231d1b --- /dev/null +++ b/tensorlayer/layers/utils.py @@ -0,0 +1,440 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorflow as tf +from tensorflow.python.ops.rnn_cell import LSTMStateTuple + +import tensorlayer as tl +from tensorlayer import logging +from tensorlayer.decorators import deprecated, deprecated_alias +from tensorlayer.backend.ops.load_backend import BACKEND + +__all__ = [ + 'cabs', + 'compute_alpha', + 'flatten_reshape', + 'get_collection_trainable', + 'get_layers_with_name', + 'get_variables_with_name', + 'initialize_global_variables', + 'initialize_rnn_state', + 'list_remove_repeat', + 'merge_networks', + 'print_all_variables', + 'quantize', + 'quantize_active', + 'quantize_weight', + 'quantize_active_overflow', + 'quantize_weight_overflow', + 'set_name_reuse', + 'ternary_operation', +] + +########## Module Public Functions ########## + + +def cabs(x): + return tf.minimum(1.0, tf.abs(x), name='cabs') + + +def compute_alpha(x): + """Computing the scale parameter.""" + threshold = _compute_threshold(x) + alpha1_temp1 = tf.where(tf.greater(x, threshold), x, tf.zeros_like(x, tf.float32)) + alpha1_temp2 = tf.where(tf.less(x, -threshold), x, tf.zeros_like(x, tf.float32)) + alpha_array = tf.add(alpha1_temp1, alpha1_temp2, name=None) + alpha_array_abs = tf.abs(alpha_array) + alpha_array_abs1 = tf.where( + tf.greater(alpha_array_abs, 0), tf.ones_like(alpha_array_abs, tf.float32), + tf.zeros_like(alpha_array_abs, tf.float32) + ) + alpha_sum = tf.reduce_sum(input_tensor=alpha_array_abs) + n = tf.reduce_sum(input_tensor=alpha_array_abs1) + # alpha = tf.compat.v1.div(alpha_sum, n) + alpha = tf.math.divide(alpha_sum, n) + return alpha + + +def flatten_reshape(variable, name='flatten'): + """Reshapes a high-dimension vector input. + + [batch_size, mask_row, mask_col, n_mask] ---> [batch_size, mask_row x mask_col x n_mask] + + Parameters + ---------- + variable : TensorFlow variable or tensor + The variable or tensor to be flatten. + name : str + A unique layer name. + + Returns + ------- + Tensor + Flatten Tensor + + """ + dim = 1 + for d in tl.get_tensor_shape(variable)[1:]: # variable.get_shape()[1:].as_list(): + dim *= d + return tl.reshape(variable, shape=[-1, dim]) + + +def get_collection_trainable(name=''): + variables = [] + for p in tf.compat.v1.trainable_variables(): + # print(p.name.rpartition('/')[0], self.name) + if p.name.rpartition('/')[0] == name: + variables.append(p) + return variables + + +@deprecated_alias(printable='verbose', end_support_version=1.9) # TODO remove this line for the 1.9 release +def get_layers_with_name(net, name="", verbose=False): + """Get a list of layers' output in a network by a given name scope. + + Parameters + ----------- + net : :class:`Layer` + The last layer of the network. + name : str + Get the layers' output that contain this name. + verbose : boolean + If True, print information of all the layers' output + + Returns + -------- + list of Tensor + A list of layers' output (TensorFlow tensor) + + Examples + --------- + >>> import tensorlayer as tl + >>> layers = tl.layers.get_layers_with_name(net, "CNN", True) + + """ + logging.info(" [*] geting layers with %s" % name) + + layers = [] + i = 0 + + for layer in net.all_layers: + # logging.info(type(layer.name)) + if name in layer.name: + layers.append(layer) + + if verbose: + logging.info(" got {:3}: {:15} {}".format(i, layer.name, str(layer.get_shape()))) + i = i + 1 + + return layers + + +def get_variable_with_initializer(scope_name, var_name, shape, init=tl.initializers.random_normal(), trainable=True): + # FIXME: documentation needed + var_name = scope_name + "/" + var_name + # FIXME: not sure whether this is correct? + # TODO mindspore weights shape : [out_channel, in_channel, kernel_h, kernel_w] + if BACKEND == 'mindspore': + if len(shape) == 2: + pass + else: + shape = shape[::-1] + + initial_value = init(shape=shape) + + if BACKEND == 'dragon': + return initial_value + + var = tl.Variable(initial_value=initial_value, name=var_name, trainable=trainable) + return var + + +@deprecated_alias(printable='verbose', end_support_version=1.9) # TODO remove this line for the 1.9 release +def get_variables_with_name(name=None, train_only=True, verbose=False): + """Get a list of TensorFlow variables by a given name scope. + + Parameters + ---------- + name : str + Get the variables that contain this name. + train_only : boolean + If Ture, only get the trainable variables. + verbose : boolean + If True, print the information of all variables. + + Returns + ------- + list of Tensor + A list of TensorFlow variables + + Examples + -------- + >>> import tensorlayer as tl + >>> dense_vars = tl.layers.get_variables_with_name('dense', True, True) + + """ + if name is None: + raise Exception("please input a name") + + logging.info(" [*] geting variables with %s" % name) + + # tvar = tf.trainable_variables() if train_only else tf.all_variables() + if train_only: + t_vars = tf.compat.v1.trainable_variables() + + else: + t_vars = tf.compat.v1.global_variables() + + d_vars = [var for var in t_vars if name in var.name] + + if verbose: + for idx, v in enumerate(d_vars): + logging.info(" got {:3}: {:15} {}".format(idx, v.name, str(v.get_shape()))) + + return d_vars + + +@deprecated( + date="2018-09-30", instructions="This API is deprecated in favor of `sess.run(tf.global_variables_initializer())`" +) +def initialize_global_variables(sess): + """Initialize the global variables of TensorFlow. + + Run ``sess.run(tf.global_variables_initializer())`` for TF 0.12+ or + ``sess.run(tf.initialize_all_variables())`` for TF 0.11. + + Parameters + ---------- + sess : Session + TensorFlow session. + + """ + if sess is None: + raise AssertionError('The session must be defined') + + sess.run(tf.compat.v1.global_variables_initializer()) + + +def initialize_rnn_state(state, feed_dict=None): + """Returns the initialized RNN state. + The inputs are `LSTMStateTuple` or `State` of `RNNCells`, and an optional `feed_dict`. + + Parameters + ---------- + state : RNN state. + The TensorFlow's RNN state. + feed_dict : dictionary + Initial RNN state; if None, returns zero state. + + Returns + ------- + RNN state + The TensorFlow's RNN state. + + """ + if isinstance(state, LSTMStateTuple): + c = state.c.eval(feed_dict=feed_dict) + h = state.h.eval(feed_dict=feed_dict) + return c, h + else: + new_state = state.eval(feed_dict=feed_dict) + return new_state + + +def list_remove_repeat(x): + """Remove the repeated items in a list, and return the processed list. + You may need it to create merged layer like Concat, Elementwise and etc. + + Parameters + ---------- + x : list + Input + + Returns + ------- + list + A list that after removing it's repeated items + + Examples + ------- + >>> l = [2, 3, 4, 2, 3] + >>> l = list_remove_repeat(l) + [2, 3, 4] + + """ + y = [] + for i in x: + if i not in y: + y.append(i) + + return y + + +def merge_networks(layers=None): + """Merge all parameters, layers and dropout probabilities to a :class:`Layer`. + The output of return network is the first network in the list. + + Parameters + ---------- + layers : list of :class:`Layer` + Merge all parameters, layers and dropout probabilities to the first layer in the list. + + Returns + -------- + :class:`Layer` + The network after merging all parameters, layers and dropout probabilities to the first network in the list. + + Examples + --------- + >>> import tensorlayer as tl + >>> n1 = ... + >>> n2 = ... + >>> n1 = tl.layers.merge_networks([n1, n2]) + + """ + if layers is None: + raise Exception("layers should be a list of TensorLayer's Layers.") + layer = layers[0] + + all_params = [] + all_layers = [] + all_drop = {} + + for l in layers: + all_params.extend(l.all_params) + all_layers.extend(l.all_layers) + all_drop.update(l.all_drop) + + layer.all_params = list(all_params) + layer.all_layers = list(all_layers) + layer.all_drop = dict(all_drop) + + layer.all_layers = list_remove_repeat(layer.all_layers) + layer.all_params = list_remove_repeat(layer.all_params) + + return layer + + +def print_all_variables(train_only=False): + """Print information of trainable or all variables, + without ``tl.layers.initialize_global_variables(sess)``. + + Parameters + ---------- + train_only : boolean + Whether print trainable variables only. + - If True, print the trainable variables. + - If False, print all variables. + + """ + # tvar = tf.trainable_variables() if train_only else tf.all_variables() + if train_only: + t_vars = tf.compat.v1.trainable_variables() + logging.info(" [*] printing trainable variables") + + else: + t_vars = tf.compat.v1.global_variables() + logging.info(" [*] printing global variables") + + for idx, v in enumerate(t_vars): + logging.info(" var {:3}: {:15} {}".format(idx, str(v.get_shape()), v.name)) + + +def quantize(x): + # ref: https://github.com/AngusG/tensorflow-xnor-bnn/blob/master/models/binary_net.py#L70 + # https://github.com/itayhubara/BinaryNet.tf/blob/master/nnUtils.py + with tf.compat.v1.get_default_graph().gradient_override_map({"Sign": "TL_Sign_QuantizeGrad"}): + return tf.sign(x) + + +def quantize_active(x, bitA): + if bitA == 32: + return x + return _quantize_dorefa(x, bitA) + + +def quantize_weight(x, bitW, force_quantization=False): + G = tf.compat.v1.get_default_graph() + if bitW == 32 and not force_quantization: + return x + if bitW == 1: # BWN + with G.gradient_override_map({"Sign": "Identity"}): + E = tf.stop_gradient(tf.reduce_mean(input_tensor=tf.abs(x))) + return tf.sign(x / E) * E + x = tf.clip_by_value(x * 0.5 + 0.5, 0.0, 1.0) # it seems as though most weights are within -1 to 1 region anyways + return 2 * _quantize_dorefa(x, bitW) - 1 + + +def quantize_active_overflow(x, bitA): + if bitA == 32: + return x + return _quantize_overflow(x, bitA) + + +def quantize_weight_overflow(x, bitW): + if bitW == 32: + return x + return _quantize_overflow(x, bitW) + + +@deprecated(date="2018-06-30", instructions="TensorLayer relies on TensorFlow to check name reusing") +def set_name_reuse(enable=True): + logging.warning('this method is DEPRECATED and has no effect, please remove it from your code.') + + +def ternary_operation(x): + """Ternary operation use threshold computed with weights.""" + g = tf.compat.v1.get_default_graph() + with g.gradient_override_map({"Sign": "Identity"}): + threshold = _compute_threshold(x) + x = tf.sign(tf.add(tf.sign(tf.add(x, threshold)), tf.sign(tf.add(x, -threshold)))) + return x + + +########## Module Private Functions ########## + + +@tf.RegisterGradient("TL_Sign_QuantizeGrad") +def _quantize_grad(op, grad): + """Clip and binarize tensor using the straight through estimator (STE) for the gradient.""" + return tf.clip_by_value(grad, -1, 1) + + +def _quantize_dorefa(x, k): + G = tf.compat.v1.get_default_graph() + n = float(2**k - 1) + with G.gradient_override_map({"Round": "Identity"}): + return tf.round(x * n) / n + + +def _quantize_overflow(x, k): + G = tf.compat.v1.get_default_graph() + n = float(2**k - 1) + max_value = tf.reduce_max(input_tensor=x) + min_value = tf.reduce_min(input_tensor=x) + with G.gradient_override_map({"Round": "Identity"}): + step = tf.stop_gradient((max_value - min_value) / n) + return tf.round((tf.maximum(tf.minimum(x, max_value), min_value) - min_value) / step) * step + min_value + + +def _compute_threshold(x): + """ + ref: https://github.com/XJTUWYD/TWN + Computing the threshold. + """ + x_sum = tf.reduce_sum(input_tensor=tf.abs(x), axis=None, keepdims=False, name=None) + # threshold = tf.compat.v1.div(x_sum, tf.cast(tf.size(input=x), tf.float32), name=None) + threshold = tf.math.divide(x_sum, tf.cast(tf.size(input=x), tf.float32), name=None) + threshold = tf.multiply(0.7, threshold, name=None) + return threshold + + +def mean_var_with_update(update_moving_mean, update_moving_variance, mean, variance): + with tf.control_dependencies([update_moving_mean, update_moving_variance]): + return tf.identity(mean), tf.identity(variance) + +def w_fold(w, gama, var, epsilon): + return tf.compat.v1.div(tf.multiply(gama, w), tf.sqrt(var + epsilon)) + +def bias_fold(beta, gama, mean, var, epsilon): + return tf.subtract(beta, tf.compat.v1.div(tf.multiply(gama, mean), tf.sqrt(var + epsilon))) \ No newline at end of file diff --git a/tensorlayer/lazy_imports.py b/tensorlayer/lazy_imports.py new file mode 100644 index 0000000..4a079a4 --- /dev/null +++ b/tensorlayer/lazy_imports.py @@ -0,0 +1,99 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +"""This module provides lazy import functionality to improve the import +performance of nitime. For example, some parts of nitime leverage and import +matplotlib, which is quite a big package, yet most of the nitime code does not +depend on matplotlib. By lazily-loading a module, we defer the overhead of +importing it until the first time it is actually used, thereby speeding up +nitime imports. + +A generic :class:`LazyImport` class is implemented which takes the module name +as a parameter, and acts as a proxy for that module, importing it only when +the module is used, but effectively acting as the module in every other way +(including inside IPython with respect to introspection and tab completion) +with the *exception* of reload() - reloading a :class:`LazyImport` raises an +:class:`ImportError`. + +Commonly used nitime lazy imports are also defined in :mod:`nitime.lazy`, so +they can be reused throughout nitime. +""" +import os +import sys +import types + + +class LazyImport(types.ModuleType): + """ + This class takes the module name as a parameter, and acts as a proxy for + that module, importing it only when the module is used, but effectively + acting as the module in every other way (including inside IPython with + respect to introspection and tab completion) with the *exception* of + reload()- reloading a :class:`LazyImport` raises an :class:`ImportError`. + + >>> mlab = LazyImport('matplotlib.mlab') + + No import happens on the above line, until we do something like call an + ``mlab`` method or try to do tab completion or introspection on ``mlab`` + in IPython. + + >>> mlab + + + Now the :class:`LazyImport` will do an actual import, and call the dist + function of the imported module. + + >>> mlab.dist(1969,2011) + 42.0 + """ + + def __getattribute__(self, x): + # This method will be called only once, since we'll change + # self.__class__ to LoadedLazyImport, and __getattribute__ will point + # to module.__getattribute__ + + name = object.__getattribute__(self, '__name__') + __import__(name) + + # if name above is 'package.foo.bar', package is returned, the docs + # recommend that in order to get back the full thing, that we import + # and then lookup the full name is sys.modules, see: + # http://docs.python.org/library/functions.html#__import__ + + module = sys.modules[name] + + # Now that we've done the import, cutout the middleman and make self + # act as the imported module + + class LoadedLazyImport(types.ModuleType): + __getattribute__ = module.__getattribute__ + __repr__ = module.__repr__ + + object.__setattr__(self, '__class__', LoadedLazyImport) + + # The next line will make "reload(l)" a silent no-op + return module.__getattribute__(x) + + def __repr__(self): + return "" % object.__getattribute__(self, '__name__') + + +if 'READTHEDOCS' in os.environ: + lazy_doc = """ + WARNING: To get Sphinx documentation to build we disable + LazyImports, which makes Sphinx incorrectly report this + class as having a base class of object. In reality, + :class:`LazyImport`'s base class is + :class:`types.ModuleType`. + """ + + lazy_doc += LazyImport.__doc__ + + class LazyImport(object): + __doc__ = lazy_doc + + def __init__(self, x): + __import__(x) + self.module = sys.modules[x] + + def __getattr__(self, x): + return self.module.__getattribute__(x) diff --git a/tensorlayer/logging/__init__.py b/tensorlayer/logging/__init__.py new file mode 100644 index 0000000..e3c0dac --- /dev/null +++ b/tensorlayer/logging/__init__.py @@ -0,0 +1,34 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +""" +TensorLayer provides rich layer implementations trailed for +various benchmarks and domain-specific problems. In addition, we also +support transparent access to native TensorFlow parameters. +For example, we provide not only layers for local response normalization, but also +layers that allow user to apply ``tf.ops.lrn`` on ``network.outputs``. +More functions can be found in `TensorFlow API `__. +""" + +from tensorlayer.lazy_imports import LazyImport + +from .tl_logging import * + +# Lazy Imports +contrib = LazyImport("tensorlayer.logging.contrib") + +__all__ = [ + # tl_logging + 'DEBUG', + 'debug', + 'ERROR', + 'error', + 'FATAL', + 'fatal', + 'INFO', + 'info', + 'WARN', + 'warn', + 'warning', + 'set_verbosity', + 'get_verbosity' +] diff --git a/tensorlayer/logging/contrib/__init__.py b/tensorlayer/logging/contrib/__init__.py new file mode 100644 index 0000000..69a3ccb --- /dev/null +++ b/tensorlayer/logging/contrib/__init__.py @@ -0,0 +1,12 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +""" +TensorLayer provides rich layer implementations trailed for +various benchmarks and domain-specific problems. In addition, we also +support transparent access to native TensorFlow parameters. +For example, we provide not only layers for local response normalization, but also +layers that allow user to apply ``tf.ops.lrn`` on ``network.outputs``. +More functions can be found in `TensorFlow API `__. +""" + +from .hyperdash import * diff --git a/tensorlayer/logging/contrib/hyperdash.py b/tensorlayer/logging/contrib/hyperdash.py new file mode 100644 index 0000000..7c21e65 --- /dev/null +++ b/tensorlayer/logging/contrib/hyperdash.py @@ -0,0 +1,63 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +from __future__ import absolute_import + +import hyperdash as hd +import tensorlayer as tl + +__all__ = ["HyperDashHandler", "monitor", "Experiment", "IPythonMagicsWrapper"] + + +class HyperDashHandler(object): + apikey = None + + @classmethod + def reset_apikey(cls): + cls.apikey = None + + @classmethod + def set_apikey(cls, apikey): + cls.apikey = apikey + + @classmethod + def get_apikey(cls): + + if cls.apikey is None: + raise ValueError( + "Hyperdash API is not set.\n" + "You can obtain your API Key using: `hyperdash login --email` or `hyperdash login --github`\n" + "You should first call `HyperDashHandler.set_apikey('my_api_key')` in order to use `hyperdash`" + ) + + tl.logging.debug("Hyperdash API Key: %s" % cls.apikey) + + return cls.apikey + + @classmethod + def monitor(cls, model_name, api_key=None, capture_io=True): + + if api_key is not None: + cls.set_apikey(api_key) + + return hd.monitor(model_name, api_key_getter=cls.get_apikey, capture_io=capture_io) + + +class Experiment(hd.Experiment): + + def __init__( + self, + model_name, + api_key=None, + capture_io=True, + ): + + if api_key is not None: + HyperDashHandler.set_apikey(api_key) + + super(Experiment, + self).__init__(model_name=model_name, api_key_getter=HyperDashHandler.get_apikey, capture_io=capture_io) + + +monitor = HyperDashHandler.monitor +IPythonMagicsWrapper = hd.IPythonMagicsWrapper diff --git a/tensorlayer/logging/tl_logging.py b/tensorlayer/logging/tl_logging.py new file mode 100644 index 0000000..415fcd3 --- /dev/null +++ b/tensorlayer/logging/tl_logging.py @@ -0,0 +1,262 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import logging as _logging +import os as _os +import sys as _sys +import threading +import time as _time +from logging import DEBUG, ERROR, FATAL, INFO, WARN + +import six + +from tensorlayer.decorators import deprecated + +__all__ = [ + 'DEBUG', + 'debug', + 'ERROR', + 'error', + 'FATAL', + 'fatal', + 'INFO', + 'info', + 'WARN', + 'warning', + 'warn', # Deprecated + 'set_verbosity', + 'get_verbosity' +] + +# Don't use this directly. Use _get_logger() instead. +_logger = None +_logger_lock = threading.Lock() + +_level_names = { + FATAL: 'FATAL', + ERROR: 'ERROR', + WARN: 'WARN', + INFO: 'INFO', + DEBUG: 'DEBUG', +} + + +def _get_logger(): + global _logger + + # Use double-checked locking to avoid taking lock unnecessarily. + if _logger is not None: + return _logger + + _logger_lock.acquire() + + try: + if _logger: + return _logger + + # Scope the TensorFlow logger to not conflict with users' loggers. + logger = _logging.getLogger('tensorlayer') + + # Don't further configure the TensorFlow logger if the root logger is + # already configured. This prevents double logging in those cases. + if not _logging.getLogger().handlers: + # Determine whether we are in an interactive environment + # This is only defined in interactive shells. + if hasattr(_sys, "ps1"): + _interactive = True + else: + _interactive = _sys.flags.interactive + + # If we are in an interactive environment (like Jupyter), set loglevel + # to INFO and pipe the output to stdout. + if _interactive: + logger.setLevel(INFO) + _logging_target = _sys.stdout + else: + _logging_target = _sys.stderr + + # Add the output handler. + _handler = _logging.StreamHandler(_logging_target) + _handler.setFormatter(_logging.Formatter('[TL] %(message)s')) + logger.addHandler(_handler) + + _logger = logger + return _logger + + finally: + _logger_lock.release() + + +def log(level, msg, *args, **kwargs): + _get_logger().log(level, msg, *args, **kwargs) + + +def debug(msg, *args, **kwargs): + _get_logger().debug(msg, *args, **kwargs) + + +def info(msg, *args, **kwargs): + _get_logger().info(msg, *args, **kwargs) + + +def error(msg, *args, **kwargs): + _get_logger().error("ERROR: %s" % msg, *args, **kwargs) + + +def fatal(msg, *args, **kwargs): + _get_logger().fatal("FATAL: %s" % msg, *args, **kwargs) + + +@deprecated(date="2018-09-30", instructions="This API is deprecated. Please use as `tl.logging.warning`") +def warn(msg, *args, **kwargs): + warning(msg, *args, **kwargs) + + +def warning(msg, *args, **kwargs): + _get_logger().warning("WARNING: %s" % msg, *args, **kwargs) + + +# Mask to convert integer thread ids to unsigned quantities for logging +# purposes +_THREAD_ID_MASK = 2 * _sys.maxsize + 1 + +_log_prefix = None # later set to google2_log_prefix + +# Counter to keep track of number of log entries per token. +_log_counter_per_token = {} + + +def TaskLevelStatusMessage(msg): + error(msg) + + +def flush(): + raise NotImplementedError() + + +def vlog(level, msg, *args, **kwargs): + _get_logger().log(level, msg, *args, **kwargs) + + +def _GetNextLogCountPerToken(token): + """Wrapper for _log_counter_per_token. + + Args: + token: The token for which to look up the count. + + Returns: + The number of times this function has been called with + *token* as an argument (starting at 0) + """ + global _log_counter_per_token # pylint: disable=global-variable-not-assigned + _log_counter_per_token[token] = 1 + _log_counter_per_token.get(token, -1) + return _log_counter_per_token[token] + + +def log_every_n(level, msg, n, *args): + """Log 'msg % args' at level 'level' once per 'n' times. + + Logs the 1st call, (N+1)st call, (2N+1)st call, etc. + Not threadsafe. + + Args: + level: The level at which to log. + msg: The message to be logged. + n: The number of times this should be called before it is logged. + *args: The args to be substituted into the msg. + """ + count = _GetNextLogCountPerToken(_GetFileAndLine()) + log_if(level, msg, not (count % n), *args) + + +def log_first_n(level, msg, n, *args): # pylint: disable=g-bad-name + """Log 'msg % args' at level 'level' only first 'n' times. + + Not threadsafe. + + Args: + level: The level at which to log. + msg: The message to be logged. + n: The number of times this should be called before it is logged. + *args: The args to be substituted into the msg. + """ + count = _GetNextLogCountPerToken(_GetFileAndLine()) + log_if(level, msg, count < n, *args) + + +def log_if(level, msg, condition, *args): + """Log 'msg % args' at level 'level' only if condition is fulfilled.""" + if condition: + vlog(level, msg, *args) + + +def _GetFileAndLine(): + """Returns (filename, linenumber) for the stack frame.""" + # Use sys._getframe(). This avoids creating a traceback object. + # pylint: disable=protected-access + f = _sys._getframe() + # pylint: enable=protected-access + our_file = f.f_code.co_filename + f = f.f_back + while f: + code = f.f_code + if code.co_filename != our_file: + return (code.co_filename, f.f_lineno) + f = f.f_back + return ('', 0) + + +def google2_log_prefix(level, timestamp=None, file_and_line=None): + """Assemble a logline prefix using the google2 format.""" + # pylint: disable=global-variable-not-assigned + global _level_names + # pylint: enable=global-variable-not-assigned + + # Record current time + now = timestamp or _time.time() + now_tuple = _time.localtime(now) + now_microsecond = int(1e6 * (now % 1.0)) + + (filename, line) = file_and_line or _GetFileAndLine() + basename = _os.path.basename(filename) + + # Severity string + severity = 'I' + if level in _level_names: + severity = _level_names[level][0] + + s = '%c%02d%02d %02d: %02d: %02d.%06d %5d %s: %d] ' % ( + severity, + now_tuple[1], # month + now_tuple[2], # day + now_tuple[3], # hour + now_tuple[4], # min + now_tuple[5], # sec + now_microsecond, + _get_thread_id(), + basename, + line + ) + + return s + + +def get_verbosity(): + """Return how much logging output will be produced.""" + return _get_logger().getEffectiveLevel() + + +def set_verbosity(v): + """Sets the threshold for what messages will be logged.""" + _get_logger().setLevel(v) + + +def _get_thread_id(): + """Get id of current thread, suitable for logging as an unsigned quantity.""" + # pylint: disable=protected-access + thread_id = six.moves._thread.get_ident() + # pylint:enable=protected-access + return thread_id & _THREAD_ID_MASK + + +_log_prefix = google2_log_prefix diff --git a/tensorlayer/models/__init__.py b/tensorlayer/models/__init__.py new file mode 100644 index 0000000..2ebf9f0 --- /dev/null +++ b/tensorlayer/models/__init__.py @@ -0,0 +1,12 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +# """A collections of pre-defined well known models.""" + +from .core import * +# from .resnet import ResNet50 +# from .mobilenetv1 import MobileNetV1 +# from .squeezenetv1 import SqueezeNetV1 +# from .vgg import * +# from .seq2seq import Seq2seq +# from .seq2seq_with_attention import Seq2seqLuongAttention diff --git a/tensorlayer/models/core.py b/tensorlayer/models/core.py new file mode 100644 index 0000000..15b5439 --- /dev/null +++ b/tensorlayer/models/core.py @@ -0,0 +1,392 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from collections.abc import Iterable +from tensorlayer.files import utils +from tensorlayer import logging +import tensorlayer as tl +from tensorlayer.layers.core import Module +import numpy as np +import os +import time + +if tl.BACKEND == 'tensorflow': + import tensorflow as tf +if tl.BACKEND == 'mindspore': + import mindspore as ms + from mindspore.ops import composite + from mindspore.ops import operations as P + from mindspore.ops import functional as F + # from mindspore.parallel._utils import (_get_device_num, _get_mirror_mean, _get_parallel_mode) + # from mindspore.train.parallel_utils import ParallelMode + from mindspore.nn.wrap import DistributedGradReducer + from mindspore.common import ParameterTuple + + +class Model: + """ + High-Level API for Training or Testing. + + `Model` groups layers into an object with training and inference features. + + Args: + network : The training or testing network. + loss_fn : Objective function, if loss_fn is None, the + network should contain the logic of loss and grads calculation, and the logic + of parallel if needed. Default: None. + optimizer : Optimizer for updating the weights. Default: None. + metrics (Union[dict, set]): Dict or set of metrics to be evaluated by the model during + training and testing. eg: {'accuracy', 'recall'}. Default: None. + eval_network (Cell): Network for evaluation. If not defined, `network` and `loss_fn` would be wrapped as + `eval_network`. Default: None. + eval_indexes (list): In case of defining the `eval_network`, if `eval_indexes` is None, all outputs of + `eval_network` would be passed to metrics, otherwise `eval_indexes` must contain three + elements, representing the positions of loss value, predict value and label, the loss + value would be passed to `Loss` metric, predict value and label would be passed to other + metric. Default: None. + amp_level (str): Option for argument `level` in `mindspore.amp.build_train_network`, level for mixed + precision training. Supports [O0, O2, O3]. Default: "O0". + + - O0: Do not change. + - O2: Cast network to float16, keep batchnorm run in float32, using dynamic loss scale. + - O3: Cast network to float16, with additional property 'keep_batchnorm_fp32=False'. + + O2 is recommended on GPU, O3 is recommended on Ascend. + + loss_scale_manager (Union[None, LossScaleManager]): If None, not scale the loss, or else + scale the loss by LossScaleManager. If it is set, overwrite the level setting. It's a eyword argument. + e.g. Use `loss_scale_manager=None` to set the value. + keep_batchnorm_fp32 (bool): Keep Batchnorm run in `float32`. If set, overwrite the level setting. Default: True. + + Examples: + >>> import tensorlayer as tl + >>> class Net(Module): + >>> def __init__(self): + >>> super(Net, self).__init__() + >>> self.conv = tl.layers.Conv2d(n_filter=32, filter_size=(3, 3), strides=(2, 2), in_channels=5, name='conv2d') + >>> self.bn = tl.layers.BatchNorm2d(num_features=32, act=tl.ReLU) + >>> self.flatten = tl.layers.Flatten() + >>> self.fc = tl.layers.Dense(n_units=12, in_channels=32*224*224) # padding=0 + >>> + >>> def construct(self, x): + >>> x = self.conv(x) + >>> x = self.bn(x) + >>> x = self.flatten(x) + >>> out = self.fc(x) + >>> return out + >>> + >>> net = Net() + >>> loss = tl.cost.SoftmaxCrossEntropyWithLogits(is_grad=False, sparse=True) + >>> optim = tl.layers.Momentum(params=net.trainable_weights, learning_rate=0.1, momentum=0.9) + >>> model = Model(net, loss_fn=loss, optimizer=optim, metrics=None) + >>> dataset = get_dataset() + >>> model.train(2, dataset) + """ + + def __init__( + self, network, loss_fn=None, optimizer=None, metrics=None, eval_network=None, eval_indexes=None, amp_level="O0", + **kwargs + ): + self.network = network + self.loss_fn = loss_fn + self.optimizer = optimizer + self.metrics = metrics + self.all_weights = network.all_weights + self.train_weights = self.network.trainable_weights + + def train(self, n_epoch, train_dataset=None, test_dataset=False, print_train_batch=False, print_freq=5): + if not isinstance(train_dataset, Iterable): + raise Exception("Expected type in (train_dataset, Iterable), but got {}.".format(type(train_dataset))) + + if tl.BACKEND == 'tensorflow': + self.tf_train( + n_epoch=n_epoch, train_dataset=train_dataset, network=self.network, loss_fn=self.loss_fn, + train_weights=self.train_weights, optimizer=self.optimizer, metrics=self.metrics, + print_train_batch=print_train_batch, print_freq=print_freq, test_dataset=test_dataset + ) + elif tl.BACKEND == 'mindspore': + self.ms_train( + n_epoch=n_epoch, train_dataset=train_dataset, network=self.network, loss_fn=self.loss_fn, + train_weights=self.train_weights, optimizer=self.optimizer, metrics=self.metrics, + print_train_batch=print_train_batch, print_freq=print_freq, test_dataset=test_dataset + ) + + def eval(self, test_dataset): + self.network.eval() + test_loss, test_acc, n_iter = 0, 0, 0 + for X_batch, y_batch in test_dataset: + _logits = self.network(X_batch) + test_loss += self.loss_fn(_logits, y_batch) + if self.metrics: + test_acc += self.metrics(_logits, y_batch) + else: + test_acc += np.mean(np.equal(np.argmax(_logits, 1), y_batch)) + n_iter += 1 + print(" test loss: {}".format(test_loss / n_iter)) + print(" test acc: {}".format(test_acc / n_iter)) + + def save_weights(self, file_path, format=None): + """Input file_path, save model weights into a file of given format. + Use self.load_weights() to restore. + + Parameters + ---------- + file_path : str + Filename to which the model weights will be saved. + format : str or None + Saved file format. + Value should be None, 'hdf5', 'npz', 'npz_dict' or 'ckpt'. Other format is not supported now. + 1) If this is set to None, then the postfix of file_path will be used to decide saved format. + If the postfix is not in ['h5', 'hdf5', 'npz', 'ckpt'], then file will be saved in hdf5 format by default. + 2) 'hdf5' will save model weights name in a list and each layer has its weights stored in a group of + the hdf5 file. + 3) 'npz' will save model weights sequentially into a npz file. + 4) 'npz_dict' will save model weights along with its name as a dict into a npz file. + 5) 'ckpt' will save model weights into a tensorflow ckpt file. + + Default None. + + Examples + -------- + 1) Save model weights in hdf5 format by default. + >>> net = vgg16() + >>> net.save_weights('./model.h5') + ... + >>> net.load_weights('./model.h5') + + 2) Save model weights in npz/npz_dict format + >>> net = vgg16() + >>> net.save_weights('./model.npz') + >>> net.save_weights('./model.npz', format='npz_dict') + + """ + + # self.all_weights = self.network.all_weights + if self.all_weights is None or len(self.all_weights) == 0: + logging.warning("Model contains no weights or layers haven't been built, nothing will be saved") + return + + if format is None: + postfix = file_path.split('.')[-1] + if postfix in ['h5', 'hdf5', 'npz', 'ckpt']: + format = postfix + else: + format = 'hdf5' + + if format == 'hdf5' or format == 'h5': + utils.save_weights_to_hdf5(file_path, self) + elif format == 'npz': + utils.save_npz(self.all_weights, file_path) + elif format == 'npz_dict': + utils.save_npz_dict(self.all_weights, file_path) + elif format == 'ckpt': + # TODO: enable this when tf save ckpt is enabled + raise NotImplementedError("ckpt load/save is not supported now.") + else: + raise ValueError( + "Save format must be 'hdf5', 'npz', 'npz_dict' or 'ckpt'." + "Other format is not supported now." + ) + + def load_weights(self, file_path, format=None, in_order=True, skip=False): + """Load model weights from a given file, which should be previously saved by self.save_weights(). + + Parameters + ---------- + file_path : str + Filename from which the model weights will be loaded. + format : str or None + If not specified (None), the postfix of the file_path will be used to decide its format. If specified, + value should be 'hdf5', 'npz', 'npz_dict' or 'ckpt'. Other format is not supported now. + In addition, it should be the same format when you saved the file using self.save_weights(). + Default is None. + in_order : bool + Allow loading weights into model in a sequential way or by name. Only useful when 'format' is 'hdf5'. + If 'in_order' is True, weights from the file will be loaded into model in a sequential way. + If 'in_order' is False, weights from the file will be loaded into model by matching the name + with the weights of the model, particularly useful when trying to restore model in eager(graph) mode from + a weights file which is saved in graph(eager) mode. + Default is True. + skip : bool + Allow skipping weights whose name is mismatched between the file and model. Only useful when 'format' is + 'hdf5' or 'npz_dict'. If 'skip' is True, 'in_order' argument will be ignored and those loaded weights + whose name is not found in model weights (self.all_weights) will be skipped. If 'skip' is False, error will + occur when mismatch is found. + Default is False. + + Examples + -------- + 1) load model from a hdf5 file. + >>> net = tl.models.vgg16() + >>> net.load_weights('./model_graph.h5', in_order=False, skip=True) # load weights by name, skipping mismatch + >>> net.load_weights('./model_eager.h5') # load sequentially + + 2) load model from a npz file + >>> net.load_weights('./model.npz') + + 2) load model from a npz file, which is saved as npz_dict previously + >>> net.load_weights('./model.npz', format='npz_dict') + + Notes + ------- + 1) 'in_order' is only useful when 'format' is 'hdf5'. If you are trying to load a weights file which is + saved in a different mode, it is recommended to set 'in_order' be True. + 2) 'skip' is useful when 'format' is 'hdf5' or 'npz_dict'. If 'skip' is True, + 'in_order' argument will be ignored. + + """ + if not os.path.exists(file_path): + raise FileNotFoundError("file {} doesn't exist.".format(file_path)) + + if format is None: + format = file_path.split('.')[-1] + + if format == 'hdf5' or format == 'h5': + if skip ==True or in_order == False: + # load by weights name + utils.load_hdf5_to_weights(file_path, self, skip) + else: + # load in order + utils.load_hdf5_to_weights_in_order(file_path, self) + elif format == 'npz': + utils.load_and_assign_npz(file_path, self) + elif format == 'npz_dict': + utils.load_and_assign_npz_dict(file_path, self, skip) + elif format == 'ckpt': + # TODO: enable this when tf save ckpt is enabled + raise NotImplementedError("ckpt load/save is not supported now.") + else: + raise ValueError( + "File format must be 'hdf5', 'npz', 'npz_dict' or 'ckpt'. " + "Other format is not supported now." + ) + + def tf_train( + self, n_epoch, train_dataset, network, loss_fn, train_weights, optimizer, metrics, print_train_batch, + print_freq, test_dataset + ): + for epoch in range(n_epoch): + start_time = time.time() + + train_loss, train_acc, n_iter = 0, 0, 0 + for X_batch, y_batch in train_dataset: + network.set_train() + + with tf.GradientTape() as tape: + # compute outputs + _logits = network(X_batch) + # compute loss and update model + _loss_ce = loss_fn(_logits, y_batch) + + grad = tape.gradient(_loss_ce, train_weights) + optimizer.apply_gradients(zip(grad, train_weights)) + + train_loss += _loss_ce + if metrics: + train_acc += metrics(_logits, y_batch) + else: + train_acc += np.mean(np.equal(np.argmax(_logits, 1), y_batch)) + n_iter += 1 + + if print_train_batch: + print("Epoch {} of {} took {}".format(epoch + 1, n_epoch, time.time() - start_time)) + print(" train loss: {}".format(train_loss / n_iter)) + print(" train acc: {}".format(train_acc / n_iter)) + + if epoch + 1 == 1 or (epoch + 1) % print_freq == 0: + print("Epoch {} of {} took {}".format(epoch + 1, n_epoch, time.time() - start_time)) + print(" train loss: {}".format(train_loss / n_iter)) + print(" train acc: {}".format(train_acc / n_iter)) + + if test_dataset: + # use training and evaluation sets to evaluate the model every print_freq epoch + if epoch + 1 == 1 or (epoch + 1) % print_freq == 0: + network.eval() + val_loss, val_acc, n_iter = 0, 0, 0 + for X_batch, y_batch in test_dataset: + _logits = network(X_batch) # is_train=False, disable dropout + val_loss += loss_fn(_logits, y_batch, name='eval_loss') + if metrics: + val_acc += metrics(_logits, y_batch) + else: + val_acc += np.mean(np.equal(np.argmax(_logits, 1), y_batch)) + n_iter += 1 + print(" val loss: {}".format(val_loss / n_iter)) + print(" val acc: {}".format(val_acc / n_iter)) + + def ms_train( + self, n_epoch, train_dataset, network, loss_fn, train_weights, optimizer, metrics, print_train_batch, + print_freq, test_dataset + ): + net_with_criterion = WithLoss(network, loss_fn) + train_network = GradWrap(net_with_criterion) + train_network.set_train() + for epoch in range(n_epoch): + start_time = time.time() + train_loss, train_acc, n_iter = 0, 0, 0 + for X_batch, y_batch in train_dataset: + output = network(X_batch) + loss_output = loss_fn(output, y_batch) + grads = train_network(X_batch, y_batch) + success = optimizer.apply_gradients(zip(grads, train_weights)) + loss = loss_output.asnumpy() + train_loss += loss + if metrics: + train_acc += metrics(output, y_batch) + else: + train_acc += np.mean((P.Equal()(P.Argmax(axis=1)(output), y_batch).asnumpy())) + n_iter += 1 + + if print_train_batch: + print("Epoch {} of {} took {}".format(epoch + 1, n_epoch, time.time() - start_time)) + print(" train loss: {}".format(train_loss / n_iter)) + print(" train acc: {}".format(train_acc / n_iter)) + + if epoch + 1 == 1 or (epoch + 1) % print_freq == 0: + print("Epoch {} of {} took {}".format(epoch + 1, n_epoch, time.time() - start_time)) + print(" train loss: {}".format(train_loss / n_iter)) + print(" train acc: {}".format(train_acc / n_iter)) + + if test_dataset: + # use training and evaluation sets to evaluate the model every print_freq epoch + if epoch + 1 == 1 or (epoch + 1) % print_freq == 0: + network.eval() + val_loss, val_acc, n_iter = 0, 0, 0 + for X_batch, y_batch in test_dataset: + _logits = network(X_batch) + val_loss += loss_fn(_logits, y_batch, name='eval_loss') + if metrics: + val_acc += metrics(_logits, y_batch) + else: + val_acc += np.mean((P.Equal()(P.Argmax(axis=1)(output), y_batch).asnumpy())) + n_iter += 1 + print(" val loss: {}".format(val_loss / n_iter)) + print(" val acc: {}".format(val_acc / n_iter)) + + +class WithLoss(Module): + + def __init__(self, backbone, loss_fn): + super(WithLoss, self).__init__() + self._backbone = backbone + self._loss_fn = loss_fn + + def construct(self, data, label): + out = self._backbone(data) + return self._loss_fn(out, label) + + @property + def backbone_network(self): + return self._backbone + + +class GradWrap(Module): + """ GradWrap definition """ + + def __init__(self, network): + super(GradWrap, self).__init__(auto_prefix=False) + self.network = network + self.weights = ParameterTuple(network.trainable_weights) + + def forward(self, x, label): + return composite.GradOperation(get_by_list=True)(self.network, self.weights)(x, label) diff --git a/tensorlayer/models/models/resnet50_weights_tf_dim_ordering_tf_kernels.h5 b/tensorlayer/models/models/resnet50_weights_tf_dim_ordering_tf_kernels.h5 new file mode 100644 index 0000000..904349f Binary files /dev/null and b/tensorlayer/models/models/resnet50_weights_tf_dim_ordering_tf_kernels.h5 differ diff --git a/tensorlayer/nlp.py b/tensorlayer/nlp.py new file mode 100644 index 0000000..1f22584 --- /dev/null +++ b/tensorlayer/nlp.py @@ -0,0 +1,1174 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import collections +import os +import random +import re +import subprocess +import tempfile +import warnings +from collections import Counter + +import numpy as np +import six as _six +import tensorflow as tf +from six.moves import urllib, xrange +from tensorflow.python.platform import gfile + +import tensorlayer as tl +from tensorlayer.lazy_imports import LazyImport + +nltk = LazyImport("nltk") + +__all__ = [ + 'generate_skip_gram_batch', + 'sample', + 'sample_top', + 'SimpleVocabulary', + 'Vocabulary', + 'process_sentence', + 'create_vocab', + 'simple_read_words', + 'read_words', + 'read_analogies_file', + 'build_vocab', + 'build_reverse_dictionary', + 'build_words_dataset', + 'words_to_word_ids', + 'word_ids_to_words', + 'save_vocab', + 'basic_tokenizer', + 'create_vocabulary', + 'initialize_vocabulary', + 'sentence_to_token_ids', + 'data_to_token_ids', + 'moses_multi_bleu', +] + + +def as_bytes(bytes_or_text, encoding='utf-8'): + """Converts either bytes or unicode to `bytes`, using utf-8 encoding for text. + Args: + bytes_or_text: A `bytes`, `str`, or `unicode` object. + encoding: A string indicating the charset for encoding unicode. + Returns: + A `bytes` object. + Raises: + TypeError: If `bytes_or_text` is not a binary or unicode string. + """ + if isinstance(bytes_or_text, _six.text_type): + return bytes_or_text.encode(encoding) + elif isinstance(bytes_or_text, bytes): + return bytes_or_text + else: + raise TypeError('Expected binary or unicode string, got %r' % (bytes_or_text, )) + + +def as_text(bytes_or_text, encoding='utf-8'): + """Returns the given argument as a unicode string. + Args: + bytes_or_text: A `bytes`, `str`, or `unicode` object. + encoding: A string indicating the charset for decoding unicode. + Returns: + A `unicode` (Python 2) or `str` (Python 3) object. + Raises: + TypeError: If `bytes_or_text` is not a binary or unicode string. + """ + if isinstance(bytes_or_text, _six.text_type): + return bytes_or_text + elif isinstance(bytes_or_text, bytes): + return bytes_or_text.decode(encoding) + else: + raise TypeError('Expected binary or unicode string, got %r' % bytes_or_text) + + +def generate_skip_gram_batch(data, batch_size, num_skips, skip_window, data_index=0): + """Generate a training batch for the Skip-Gram model. + + See `Word2Vec example `__. + + Parameters + ---------- + data : list of data + To present context, usually a list of integers. + batch_size : int + Batch size to return. + num_skips : int + How many times to reuse an input to generate a label. + skip_window : int + How many words to consider left and right. + data_index : int + Index of the context location. This code use `data_index` to instead of yield like ``tl.iterate``. + + Returns + ------- + batch : list of data + Inputs. + labels : list of data + Labels + data_index : int + Index of the context location. + + Examples + -------- + Setting num_skips=2, skip_window=1, use the right and left words. + In the same way, num_skips=4, skip_window=2 means use the nearby 4 words. + + >>> data = [1,2,3,4,5,6,7,8,9,10,11] + >>> batch, labels, data_index = tl.nlp.generate_skip_gram_batch(data=data, batch_size=8, num_skips=2, skip_window=1, data_index=0) + >>> print(batch) + [2 2 3 3 4 4 5 5] + >>> print(labels) + [[3] + [1] + [4] + [2] + [5] + [3] + [4] + [6]] + + """ + # global data_index # you can put data_index outside the function, then + # modify the global data_index in the function without return it. + # note: without using yield, this code use data_index to instead. + + if batch_size % num_skips != 0: + raise Exception("batch_size should be able to be divided by num_skips.") + if num_skips > 2 * skip_window: + raise Exception("num_skips <= 2 * skip_window") + batch = np.ndarray(shape=(batch_size), dtype=np.int32) + labels = np.ndarray(shape=(batch_size, 1), dtype=np.int32) + span = 2 * skip_window + 1 # [ skip_window target skip_window ] + buffer = collections.deque(maxlen=span) + for _ in range(span): + buffer.append(data[data_index]) + data_index = (data_index + 1) % len(data) + for i in range(batch_size // num_skips): + target = skip_window # target label at the center of the buffer + targets_to_avoid = [skip_window] + for j in range(num_skips): + while target in targets_to_avoid: + target = random.randint(0, span - 1) + targets_to_avoid.append(target) + batch[i * num_skips + j] = buffer[skip_window] + labels[i * num_skips + j, 0] = buffer[target] + buffer.append(data[data_index]) + data_index = (data_index + 1) % len(data) + return batch, labels, data_index + + +def sample(a=None, temperature=1.0): + """Sample an index from a probability array. + + Parameters + ---------- + a : list of float + List of probabilities. + temperature : float or None + The higher the more uniform. When a = [0.1, 0.2, 0.7], + - temperature = 0.7, the distribution will be sharpen [0.05048273, 0.13588945, 0.81362782] + - temperature = 1.0, the distribution will be the same [0.1, 0.2, 0.7] + - temperature = 1.5, the distribution will be filtered [0.16008435, 0.25411807, 0.58579758] + - If None, it will be ``np.argmax(a)`` + + Notes + ------ + - No matter what is the temperature and input list, the sum of all probabilities will be one. Even if input list = [1, 100, 200], the sum of all probabilities will still be one. + - For large vocabulary size, choice a higher temperature or ``tl.nlp.sample_top`` to avoid error. + + """ + if a is None: + raise Exception("a : list of float") + b = np.copy(a) + try: + if temperature == 1: + return np.argmax(np.random.multinomial(1, a, 1)) + if temperature is None: + return np.argmax(a) + else: + a = np.log(a) / temperature + a = np.exp(a) / np.sum(np.exp(a)) + return np.argmax(np.random.multinomial(1, a, 1)) + except Exception: + # np.set_printoptions(threshold=np.nan) + # tl.logging.info(a) + # tl.logging.info(np.sum(a)) + # tl.logging.info(np.max(a)) + # tl.logging.info(np.min(a)) + # exit() + message = "For large vocabulary_size, choice a higher temperature\ + to avoid log error. Hint : use ``sample_top``. " + + warnings.warn(message, Warning) + # tl.logging.info(a) + # tl.logging.info(b) + return np.argmax(np.random.multinomial(1, b, 1)) + + +def sample_top(a=None, top_k=10): + """Sample from ``top_k`` probabilities. + + Parameters + ---------- + a : list of float + List of probabilities. + top_k : int + Number of candidates to be considered. + + """ + if a is None: + a = [] + + idx = np.argpartition(a, -top_k)[-top_k:] + probs = a[idx] + # tl.logging.info("new %f" % probs) + probs = probs / np.sum(probs) + choice = np.random.choice(idx, p=probs) + return choice + # old implementation + # a = np.array(a) + # idx = np.argsort(a)[::-1] + # idx = idx[:top_k] + # # a = a[idx] + # probs = a[idx] + # tl.logging.info("prev %f" % probs) + # # probs = probs / np.sum(probs) + # # choice = np.random.choice(idx, p=probs) + # # return choice + + +# Vector representations of words (Advanced) UNDOCUMENT +class SimpleVocabulary(object): + """Simple vocabulary wrapper, see create_vocab(). + + Parameters + ------------ + vocab : dictionary + A dictionary that maps word to ID. + unk_id : int + The ID for 'unknown' word. + + """ + + def __init__(self, vocab, unk_id): + """Initialize the vocabulary.""" + self._vocab = vocab + self._unk_id = unk_id + + def word_to_id(self, word): + """Returns the integer id of a word string.""" + if word in self._vocab: + return self._vocab[word] + else: + return self._unk_id + + +class Vocabulary(object): + """Create Vocabulary class from a given vocabulary and its id-word, word-id convert. + See create_vocab() and ``tutorial_tfrecord3.py``. + + Parameters + ----------- + vocab_file : str + The file contains the vocabulary (can be created via ``tl.nlp.create_vocab``), where the words are the first whitespace-separated token on each line (other tokens are ignored) and the word ids are the corresponding line numbers. + start_word : str + Special word denoting sentence start. + end_word : str + Special word denoting sentence end. + unk_word : str + Special word denoting unknown words. + + Attributes + ------------ + vocab : dictionary + A dictionary that maps word to ID. + reverse_vocab : list of int + A list that maps ID to word. + start_id : int + For start ID. + end_id : int + For end ID. + unk_id : int + For unknown ID. + pad_id : int + For Padding ID. + + Examples + ------------- + The vocab file looks like follow, includes `start_word` , `end_word` ... + + >>> a 969108 + >>> 586368 + >>> 586368 + >>> . 440479 + >>> on 213612 + >>> of 202290 + >>> the 196219 + >>> in 182598 + >>> with 152984 + >>> and 139109 + >>> is 97322 + + """ + + def __init__(self, vocab_file, start_word="", end_word="", unk_word="", pad_word=""): + if not tf.io.gfile.exists(vocab_file): + tl.logging.fatal("Vocab file %s not found." % vocab_file) + tl.logging.info("Initializing vocabulary from file: %s" % vocab_file) + + with tf.io.gfile.GFile(vocab_file, mode="r") as f: + reverse_vocab = list(f.readlines()) + reverse_vocab = [line.split()[0] for line in reverse_vocab] + # assert start_word in reverse_vocab + # assert end_word in reverse_vocab + if start_word not in reverse_vocab: # haodong + reverse_vocab.append(start_word) + if end_word not in reverse_vocab: + reverse_vocab.append(end_word) + if unk_word not in reverse_vocab: + reverse_vocab.append(unk_word) + if pad_word not in reverse_vocab: + reverse_vocab.append(pad_word) + + vocab = dict([(x, y) for (y, x) in enumerate(reverse_vocab)]) + + tl.logging.info("Vocabulary from %s : %s %s %s" % (vocab_file, start_word, end_word, unk_word)) + tl.logging.info(" vocabulary with %d words (includes start_word, end_word, unk_word)" % len(vocab)) + # tl.logging.info(" vocabulary with %d words" % len(vocab)) + + self.vocab = vocab # vocab[word] = id + self.reverse_vocab = reverse_vocab # reverse_vocab[id] = word + + # Save special word ids. + self.start_id = vocab[start_word] + self.end_id = vocab[end_word] + self.unk_id = vocab[unk_word] + self.pad_id = vocab[pad_word] + tl.logging.info(" start_id: %d" % self.start_id) + tl.logging.info(" end_id : %d" % self.end_id) + tl.logging.info(" unk_id : %d" % self.unk_id) + tl.logging.info(" pad_id : %d" % self.pad_id) + + def word_to_id(self, word): + """Returns the integer word id of a word string.""" + if word in self.vocab: + return self.vocab[word] + else: + return self.unk_id + + def id_to_word(self, word_id): + """Returns the word string of an integer word id.""" + if word_id >= len(self.reverse_vocab): + return self.reverse_vocab[self.unk_id] + else: + return self.reverse_vocab[word_id] + + +def process_sentence(sentence, start_word="", end_word=""): + """Seperate a sentence string into a list of string words, add start_word and end_word, + see ``create_vocab()`` and ``tutorial_tfrecord3.py``. + + Parameters + ---------- + sentence : str + A sentence. + start_word : str or None + The start word. If None, no start word will be appended. + end_word : str or None + The end word. If None, no end word will be appended. + + Returns + --------- + list of str + A list of strings that separated into words. + + Examples + ----------- + >>> c = "how are you?" + >>> c = tl.nlp.process_sentence(c) + >>> print(c) + ['', 'how', 'are', 'you', '?', ''] + + Notes + ------- + - You have to install the following package. + - `Installing NLTK `__ + - `Installing NLTK data `__ + + """ + if start_word is not None: + process_sentence = [start_word] + else: + process_sentence = [] + process_sentence.extend(nltk.tokenize.word_tokenize(sentence.lower())) + + if end_word is not None: + process_sentence.append(end_word) + return process_sentence + + +def create_vocab(sentences, word_counts_output_file, min_word_count=1): + """Creates the vocabulary of word to word_id. + + See ``tutorial_tfrecord3.py``. + + The vocabulary is saved to disk in a text file of word counts. The id of each + word in the file is its corresponding 0-based line number. + + Parameters + ------------ + sentences : list of list of str + All sentences for creating the vocabulary. + word_counts_output_file : str + The file name. + min_word_count : int + Minimum number of occurrences for a word. + + Returns + -------- + :class:`SimpleVocabulary` + The simple vocabulary object, see :class:`Vocabulary` for more. + + Examples + -------- + Pre-process sentences + + >>> captions = ["one two , three", "four five five"] + >>> processed_capts = [] + >>> for c in captions: + >>> c = tl.nlp.process_sentence(c, start_word="", end_word="") + >>> processed_capts.append(c) + >>> print(processed_capts) + ...[['', 'one', 'two', ',', 'three', ''], ['', 'four', 'five', 'five', '']] + + Create vocabulary + + >>> tl.nlp.create_vocab(processed_capts, word_counts_output_file='vocab.txt', min_word_count=1) + Creating vocabulary. + Total words: 8 + Words in vocabulary: 8 + Wrote vocabulary file: vocab.txt + + Get vocabulary object + + >>> vocab = tl.nlp.Vocabulary('vocab.txt', start_word="", end_word="", unk_word="") + INFO:tensorflow:Initializing vocabulary from file: vocab.txt + [TL] Vocabulary from vocab.txt : + vocabulary with 10 words (includes start_word, end_word, unk_word) + start_id: 2 + end_id: 3 + unk_id: 9 + pad_id: 0 + + """ + tl.logging.info("Creating vocabulary.") + + counter = Counter() + + for c in sentences: + counter.update(c) + # tl.logging.info('c',c) + tl.logging.info(" Total words: %d" % len(counter)) + + # Filter uncommon words and sort by descending count. + word_counts = [x for x in counter.items() if x[1] >= min_word_count] + word_counts.sort(key=lambda x: x[1], reverse=True) + word_counts = [("", 0)] + word_counts # 1st id should be reserved for padding + # tl.logging.info(word_counts) + tl.logging.info(" Words in vocabulary: %d" % len(word_counts)) + + # Write out the word counts file. + with tf.io.gfile.GFile(word_counts_output_file, "w") as f: + f.write("\n".join(["%s %d" % (w, c) for w, c in word_counts])) + tl.logging.info(" Wrote vocabulary file: %s" % word_counts_output_file) + + # Create the vocabulary dictionary. + reverse_vocab = [x[0] for x in word_counts] + unk_id = len(reverse_vocab) + vocab_dict = dict([(x, y) for (y, x) in enumerate(reverse_vocab)]) + vocab = SimpleVocabulary(vocab_dict, unk_id) + + return vocab + + +# Vector representations of words +def simple_read_words(filename="nietzsche.txt"): + """Read context from file without any preprocessing. + + Parameters + ---------- + filename : str + A file path (like .txt file) + + Returns + -------- + str + The context in a string. + + """ + with open(filename, "r") as f: + words = f.read() + return words + + +def read_words(filename="nietzsche.txt", replace=None): + """Read list format context from a file. + + For customized read_words method, see ``tutorial_generate_text.py``. + + Parameters + ---------- + filename : str + a file path. + replace : list of str + replace original string by target string. + + Returns + ------- + list of str + The context in a list (split using space). + """ + if replace is None: + replace = ['\n', ''] + + with tf.io.gfile.GFile(filename, "r") as f: + try: # python 3.4 or older + context_list = f.read().replace(*replace).split() + except Exception: # python 3.5 + f.seek(0) + replace = [x.encode('utf-8') for x in replace] + context_list = f.read().replace(*replace).split() + return context_list + + +def read_analogies_file(eval_file='questions-words.txt', word2id=None): + """Reads through an analogy question file, return its id format. + + Parameters + ---------- + eval_file : str + The file name. + word2id : dictionary + a dictionary that maps word to ID. + + Returns + -------- + numpy.array + A ``[n_examples, 4]`` numpy array containing the analogy question's word IDs. + + Examples + --------- + The file should be in this format + + >>> : capital-common-countries + >>> Athens Greece Baghdad Iraq + >>> Athens Greece Bangkok Thailand + >>> Athens Greece Beijing China + >>> Athens Greece Berlin Germany + >>> Athens Greece Bern Switzerland + >>> Athens Greece Cairo Egypt + >>> Athens Greece Canberra Australia + >>> Athens Greece Hanoi Vietnam + >>> Athens Greece Havana Cuba + + Get the tokenized analogy question data + + >>> words = tl.files.load_matt_mahoney_text8_dataset() + >>> data, count, dictionary, reverse_dictionary = tl.nlp.build_words_dataset(words, vocabulary_size, True) + >>> analogy_questions = tl.nlp.read_analogies_file(eval_file='questions-words.txt', word2id=dictionary) + >>> print(analogy_questions) + [[ 3068 1248 7161 1581] + [ 3068 1248 28683 5642] + [ 3068 1248 3878 486] + ..., + [ 1216 4309 19982 25506] + [ 1216 4309 3194 8650] + [ 1216 4309 140 312]] + + """ + if word2id is None: + word2id = {} + + questions = [] + questions_skipped = 0 + + with open(eval_file, "rb") as analogy_f: + for line in analogy_f: + if line.startswith(b":"): # Skip comments. + continue + words = line.strip().lower().split(b" ") # lowercase + ids = [word2id.get(w.strip().decode()) for w in words] + if None in ids or len(ids) != 4: + questions_skipped += 1 + else: + questions.append(np.array(ids)) + tl.logging.info("Eval analogy file: %s" % eval_file) + tl.logging.info("Questions: %d", len(questions)) + tl.logging.info("Skipped: %d", questions_skipped) + analogy_questions = np.array(questions, dtype=np.int32) + return analogy_questions + + +def build_vocab(data): + """Build vocabulary. + + Given the context in list format. + Return the vocabulary, which is a dictionary for word to id. + e.g. {'campbell': 2587, 'atlantic': 2247, 'aoun': 6746 .... } + + Parameters + ---------- + data : list of str + The context in list format + + Returns + -------- + dictionary + that maps word to unique ID. e.g. {'campbell': 2587, 'atlantic': 2247, 'aoun': 6746 .... } + + References + --------------- + - `tensorflow.models.rnn.ptb.reader `_ + + Examples + -------- + >>> data_path = os.getcwd() + '/simple-examples/data' + >>> train_path = os.path.join(data_path, "ptb.train.txt") + >>> word_to_id = build_vocab(read_txt_words(train_path)) + + """ + # data = _read_words(filename) + counter = collections.Counter(data) + # tl.logging.info('counter %s' % counter) # dictionary for the occurrence number of each word, e.g. 'banknote': 1, 'photography': 1, 'kia': 1 + count_pairs = sorted(counter.items(), key=lambda x: (-x[1], x[0])) + # tl.logging.info('count_pairs %s' % count_pairs) # convert dictionary to list of tuple, e.g. ('ssangyong', 1), ('swapo', 1), ('wachter', 1) + words, _ = list(zip(*count_pairs)) + word_to_id = dict(zip(words, range(len(words)))) + # tl.logging.info(words) # list of words + # tl.logging.info(word_to_id) # dictionary for word to id, e.g. 'campbell': 2587, 'atlantic': 2247, 'aoun': 6746 + return word_to_id + + +def build_reverse_dictionary(word_to_id): + """Given a dictionary that maps word to integer id. + Returns a reverse dictionary that maps a id to word. + + Parameters + ---------- + word_to_id : dictionary + that maps word to ID. + + Returns + -------- + dictionary + A dictionary that maps IDs to words. + + """ + reverse_dictionary = dict(zip(word_to_id.values(), word_to_id.keys())) + return reverse_dictionary + + +def build_words_dataset(words=None, vocabulary_size=50000, printable=True, unk_key='UNK'): + """Build the words dictionary and replace rare words with 'UNK' token. + The most common word has the smallest integer id. + + Parameters + ---------- + words : list of str or byte + The context in list format. You may need to do preprocessing on the words, such as lower case, remove marks etc. + vocabulary_size : int + The maximum vocabulary size, limiting the vocabulary size. Then the script replaces rare words with 'UNK' token. + printable : boolean + Whether to print the read vocabulary size of the given words. + unk_key : str + Represent the unknown words. + + Returns + -------- + data : list of int + The context in a list of ID. + count : list of tuple and list + Pair words and IDs. + - count[0] is a list : the number of rare words + - count[1:] are tuples : the number of occurrence of each word + - e.g. [['UNK', 418391], (b'the', 1061396), (b'of', 593677), (b'and', 416629), (b'one', 411764)] + dictionary : dictionary + It is `word_to_id` that maps word to ID. + reverse_dictionary : a dictionary + It is `id_to_word` that maps ID to word. + + Examples + -------- + >>> words = tl.files.load_matt_mahoney_text8_dataset() + >>> vocabulary_size = 50000 + >>> data, count, dictionary, reverse_dictionary = tl.nlp.build_words_dataset(words, vocabulary_size) + + References + ----------------- + - `tensorflow/examples/tutorials/word2vec/word2vec_basic.py `__ + + """ + if words is None: + raise Exception("words : list of str or byte") + + count = [[unk_key, -1]] + count.extend(collections.Counter(words).most_common(vocabulary_size - 1)) + dictionary = dict() + for word, _ in count: + dictionary[word] = len(dictionary) + data = list() + unk_count = 0 + for word in words: + if word in dictionary: + index = dictionary[word] + else: + index = 0 # dictionary['UNK'] + unk_count += 1 + data.append(index) + count[0][1] = unk_count + reverse_dictionary = dict(zip(dictionary.values(), dictionary.keys())) + if printable: + tl.logging.info('Real vocabulary size %d' % len(collections.Counter(words).keys())) + tl.logging.info('Limited vocabulary size {}'.format(vocabulary_size)) + if len(collections.Counter(words).keys()) < vocabulary_size: + raise Exception( + "len(collections.Counter(words).keys()) >= vocabulary_size , the limited vocabulary_size must be less than or equal to the read vocabulary_size" + ) + return data, count, dictionary, reverse_dictionary + + +def words_to_word_ids(data=None, word_to_id=None, unk_key='UNK'): + """Convert a list of string (words) to IDs. + + Parameters + ---------- + data : list of string or byte + The context in list format + word_to_id : a dictionary + that maps word to ID. + unk_key : str + Represent the unknown words. + + Returns + -------- + list of int + A list of IDs to represent the context. + + Examples + -------- + >>> words = tl.files.load_matt_mahoney_text8_dataset() + >>> vocabulary_size = 50000 + >>> data, count, dictionary, reverse_dictionary = tl.nlp.build_words_dataset(words, vocabulary_size, True) + >>> context = [b'hello', b'how', b'are', b'you'] + >>> ids = tl.nlp.words_to_word_ids(words, dictionary) + >>> context = tl.nlp.word_ids_to_words(ids, reverse_dictionary) + >>> print(ids) + [6434, 311, 26, 207] + >>> print(context) + [b'hello', b'how', b'are', b'you'] + + References + --------------- + - `tensorflow.models.rnn.ptb.reader `__ + + """ + if data is None: + raise Exception("data : list of string or byte") + if word_to_id is None: + raise Exception("word_to_id : a dictionary") + # if isinstance(data[0], six.string_types): + # tl.logging.info(type(data[0])) + # # exit() + # tl.logging.info(data[0]) + # tl.logging.info(word_to_id) + # return [word_to_id[str(word)] for word in data] + # else: + + word_ids = [] + for word in data: + if word_to_id.get(word) is not None: + word_ids.append(word_to_id[word]) + else: + word_ids.append(word_to_id[unk_key]) + return word_ids + # return [word_to_id[word] for word in data] # this one + + # if isinstance(data[0], str): + # # tl.logging.info('is a string object') + # return [word_to_id[word] for word in data] + # else:#if isinstance(s, bytes): + # # tl.logging.info('is a unicode object') + # # tl.logging.info(data[0]) + # return [word_to_id[str(word)] f + + +def word_ids_to_words(data, id_to_word): + """Convert a list of integer to strings (words). + + Parameters + ---------- + data : list of int + The context in list format. + id_to_word : dictionary + a dictionary that maps ID to word. + + Returns + -------- + list of str + A list of string or byte to represent the context. + + Examples + --------- + see ``tl.nlp.words_to_word_ids`` + + """ + return [id_to_word[i] for i in data] + + +def save_vocab(count=None, name='vocab.txt'): + """Save the vocabulary to a file so the model can be reloaded. + + Parameters + ---------- + count : a list of tuple and list + count[0] is a list : the number of rare words, + count[1:] are tuples : the number of occurrence of each word, + e.g. [['UNK', 418391], (b'the', 1061396), (b'of', 593677), (b'and', 416629), (b'one', 411764)] + + Examples + --------- + >>> words = tl.files.load_matt_mahoney_text8_dataset() + >>> vocabulary_size = 50000 + >>> data, count, dictionary, reverse_dictionary = tl.nlp.build_words_dataset(words, vocabulary_size, True) + >>> tl.nlp.save_vocab(count, name='vocab_text8.txt') + >>> vocab_text8.txt + UNK 418391 + the 1061396 + of 593677 + and 416629 + one 411764 + in 372201 + a 325873 + to 316376 + + """ + if count is None: + count = [] + + pwd = os.getcwd() + vocabulary_size = len(count) + with open(os.path.join(pwd, name), "w") as f: + for i in xrange(vocabulary_size): + f.write("%s %d\n" % (as_text(count[i][0]), count[i][1])) + tl.logging.info("%d vocab saved to %s in %s" % (vocabulary_size, name, pwd)) + + +# Functions for translation + + +def basic_tokenizer(sentence, _WORD_SPLIT=re.compile(b"([.,!?\"':;)(])")): + """Very basic tokenizer: split the sentence into a list of tokens. + + Parameters + ----------- + sentence : tensorflow.python.platform.gfile.GFile Object + _WORD_SPLIT : regular expression for word spliting. + + + Examples + -------- + >>> see create_vocabulary + >>> from tensorflow.python.platform import gfile + >>> train_path = "wmt/giga-fren.release2" + >>> with gfile.GFile(train_path + ".en", mode="rb") as f: + >>> for line in f: + >>> tokens = tl.nlp.basic_tokenizer(line) + >>> tl.logging.info(tokens) + >>> exit() + [b'Changing', b'Lives', b'|', b'Changing', b'Society', b'|', b'How', + b'It', b'Works', b'|', b'Technology', b'Drives', b'Change', b'Home', + b'|', b'Concepts', b'|', b'Teachers', b'|', b'Search', b'|', b'Overview', + b'|', b'Credits', b'|', b'HHCC', b'Web', b'|', b'Reference', b'|', + b'Feedback', b'Virtual', b'Museum', b'of', b'Canada', b'Home', b'Page'] + + References + ---------- + - Code from ``/tensorflow/models/rnn/translation/data_utils.py`` + + """ + words = [] + sentence = as_bytes(sentence) + for space_separated_fragment in sentence.strip().split(): + words.extend(re.split(_WORD_SPLIT, space_separated_fragment)) + return [w for w in words if w] + + +def create_vocabulary( + vocabulary_path, data_path, max_vocabulary_size, tokenizer=None, normalize_digits=True, + _DIGIT_RE=re.compile(br"\d"), _START_VOCAB=None +): + r"""Create vocabulary file (if it does not exist yet) from data file. + + Data file is assumed to contain one sentence per line. Each sentence is + tokenized and digits are normalized (if normalize_digits is set). + Vocabulary contains the most-frequent tokens up to max_vocabulary_size. + We write it to vocabulary_path in a one-token-per-line format, so that later + token in the first line gets id=0, second line gets id=1, and so on. + + Parameters + ----------- + vocabulary_path : str + Path where the vocabulary will be created. + data_path : str + Data file that will be used to create vocabulary. + max_vocabulary_size : int + Limit on the size of the created vocabulary. + tokenizer : function + A function to use to tokenize each data sentence. If None, basic_tokenizer will be used. + normalize_digits : boolean + If true, all digits are replaced by `0`. + _DIGIT_RE : regular expression function + Default is ``re.compile(br"\d")``. + _START_VOCAB : list of str + The pad, go, eos and unk token, default is ``[b"_PAD", b"_GO", b"_EOS", b"_UNK"]``. + + References + ---------- + - Code from ``/tensorflow/models/rnn/translation/data_utils.py`` + + """ + if _START_VOCAB is None: + _START_VOCAB = [b"_PAD", b"_GO", b"_EOS", b"_UNK"] + if not gfile.Exists(vocabulary_path): + tl.logging.info("Creating vocabulary %s from data %s" % (vocabulary_path, data_path)) + vocab = {} + with gfile.GFile(data_path, mode="rb") as f: + counter = 0 + for line in f: + counter += 1 + if counter % 100000 == 0: + tl.logging.info(" processing line %d" % counter) + tokens = tokenizer(line) if tokenizer else basic_tokenizer(line) + for w in tokens: + word = re.sub(_DIGIT_RE, b"0", w) if normalize_digits else w + if word in vocab: + vocab[word] += 1 + else: + vocab[word] = 1 + vocab_list = _START_VOCAB + sorted(vocab, key=vocab.get, reverse=True) + if len(vocab_list) > max_vocabulary_size: + vocab_list = vocab_list[:max_vocabulary_size] + with gfile.GFile(vocabulary_path, mode="wb") as vocab_file: + for w in vocab_list: + vocab_file.write(w + b"\n") + else: + tl.logging.info("Vocabulary %s from data %s exists" % (vocabulary_path, data_path)) + + +def initialize_vocabulary(vocabulary_path): + """Initialize vocabulary from file, return the `word_to_id` (dictionary) + and `id_to_word` (list). + + We assume the vocabulary is stored one-item-per-line, so a file will result in a vocabulary {"dog": 0, "cat": 1}, and this function will also return the reversed-vocabulary ["dog", "cat"]. + + Parameters + ----------- + vocabulary_path : str + Path to the file containing the vocabulary. + + Returns + -------- + vocab : dictionary + a dictionary that maps word to ID. + rev_vocab : list of int + a list that maps ID to word. + + Examples + --------- + >>> Assume 'test' contains + dog + cat + bird + >>> vocab, rev_vocab = tl.nlp.initialize_vocabulary("test") + >>> print(vocab) + >>> {b'cat': 1, b'dog': 0, b'bird': 2} + >>> print(rev_vocab) + >>> [b'dog', b'cat', b'bird'] + + Raises + ------- + ValueError : if the provided vocabulary_path does not exist. + + """ + if gfile.Exists(vocabulary_path): + rev_vocab = [] + with gfile.GFile(vocabulary_path, mode="rb") as f: + rev_vocab.extend(f.readlines()) + rev_vocab = [as_bytes(line.strip()) for line in rev_vocab] + vocab = dict([(x, y) for (y, x) in enumerate(rev_vocab)]) + return vocab, rev_vocab + else: + raise ValueError("Vocabulary file %s not found.", vocabulary_path) + + +def sentence_to_token_ids( + sentence, vocabulary, tokenizer=None, normalize_digits=True, UNK_ID=3, _DIGIT_RE=re.compile(br"\d") +): + """Convert a string to list of integers representing token-ids. + + For example, a sentence "I have a dog" may become tokenized into + ["I", "have", "a", "dog"] and with vocabulary {"I": 1, "have": 2, + "a": 4, "dog": 7"} this function will return [1, 2, 4, 7]. + + Parameters + ----------- + sentence : tensorflow.python.platform.gfile.GFile Object + The sentence in bytes format to convert to token-ids, see ``basic_tokenizer()`` and ``data_to_token_ids()``. + vocabulary : dictionary + Mmapping tokens to integers. + tokenizer : function + A function to use to tokenize each sentence. If None, ``basic_tokenizer`` will be used. + normalize_digits : boolean + If true, all digits are replaced by 0. + + Returns + -------- + list of int + The token-ids for the sentence. + + """ + if tokenizer: + words = tokenizer(sentence) + else: + words = basic_tokenizer(sentence) + if not normalize_digits: + return [vocabulary.get(w, UNK_ID) for w in words] + # Normalize digits by 0 before looking words up in the vocabulary. + return [vocabulary.get(re.sub(_DIGIT_RE, b"0", w), UNK_ID) for w in words] + + +def data_to_token_ids( + data_path, target_path, vocabulary_path, tokenizer=None, normalize_digits=True, UNK_ID=3, + _DIGIT_RE=re.compile(br"\d") +): + """Tokenize data file and turn into token-ids using given vocabulary file. + + This function loads data line-by-line from data_path, calls the above + sentence_to_token_ids, and saves the result to target_path. See comment + for sentence_to_token_ids on the details of token-ids format. + + Parameters + ----------- + data_path : str + Path to the data file in one-sentence-per-line format. + target_path : str + Path where the file with token-ids will be created. + vocabulary_path : str + Path to the vocabulary file. + tokenizer : function + A function to use to tokenize each sentence. If None, ``basic_tokenizer`` will be used. + normalize_digits : boolean + If true, all digits are replaced by 0. + + References + ---------- + - Code from ``/tensorflow/models/rnn/translation/data_utils.py`` + + """ + if not gfile.Exists(target_path): + tl.logging.info("Tokenizing data in %s" % data_path) + vocab, _ = initialize_vocabulary(vocabulary_path) + with gfile.GFile(data_path, mode="rb") as data_file: + with gfile.GFile(target_path, mode="w") as tokens_file: + counter = 0 + for line in data_file: + counter += 1 + if counter % 100000 == 0: + tl.logging.info(" tokenizing line %d" % counter) + token_ids = sentence_to_token_ids( + line, vocab, tokenizer, normalize_digits, UNK_ID=UNK_ID, _DIGIT_RE=_DIGIT_RE + ) + tokens_file.write(" ".join([str(tok) for tok in token_ids]) + "\n") + else: + tl.logging.info("Target path %s exists" % target_path) + + +def moses_multi_bleu(hypotheses, references, lowercase=False): + """Calculate the bleu score for hypotheses and references + using the MOSES ulti-bleu.perl script. + + Parameters + ------------ + hypotheses : numpy.array.string + A numpy array of strings where each string is a single example. + references : numpy.array.string + A numpy array of strings where each string is a single example. + lowercase : boolean + If True, pass the "-lc" flag to the multi-bleu script + + Examples + --------- + >>> hypotheses = ["a bird is flying on the sky"] + >>> references = ["two birds are flying on the sky", "a bird is on the top of the tree", "an airplane is on the sky",] + >>> score = tl.nlp.moses_multi_bleu(hypotheses, references) + + Returns + -------- + float + The BLEU score + + References + ---------- + - `Google/seq2seq/metric/bleu `__ + + """ + if np.size(hypotheses) == 0: + return np.float32(0.0) + + # Get MOSES multi-bleu script + try: + multi_bleu_path, _ = urllib.request.urlretrieve( + "https://raw.githubusercontent.com/moses-smt/mosesdecoder/" + "master/scripts/generic/multi-bleu.perl" + ) + os.chmod(multi_bleu_path, 0o755) + except Exception: # pylint: disable=W0702 + tl.logging.info("Unable to fetch multi-bleu.perl script, using local.") + metrics_dir = os.path.dirname(os.path.realpath(__file__)) + bin_dir = os.path.abspath(os.path.join(metrics_dir, "..", "..", "bin")) + multi_bleu_path = os.path.join(bin_dir, "tools/multi-bleu.perl") + + # Dump hypotheses and references to tempfiles + hypothesis_file = tempfile.NamedTemporaryFile() + hypothesis_file.write("\n".join(hypotheses).encode("utf-8")) + hypothesis_file.write(b"\n") + hypothesis_file.flush() + reference_file = tempfile.NamedTemporaryFile() + reference_file.write("\n".join(references).encode("utf-8")) + reference_file.write(b"\n") + reference_file.flush() + + # Calculate BLEU using multi-bleu script + with open(hypothesis_file.name, "r") as read_pred: + bleu_cmd = [multi_bleu_path] + if lowercase: + bleu_cmd += ["-lc"] + bleu_cmd += [reference_file.name] + try: + bleu_out = subprocess.check_output(bleu_cmd, stdin=read_pred, stderr=subprocess.STDOUT) + bleu_out = bleu_out.decode("utf-8") + bleu_score = re.search(r"BLEU = (.+?),", bleu_out).group(1) + bleu_score = float(bleu_score) + except subprocess.CalledProcessError as error: + if error.output is not None: + tl.logging.warning("multi-bleu.perl script returned non-zero exit code") + tl.logging.warning(error.output) + bleu_score = np.float32(0.0) + + # Close temp files + hypothesis_file.close() + reference_file.close() + + return np.float32(bleu_score) diff --git a/tensorlayer/optimizers/__init__.py b/tensorlayer/optimizers/__init__.py new file mode 100644 index 0000000..ffe9995 --- /dev/null +++ b/tensorlayer/optimizers/__init__.py @@ -0,0 +1,25 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +""" +TensorLayer provides rich layer implementations trailed for +various benchmarks and domain-specific problems. In addition, we also +support transparent access to native TensorFlow parameters. +For example, we provide not only layers for local response normalization, but also +layers that allow user to apply ``tf.ops.lrn`` on ``network.outputs``. +More functions can be found in `TensorFlow API `__. +""" + +from .amsgrad import AMSGrad + +# ['Adadelta', 'Adagrad', 'Adam', 'Admax', 'Ftrl', 'Nadam', 'RMSprop', 'SGD', 'Momentum', 'Lamb', 'LARS'] +from .load_optimizers_backend import Adadelta +from .load_optimizers_backend import Adagrad +from .load_optimizers_backend import Adam +from .load_optimizers_backend import Admax +from .load_optimizers_backend import Ftrl +from .load_optimizers_backend import Nadam +from .load_optimizers_backend import RMSprop +from .load_optimizers_backend import SGD +from .load_optimizers_backend import Momentum +from .load_optimizers_backend import Lamb +from .load_optimizers_backend import LARS diff --git a/tensorlayer/optimizers/amsgrad.py b/tensorlayer/optimizers/amsgrad.py new file mode 100644 index 0000000..446426c --- /dev/null +++ b/tensorlayer/optimizers/amsgrad.py @@ -0,0 +1,195 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +"""AMSGrad Implementation based on the paper: "On the Convergence of Adam and Beyond" (ICLR 2018) +Article Link: https://openreview.net/pdf?id=ryQu7f-RZ +Original Implementation by: https://github.com/taki0112/AMSGrad-Tensorflow +""" + +from tensorflow.python.eager import context +from tensorflow.python.framework import ops +from tensorflow.python.ops import (control_flow_ops, math_ops, resource_variable_ops, state_ops, variable_scope) +from tensorflow.python.training import optimizer + + +class AMSGrad(optimizer.Optimizer): + """Implementation of the AMSGrad optimization algorithm. + + See: `On the Convergence of Adam and Beyond - [Reddi et al., 2018] `__. + + Parameters + ---------- + learning_rate: float + A Tensor or a floating point value. The learning rate. + beta1: float + A float value or a constant float tensor. + The exponential decay rate for the 1st moment estimates. + beta2: float + A float value or a constant float tensor. + The exponential decay rate for the 2nd moment estimates. + epsilon: float + A small constant for numerical stability. + This epsilon is "epsilon hat" in the Kingma and Ba paper + (in the formula just before Section 2.1), not the epsilon in Algorithm 1 of the paper. + use_locking: bool + If True use locks for update operations. + name: str + Optional name for the operations created when applying gradients. + Defaults to "AMSGrad". + """ + + def __init__(self, learning_rate=0.01, beta1=0.9, beta2=0.99, epsilon=1e-8, use_locking=False, name="AMSGrad"): + """Construct a new Adam optimizer.""" + super(AMSGrad, self).__init__(use_locking, name) + self._lr = learning_rate + self._beta1 = beta1 + self._beta2 = beta2 + self._epsilon = epsilon + + self._lr_t = None + self._beta1_t = None + self._beta2_t = None + self._epsilon_t = None + + self._beta1_power = None + self._beta2_power = None + + def _create_slots(self, var_list): + first_var = min(var_list, key=lambda x: x.name) + + create_new = self._beta1_power is None + if not create_new and context.in_graph_mode(): + create_new = (self._beta1_power.graph is not first_var.graph) + + if create_new: + with ops.colocate_with(first_var): + self._beta1_power = variable_scope.variable(self._beta1, name="beta1_power", trainable=False) + self._beta2_power = variable_scope.variable(self._beta2, name="beta2_power", trainable=False) + # Create slots for the first and second moments. + for v in var_list: + self._zeros_slot(v, "m", self._name) + self._zeros_slot(v, "v", self._name) + self._zeros_slot(v, "vhat", self._name) + + def _prepare(self): + self._lr_t = ops.convert_to_tensor(self._lr) + self._beta1_t = ops.convert_to_tensor(self._beta1) + self._beta2_t = ops.convert_to_tensor(self._beta2) + self._epsilon_t = ops.convert_to_tensor(self._epsilon) + + def _apply_dense(self, grad, var): + beta1_power = math_ops.cast(self._beta1_power, var.dtype.base_dtype) + beta2_power = math_ops.cast(self._beta2_power, var.dtype.base_dtype) + lr_t = math_ops.cast(self._lr_t, var.dtype.base_dtype) + beta1_t = math_ops.cast(self._beta1_t, var.dtype.base_dtype) + beta2_t = math_ops.cast(self._beta2_t, var.dtype.base_dtype) + epsilon_t = math_ops.cast(self._epsilon_t, var.dtype.base_dtype) + + lr = (lr_t * math_ops.sqrt(1 - beta2_power) / (1 - beta1_power)) + + # m_t = beta1 * m + (1 - beta1) * g_t + m = self.get_slot(var, "m") + m_scaled_g_values = grad * (1 - beta1_t) + m_t = state_ops.assign(m, beta1_t * m + m_scaled_g_values, use_locking=self._use_locking) + + # v_t = beta2 * v + (1 - beta2) * (g_t * g_t) + v = self.get_slot(var, "v") + v_scaled_g_values = (grad * grad) * (1 - beta2_t) + v_t = state_ops.assign(v, beta2_t * v + v_scaled_g_values, use_locking=self._use_locking) + + # amsgrad + vhat = self.get_slot(var, "vhat") + vhat_t = state_ops.assign(vhat, math_ops.maximum(v_t, vhat)) + v_sqrt = math_ops.sqrt(vhat_t) + + var_update = state_ops.assign_sub(var, lr * m_t / (v_sqrt + epsilon_t), use_locking=self._use_locking) + return control_flow_ops.group(*[var_update, m_t, v_t, vhat_t]) + + def _resource_apply_dense(self, grad, var): + var = var.handle + beta1_power = math_ops.cast(self._beta1_power, grad.dtype.base_dtype) + beta2_power = math_ops.cast(self._beta2_power, grad.dtype.base_dtype) + lr_t = math_ops.cast(self._lr_t, grad.dtype.base_dtype) + beta1_t = math_ops.cast(self._beta1_t, grad.dtype.base_dtype) + beta2_t = math_ops.cast(self._beta2_t, grad.dtype.base_dtype) + epsilon_t = math_ops.cast(self._epsilon_t, grad.dtype.base_dtype) + + lr = (lr_t * math_ops.sqrt(1 - beta2_power) / (1 - beta1_power)) + + # m_t = beta1 * m + (1 - beta1) * g_t + m = self.get_slot(var, "m").handle + m_scaled_g_values = grad * (1 - beta1_t) + m_t = state_ops.assign(m, beta1_t * m + m_scaled_g_values, use_locking=self._use_locking) + + # v_t = beta2 * v + (1 - beta2) * (g_t * g_t) + v = self.get_slot(var, "v").handle + v_scaled_g_values = (grad * grad) * (1 - beta2_t) + v_t = state_ops.assign(v, beta2_t * v + v_scaled_g_values, use_locking=self._use_locking) + + # amsgrad + vhat = self.get_slot(var, "vhat").handle + vhat_t = state_ops.assign(vhat, math_ops.maximum(v_t, vhat)) + v_sqrt = math_ops.sqrt(vhat_t) + + var_update = state_ops.assign_sub(var, lr * m_t / (v_sqrt + epsilon_t), use_locking=self._use_locking) + return control_flow_ops.group(*[var_update, m_t, v_t, vhat_t]) + + def _apply_sparse_shared(self, grad, var, indices, scatter_add): + beta1_power = math_ops.cast(self._beta1_power, var.dtype.base_dtype) + beta2_power = math_ops.cast(self._beta2_power, var.dtype.base_dtype) + lr_t = math_ops.cast(self._lr_t, var.dtype.base_dtype) + beta1_t = math_ops.cast(self._beta1_t, var.dtype.base_dtype) + beta2_t = math_ops.cast(self._beta2_t, var.dtype.base_dtype) + epsilon_t = math_ops.cast(self._epsilon_t, var.dtype.base_dtype) + + lr = (lr_t * math_ops.sqrt(1 - beta2_power) / (1 - beta1_power)) + + # m_t = beta1 * m + (1 - beta1) * g_t + m = self.get_slot(var, "m") + m_scaled_g_values = grad * (1 - beta1_t) + m_t = state_ops.assign(m, m * beta1_t, use_locking=self._use_locking) + with ops.control_dependencies([m_t]): + m_t = scatter_add(m, indices, m_scaled_g_values) + + # v_t = beta2 * v + (1 - beta2) * (g_t * g_t) + v = self.get_slot(var, "v") + v_scaled_g_values = (grad * grad) * (1 - beta2_t) + v_t = state_ops.assign(v, v * beta2_t, use_locking=self._use_locking) + with ops.control_dependencies([v_t]): + v_t = scatter_add(v, indices, v_scaled_g_values) + + # amsgrad + vhat = self.get_slot(var, "vhat") + vhat_t = state_ops.assign(vhat, math_ops.maximum(v_t, vhat)) + v_sqrt = math_ops.sqrt(vhat_t) + var_update = state_ops.assign_sub(var, lr * m_t / (v_sqrt + epsilon_t), use_locking=self._use_locking) + return control_flow_ops.group(*[var_update, m_t, v_t, vhat_t]) + + def _apply_sparse(self, grad, var): + return self._apply_sparse_shared( + grad.values, + var, + grad.indices, + lambda x, i, v: state_ops. + scatter_add( # pylint: disable=g-long-lambda + x, i, v, use_locking=self._use_locking + ) + ) + + def _resource_scatter_add(self, x, i, v): + with ops.control_dependencies([resource_variable_ops.resource_scatter_add(x.handle, i, v)]): + return x.value() + + def _resource_apply_sparse(self, grad, var, indices): + return self._apply_sparse_shared(grad, var, indices, self._resource_scatter_add) + + def _finish(self, update_ops, name_scope): + # Update the power accumulators. + with ops.control_dependencies(update_ops): + with ops.colocate_with(self._beta1_power): + update_beta1 = self._beta1_power.assign( + self._beta1_power * self._beta1_t, use_locking=self._use_locking + ) + update_beta2 = self._beta2_power.assign( + self._beta2_power * self._beta2_t, use_locking=self._use_locking + ) + return control_flow_ops.group(*update_ops + [update_beta1, update_beta2], name=name_scope) diff --git a/tensorlayer/optimizers/dragon_optimizers.py b/tensorlayer/optimizers/dragon_optimizers.py new file mode 100644 index 0000000..523e785 --- /dev/null +++ b/tensorlayer/optimizers/dragon_optimizers.py @@ -0,0 +1,56 @@ +from __future__ import absolute_import, division, print_function +import dragon as dg + +__all__ = ['Adadelta', 'Adagrad', 'Adam', 'Admax', 'Ftrl', 'Nadam', 'RMSprop', 'SGD', 'Momentum', 'Lamb', 'LARS'] + +# Add module aliases + + +# learning_rate=0.001, rho=0.95, epsilon=1e-07, name='Adadelta' +def Adadelta(**kwargs): + raise NotImplementedError('Adadelta optimizer function not implemented') + + +# learning_rate=0.001, initial_accumulator_value=0.1, epsilon=1e-07,name='Adagrad' +def Adagrad(**kwargs): + raise NotImplementedError('Adagrad optimizer function not implemented') + + +# learning_rate=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-07, amsgrad=False,name='Adam' +Adam = dg.optimizers.Adam + + +# learning_rate=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-07, name='Adamax' +def Admax(**kwargs): + raise NotImplementedError('Admax optimizer function not implemented') + + +# learning_rate=0.001, learning_rate_power=-0.5, initial_accumulator_value=0.1, +# l1_regularization_strength=0.0, l2_regularization_strength=0.0, name='Ftrl',l2_shrinkage_regularization_strength=0.0 +def Ftrl(**kwargs): + raise NotImplementedError('Ftrl optimizer function not implemented') + + +# learning_rate=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-07, name='Nadam', +def Nadam(**kwargs): + raise NotImplementedError('Nadam optimizer function not implemented') + + +# learning_rate=0.001, rho=0.9, momentum=0.0, epsilon=1e-07, centered=False,name='RMSprop' +RMSprop = dg.optimizers.RMSprop + +# learning_rate=0.01, momentum=0.0, nesterov=False, name='SGD' +SGD = dg.optimizers.SGD + + +# learning_rate, momentum, use_locking=False, name='Momentum', use_nesterov=False +def Momentum(**kwargs): + raise NotImplementedError('Momentum optimizer function not implemented') + + +def Lamb(**kwargs): + raise NotImplementedError('Lamb optimizer function not implemented') + + +def LARS(**kwargs): + raise NotImplementedError('LARS optimizer function not implemented') diff --git a/tensorlayer/optimizers/load_optimizers_backend.py b/tensorlayer/optimizers/load_optimizers_backend.py new file mode 100644 index 0000000..31a905a --- /dev/null +++ b/tensorlayer/optimizers/load_optimizers_backend.py @@ -0,0 +1,16 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, print_function +from tensorlayer.backend.ops.load_backend import BACKEND + +if BACKEND == 'tensorflow': + from .tensorflow_optimizers import * +elif BACKEND == 'mindspore': + from .mindspore_optimizers import * +elif BACKEND == 'dragon': + from .dragon_optimizers import * +elif BACKEND == 'paddle': + from .paddle_optimizers import * +else: + raise NotImplementedError("This backend is not supported") diff --git a/tensorlayer/optimizers/mindspore_optimizers.py b/tensorlayer/optimizers/mindspore_optimizers.py new file mode 100644 index 0000000..659a749 --- /dev/null +++ b/tensorlayer/optimizers/mindspore_optimizers.py @@ -0,0 +1,158 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, print_function +from mindspore.nn import optim as optimizer +import mindspore as ms +from mindspore.nn import Cell + +__all__ = ['Adadelta', 'Adagrad', 'Adam', 'Admax', 'Ftrl', 'Nadam', 'RMSprop', 'SGD', 'Momentum', 'Lamb', 'LARS'] + + +class Adadelta(Cell): + + def __init__(self): + pass + + def app_gradients(self): + raise Exception('Adadelta optimizer function not implemented') + + +class Adagrad(Cell): + + def __init__(self): + pass + + def app_gradients(self): + raise Exception('Adagrad optimizer function not implemented') + + +class Adam(Cell): + + def __init__( + self, + learning_rate=0.001, + beta_1=0.9, + beta_2=0.999, + epsilon=1e-8, + ): + self.adam = optimizer.Adam + self.learn_rate = learning_rate + self.beta_1 = beta_1 + self.beta_2 = beta_2 + self.epsilon = epsilon + + def apply_gradients(self, grads_and_vars): + grads, vars = list(zip(*grads_and_vars)) + optimizer_adam = self.adam( + vars, learning_rate=self.learn_rate, beta1=self.beta_1, beta2=self.beta_2, eps=self.epsilon + ) + optimizer_adam(grads) + + +class Admax(Cell): + + def __init__(self): + pass + + def app_gradients(self): + raise Exception('Admax optimizer function not implemented') + + +class Ftrl(Cell): + + def __init__(self): + pass + + def app_gradients(self): + raise Exception('Ftrl optimizer function not implemented') + + +class Nadam(Cell): + + def __init__(self): + pass + + def app_gradients(self): + raise Exception('Nadam optimizer function not implemented') + + +class RMSprop(Cell): + + def __init__(self): + pass + + def app_gradients(self): + raise Exception('RMSprop optimizer function not implemented') + + +class RMSprop(Cell): + + def __init__(self): + pass + + def app_gradients(self): + raise Exception('RMSprop optimizer function not implemented') + + +class SGD(Cell): + + def __init__(self, learning_rate, momentum): + self.sgd = optimizer.SGD + self.learn_rate = learning_rate + self.momentum = momentum + + def apply_gradients(self, grads_and_vars): + grads, vars = list(zip(*grads_and_vars)) + optimizer_sgd = self.sgd(vars, learning_rate=self.learn_rate, momentum=self.momentum) + optimizer_sgd(grads) + + +class Momentum(Cell): + + def __init__(self, learning_rate, momentum): + self.mom = optimizer.Momentum + self.learn_rate = learning_rate + self.momentum = momentum + + def apply_gradients(self, grads_and_vars, **kwargs): + grads, vars = list(zip(*grads_and_vars)) + optimizer_mom = self.mom(vars, learning_rate=self.learn_rate, momentum=self.momentum, **kwargs) + optimizer_mom(grads) + + +class Lamb(Cell): + + def __init__( + self, decay_steps, warmup_steps=0, start_learning_rate=0.1, end_learning_rate=0.0001, power=1.0, beta1=0.9, + beta2=0.999, eps=1e-06, weight_decay=0.0 + ): + self.lamb = optimizer.Lamb + self.decay_steps = decay_steps + self.warmup_steps = warmup_steps + self.start_learning_rate = start_learning_rate + self.end_learning_rate = end_learning_rate + self.power = power + self.beta1 = beta1 + self.beta2 = beta2 + self.eps = eps + self.weight_decay = weight_decay + + def apply_gradients(self, grads_and_vars): + grads, vars = list(zip(*grads_and_vars)) + optimizer_lamb = self.lamb( + params=vars, decay_steps=self.decay_steps, warmup_steps=self.warmup_steps, + start_learning_rate=self.start_learning_rate, end_learning_rate=self.end_learning_rate, power=self.power, + beta1=self.beta1, beta2=self.beta2, eps=self.eps, weight_decay=self.weight_decay + ) + optimizer_lamb(grads) + + +class LARS(object): + + def __init__(self, optimizer, **kwargs): + self.lars = ms.nn.LARS(optimizer=optimizer, **kwargs) + + def apply_gradients(self, grads_and_vars): + grads, _ = list(zip(*grads_and_vars)) + self.lars(grads) diff --git a/tensorlayer/optimizers/paddle_optimizers.py b/tensorlayer/optimizers/paddle_optimizers.py new file mode 100644 index 0000000..cbc1c2a --- /dev/null +++ b/tensorlayer/optimizers/paddle_optimizers.py @@ -0,0 +1,44 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, print_function + +__all__ = ['Adadelta', 'Adagrad', 'Adam', 'Admax', 'Ftrl', 'Nadam', 'RMSprop', 'SGD', 'Momentum', 'Lamb', 'LARS'] + +# Add module aliases + +# learning_rate=0.001, rho=0.95, epsilon=1e-07, name='Adadelta' +Adadelta = None + +# learning_rate=0.001, initial_accumulator_value=0.1, epsilon=1e-07,name='Adagrad' +Adagrad = None + +# learning_rate=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-07, amsgrad=False,name='Adam' +Adam = None + +# learning_rate=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-07, name='Adamax' +Admax = None + +# learning_rate=0.001, learning_rate_power=-0.5, initial_accumulator_value=0.1, +# l1_regularization_strength=0.0, l2_regularization_strength=0.0, name='Ftrl',l2_shrinkage_regularization_strength=0.0 +Ftrl = None + +# learning_rate=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-07, name='Nadam', +Nadam = None + +# learning_rate=0.001, rho=0.9, momentum=0.0, epsilon=1e-07, centered=False,name='RMSprop' +RMSprop = None + +# learning_rate=0.01, momentum=0.0, nesterov=False, name='SGD' +SGD = None + +# learning_rate, momentum, use_locking=False, name='Momentum', use_nesterov=False +Momentum = None + + +def Lamb(**kwargs): + raise Exception('Lamb optimizer function not implemented') + + +def LARS(**kwargs): + raise Exception('LARS optimizer function not implemented') diff --git a/tensorlayer/optimizers/tensorflow_optimizers.py b/tensorlayer/optimizers/tensorflow_optimizers.py new file mode 100644 index 0000000..0cae4cc --- /dev/null +++ b/tensorlayer/optimizers/tensorflow_optimizers.py @@ -0,0 +1,45 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, print_function +import tensorflow as tf + +__all__ = ['Adadelta', 'Adagrad', 'Adam', 'Admax', 'Ftrl', 'Nadam', 'RMSprop', 'SGD', 'Momentum', 'Lamb', 'LARS'] + +# Add module aliases + +# learning_rate=0.001, rho=0.95, epsilon=1e-07, name='Adadelta' +Adadelta = tf.optimizers.Adadelta + +# learning_rate=0.001, initial_accumulator_value=0.1, epsilon=1e-07,name='Adagrad' +Adagrad = tf.optimizers.Adagrad + +# learning_rate=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-07, amsgrad=False,name='Adam' +Adam = tf.optimizers.Adam + +# learning_rate=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-07, name='Adamax' +Admax = tf.optimizers.Adamax + +# learning_rate=0.001, learning_rate_power=-0.5, initial_accumulator_value=0.1, +# l1_regularization_strength=0.0, l2_regularization_strength=0.0, name='Ftrl',l2_shrinkage_regularization_strength=0.0 +Ftrl = tf.optimizers.Ftrl + +# learning_rate=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-07, name='Nadam', +Nadam = tf.optimizers.Nadam + +# learning_rate=0.001, rho=0.9, momentum=0.0, epsilon=1e-07, centered=False,name='RMSprop' +RMSprop = tf.optimizers.RMSprop + +# learning_rate=0.01, momentum=0.0, nesterov=False, name='SGD' +SGD = tf.optimizers.SGD + +# learning_rate, momentum, use_locking=False, name='Momentum', use_nesterov=False +Momentum = tf.compat.v1.train.MomentumOptimizer + + +def Lamb(**kwargs): + raise Exception('Lamb optimizer function not implemented') + + +def LARS(**kwargs): + raise Exception('LARS optimizer function not implemented') diff --git a/tensorlayer/package_info.py b/tensorlayer/package_info.py new file mode 100644 index 0000000..de5a884 --- /dev/null +++ b/tensorlayer/package_info.py @@ -0,0 +1,24 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- +"""Deep learning and Reinforcement learning library for Researchers and Engineers.""" + +MAJOR = 2 +MINOR = 2 +PATCH = 0 +PRE_RELEASE = '' +# Use the following formatting: (major, minor, patch, prerelease) +VERSION = (MAJOR, MINOR, PATCH, PRE_RELEASE) + +__shortversion__ = '.'.join(map(str, VERSION[:3])) +__version__ = '.'.join(map(str, VERSION[:3])) + ''.join(VERSION[3:]) + +__package_name__ = 'tensorlayer' +__contact_names__ = 'TensorLayer Contributors' +__contact_emails__ = 'tensorlayer@gmail.com' +__homepage__ = 'http://tensorlayer.readthedocs.io/en/latest/' +__repository_url__ = 'https://github.com/tensorlayer/tensorlayer' +__download_url__ = 'https://github.com/tensorlayer/tensorlayer' +__description__ = 'High Level Tensorflow Deep Learning Library for Researcher and Engineer.' +__license__ = 'apache' +__keywords__ = 'deep learning, machine learning, computer vision, nlp, ' +__keywords__ += 'supervised learning, unsupervised learning, reinforcement learning, tensorflow' diff --git a/tensorlayer/prepro.py b/tensorlayer/prepro.py new file mode 100644 index 0000000..def0199 --- /dev/null +++ b/tensorlayer/prepro.py @@ -0,0 +1,4114 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import copy +import math +import random +import threading +import time + +import numpy as np +import PIL +import scipy +import scipy.ndimage as ndi +import skimage +from scipy import linalg +from scipy.ndimage.filters import gaussian_filter +from scipy.ndimage.interpolation import map_coordinates +from six.moves import range +from skimage import exposure, transform +from skimage.morphology import binary_dilation as _binary_dilation +from skimage.morphology import binary_erosion as _binary_erosion +from skimage.morphology import disk +from skimage.morphology import erosion as _erosion +from skimage.transform import resize + +import tensorlayer as tl +from tensorlayer.lazy_imports import LazyImport + +cv2 = LazyImport("cv2") + +# linalg https://docs.scipy.org/doc/scipy/reference/linalg.html +# ndimage https://docs.scipy.org/doc/scipy/reference/ndimage.html + +__all__ = [ + 'threading_data', + 'affine_rotation_matrix', + 'affine_horizontal_flip_matrix', + 'affine_shift_matrix', + 'affine_shear_matrix', + 'affine_zoom_matrix', + 'affine_respective_zoom_matrix', + 'transform_matrix_offset_center', + 'affine_transform', + 'affine_transform_cv2', + 'affine_transform_keypoints', + 'projective_transform_by_points', + 'rotation', + 'rotation_multi', + 'crop', + 'crop_multi', + 'flip_axis', + 'flip_axis_multi', + 'shift', + 'shift_multi', + 'shear', + 'shear_multi', + 'shear2', + 'shear_multi2', + 'swirl', + 'swirl_multi', + 'elastic_transform', + 'elastic_transform_multi', + 'zoom', + 'respective_zoom', + 'zoom_multi', + 'brightness', + 'brightness_multi', + 'illumination', + 'rgb_to_hsv', + 'hsv_to_rgb', + 'adjust_hue', + 'imresize', + 'pixel_value_scale', + 'samplewise_norm', + 'featurewise_norm', + 'get_zca_whitening_principal_components_img', + 'zca_whitening', + 'channel_shift', + 'channel_shift_multi', + 'drop', + 'array_to_img', + 'find_contours', + 'pt2map', + 'binary_dilation', + 'dilation', + 'binary_erosion', + 'erosion', + 'obj_box_coords_rescale', + 'obj_box_coord_rescale', + 'obj_box_coord_scale_to_pixelunit', + 'obj_box_coord_centroid_to_upleft_butright', + 'obj_box_coord_upleft_butright_to_centroid', + 'obj_box_coord_centroid_to_upleft', + 'obj_box_coord_upleft_to_centroid', + 'parse_darknet_ann_str_to_list', + 'parse_darknet_ann_list_to_cls_box', + 'obj_box_left_right_flip', + 'obj_box_imresize', + 'obj_box_crop', + 'obj_box_shift', + 'obj_box_zoom', + 'pad_sequences', + 'remove_pad_sequences', + 'process_sequences', + 'sequences_add_start_id', + 'sequences_add_end_id', + 'sequences_add_end_id_after_pad', + 'sequences_get_mask', + 'keypoint_random_crop', + 'keypoint_resize_random_crop', + 'keypoint_random_rotate', + 'keypoint_random_flip', + 'keypoint_random_resize', + 'keypoint_random_resize_shortestedge', +] + + +def threading_data(data=None, fn=None, thread_count=None, **kwargs): + """Process a batch of data by given function by threading. + + Usually be used for data augmentation. + + Parameters + ----------- + data : numpy.array or others + The data to be processed. + thread_count : int + The number of threads to use. + fn : function + The function for data processing. + more args : the args for `fn` + Ssee Examples below. + + Examples + -------- + Process images. + + >>> images, _, _, _ = tl.files.load_cifar10_dataset(shape=(-1, 32, 32, 3)) + >>> images = tl.prepro.threading_data(images[0:32], tl.prepro.zoom, zoom_range=[0.5, 1]) + + Customized image preprocessing function. + + >>> def distort_img(x): + >>> x = tl.prepro.flip_axis(x, axis=0, is_random=True) + >>> x = tl.prepro.flip_axis(x, axis=1, is_random=True) + >>> x = tl.prepro.crop(x, 100, 100, is_random=True) + >>> return x + >>> images = tl.prepro.threading_data(images, distort_img) + + Process images and masks together (Usually be used for image segmentation). + + >>> X, Y --> [batch_size, row, col, 1] + >>> data = tl.prepro.threading_data([_ for _ in zip(X, Y)], tl.prepro.zoom_multi, zoom_range=[0.5, 1], is_random=True) + data --> [batch_size, 2, row, col, 1] + >>> X_, Y_ = data.transpose((1,0,2,3,4)) + X_, Y_ --> [batch_size, row, col, 1] + >>> tl.vis.save_image(X_, 'images.png') + >>> tl.vis.save_image(Y_, 'masks.png') + + Process images and masks together by using ``thread_count``. + + >>> X, Y --> [batch_size, row, col, 1] + >>> data = tl.prepro.threading_data(X, tl.prepro.zoom_multi, 8, zoom_range=[0.5, 1], is_random=True) + data --> [batch_size, 2, row, col, 1] + >>> X_, Y_ = data.transpose((1,0,2,3,4)) + X_, Y_ --> [batch_size, row, col, 1] + >>> tl.vis.save_image(X_, 'after.png') + >>> tl.vis.save_image(Y_, 'before.png') + + Customized function for processing images and masks together. + + >>> def distort_img(data): + >>> x, y = data + >>> x, y = tl.prepro.flip_axis_multi([x, y], axis=0, is_random=True) + >>> x, y = tl.prepro.flip_axis_multi([x, y], axis=1, is_random=True) + >>> x, y = tl.prepro.crop_multi([x, y], 100, 100, is_random=True) + >>> return x, y + + >>> X, Y --> [batch_size, row, col, channel] + >>> data = tl.prepro.threading_data([_ for _ in zip(X, Y)], distort_img) + >>> X_, Y_ = data.transpose((1,0,2,3,4)) + + Returns + ------- + list or numpyarray + The processed results. + + References + ---------- + - `python queue `__ + - `run with limited queue `__ + + """ + + def apply_fn(results, i, data, kwargs): + results[i] = fn(data, **kwargs) + + if thread_count is None: + results = [None] * len(data) + threads = [] + # for i in range(len(data)): + # t = threading.Thread(name='threading_and_return', target=apply_fn, args=(results, i, data[i], kwargs)) + for i, d in enumerate(data): + t = threading.Thread(name='threading_and_return', target=apply_fn, args=(results, i, d, kwargs)) + t.start() + threads.append(t) + else: + divs = np.linspace(0, len(data), thread_count + 1) + divs = np.round(divs).astype(int) + results = [None] * thread_count + threads = [] + for i in range(thread_count): + t = threading.Thread( + name='threading_and_return', target=apply_fn, args=(results, i, data[divs[i]:divs[i + 1]], kwargs) + ) + t.start() + threads.append(t) + + for t in threads: + t.join() + + if thread_count is None: + try: + return np.asarray(results) + except Exception: + return results + else: + return np.concatenate(results) + + +def affine_rotation_matrix(angle=(-20, 20)): + """Create an affine transform matrix for image rotation. + NOTE: In OpenCV, x is width and y is height. + + Parameters + ----------- + angle : int/float or tuple of two int/float + Degree to rotate, usually -180 ~ 180. + - int/float, a fixed angle. + - tuple of 2 floats/ints, randomly sample a value as the angle between these 2 values. + + Returns + ------- + numpy.array + An affine transform matrix. + + """ + if isinstance(angle, tuple): + theta = np.pi / 180 * np.random.uniform(angle[0], angle[1]) + else: + theta = np.pi / 180 * angle + rotation_matrix = np.array([[np.cos(theta), np.sin(theta), 0], \ + [-np.sin(theta), np.cos(theta), 0], \ + [0, 0, 1]]) + return rotation_matrix + + +def affine_horizontal_flip_matrix(prob=0.5): + """Create an affine transformation matrix for image horizontal flipping. + NOTE: In OpenCV, x is width and y is height. + + Parameters + ---------- + prob : float + Probability to flip the image. 1.0 means always flip. + + Returns + ------- + numpy.array + An affine transform matrix. + + """ + factor = np.random.uniform(0, 1) + if prob >= factor: + filp_matrix = np.array([[ -1. , 0., 0. ], \ + [ 0., 1., 0. ], \ + [ 0., 0., 1. ]]) + return filp_matrix + else: + filp_matrix = np.array([[ 1. , 0., 0. ], \ + [ 0., 1., 0. ], \ + [ 0., 0., 1. ]]) + return filp_matrix + + +def affine_vertical_flip_matrix(prob=0.5): + """Create an affine transformation for image vertical flipping. + NOTE: In OpenCV, x is width and y is height. + + Parameters + ---------- + prob : float + Probability to flip the image. 1.0 means always flip. + + Returns + ------- + numpy.array + An affine transform matrix. + + """ + factor = np.random.uniform(0, 1) + if prob >= factor: + filp_matrix = np.array([[ 1. , 0., 0. ], \ + [ 0., -1., 0. ], \ + [ 0., 0., 1. ]]) + return filp_matrix + else: + filp_matrix = np.array([[ 1. , 0., 0. ], \ + [ 0., 1., 0. ], \ + [ 0., 0., 1. ]]) + return filp_matrix + + +def affine_shift_matrix(wrg=(-0.1, 0.1), hrg=(-0.1, 0.1), w=200, h=200): + """Create an affine transform matrix for image shifting. + NOTE: In OpenCV, x is width and y is height. + + Parameters + ----------- + wrg : float or tuple of floats + Range to shift on width axis, -1 ~ 1. + - float, a fixed distance. + - tuple of 2 floats, randomly sample a value as the distance between these 2 values. + hrg : float or tuple of floats + Range to shift on height axis, -1 ~ 1. + - float, a fixed distance. + - tuple of 2 floats, randomly sample a value as the distance between these 2 values. + w, h : int + The width and height of the image. + + Returns + ------- + numpy.array + An affine transform matrix. + + """ + if isinstance(wrg, tuple): + tx = np.random.uniform(wrg[0], wrg[1]) * w + else: + tx = wrg * w + if isinstance(hrg, tuple): + ty = np.random.uniform(hrg[0], hrg[1]) * h + else: + ty = hrg * h + shift_matrix = np.array([[1, 0, tx], \ + [0, 1, ty], \ + [0, 0, 1]]) + return shift_matrix + + +def affine_shear_matrix(x_shear=(-0.1, 0.1), y_shear=(-0.1, 0.1)): + """Create affine transform matrix for image shearing. + NOTE: In OpenCV, x is width and y is height. + + Parameters + ----------- + shear : tuple of two floats + Percentage of shears for width and height directions. + + Returns + ------- + numpy.array + An affine transform matrix. + + """ + # if len(shear) != 2: + # raise AssertionError( + # "shear should be tuple of 2 floats, or you want to use tl.prepro.shear rather than tl.prepro.shear2 ?" + # ) + # if isinstance(shear, tuple): + # shear = list(shear) + # if is_random: + # shear[0] = np.random.uniform(-shear[0], shear[0]) + # shear[1] = np.random.uniform(-shear[1], shear[1]) + if isinstance(x_shear, tuple): + x_shear = np.random.uniform(x_shear[0], x_shear[1]) + if isinstance(y_shear, tuple): + y_shear = np.random.uniform(y_shear[0], y_shear[1]) + + shear_matrix = np.array([[1, x_shear, 0], \ + [y_shear, 1, 0], \ + [0, 0, 1]]) + return shear_matrix + + +def affine_zoom_matrix(zoom_range=(0.8, 1.1)): + """Create an affine transform matrix for zooming/scaling an image's height and width. + OpenCV format, x is width. + + Parameters + ----------- + x : numpy.array + An image with dimension of [row, col, channel] (default). + zoom_range : float or tuple of 2 floats + The zooming/scaling ratio, greater than 1 means larger. + - float, a fixed ratio. + - tuple of 2 floats, randomly sample a value as the ratio between these 2 values. + + Returns + ------- + numpy.array + An affine transform matrix. + + """ + + if isinstance(zoom_range, (float, int)): + scale = zoom_range + elif isinstance(zoom_range, tuple): + scale = np.random.uniform(zoom_range[0], zoom_range[1]) + else: + raise Exception("zoom_range: float or tuple of 2 floats") + + zoom_matrix = np.array([[scale, 0, 0], \ + [0, scale, 0], \ + [0, 0, 1]]) + return zoom_matrix + + +def affine_respective_zoom_matrix(w_range=0.8, h_range=1.1): + """Get affine transform matrix for zooming/scaling that height and width are changed independently. + OpenCV format, x is width. + + Parameters + ----------- + w_range : float or tuple of 2 floats + The zooming/scaling ratio of width, greater than 1 means larger. + - float, a fixed ratio. + - tuple of 2 floats, randomly sample a value as the ratio between 2 values. + h_range : float or tuple of 2 floats + The zooming/scaling ratio of height, greater than 1 means larger. + - float, a fixed ratio. + - tuple of 2 floats, randomly sample a value as the ratio between 2 values. + + Returns + ------- + numpy.array + An affine transform matrix. + + """ + + if isinstance(h_range, (float, int)): + zy = h_range + elif isinstance(h_range, tuple): + zy = np.random.uniform(h_range[0], h_range[1]) + else: + raise Exception("h_range: float or tuple of 2 floats") + + if isinstance(w_range, (float, int)): + zx = w_range + elif isinstance(w_range, tuple): + zx = np.random.uniform(w_range[0], w_range[1]) + else: + raise Exception("w_range: float or tuple of 2 floats") + + zoom_matrix = np.array([[zx, 0, 0], \ + [0, zy, 0], \ + [0, 0, 1]]) + return zoom_matrix + + +# affine transform +def transform_matrix_offset_center(matrix, y, x): + """Convert the matrix from Cartesian coordinates (the origin in the middle of image) to Image coordinates (the origin on the top-left of image). + + Parameters + ---------- + matrix : numpy.array + Transform matrix. + x and y : 2 int + Size of image. + + Returns + ------- + numpy.array + The transform matrix. + + Examples + -------- + - See ``tl.prepro.rotation``, ``tl.prepro.shear``, ``tl.prepro.zoom``. + """ + o_x = (x - 1) / 2.0 + o_y = (y - 1) / 2.0 + offset_matrix = np.array([[1, 0, o_x], [0, 1, o_y], [0, 0, 1]]) + reset_matrix = np.array([[1, 0, -o_x], [0, 1, -o_y], [0, 0, 1]]) + transform_matrix = np.dot(np.dot(offset_matrix, matrix), reset_matrix) + return transform_matrix + + +def affine_transform(x, transform_matrix, channel_index=2, fill_mode='nearest', cval=0., order=1): + """Return transformed images by given an affine matrix in Scipy format (x is height). + + Parameters + ---------- + x : numpy.array + An image with dimension of [row, col, channel] (default). + transform_matrix : numpy.array + Transform matrix (offset center), can be generated by ``transform_matrix_offset_center`` + channel_index : int + Index of channel, default 2. + fill_mode : str + Method to fill missing pixel, default `nearest`, more options `constant`, `reflect` or `wrap`, see `scipy ndimage affine_transform `__ + cval : float + Value used for points outside the boundaries of the input if mode='constant'. Default is 0.0 + order : int + The order of interpolation. The order has to be in the range 0-5: + - 0 Nearest-neighbor + - 1 Bi-linear (default) + - 2 Bi-quadratic + - 3 Bi-cubic + - 4 Bi-quartic + - 5 Bi-quintic + - `scipy ndimage affine_transform `__ + + Returns + ------- + numpy.array + A processed image. + + Examples + -------- + >>> M_shear = tl.prepro.affine_shear_matrix(intensity=0.2, is_random=False) + >>> M_zoom = tl.prepro.affine_zoom_matrix(zoom_range=0.8) + >>> M_combined = M_shear.dot(M_zoom) + >>> transform_matrix = tl.prepro.transform_matrix_offset_center(M_combined, h, w) + >>> result = tl.prepro.affine_transform(image, transform_matrix) + + """ + # transform_matrix = transform_matrix_offset_center() + # asdihasid + # asd + + x = np.rollaxis(x, channel_index, 0) + final_affine_matrix = transform_matrix[:2, :2] + final_offset = transform_matrix[:2, 2] + channel_images = [ + ndi.interpolation.affine_transform( + x_channel, final_affine_matrix, final_offset, order=order, mode=fill_mode, cval=cval + ) for x_channel in x + ] + x = np.stack(channel_images, axis=0) + x = np.rollaxis(x, 0, channel_index + 1) + return x + + +apply_transform = affine_transform + + +def affine_transform_cv2(x, transform_matrix, flags=None, border_mode='constant'): + """Return transformed images by given an affine matrix in OpenCV format (x is width). (Powered by OpenCV2, faster than ``tl.prepro.affine_transform``) + + Parameters + ---------- + x : numpy.array + An image with dimension of [row, col, channel] (default). + transform_matrix : numpy.array + A transform matrix, OpenCV format. + border_mode : str + - `constant`, pad the image with a constant value (i.e. black or 0) + - `replicate`, the row or column at the very edge of the original is replicated to the extra border. + + Examples + -------- + >>> M_shear = tl.prepro.affine_shear_matrix(intensity=0.2, is_random=False) + >>> M_zoom = tl.prepro.affine_zoom_matrix(zoom_range=0.8) + >>> M_combined = M_shear.dot(M_zoom) + >>> result = tl.prepro.affine_transform_cv2(image, M_combined) + """ + rows, cols = x.shape[0], x.shape[1] + if flags is None: + flags = cv2.INTER_AREA + if border_mode is 'constant': + border_mode = cv2.BORDER_CONSTANT + elif border_mode is 'replicate': + border_mode = cv2.BORDER_REPLICATE + else: + raise Exception("unsupport border_mode, check cv.BORDER_ for more details.") + return cv2.warpAffine(x, transform_matrix[0:2,:], \ + (cols,rows), flags=flags, borderMode=border_mode) + + +def affine_transform_keypoints(coords_list, transform_matrix): + """Transform keypoint coordinates according to a given affine transform matrix. + OpenCV format, x is width. + + Note that, for pose estimation task, flipping requires maintaining the left and right body information. + We should not flip the left and right body, so please use ``tl.prepro.keypoint_random_flip``. + + Parameters + ----------- + coords_list : list of list of tuple/list + The coordinates + e.g., the keypoint coordinates of every person in an image. + transform_matrix : numpy.array + Transform matrix, OpenCV format. + + Examples + --------- + >>> # 1. get all affine transform matrices + >>> M_rotate = tl.prepro.affine_rotation_matrix(angle=20) + >>> M_flip = tl.prepro.affine_horizontal_flip_matrix(prob=1) + >>> # 2. combine all affine transform matrices to one matrix + >>> M_combined = dot(M_flip).dot(M_rotate) + >>> # 3. transfrom the matrix from Cartesian coordinate (the origin in the middle of image) + >>> # to Image coordinate (the origin on the top-left of image) + >>> transform_matrix = tl.prepro.transform_matrix_offset_center(M_combined, x=w, y=h) + >>> # 4. then we can transfrom the image once for all transformations + >>> result = tl.prepro.affine_transform_cv2(image, transform_matrix) # 76 times faster + >>> # 5. transform keypoint coordinates + >>> coords = [[(50, 100), (100, 100), (100, 50), (200, 200)], [(250, 50), (200, 50), (200, 100)]] + >>> coords_result = tl.prepro.affine_transform_keypoints(coords, transform_matrix) + """ + coords_result_list = [] + for coords in coords_list: + coords = np.asarray(coords) + coords = coords.transpose([1, 0]) + coords = np.insert(coords, 2, 1, axis=0) + # print(coords) + # print(transform_matrix) + coords_result = np.matmul(transform_matrix, coords) + coords_result = coords_result[0:2, :].transpose([1, 0]) + coords_result_list.append(coords_result) + return coords_result_list + + +def projective_transform_by_points( + x, src, dst, map_args=None, output_shape=None, order=1, mode='constant', cval=0.0, clip=True, preserve_range=False +): + """Projective transform by given coordinates, usually 4 coordinates. + + see `scikit-image `__. + + Parameters + ----------- + x : numpy.array + An image with dimension of [row, col, channel] (default). + src : list or numpy + The original coordinates, usually 4 coordinates of (width, height). + dst : list or numpy + The coordinates after transformation, the number of coordinates is the same with src. + map_args : dictionary or None + Keyword arguments passed to inverse map. + output_shape : tuple of 2 int + Shape of the output image generated. By default the shape of the input image is preserved. Note that, even for multi-band images, only rows and columns need to be specified. + order : int + The order of interpolation. The order has to be in the range 0-5: + - 0 Nearest-neighbor + - 1 Bi-linear (default) + - 2 Bi-quadratic + - 3 Bi-cubic + - 4 Bi-quartic + - 5 Bi-quintic + mode : str + One of `constant` (default), `edge`, `symmetric`, `reflect` or `wrap`. + Points outside the boundaries of the input are filled according to the given mode. Modes match the behaviour of numpy.pad. + cval : float + Used in conjunction with mode `constant`, the value outside the image boundaries. + clip : boolean + Whether to clip the output to the range of values of the input image. This is enabled by default, since higher order interpolation may produce values outside the given input range. + preserve_range : boolean + Whether to keep the original range of values. Otherwise, the input image is converted according to the conventions of img_as_float. + + Returns + ------- + numpy.array + A processed image. + + Examples + -------- + Assume X is an image from CIFAR-10, i.e. shape == (32, 32, 3) + + >>> src = [[0,0],[0,32],[32,0],[32,32]] # [w, h] + >>> dst = [[10,10],[0,32],[32,0],[32,32]] + >>> x = tl.prepro.projective_transform_by_points(X, src, dst) + + References + ----------- + - `scikit-image : geometric transformations `__ + - `scikit-image : examples `__ + + """ + if map_args is None: + map_args = {} + # if type(src) is list: + if isinstance(src, list): # convert to numpy + src = np.array(src) + # if type(dst) is list: + if isinstance(dst, list): + dst = np.array(dst) + if np.max(x) > 1: # convert to [0, 1] + x = x / 255 + + m = transform.ProjectiveTransform() + m.estimate(dst, src) + warped = transform.warp( + x, m, map_args=map_args, output_shape=output_shape, order=order, mode=mode, cval=cval, clip=clip, + preserve_range=preserve_range + ) + return warped + + +# rotate +def rotation( + x, rg=20, is_random=False, row_index=0, col_index=1, channel_index=2, fill_mode='nearest', cval=0., order=1 +): + """Rotate an image randomly or non-randomly. + + Parameters + ----------- + x : numpy.array + An image with dimension of [row, col, channel] (default). + rg : int or float + Degree to rotate, usually 0 ~ 180. + is_random : boolean + If True, randomly rotate. Default is False + row_index col_index and channel_index : int + Index of row, col and channel, default (0, 1, 2), for theano (1, 2, 0). + fill_mode : str + Method to fill missing pixel, default `nearest`, more options `constant`, `reflect` or `wrap`, see `scipy ndimage affine_transform `__ + cval : float + Value used for points outside the boundaries of the input if mode=`constant`. Default is 0.0 + order : int + The order of interpolation. The order has to be in the range 0-5. See ``tl.prepro.affine_transform`` and `scipy ndimage affine_transform `__ + + Returns + ------- + numpy.array + A processed image. + + Examples + --------- + >>> x --> [row, col, 1] + >>> x = tl.prepro.rotation(x, rg=40, is_random=False) + >>> tl.vis.save_image(x, 'im.png') + + """ + if is_random: + theta = np.pi / 180 * np.random.uniform(-rg, rg) + else: + theta = np.pi / 180 * rg + rotation_matrix = np.array([[np.cos(theta), -np.sin(theta), 0], [np.sin(theta), np.cos(theta), 0], [0, 0, 1]]) + + h, w = x.shape[row_index], x.shape[col_index] + transform_matrix = transform_matrix_offset_center(rotation_matrix, h, w) + x = affine_transform(x, transform_matrix, channel_index, fill_mode, cval, order) + return x + + +def rotation_multi( + x, rg=20, is_random=False, row_index=0, col_index=1, channel_index=2, fill_mode='nearest', cval=0., order=1 +): + """Rotate multiple images with the same arguments, randomly or non-randomly. + Usually be used for image segmentation which x=[X, Y], X and Y should be matched. + + Parameters + ----------- + x : list of numpy.array + List of images with dimension of [n_images, row, col, channel] (default). + others : args + See ``tl.prepro.rotation``. + + Returns + ------- + numpy.array + A list of processed images. + + Examples + -------- + >>> x, y --> [row, col, 1] greyscale + >>> x, y = tl.prepro.rotation_multi([x, y], rg=90, is_random=False) + + """ + if is_random: + theta = np.pi / 180 * np.random.uniform(-rg, rg) + else: + theta = np.pi / 180 * rg + rotation_matrix = np.array([[np.cos(theta), -np.sin(theta), 0], [np.sin(theta), np.cos(theta), 0], [0, 0, 1]]) + + h, w = x[0].shape[row_index], x[0].shape[col_index] + transform_matrix = transform_matrix_offset_center(rotation_matrix, h, w) + results = [] + for data in x: + results.append(affine_transform(data, transform_matrix, channel_index, fill_mode, cval, order)) + return np.asarray(results) + + +# crop +def crop(x, wrg, hrg, is_random=False, row_index=0, col_index=1): + """Randomly or centrally crop an image. + + Parameters + ---------- + x : numpy.array + An image with dimension of [row, col, channel] (default). + wrg : int + Size of width. + hrg : int + Size of height. + is_random : boolean, + If True, randomly crop, else central crop. Default is False. + row_index: int + index of row. + col_index: int + index of column. + + Returns + ------- + numpy.array + A processed image. + + """ + h, w = x.shape[row_index], x.shape[col_index] + + if (h < hrg) or (w < wrg): + raise AssertionError("The size of cropping should smaller than or equal to the original image") + + if is_random: + h_offset = int(np.random.uniform(0, h - hrg)) + w_offset = int(np.random.uniform(0, w - wrg)) + # tl.logging.info(h_offset, w_offset, x[h_offset: hrg+h_offset ,w_offset: wrg+w_offset].shape) + return x[h_offset:hrg + h_offset, w_offset:wrg + w_offset] + else: # central crop + h_offset = int(np.floor((h - hrg) / 2.)) + w_offset = int(np.floor((w - wrg) / 2.)) + h_end = h_offset + hrg + w_end = w_offset + wrg + return x[h_offset:h_end, w_offset:w_end] + # old implementation + # h_offset = (h - hrg)/2 + # w_offset = (w - wrg)/2 + # tl.logging.info(x[h_offset: h-h_offset ,w_offset: w-w_offset].shape) + # return x[h_offset: h-h_offset ,w_offset: w-w_offset] + # central crop + + +def crop_multi(x, wrg, hrg, is_random=False, row_index=0, col_index=1): + """Randomly or centrally crop multiple images. + + Parameters + ---------- + x : list of numpy.array + List of images with dimension of [n_images, row, col, channel] (default). + others : args + See ``tl.prepro.crop``. + + Returns + ------- + numpy.array + A list of processed images. + + """ + h, w = x[0].shape[row_index], x[0].shape[col_index] + + if (h < hrg) or (w < wrg): + raise AssertionError("The size of cropping should smaller than or equal to the original image") + + if is_random: + h_offset = int(np.random.uniform(0, h - hrg)) + w_offset = int(np.random.uniform(0, w - wrg)) + results = [] + for data in x: + results.append(data[h_offset:hrg + h_offset, w_offset:wrg + w_offset]) + return np.asarray(results) + else: + # central crop + h_offset = int(np.floor((h - hrg) / 2.)) + w_offset = int(np.floor((w - wrg) / 2.)) + results = [] + for data in x: + results.append(data[h_offset:h - h_offset, w_offset:w - w_offset]) + return np.asarray(results) + + +# flip +def flip_axis(x, axis=1, is_random=False): + """Flip the axis of an image, such as flip left and right, up and down, randomly or non-randomly, + + Parameters + ---------- + x : numpy.array + An image with dimension of [row, col, channel] (default). + axis : int + Which axis to flip. + - 0, flip up and down + - 1, flip left and right + - 2, flip channel + is_random : boolean + If True, randomly flip. Default is False. + + Returns + ------- + numpy.array + A processed image. + + """ + if is_random: + factor = np.random.uniform(-1, 1) + if factor > 0: + x = np.asarray(x).swapaxes(axis, 0) + x = x[::-1, ...] + x = x.swapaxes(0, axis) + return x + else: + return x + else: + x = np.asarray(x).swapaxes(axis, 0) + x = x[::-1, ...] + x = x.swapaxes(0, axis) + return x + + +def flip_axis_multi(x, axis, is_random=False): + """Flip the axises of multiple images together, such as flip left and right, up and down, randomly or non-randomly, + + Parameters + ----------- + x : list of numpy.array + List of images with dimension of [n_images, row, col, channel] (default). + others : args + See ``tl.prepro.flip_axis``. + + Returns + ------- + numpy.array + A list of processed images. + + """ + if is_random: + factor = np.random.uniform(-1, 1) + if factor > 0: + # x = np.asarray(x).swapaxes(axis, 0) + # x = x[::-1, ...] + # x = x.swapaxes(0, axis) + # return x + results = [] + for data in x: + data = np.asarray(data).swapaxes(axis, 0) + data = data[::-1, ...] + data = data.swapaxes(0, axis) + results.append(data) + return np.asarray(results) + else: + return np.asarray(x) + else: + # x = np.asarray(x).swapaxes(axis, 0) + # x = x[::-1, ...] + # x = x.swapaxes(0, axis) + # return x + results = [] + for data in x: + data = np.asarray(data).swapaxes(axis, 0) + data = data[::-1, ...] + data = data.swapaxes(0, axis) + results.append(data) + return np.asarray(results) + + +# shift +def shift( + x, wrg=0.1, hrg=0.1, is_random=False, row_index=0, col_index=1, channel_index=2, fill_mode='nearest', cval=0., + order=1 +): + """Shift an image randomly or non-randomly. + + Parameters + ----------- + x : numpy.array + An image with dimension of [row, col, channel] (default). + wrg : float + Percentage of shift in axis x, usually -0.25 ~ 0.25. + hrg : float + Percentage of shift in axis y, usually -0.25 ~ 0.25. + is_random : boolean + If True, randomly shift. Default is False. + row_index col_index and channel_index : int + Index of row, col and channel, default (0, 1, 2), for theano (1, 2, 0). + fill_mode : str + Method to fill missing pixel, default `nearest`, more options `constant`, `reflect` or `wrap`, see `scipy ndimage affine_transform `__ + cval : float + Value used for points outside the boundaries of the input if mode='constant'. Default is 0.0. + order : int + The order of interpolation. The order has to be in the range 0-5. See ``tl.prepro.affine_transform`` and `scipy ndimage affine_transform `__ + + Returns + ------- + numpy.array + A processed image. + + """ + h, w = x.shape[row_index], x.shape[col_index] + if is_random: + tx = np.random.uniform(-hrg, hrg) * h + ty = np.random.uniform(-wrg, wrg) * w + else: + tx, ty = hrg * h, wrg * w + translation_matrix = np.array([[1, 0, tx], [0, 1, ty], [0, 0, 1]]) + + transform_matrix = translation_matrix # no need to do offset + x = affine_transform(x, transform_matrix, channel_index, fill_mode, cval, order) + return x + + +def shift_multi( + x, wrg=0.1, hrg=0.1, is_random=False, row_index=0, col_index=1, channel_index=2, fill_mode='nearest', cval=0., + order=1 +): + """Shift images with the same arguments, randomly or non-randomly. + Usually be used for image segmentation which x=[X, Y], X and Y should be matched. + + Parameters + ----------- + x : list of numpy.array + List of images with dimension of [n_images, row, col, channel] (default). + others : args + See ``tl.prepro.shift``. + + Returns + ------- + numpy.array + A list of processed images. + + """ + h, w = x[0].shape[row_index], x[0].shape[col_index] + if is_random: + tx = np.random.uniform(-hrg, hrg) * h + ty = np.random.uniform(-wrg, wrg) * w + else: + tx, ty = hrg * h, wrg * w + translation_matrix = np.array([[1, 0, tx], [0, 1, ty], [0, 0, 1]]) + + transform_matrix = translation_matrix # no need to do offset + results = [] + for data in x: + results.append(affine_transform(data, transform_matrix, channel_index, fill_mode, cval, order)) + return np.asarray(results) + + +# shear +def shear( + x, intensity=0.1, is_random=False, row_index=0, col_index=1, channel_index=2, fill_mode='nearest', cval=0., order=1 +): + """Shear an image randomly or non-randomly. + + Parameters + ----------- + x : numpy.array + An image with dimension of [row, col, channel] (default). + intensity : float + Percentage of shear, usually -0.5 ~ 0.5 (is_random==True), 0 ~ 0.5 (is_random==False), + you can have a quick try by shear(X, 1). + is_random : boolean + If True, randomly shear. Default is False. + row_index col_index and channel_index : int + Index of row, col and channel, default (0, 1, 2), for theano (1, 2, 0). + fill_mode : str + Method to fill missing pixel, default `nearest`, more options `constant`, `reflect` or `wrap`, see and `scipy ndimage affine_transform `__ + cval : float + Value used for points outside the boundaries of the input if mode='constant'. Default is 0.0. + order : int + The order of interpolation. The order has to be in the range 0-5. See ``tl.prepro.affine_transform`` and `scipy ndimage affine_transform `__ + + Returns + ------- + numpy.array + A processed image. + + References + ----------- + - `Affine transformation `__ + + """ + if is_random: + shear = np.random.uniform(-intensity, intensity) + else: + shear = intensity + shear_matrix = np.array([[1, -np.sin(shear), 0], [0, np.cos(shear), 0], [0, 0, 1]]) + + h, w = x.shape[row_index], x.shape[col_index] + transform_matrix = transform_matrix_offset_center(shear_matrix, h, w) + x = affine_transform(x, transform_matrix, channel_index, fill_mode, cval, order) + return x + + +def shear_multi( + x, intensity=0.1, is_random=False, row_index=0, col_index=1, channel_index=2, fill_mode='nearest', cval=0., order=1 +): + """Shear images with the same arguments, randomly or non-randomly. + Usually be used for image segmentation which x=[X, Y], X and Y should be matched. + + Parameters + ----------- + x : list of numpy.array + List of images with dimension of [n_images, row, col, channel] (default). + others : args + See ``tl.prepro.shear``. + + Returns + ------- + numpy.array + A list of processed images. + + """ + if is_random: + shear = np.random.uniform(-intensity, intensity) + else: + shear = intensity + shear_matrix = np.array([[1, -np.sin(shear), 0], [0, np.cos(shear), 0], [0, 0, 1]]) + + h, w = x[0].shape[row_index], x[0].shape[col_index] + transform_matrix = transform_matrix_offset_center(shear_matrix, h, w) + results = [] + for data in x: + results.append(affine_transform(data, transform_matrix, channel_index, fill_mode, cval, order)) + return np.asarray(results) + + +def shear2( + x, shear=(0.1, 0.1), is_random=False, row_index=0, col_index=1, channel_index=2, fill_mode='nearest', cval=0., + order=1 +): + """Shear an image randomly or non-randomly. + + Parameters + ----------- + x : numpy.array + An image with dimension of [row, col, channel] (default). + shear : tuple of two floats + Percentage of shear for height and width direction (0, 1). + is_random : boolean + If True, randomly shear. Default is False. + row_index col_index and channel_index : int + Index of row, col and channel, default (0, 1, 2), for theano (1, 2, 0). + fill_mode : str + Method to fill missing pixel, default `nearest`, more options `constant`, `reflect` or `wrap`, see `scipy ndimage affine_transform `__ + cval : float + Value used for points outside the boundaries of the input if mode='constant'. Default is 0.0. + order : int + The order of interpolation. The order has to be in the range 0-5. See ``tl.prepro.affine_transform`` and `scipy ndimage affine_transform `__ + + Returns + ------- + numpy.array + A processed image. + + References + ----------- + - `Affine transformation `__ + + """ + if len(shear) != 2: + raise AssertionError( + "shear should be tuple of 2 floats, or you want to use tl.prepro.shear rather than tl.prepro.shear2 ?" + ) + if isinstance(shear, tuple): + shear = list(shear) + if is_random: + shear[0] = np.random.uniform(-shear[0], shear[0]) + shear[1] = np.random.uniform(-shear[1], shear[1]) + + shear_matrix = np.array([[1, shear[0], 0], \ + [shear[1], 1, 0], \ + [0, 0, 1]]) + + h, w = x.shape[row_index], x.shape[col_index] + transform_matrix = transform_matrix_offset_center(shear_matrix, h, w) + x = affine_transform(x, transform_matrix, channel_index, fill_mode, cval, order) + return x + + +def shear_multi2( + x, shear=(0.1, 0.1), is_random=False, row_index=0, col_index=1, channel_index=2, fill_mode='nearest', cval=0., + order=1 +): + """Shear images with the same arguments, randomly or non-randomly. + Usually be used for image segmentation which x=[X, Y], X and Y should be matched. + + Parameters + ----------- + x : list of numpy.array + List of images with dimension of [n_images, row, col, channel] (default). + others : args + See ``tl.prepro.shear2``. + + Returns + ------- + numpy.array + A list of processed images. + + """ + if len(shear) != 2: + raise AssertionError( + "shear should be tuple of 2 floats, or you want to use tl.prepro.shear_multi rather than tl.prepro.shear_multi2 ?" + ) + if isinstance(shear, tuple): + shear = list(shear) + if is_random: + shear[0] = np.random.uniform(-shear[0], shear[0]) + shear[1] = np.random.uniform(-shear[1], shear[1]) + + shear_matrix = np.array([[1, shear[0], 0], [shear[1], 1, 0], [0, 0, 1]]) + + h, w = x[0].shape[row_index], x[0].shape[col_index] + transform_matrix = transform_matrix_offset_center(shear_matrix, h, w) + results = [] + for data in x: + results.append(affine_transform(data, transform_matrix, channel_index, fill_mode, cval, order)) + return np.asarray(results) + + +# swirl +def swirl( + x, center=None, strength=1, radius=100, rotation=0, output_shape=None, order=1, mode='constant', cval=0, clip=True, + preserve_range=False, is_random=False +): + """Swirl an image randomly or non-randomly, see `scikit-image swirl API `__ + and `example `__. + + Parameters + ----------- + x : numpy.array + An image with dimension of [row, col, channel] (default). + center : tuple or 2 int or None + Center coordinate of transformation (optional). + strength : float + The amount of swirling applied. + radius : float + The extent of the swirl in pixels. The effect dies out rapidly beyond radius. + rotation : float + Additional rotation applied to the image, usually [0, 360], relates to center. + output_shape : tuple of 2 int or None + Shape of the output image generated (height, width). By default the shape of the input image is preserved. + order : int, optional + The order of the spline interpolation, default is 1. The order has to be in the range 0-5. See skimage.transform.warp for detail. + mode : str + One of `constant` (default), `edge`, `symmetric` `reflect` and `wrap`. + Points outside the boundaries of the input are filled according to the given mode, with `constant` used as the default. Modes match the behaviour of numpy.pad. + cval : float + Used in conjunction with mode `constant`, the value outside the image boundaries. + clip : boolean + Whether to clip the output to the range of values of the input image. This is enabled by default, since higher order interpolation may produce values outside the given input range. + preserve_range : boolean + Whether to keep the original range of values. Otherwise, the input image is converted according to the conventions of img_as_float. + is_random : boolean, + If True, random swirl. Default is False. + - random center = [(0 ~ x.shape[0]), (0 ~ x.shape[1])] + - random strength = [0, strength] + - random radius = [1e-10, radius] + - random rotation = [-rotation, rotation] + + Returns + ------- + numpy.array + A processed image. + + Examples + --------- + >>> x --> [row, col, 1] greyscale + >>> x = tl.prepro.swirl(x, strength=4, radius=100) + + """ + if radius == 0: + raise AssertionError("Invalid radius value") + + rotation = np.pi / 180 * rotation + if is_random: + center_h = int(np.random.uniform(0, x.shape[0])) + center_w = int(np.random.uniform(0, x.shape[1])) + center = (center_h, center_w) + strength = np.random.uniform(0, strength) + radius = np.random.uniform(1e-10, radius) + rotation = np.random.uniform(-rotation, rotation) + + max_v = np.max(x) + if max_v > 1: # Note: the input of this fn should be [-1, 1], rescale is required. + x = x / max_v + swirled = skimage.transform.swirl( + x, center=center, strength=strength, radius=radius, rotation=rotation, output_shape=output_shape, order=order, + mode=mode, cval=cval, clip=clip, preserve_range=preserve_range + ) + if max_v > 1: + swirled = swirled * max_v + return swirled + + +def swirl_multi( + x, center=None, strength=1, radius=100, rotation=0, output_shape=None, order=1, mode='constant', cval=0, clip=True, + preserve_range=False, is_random=False +): + """Swirl multiple images with the same arguments, randomly or non-randomly. + Usually be used for image segmentation which x=[X, Y], X and Y should be matched. + + Parameters + ----------- + x : list of numpy.array + List of images with dimension of [n_images, row, col, channel] (default). + others : args + See ``tl.prepro.swirl``. + + Returns + ------- + numpy.array + A list of processed images. + + """ + if radius == 0: + raise AssertionError("Invalid radius value") + + rotation = np.pi / 180 * rotation + if is_random: + center_h = int(np.random.uniform(0, x[0].shape[0])) + center_w = int(np.random.uniform(0, x[0].shape[1])) + center = (center_h, center_w) + strength = np.random.uniform(0, strength) + radius = np.random.uniform(1e-10, radius) + rotation = np.random.uniform(-rotation, rotation) + + results = [] + for data in x: + max_v = np.max(data) + if max_v > 1: # Note: the input of this fn should be [-1, 1], rescale is required. + data = data / max_v + swirled = skimage.transform.swirl( + data, center=center, strength=strength, radius=radius, rotation=rotation, output_shape=output_shape, + order=order, mode=mode, cval=cval, clip=clip, preserve_range=preserve_range + ) + if max_v > 1: + swirled = swirled * max_v + results.append(swirled) + return np.asarray(results) + + +# elastic_transform +def elastic_transform(x, alpha, sigma, mode="constant", cval=0, is_random=False): + """Elastic transformation for image as described in `[Simard2003] `__. + + Parameters + ----------- + x : numpy.array + A greyscale image. + alpha : float + Alpha value for elastic transformation. + sigma : float or sequence of float + The smaller the sigma, the more transformation. Standard deviation for Gaussian kernel. The standard deviations of the Gaussian filter are given for each axis as a sequence, or as a single number, in which case it is equal for all axes. + mode : str + See `scipy.ndimage.filters.gaussian_filter `__. Default is `constant`. + cval : float, + Used in conjunction with `mode` of `constant`, the value outside the image boundaries. + is_random : boolean + Default is False. + + Returns + ------- + numpy.array + A processed image. + + Examples + --------- + >>> x = tl.prepro.elastic_transform(x, alpha=x.shape[1]*3, sigma=x.shape[1]*0.07) + + References + ------------ + - `Github `__. + - `Kaggle `__ + + """ + if is_random is False: + random_state = np.random.RandomState(None) + else: + random_state = np.random.RandomState(int(time.time())) + # + is_3d = False + if len(x.shape) == 3 and x.shape[-1] == 1: + x = x[:, :, 0] + is_3d = True + elif len(x.shape) == 3 and x.shape[-1] != 1: + raise Exception("Only support greyscale image") + + if len(x.shape) != 2: + raise AssertionError("input should be grey-scale image") + + shape = x.shape + + dx = gaussian_filter((random_state.rand(*shape) * 2 - 1), sigma, mode=mode, cval=cval) * alpha + dy = gaussian_filter((random_state.rand(*shape) * 2 - 1), sigma, mode=mode, cval=cval) * alpha + + x_, y_ = np.meshgrid(np.arange(shape[0]), np.arange(shape[1]), indexing='ij') + indices = np.reshape(x_ + dx, (-1, 1)), np.reshape(y_ + dy, (-1, 1)) + if is_3d: + return map_coordinates(x, indices, order=1).reshape((shape[0], shape[1], 1)) + else: + return map_coordinates(x, indices, order=1).reshape(shape) + + +def elastic_transform_multi(x, alpha, sigma, mode="constant", cval=0, is_random=False): + """Elastic transformation for images as described in `[Simard2003] `__. + + Parameters + ----------- + x : list of numpy.array + List of greyscale images. + others : args + See ``tl.prepro.elastic_transform``. + + Returns + ------- + numpy.array + A list of processed images. + + """ + if is_random is False: + random_state = np.random.RandomState(None) + else: + random_state = np.random.RandomState(int(time.time())) + + shape = x[0].shape + if len(shape) == 3: + shape = (shape[0], shape[1]) + new_shape = random_state.rand(*shape) + + results = [] + for data in x: + is_3d = False + if len(data.shape) == 3 and data.shape[-1] == 1: + data = data[:, :, 0] + is_3d = True + elif len(data.shape) == 3 and data.shape[-1] != 1: + raise Exception("Only support greyscale image") + + if len(data.shape) != 2: + raise AssertionError("input should be grey-scale image") + + dx = gaussian_filter((new_shape * 2 - 1), sigma, mode=mode, cval=cval) * alpha + dy = gaussian_filter((new_shape * 2 - 1), sigma, mode=mode, cval=cval) * alpha + + x_, y_ = np.meshgrid(np.arange(shape[0]), np.arange(shape[1]), indexing='ij') + indices = np.reshape(x_ + dx, (-1, 1)), np.reshape(y_ + dy, (-1, 1)) + # tl.logging.info(data.shape) + if is_3d: + results.append(map_coordinates(data, indices, order=1).reshape((shape[0], shape[1], 1))) + else: + results.append(map_coordinates(data, indices, order=1).reshape(shape)) + return np.asarray(results) + + +# zoom +def zoom(x, zoom_range=(0.9, 1.1), flags=None, border_mode='constant'): + """Zooming/Scaling a single image that height and width are changed together. + + Parameters + ----------- + x : numpy.array + An image with dimension of [row, col, channel] (default). + zoom_range : float or tuple of 2 floats + The zooming/scaling ratio, greater than 1 means larger. + - float, a fixed ratio. + - tuple of 2 floats, randomly sample a value as the ratio between 2 values. + border_mode : str + - `constant`, pad the image with a constant value (i.e. black or 0) + - `replicate`, the row or column at the very edge of the original is replicated to the extra border. + + Returns + ------- + numpy.array + A processed image. + + """ + zoom_matrix = affine_zoom_matrix(zoom_range=zoom_range) + h, w = x.shape[0], x.shape[1] + transform_matrix = transform_matrix_offset_center(zoom_matrix, h, w) + x = affine_transform_cv2(x, transform_matrix, flags=flags, border_mode=border_mode) + return x + + +def respective_zoom(x, h_range=(0.9, 1.1), w_range=(0.9, 1.1), flags=None, border_mode='constant'): + """Zooming/Scaling a single image that height and width are changed independently. + + Parameters + ----------- + x : numpy.array + An image with dimension of [row, col, channel] (default). + h_range : float or tuple of 2 floats + The zooming/scaling ratio of height, greater than 1 means larger. + - float, a fixed ratio. + - tuple of 2 floats, randomly sample a value as the ratio between 2 values. + w_range : float or tuple of 2 floats + The zooming/scaling ratio of width, greater than 1 means larger. + - float, a fixed ratio. + - tuple of 2 floats, randomly sample a value as the ratio between 2 values. + border_mode : str + - `constant`, pad the image with a constant value (i.e. black or 0) + - `replicate`, the row or column at the very edge of the original is replicated to the extra border. + + Returns + ------- + numpy.array + A processed image. + + """ + zoom_matrix = affine_respective_zoom_matrix(h_range=h_range, w_range=w_range) + h, w = x.shape[0], x.shape[1] + transform_matrix = transform_matrix_offset_center(zoom_matrix, h, w) + x = affine_transform_cv2( + x, transform_matrix, flags=flags, border_mode=border_mode + ) #affine_transform(x, transform_matrix, channel_index, fill_mode, cval, order) + return x + + +def zoom_multi(x, zoom_range=(0.9, 1.1), flags=None, border_mode='constant'): + """Zoom in and out of images with the same arguments, randomly or non-randomly. + Usually be used for image segmentation which x=[X, Y], X and Y should be matched. + + Parameters + ----------- + x : list of numpy.array + List of images with dimension of [n_images, row, col, channel] (default). + others : args + See ``tl.prepro.zoom``. + + Returns + ------- + numpy.array + A list of processed images. + + """ + + zoom_matrix = affine_zoom_matrix(zoom_range=zoom_range) + results = [] + for img in x: + h, w = x.shape[0], x.shape[1] + transform_matrix = transform_matrix_offset_center(zoom_matrix, h, w) + results.append(affine_transform_cv2(x, transform_matrix, flags=flags, border_mode=border_mode)) + return results + + +# image = tf.image.random_brightness(image, max_delta=32. / 255.) +# image = tf.image.random_saturation(image, lower=0.5, upper=1.5) +# image = tf.image.random_hue(image, max_delta=0.032) +# image = tf.image.random_contrast(image, lower=0.5, upper=1.5) + + +def brightness(x, gamma=1, gain=1, is_random=False): + """Change the brightness of a single image, randomly or non-randomly. + + Parameters + ----------- + x : numpy.array + An image with dimension of [row, col, channel] (default). + gamma : float + Non negative real number. Default value is 1. + - Small than 1 means brighter. + - If `is_random` is True, gamma in a range of (1-gamma, 1+gamma). + gain : float + The constant multiplier. Default value is 1. + is_random : boolean + If True, randomly change brightness. Default is False. + + Returns + ------- + numpy.array + A processed image. + + References + ----------- + - `skimage.exposure.adjust_gamma `__ + - `chinese blog `__ + + """ + if is_random: + gamma = np.random.uniform(1 - gamma, 1 + gamma) + x = exposure.adjust_gamma(x, gamma, gain) + return x + + +def brightness_multi(x, gamma=1, gain=1, is_random=False): + """Change the brightness of multiply images, randomly or non-randomly. + Usually be used for image segmentation which x=[X, Y], X and Y should be matched. + + Parameters + ----------- + x : list of numpyarray + List of images with dimension of [n_images, row, col, channel] (default). + others : args + See ``tl.prepro.brightness``. + + Returns + ------- + numpy.array + A list of processed images. + + """ + if is_random: + gamma = np.random.uniform(1 - gamma, 1 + gamma) + + results = [] + for data in x: + results.append(exposure.adjust_gamma(data, gamma, gain)) + return np.asarray(results) + + +def illumination(x, gamma=1., contrast=1., saturation=1., is_random=False): + """Perform illumination augmentation for a single image, randomly or non-randomly. + + Parameters + ----------- + x : numpy.array + An image with dimension of [row, col, channel] (default). + gamma : float + Change brightness (the same with ``tl.prepro.brightness``) + - if is_random=False, one float number, small than one means brighter, greater than one means darker. + - if is_random=True, tuple of two float numbers, (min, max). + contrast : float + Change contrast. + - if is_random=False, one float number, small than one means blur. + - if is_random=True, tuple of two float numbers, (min, max). + saturation : float + Change saturation. + - if is_random=False, one float number, small than one means unsaturation. + - if is_random=True, tuple of two float numbers, (min, max). + is_random : boolean + If True, randomly change illumination. Default is False. + + Returns + ------- + numpy.array + A processed image. + + Examples + --------- + Random + + >>> x = tl.prepro.illumination(x, gamma=(0.5, 5.0), contrast=(0.3, 1.0), saturation=(0.7, 1.0), is_random=True) + + Non-random + + >>> x = tl.prepro.illumination(x, 0.5, 0.6, 0.8, is_random=False) + + """ + if is_random: + if not (len(gamma) == len(contrast) == len(saturation) == 2): + raise AssertionError("if is_random = True, the arguments are (min, max)") + + ## random change brightness # small --> brighter + illum_settings = np.random.randint(0, 3) # 0-brighter, 1-darker, 2 keep normal + + if illum_settings == 0: # brighter + gamma = np.random.uniform(gamma[0], 1.0) # (.5, 1.0) + elif illum_settings == 1: # darker + gamma = np.random.uniform(1.0, gamma[1]) # (1.0, 5.0) + else: + gamma = 1 + im_ = brightness(x, gamma=gamma, gain=1, is_random=False) + + # tl.logging.info("using contrast and saturation") + image = PIL.Image.fromarray(im_) # array -> PIL + contrast_adjust = PIL.ImageEnhance.Contrast(image) + image = contrast_adjust.enhance(np.random.uniform(contrast[0], contrast[1])) #0.3,0.9)) + + saturation_adjust = PIL.ImageEnhance.Color(image) + image = saturation_adjust.enhance(np.random.uniform(saturation[0], saturation[1])) # (0.7,1.0)) + im_ = np.array(image) # PIL -> array + else: + im_ = brightness(x, gamma=gamma, gain=1, is_random=False) + image = PIL.Image.fromarray(im_) # array -> PIL + contrast_adjust = PIL.ImageEnhance.Contrast(image) + image = contrast_adjust.enhance(contrast) + + saturation_adjust = PIL.ImageEnhance.Color(image) + image = saturation_adjust.enhance(saturation) + im_ = np.array(image) # PIL -> array + return np.asarray(im_) + + +def rgb_to_hsv(rgb): + """Input RGB image [0~255] return HSV image [0~1]. + + Parameters + ------------ + rgb : numpy.array + An image with values between 0 and 255. + + Returns + ------- + numpy.array + A processed image. + + """ + # Translated from source of colorsys.rgb_to_hsv + # r,g,b should be a numpy arrays with values between 0 and 255 + # rgb_to_hsv returns an array of floats between 0.0 and 1.0. + rgb = rgb.astype('float') + hsv = np.zeros_like(rgb) + # in case an RGBA array was passed, just copy the A channel + hsv[..., 3:] = rgb[..., 3:] + r, g, b = rgb[..., 0], rgb[..., 1], rgb[..., 2] + maxc = np.max(rgb[..., :3], axis=-1) + minc = np.min(rgb[..., :3], axis=-1) + hsv[..., 2] = maxc + mask = maxc != minc + hsv[mask, 1] = (maxc - minc)[mask] / maxc[mask] + rc = np.zeros_like(r) + gc = np.zeros_like(g) + bc = np.zeros_like(b) + rc[mask] = (maxc - r)[mask] / (maxc - minc)[mask] + gc[mask] = (maxc - g)[mask] / (maxc - minc)[mask] + bc[mask] = (maxc - b)[mask] / (maxc - minc)[mask] + hsv[..., 0] = np.select([r == maxc, g == maxc], [bc - gc, 2.0 + rc - bc], default=4.0 + gc - rc) + hsv[..., 0] = (hsv[..., 0] / 6.0) % 1.0 + return hsv + + +def hsv_to_rgb(hsv): + """Input HSV image [0~1] return RGB image [0~255]. + + Parameters + ------------- + hsv : numpy.array + An image with values between 0.0 and 1.0 + + Returns + ------- + numpy.array + A processed image. + """ + # Translated from source of colorsys.hsv_to_rgb + # h,s should be a numpy arrays with values between 0.0 and 1.0 + # v should be a numpy array with values between 0.0 and 255.0 + # hsv_to_rgb returns an array of uints between 0 and 255. + rgb = np.empty_like(hsv) + rgb[..., 3:] = hsv[..., 3:] + h, s, v = hsv[..., 0], hsv[..., 1], hsv[..., 2] + i = (h * 6.0).astype('uint8') + f = (h * 6.0) - i + p = v * (1.0 - s) + q = v * (1.0 - s * f) + t = v * (1.0 - s * (1.0 - f)) + i = i % 6 + conditions = [s == 0.0, i == 1, i == 2, i == 3, i == 4, i == 5] + rgb[..., 0] = np.select(conditions, [v, q, p, p, t, v], default=v) + rgb[..., 1] = np.select(conditions, [v, v, v, q, p, p], default=t) + rgb[..., 2] = np.select(conditions, [v, p, t, v, v, q], default=p) + return rgb.astype('uint8') + + +def adjust_hue(im, hout=0.66, is_offset=True, is_clip=True, is_random=False): + """Adjust hue of an RGB image. + + This is a convenience method that converts an RGB image to float representation, converts it to HSV, add an offset to the hue channel, converts back to RGB and then back to the original data type. + For TF, see `tf.image.adjust_hue `__.and `tf.image.random_hue `__. + + Parameters + ----------- + im : numpy.array + An image with values between 0 and 255. + hout : float + The scale value for adjusting hue. + - If is_offset is False, set all hue values to this value. 0 is red; 0.33 is green; 0.66 is blue. + - If is_offset is True, add this value as the offset to the hue channel. + is_offset : boolean + Whether `hout` is added on HSV as offset or not. Default is True. + is_clip : boolean + If HSV value smaller than 0, set to 0. Default is True. + is_random : boolean + If True, randomly change hue. Default is False. + + Returns + ------- + numpy.array + A processed image. + + Examples + --------- + Random, add a random value between -0.2 and 0.2 as the offset to every hue values. + + >>> im_hue = tl.prepro.adjust_hue(image, hout=0.2, is_offset=True, is_random=False) + + Non-random, make all hue to green. + + >>> im_green = tl.prepro.adjust_hue(image, hout=0.66, is_offset=False, is_random=False) + + References + ----------- + - `tf.image.random_hue `__. + - `tf.image.adjust_hue `__. + - `StackOverflow: Changing image hue with python PIL `__. + + """ + hsv = rgb_to_hsv(im) + if is_random: + hout = np.random.uniform(-hout, hout) + + if is_offset: + hsv[..., 0] += hout + else: + hsv[..., 0] = hout + + if is_clip: + hsv[..., 0] = np.clip(hsv[..., 0], 0, np.inf) # Hao : can remove green dots + + rgb = hsv_to_rgb(hsv) + return rgb + + +# # contrast +# def constant(x, cutoff=0.5, gain=10, inv=False, is_random=False): +# # TODO +# x = exposure.adjust_sigmoid(x, cutoff=cutoff, gain=gain, inv=inv) +# return x +# +# def constant_multi(): +# #TODO +# pass + + +def imresize(x, size=None, interp='bicubic', mode=None): + """Resize an image by given output size and method. + + Warning, this function will rescale the value to [0, 255]. + + Parameters + ----------- + x : numpy.array + An image with dimension of [row, col, channel] (default). + size : list of 2 int or None + For height and width. + interp : str + Interpolation method for re-sizing (`nearest`, `lanczos`, `bilinear`, `bicubic` (default) or `cubic`). + mode : str + The PIL image mode (`P`, `L`, etc.) to convert image before resizing. + + Returns + ------- + numpy.array + A processed image. + + References + ------------ + - `scipy.misc.imresize `__ + + """ + if size is None: + size = [100, 100] + + if x.shape[-1] == 1: + # greyscale + # x = scipy.misc.imresize(x[:, :, 0], size, interp=interp, mode=mode) + x = resize(x[:, :, 0], size) + return x[:, :, np.newaxis] + else: + # rgb, bgr, rgba + return resize(x, output_shape=size) + # return scipy.misc.imresize(x, size, interp=interp, mode=mode) + + +# value scale +def pixel_value_scale(im, val=0.9, clip=None, is_random=False): + """Scales each value in the pixels of the image. + + Parameters + ----------- + im : numpy.array + An image. + val : float + The scale value for changing pixel value. + - If is_random=False, multiply this value with all pixels. + - If is_random=True, multiply a value between [1-val, 1+val] with all pixels. + clip : tuple of 2 numbers + The minimum and maximum value. + is_random : boolean + If True, see ``val``. + + Returns + ------- + numpy.array + A processed image. + + Examples + ---------- + Random + + >>> im = pixel_value_scale(im, 0.1, [0, 255], is_random=True) + + Non-random + + >>> im = pixel_value_scale(im, 0.9, [0, 255], is_random=False) + + """ + + clip = clip if clip is not None else (-np.inf, np.inf) + + if is_random: + scale = 1 + np.random.uniform(-val, val) + im = im * scale + else: + im = im * val + + if len(clip) == 2: + im = np.clip(im, clip[0], clip[1]) + else: + raise Exception("clip : tuple of 2 numbers") + + return im + + +# normailization +def samplewise_norm( + x, rescale=None, samplewise_center=False, samplewise_std_normalization=False, channel_index=2, epsilon=1e-7 +): + """Normalize an image by rescale, samplewise centering and samplewise centering in order. + + Parameters + ----------- + x : numpy.array + An image with dimension of [row, col, channel] (default). + rescale : float + Rescaling factor. If None or 0, no rescaling is applied, otherwise we multiply the data by the value provided (before applying any other transformation) + samplewise_center : boolean + If True, set each sample mean to 0. + samplewise_std_normalization : boolean + If True, divide each input by its std. + epsilon : float + A small position value for dividing standard deviation. + + Returns + ------- + numpy.array + A processed image. + + Examples + -------- + >>> x = samplewise_norm(x, samplewise_center=True, samplewise_std_normalization=True) + >>> print(x.shape, np.mean(x), np.std(x)) + (160, 176, 1), 0.0, 1.0 + + Notes + ------ + When samplewise_center and samplewise_std_normalization are True. + - For greyscale image, every pixels are subtracted and divided by the mean and std of whole image. + - For RGB image, every pixels are subtracted and divided by the mean and std of this pixel i.e. the mean and std of a pixel is 0 and 1. + + """ + if rescale: + x *= rescale + + if x.shape[channel_index] == 1: + # greyscale + if samplewise_center: + x = x - np.mean(x) + if samplewise_std_normalization: + x = x / np.std(x) + return x + elif x.shape[channel_index] == 3: + # rgb + if samplewise_center: + x = x - np.mean(x, axis=channel_index, keepdims=True) + if samplewise_std_normalization: + x = x / (np.std(x, axis=channel_index, keepdims=True) + epsilon) + return x + else: + raise Exception("Unsupported channels %d" % x.shape[channel_index]) + + +def featurewise_norm(x, mean=None, std=None, epsilon=1e-7): + """Normalize every pixels by the same given mean and std, which are usually + compute from all examples. + + Parameters + ----------- + x : numpy.array + An image with dimension of [row, col, channel] (default). + mean : float + Value for subtraction. + std : float + Value for division. + epsilon : float + A small position value for dividing standard deviation. + + Returns + ------- + numpy.array + A processed image. + + """ + if mean: + x = x - mean + if std: + x = x / (std + epsilon) + return x + + +# whitening +def get_zca_whitening_principal_components_img(X): + """Return the ZCA whitening principal components matrix. + + Parameters + ----------- + x : numpy.array + Batch of images with dimension of [n_example, row, col, channel] (default). + + Returns + ------- + numpy.array + A processed image. + + """ + flatX = np.reshape(X, (X.shape[0], X.shape[1] * X.shape[2] * X.shape[3])) + tl.logging.info("zca : computing sigma ..") + sigma = np.dot(flatX.T, flatX) / flatX.shape[0] + tl.logging.info("zca : computing U, S and V ..") + U, S, _ = linalg.svd(sigma) # USV + tl.logging.info("zca : computing principal components ..") + principal_components = np.dot(np.dot(U, np.diag(1. / np.sqrt(S + 10e-7))), U.T) + return principal_components + + +def zca_whitening(x, principal_components): + """Apply ZCA whitening on an image by given principal components matrix. + + Parameters + ----------- + x : numpy.array + An image with dimension of [row, col, channel] (default). + principal_components : matrix + Matrix from ``get_zca_whitening_principal_components_img``. + + Returns + ------- + numpy.array + A processed image. + + """ + flatx = np.reshape(x, (x.size)) + # tl.logging.info(principal_components.shape, x.shape) # ((28160, 28160), (160, 176, 1)) + # flatx = np.reshape(x, (x.shape)) + # flatx = np.reshape(x, (x.shape[0], )) + # tl.logging.info(flatx.shape) # (160, 176, 1) + whitex = np.dot(flatx, principal_components) + x = np.reshape(whitex, (x.shape[0], x.shape[1], x.shape[2])) + return x + + +# developing +# def barrel_transform(x, intensity): +# # https://github.com/fchollet/keras/blob/master/keras/preprocessing/image.py +# # TODO +# pass +# +# def barrel_transform_multi(x, intensity): +# # https://github.com/fchollet/keras/blob/master/keras/preprocessing/image.py +# # TODO +# pass + + +# channel shift +def channel_shift(x, intensity, is_random=False, channel_index=2): + """Shift the channels of an image, randomly or non-randomly, see `numpy.rollaxis `__. + + Parameters + ----------- + x : numpy.array + An image with dimension of [row, col, channel] (default). + intensity : float + Intensity of shifting. + is_random : boolean + If True, randomly shift. Default is False. + channel_index : int + Index of channel. Default is 2. + + Returns + ------- + numpy.array + A processed image. + + """ + if is_random: + factor = np.random.uniform(-intensity, intensity) + else: + factor = intensity + x = np.rollaxis(x, channel_index, 0) + min_x, max_x = np.min(x), np.max(x) + channel_images = [np.clip(x_channel + factor, min_x, max_x) for x_channel in x] + x = np.stack(channel_images, axis=0) + x = np.rollaxis(x, 0, channel_index + 1) + return x + # x = np.rollaxis(x, channel_index, 0) + # min_x, max_x = np.min(x), np.max(x) + # channel_images = [np.clip(x_channel + np.random.uniform(-intensity, intensity), min_x, max_x) + # for x_channel in x] + # x = np.stack(channel_images, axis=0) + # x = np.rollaxis(x, 0, channel_index+1) + # return x + + +def channel_shift_multi(x, intensity, is_random=False, channel_index=2): + """Shift the channels of images with the same arguments, randomly or non-randomly, see `numpy.rollaxis `__. + Usually be used for image segmentation which x=[X, Y], X and Y should be matched. + + Parameters + ----------- + x : list of numpy.array + List of images with dimension of [n_images, row, col, channel] (default). + others : args + See ``tl.prepro.channel_shift``. + + Returns + ------- + numpy.array + A list of processed images. + + """ + if is_random: + factor = np.random.uniform(-intensity, intensity) + else: + factor = intensity + + results = [] + for data in x: + data = np.rollaxis(data, channel_index, 0) + min_x, max_x = np.min(data), np.max(data) + channel_images = [np.clip(x_channel + factor, min_x, max_x) for x_channel in x] + data = np.stack(channel_images, axis=0) + data = np.rollaxis(x, 0, channel_index + 1) + results.append(data) + return np.asarray(results) + + +# noise +def drop(x, keep=0.5): + """Randomly set some pixels to zero by a given keeping probability. + + Parameters + ----------- + x : numpy.array + An image with dimension of [row, col, channel] or [row, col]. + keep : float + The keeping probability (0, 1), the lower more values will be set to zero. + + Returns + ------- + numpy.array + A processed image. + + """ + if len(x.shape) == 3: + if x.shape[-1] == 3: # color + img_size = x.shape + mask = np.random.binomial(n=1, p=keep, size=x.shape[:-1]) + for i in range(3): + x[:, :, i] = np.multiply(x[:, :, i], mask) + elif x.shape[-1] == 1: # greyscale image + img_size = x.shape + x = np.multiply(x, np.random.binomial(n=1, p=keep, size=img_size)) + else: + raise Exception("Unsupported shape {}".format(x.shape)) + elif len(x.shape) == 2 or 1: # greyscale matrix (image) or vector + img_size = x.shape + x = np.multiply(x, np.random.binomial(n=1, p=keep, size=img_size)) + else: + raise Exception("Unsupported shape {}".format(x.shape)) + return x + + +# x = np.asarray([[1,2,3,4,5,6,7,8,9,10],[1,2,3,4,5,6,7,8,9,10]]) +# x = np.asarray([x,x,x,x,x,x]) +# x.shape = 10, 4, 3 +# tl.logging.info(x) +# # exit() +# tl.logging.info(x.shape) +# # exit() +# tl.logging.info(drop(x, keep=1.)) +# exit() + + +# Numpy and PIL +def array_to_img(x, dim_ordering=(0, 1, 2), scale=True): + """Converts a numpy array to PIL image object (uint8 format). + + Parameters + ---------- + x : numpy.array + An image with dimension of 3 and channels of 1 or 3. + dim_ordering : tuple of 3 int + Index of row, col and channel, default (0, 1, 2), for theano (1, 2, 0). + scale : boolean + If True, converts image to [0, 255] from any range of value like [-1, 2]. Default is True. + + Returns + ------- + PIL.image + An image. + + References + ----------- + `PIL Image.fromarray `__ + + """ + # if dim_ordering == 'default': + # dim_ordering = K.image_dim_ordering() + # if dim_ordering == 'th': # theano + # x = x.transpose(1, 2, 0) + + x = x.transpose(dim_ordering) + + if scale: + x += max(-np.min(x), 0) + x_max = np.max(x) + if x_max != 0: + # tl.logging.info(x_max) + # x /= x_max + x = x / x_max + x *= 255 + + if x.shape[2] == 3: + # RGB + return PIL.Image.fromarray(x.astype('uint8'), 'RGB') + + elif x.shape[2] == 1: + # grayscale + return PIL.Image.fromarray(x[:, :, 0].astype('uint8'), 'L') + + else: + raise Exception('Unsupported channel number: ', x.shape[2]) + + +def find_contours(x, level=0.8, fully_connected='low', positive_orientation='low'): + """Find iso-valued contours in a 2D array for a given level value, returns list of (n, 2)-ndarrays + see `skimage.measure.find_contours `__. + + Parameters + ------------ + x : 2D ndarray of double. + Input data in which to find contours. + level : float + Value along which to find contours in the array. + fully_connected : str + Either `low` or `high`. Indicates whether array elements below the given level value are to be considered fully-connected (and hence elements above the value will only be face connected), or vice-versa. (See notes below for details.) + positive_orientation : str + Either `low` or `high`. Indicates whether the output contours will produce positively-oriented polygons around islands of low- or high-valued elements. If `low` then contours will wind counter-clockwise around elements below the iso-value. Alternately, this means that low-valued elements are always on the left of the contour. + + Returns + -------- + list of (n,2)-ndarrays + Each contour is an ndarray of shape (n, 2), consisting of n (row, column) coordinates along the contour. + + """ + return skimage.measure.find_contours( + x, level, fully_connected=fully_connected, positive_orientation=positive_orientation + ) + + +def pt2map(list_points=None, size=(100, 100), val=1): + """Inputs a list of points, return a 2D image. + + Parameters + -------------- + list_points : list of 2 int + [[x, y], [x, y]..] for point coordinates. + size : tuple of 2 int + (w, h) for output size. + val : float or int + For the contour value. + + Returns + ------- + numpy.array + An image. + + """ + if list_points is None: + raise Exception("list_points : list of 2 int") + i_m = np.zeros(size) + if len(list_points) == 0: + return i_m + for xx in list_points: + for x in xx: + # tl.logging.info(x) + i_m[int(np.round(x[0]))][int(np.round(x[1]))] = val + return i_m + + +def binary_dilation(x, radius=3): + """Return fast binary morphological dilation of an image. + see `skimage.morphology.binary_dilation `__. + + Parameters + ----------- + x : 2D array + A binary image. + radius : int + For the radius of mask. + + Returns + ------- + numpy.array + A processed binary image. + + """ + mask = disk(radius) + x = _binary_dilation(x, selem=mask) + + return x + + +def dilation(x, radius=3): + """Return greyscale morphological dilation of an image, + see `skimage.morphology.dilation `__. + + Parameters + ----------- + x : 2D array + An greyscale image. + radius : int + For the radius of mask. + + Returns + ------- + numpy.array + A processed greyscale image. + + """ + mask = disk(radius) + x = dilation(x, selem=mask) + + return x + + +def binary_erosion(x, radius=3): + """Return binary morphological erosion of an image, + see `skimage.morphology.binary_erosion `__. + + Parameters + ----------- + x : 2D array + A binary image. + radius : int + For the radius of mask. + + Returns + ------- + numpy.array + A processed binary image. + + """ + mask = disk(radius) + x = _binary_erosion(x, selem=mask) + return x + + +def erosion(x, radius=3): + """Return greyscale morphological erosion of an image, + see `skimage.morphology.erosion `__. + + Parameters + ----------- + x : 2D array + A greyscale image. + radius : int + For the radius of mask. + + Returns + ------- + numpy.array + A processed greyscale image. + + """ + mask = disk(radius) + x = _erosion(x, selem=mask) + return x + + +def obj_box_coords_rescale(coords=None, shape=None): + """Scale down a list of coordinates from pixel unit to the ratio of image size i.e. in the range of [0, 1]. + + Parameters + ------------ + coords : list of list of 4 ints or None + For coordinates of more than one images .e.g.[[x, y, w, h], [x, y, w, h], ...]. + shape : list of 2 int or None + 【height, width]. + + Returns + ------- + list of list of 4 numbers + A list of new bounding boxes. + + + Examples + --------- + >>> coords = obj_box_coords_rescale(coords=[[30, 40, 50, 50], [10, 10, 20, 20]], shape=[100, 100]) + >>> print(coords) + [[0.3, 0.4, 0.5, 0.5], [0.1, 0.1, 0.2, 0.2]] + >>> coords = obj_box_coords_rescale(coords=[[30, 40, 50, 50]], shape=[50, 100]) + >>> print(coords) + [[0.3, 0.8, 0.5, 1.0]] + >>> coords = obj_box_coords_rescale(coords=[[30, 40, 50, 50]], shape=[100, 200]) + >>> print(coords) + [[0.15, 0.4, 0.25, 0.5]] + + Returns + ------- + list of 4 numbers + New coordinates. + + """ + if coords is None: + coords = [] + if shape is None: + shape = [100, 200] + + imh, imw = shape[0], shape[1] + imh = imh * 1.0 # * 1.0 for python2 : force division to be float point + imw = imw * 1.0 + coords_new = list() + for coord in coords: + + if len(coord) != 4: + raise AssertionError("coordinate should be 4 values : [x, y, w, h]") + + x = coord[0] / imw + y = coord[1] / imh + w = coord[2] / imw + h = coord[3] / imh + coords_new.append([x, y, w, h]) + return coords_new + + +def obj_box_coord_rescale(coord=None, shape=None): + """Scale down one coordinates from pixel unit to the ratio of image size i.e. in the range of [0, 1]. + It is the reverse process of ``obj_box_coord_scale_to_pixelunit``. + + Parameters + ------------ + coords : list of 4 int or None + One coordinates of one image e.g. [x, y, w, h]. + shape : list of 2 int or None + For [height, width]. + + Returns + ------- + list of 4 numbers + New bounding box. + + Examples + --------- + >>> coord = tl.prepro.obj_box_coord_rescale(coord=[30, 40, 50, 50], shape=[100, 100]) + [0.3, 0.4, 0.5, 0.5] + + """ + if coord is None: + coord = [] + if shape is None: + shape = [100, 200] + + return obj_box_coords_rescale(coords=[coord], shape=shape)[0] + + +def obj_box_coord_scale_to_pixelunit(coord, shape=None): + """Convert one coordinate [x, y, w (or x2), h (or y2)] in ratio format to image coordinate format. + It is the reverse process of ``obj_box_coord_rescale``. + + Parameters + ----------- + coord : list of 4 float + One coordinate of one image [x, y, w (or x2), h (or y2)] in ratio format, i.e value range [0~1]. + shape : tuple of 2 or None + For [height, width]. + + Returns + ------- + list of 4 numbers + New bounding box. + + Examples + --------- + >>> x, y, x2, y2 = tl.prepro.obj_box_coord_scale_to_pixelunit([0.2, 0.3, 0.5, 0.7], shape=(100, 200, 3)) + [40, 30, 100, 70] + + """ + if shape is None: + shape = [100, 100] + + imh, imw = shape[0:2] + x = int(coord[0] * imw) + x2 = int(coord[2] * imw) + y = int(coord[1] * imh) + y2 = int(coord[3] * imh) + return [x, y, x2, y2] + + +# coords = obj_box_coords_rescale(coords=[[30, 40, 50, 50], [10, 10, 20, 20]], shape=[100, 100]) +# tl.logging.info(coords) +# # [[0.3, 0.4, 0.5, 0.5], [0.1, 0.1, 0.2, 0.2]] +# coords = obj_box_coords_rescale(coords=[[30, 40, 50, 50]], shape=[50, 100]) +# tl.logging.info(coords) +# # [[0.3, 0.8, 0.5, 1.0]] +# coords = obj_box_coords_rescale(coords=[[30, 40, 50, 50]], shape=[100, 200]) +# tl.logging.info(coords) +# # [[0.15, 0.4, 0.25, 0.5]] +# exit() + + +def obj_box_coord_centroid_to_upleft_butright(coord, to_int=False): + """Convert one coordinate [x_center, y_center, w, h] to [x1, y1, x2, y2] in up-left and botton-right format. + + Parameters + ------------ + coord : list of 4 int/float + One coordinate. + to_int : boolean + Whether to convert output as integer. + + Returns + ------- + list of 4 numbers + New bounding box. + + Examples + --------- + >>> coord = obj_box_coord_centroid_to_upleft_butright([30, 40, 20, 20]) + [20, 30, 40, 50] + + """ + if len(coord) != 4: + raise AssertionError("coordinate should be 4 values : [x, y, w, h]") + + x_center, y_center, w, h = coord + x = x_center - w / 2. + y = y_center - h / 2. + x2 = x + w + y2 = y + h + if to_int: + return [int(x), int(y), int(x2), int(y2)] + else: + return [x, y, x2, y2] + + +# coord = obj_box_coord_centroid_to_upleft_butright([30, 40, 20, 20]) +# tl.logging.info(coord) [20, 30, 40, 50] +# exit() + + +def obj_box_coord_upleft_butright_to_centroid(coord): + """Convert one coordinate [x1, y1, x2, y2] to [x_center, y_center, w, h]. + It is the reverse process of ``obj_box_coord_centroid_to_upleft_butright``. + + Parameters + ------------ + coord : list of 4 int/float + One coordinate. + + Returns + ------- + list of 4 numbers + New bounding box. + + """ + if len(coord) != 4: + raise AssertionError("coordinate should be 4 values : [x1, y1, x2, y2]") + x1, y1, x2, y2 = coord + w = x2 - x1 + h = y2 - y1 + x_c = x1 + w / 2. + y_c = y1 + h / 2. + return [x_c, y_c, w, h] + + +def obj_box_coord_centroid_to_upleft(coord): + """Convert one coordinate [x_center, y_center, w, h] to [x, y, w, h]. + It is the reverse process of ``obj_box_coord_upleft_to_centroid``. + + Parameters + ------------ + coord : list of 4 int/float + One coordinate. + + Returns + ------- + list of 4 numbers + New bounding box. + + """ + if len(coord) != 4: + raise AssertionError("coordinate should be 4 values : [x, y, w, h]") + + x_center, y_center, w, h = coord + x = x_center - w / 2. + y = y_center - h / 2. + return [x, y, w, h] + + +def obj_box_coord_upleft_to_centroid(coord): + """Convert one coordinate [x, y, w, h] to [x_center, y_center, w, h]. + It is the reverse process of ``obj_box_coord_centroid_to_upleft``. + + Parameters + ------------ + coord : list of 4 int/float + One coordinate. + + Returns + ------- + list of 4 numbers + New bounding box. + + """ + if len(coord) != 4: + raise AssertionError("coordinate should be 4 values : [x, y, w, h]") + + x, y, w, h = coord + x_center = x + w / 2. + y_center = y + h / 2. + return [x_center, y_center, w, h] + + +def parse_darknet_ann_str_to_list(annotations): + r"""Input string format of class, x, y, w, h, return list of list format. + + Parameters + ----------- + annotations : str + The annotations in darkent format "class, x, y, w, h ...." seperated by "\\n". + + Returns + ------- + list of list of 4 numbers + List of bounding box. + + """ + annotations = annotations.split("\n") + ann = [] + for a in annotations: + a = a.split() + if len(a) == 5: + for i, _v in enumerate(a): + if i == 0: + a[i] = int(a[i]) + else: + a[i] = float(a[i]) + ann.append(a) + return ann + + +def parse_darknet_ann_list_to_cls_box(annotations): + """Parse darknet annotation format into two lists for class and bounding box. + + Input list of [[class, x, y, w, h], ...], return two list of [class ...] and [[x, y, w, h], ...]. + + Parameters + ------------ + annotations : list of list + A list of class and bounding boxes of images e.g. [[class, x, y, w, h], ...] + + Returns + ------- + list of int + List of class labels. + + list of list of 4 numbers + List of bounding box. + + """ + class_list = [] + bbox_list = [] + for ann in annotations: + class_list.append(ann[0]) + bbox_list.append(ann[1:]) + return class_list, bbox_list + + +def obj_box_horizontal_flip(im, coords=None, is_rescale=False, is_center=False, is_random=False): + """Left-right flip the image and coordinates for object detection. + + Parameters + ---------- + im : numpy.array + An image with dimension of [row, col, channel] (default). + coords : list of list of 4 int/float or None + Coordinates [[x, y, w, h], [x, y, w, h], ...]. + is_rescale : boolean + Set to True, if the input coordinates are rescaled to [0, 1]. Default is False. + is_center : boolean + Set to True, if the x and y of coordinates are the centroid (i.e. darknet format). Default is False. + is_random : boolean + If True, randomly flip. Default is False. + + Returns + ------- + numpy.array + A processed image + list of list of 4 numbers + A list of new bounding boxes. + + Examples + -------- + >>> im = np.zeros([80, 100]) # as an image with shape width=100, height=80 + >>> im, coords = obj_box_left_right_flip(im, coords=[[0.2, 0.4, 0.3, 0.3], [0.1, 0.5, 0.2, 0.3]], is_rescale=True, is_center=True, is_random=False) + >>> print(coords) + [[0.8, 0.4, 0.3, 0.3], [0.9, 0.5, 0.2, 0.3]] + >>> im, coords = obj_box_left_right_flip(im, coords=[[0.2, 0.4, 0.3, 0.3]], is_rescale=True, is_center=False, is_random=False) + >>> print(coords) + [[0.5, 0.4, 0.3, 0.3]] + >>> im, coords = obj_box_left_right_flip(im, coords=[[20, 40, 30, 30]], is_rescale=False, is_center=True, is_random=False) + >>> print(coords) + [[80, 40, 30, 30]] + >>> im, coords = obj_box_left_right_flip(im, coords=[[20, 40, 30, 30]], is_rescale=False, is_center=False, is_random=False) + >>> print(coords) + [[50, 40, 30, 30]] + + """ + if coords is None: + coords = [] + + def _flip(im, coords): + im = flip_axis(im, axis=1, is_random=False) + coords_new = list() + + for coord in coords: + + if len(coord) != 4: + raise AssertionError("coordinate should be 4 values : [x, y, w, h]") + + if is_rescale: + if is_center: + # x_center' = 1 - x + x = 1. - coord[0] + else: + # x_center' = 1 - x - w + x = 1. - coord[0] - coord[2] + else: + if is_center: + # x' = im.width - x + x = im.shape[1] - coord[0] + else: + # x' = im.width - x - w + x = im.shape[1] - coord[0] - coord[2] + coords_new.append([x, coord[1], coord[2], coord[3]]) + return im, coords_new + + if is_random: + factor = np.random.uniform(-1, 1) + if factor > 0: + return _flip(im, coords) + else: + return im, coords + else: + return _flip(im, coords) + + +obj_box_left_right_flip = obj_box_horizontal_flip + +# im = np.zeros([80, 100]) # as an image with shape width=100, height=80 +# im, coords = obj_box_left_right_flip(im, coords=[[0.2, 0.4, 0.3, 0.3], [0.1, 0.5, 0.2, 0.3]], is_rescale=True, is_center=True, is_random=False) +# tl.logging.info(coords) +# # [[0.8, 0.4, 0.3, 0.3], [0.9, 0.5, 0.2, 0.3]] +# im, coords = obj_box_left_right_flip(im, coords=[[0.2, 0.4, 0.3, 0.3]], is_rescale=True, is_center=False, is_random=False) +# tl.logging.info(coords) +# # [[0.5, 0.4, 0.3, 0.3]] +# im, coords = obj_box_left_right_flip(im, coords=[[20, 40, 30, 30]], is_rescale=False, is_center=True, is_random=False) +# tl.logging.info(coords) +# # [[80, 40, 30, 30]] +# im, coords = obj_box_left_right_flip(im, coords=[[20, 40, 30, 30]], is_rescale=False, is_center=False, is_random=False) +# tl.logging.info(coords) +# # [[50, 40, 30, 30]] +# exit() + + +def obj_box_imresize(im, coords=None, size=None, interp='bicubic', mode=None, is_rescale=False): + """Resize an image, and compute the new bounding box coordinates. + + Parameters + ------------- + im : numpy.array + An image with dimension of [row, col, channel] (default). + coords : list of list of 4 int/float or None + Coordinates [[x, y, w, h], [x, y, w, h], ...] + size interp and mode : args + See ``tl.prepro.imresize``. + is_rescale : boolean + Set to True, if the input coordinates are rescaled to [0, 1], then return the original coordinates. Default is False. + + Returns + ------- + numpy.array + A processed image + list of list of 4 numbers + A list of new bounding boxes. + + Examples + -------- + >>> im = np.zeros([80, 100, 3]) # as an image with shape width=100, height=80 + >>> _, coords = obj_box_imresize(im, coords=[[20, 40, 30, 30], [10, 20, 20, 20]], size=[160, 200], is_rescale=False) + >>> print(coords) + [[40, 80, 60, 60], [20, 40, 40, 40]] + >>> _, coords = obj_box_imresize(im, coords=[[20, 40, 30, 30]], size=[40, 100], is_rescale=False) + >>> print(coords) + [[20, 20, 30, 15]] + >>> _, coords = obj_box_imresize(im, coords=[[20, 40, 30, 30]], size=[60, 150], is_rescale=False) + >>> print(coords) + [[30, 30, 45, 22]] + >>> im2, coords = obj_box_imresize(im, coords=[[0.2, 0.4, 0.3, 0.3]], size=[160, 200], is_rescale=True) + >>> print(coords, im2.shape) + [[0.2, 0.4, 0.3, 0.3]] (160, 200, 3) + + """ + if coords is None: + coords = [] + if size is None: + size = [100, 100] + + imh, imw = im.shape[0:2] + imh = imh * 1.0 # * 1.0 for python2 : force division to be float point + imw = imw * 1.0 + im = imresize(im, size=size, interp=interp, mode=mode) + + if is_rescale is False: + coords_new = list() + + for coord in coords: + + if len(coord) != 4: + raise AssertionError("coordinate should be 4 values : [x, y, w, h]") + + # x' = x * (imw'/imw) + x = int(coord[0] * (size[1] / imw)) + # y' = y * (imh'/imh) + # tl.logging.info('>>', coord[1], size[0], imh) + y = int(coord[1] * (size[0] / imh)) + # w' = w * (imw'/imw) + w = int(coord[2] * (size[1] / imw)) + # h' = h * (imh'/imh) + h = int(coord[3] * (size[0] / imh)) + coords_new.append([x, y, w, h]) + return im, coords_new + else: + return im, coords + + +# im = np.zeros([80, 100, 3]) # as an image with shape width=100, height=80 +# _, coords = obj_box_imresize(im, coords=[[20, 40, 30, 30], [10, 20, 20, 20]], size=[160, 200], is_rescale=False) +# tl.logging.info(coords) +# # [[40, 80, 60, 60], [20, 40, 40, 40]] +# _, coords = obj_box_imresize(im, coords=[[20, 40, 30, 30]], size=[40, 100], is_rescale=False) +# tl.logging.info(coords) +# # [20, 20, 30, 15] +# _, coords = obj_box_imresize(im, coords=[[20, 40, 30, 30]], size=[60, 150], is_rescale=False) +# tl.logging.info(coords) +# # [30, 30, 45, 22] +# im2, coords = obj_box_imresize(im, coords=[[0.2, 0.4, 0.3, 0.3]], size=[160, 200], is_rescale=True) +# tl.logging.info(coords, im2.shape) +# # [0.2, 0.4, 0.3, 0.3] (160, 200, 3) +# exit() + + +def obj_box_crop( + im, classes=None, coords=None, wrg=100, hrg=100, is_rescale=False, is_center=False, is_random=False, thresh_wh=0.02, + thresh_wh2=12. +): + """Randomly or centrally crop an image, and compute the new bounding box coordinates. + Objects outside the cropped image will be removed. + + Parameters + ----------- + im : numpy.array + An image with dimension of [row, col, channel] (default). + classes : list of int or None + Class IDs. + coords : list of list of 4 int/float or None + Coordinates [[x, y, w, h], [x, y, w, h], ...] + wrg hrg and is_random : args + See ``tl.prepro.crop``. + is_rescale : boolean + Set to True, if the input coordinates are rescaled to [0, 1]. Default is False. + is_center : boolean, default False + Set to True, if the x and y of coordinates are the centroid (i.e. darknet format). Default is False. + thresh_wh : float + Threshold, remove the box if its ratio of width(height) to image size less than the threshold. + thresh_wh2 : float + Threshold, remove the box if its ratio of width to height or vice verse higher than the threshold. + + Returns + ------- + numpy.array + A processed image + list of int + A list of classes + list of list of 4 numbers + A list of new bounding boxes. + + """ + if classes is None: + classes = [] + if coords is None: + coords = [] + + h, w = im.shape[0], im.shape[1] + + if (h <= hrg) or (w <= wrg): + raise AssertionError("The size of cropping should smaller than the original image") + + if is_random: + h_offset = int(np.random.uniform(0, h - hrg) - 1) + w_offset = int(np.random.uniform(0, w - wrg) - 1) + h_end = hrg + h_offset + w_end = wrg + w_offset + im_new = im[h_offset:h_end, w_offset:w_end] + else: # central crop + h_offset = int(np.floor((h - hrg) / 2.)) + w_offset = int(np.floor((w - wrg) / 2.)) + h_end = h_offset + hrg + w_end = w_offset + wrg + im_new = im[h_offset:h_end, w_offset:w_end] + + # w + # _____________________________ + # | h/w offset | + # | ------- | + # h | | | | + # | | | | + # | ------- | + # | h/w end | + # |___________________________| + + def _get_coord(coord): + """Input pixel-unit [x, y, w, h] format, then make sure [x, y] it is the up-left coordinates, + before getting the new coordinates. + Boxes outsides the cropped image will be removed. + + """ + if is_center: + coord = obj_box_coord_centroid_to_upleft(coord) + + ##======= pixel unit format and upleft, w, h ==========## + + # x = np.clip( coord[0] - w_offset, 0, w_end - w_offset) + # y = np.clip( coord[1] - h_offset, 0, h_end - h_offset) + # w = np.clip( coord[2] , 0, w_end - w_offset) + # h = np.clip( coord[3] , 0, h_end - h_offset) + + x = coord[0] - w_offset + y = coord[1] - h_offset + w = coord[2] + h = coord[3] + + if x < 0: + if x + w <= 0: + return None + w = w + x + x = 0 + elif x > im_new.shape[1]: # object outside the cropped image + return None + + if y < 0: + if y + h <= 0: + return None + h = h + y + y = 0 + elif y > im_new.shape[0]: # object outside the cropped image + return None + + if (x is not None) and (x + w > im_new.shape[1]): # box outside the cropped image + w = im_new.shape[1] - x + + if (y is not None) and (y + h > im_new.shape[0]): # box outside the cropped image + h = im_new.shape[0] - y + + if (w / (h + 1.) > thresh_wh2) or (h / (w + 1.) > thresh_wh2): # object shape strange: too narrow + # tl.logging.info('xx', w, h) + return None + + if (w / (im_new.shape[1] * 1.) < thresh_wh) or (h / (im_new.shape[0] * 1.) < + thresh_wh): # object shape strange: too narrow + # tl.logging.info('yy', w, im_new.shape[1], h, im_new.shape[0]) + return None + + coord = [x, y, w, h] + + ## convert back if input format is center. + if is_center: + coord = obj_box_coord_upleft_to_centroid(coord) + + return coord + + coords_new = list() + classes_new = list() + for i, _ in enumerate(coords): + coord = coords[i] + + if len(coord) != 4: + raise AssertionError("coordinate should be 4 values : [x, y, w, h]") + + if is_rescale: + # for scaled coord, upscaled before process and scale back in the end. + coord = obj_box_coord_scale_to_pixelunit(coord, im.shape) + coord = _get_coord(coord) + if coord is not None: + coord = obj_box_coord_rescale(coord, im_new.shape) + coords_new.append(coord) + classes_new.append(classes[i]) + else: + coord = _get_coord(coord) + if coord is not None: + coords_new.append(coord) + classes_new.append(classes[i]) + return im_new, classes_new, coords_new + + +def obj_box_shift( + im, classes=None, coords=None, wrg=0.1, hrg=0.1, row_index=0, col_index=1, channel_index=2, fill_mode='nearest', + cval=0., order=1, is_rescale=False, is_center=False, is_random=False, thresh_wh=0.02, thresh_wh2=12. +): + """Shift an image randomly or non-randomly, and compute the new bounding box coordinates. + Objects outside the cropped image will be removed. + + Parameters + ----------- + im : numpy.array + An image with dimension of [row, col, channel] (default). + classes : list of int or None + Class IDs. + coords : list of list of 4 int/float or None + Coordinates [[x, y, w, h], [x, y, w, h], ...] + wrg, hrg row_index col_index channel_index is_random fill_mode cval and order : see ``tl.prepro.shift``. + is_rescale : boolean + Set to True, if the input coordinates are rescaled to [0, 1]. Default is False. + is_center : boolean + Set to True, if the x and y of coordinates are the centroid (i.e. darknet format). Default is False. + thresh_wh : float + Threshold, remove the box if its ratio of width(height) to image size less than the threshold. + thresh_wh2 : float + Threshold, remove the box if its ratio of width to height or vice verse higher than the threshold. + + + Returns + ------- + numpy.array + A processed image + list of int + A list of classes + list of list of 4 numbers + A list of new bounding boxes. + + """ + if classes is None: + classes = [] + if coords is None: + coords = [] + + imh, imw = im.shape[row_index], im.shape[col_index] + + if (hrg >= 1.0) and (hrg <= 0.) and (wrg >= 1.0) and (wrg <= 0.): + raise AssertionError("shift range should be (0, 1)") + + if is_random: + tx = np.random.uniform(-hrg, hrg) * imh + ty = np.random.uniform(-wrg, wrg) * imw + else: + tx, ty = hrg * imh, wrg * imw + translation_matrix = np.array([[1, 0, tx], [0, 1, ty], [0, 0, 1]]) + + transform_matrix = translation_matrix # no need to do offset + im_new = affine_transform(im, transform_matrix, channel_index, fill_mode, cval, order) + + # modified from obj_box_crop + def _get_coord(coord): + """Input pixel-unit [x, y, w, h] format, then make sure [x, y] it is the up-left coordinates, + before getting the new coordinates. + Boxes outsides the cropped image will be removed. + + """ + if is_center: + coord = obj_box_coord_centroid_to_upleft(coord) + + ##======= pixel unit format and upleft, w, h ==========## + x = coord[0] - ty # only change this + y = coord[1] - tx # only change this + w = coord[2] + h = coord[3] + + if x < 0: + if x + w <= 0: + return None + w = w + x + x = 0 + elif x > im_new.shape[1]: # object outside the cropped image + return None + + if y < 0: + if y + h <= 0: + return None + h = h + y + y = 0 + elif y > im_new.shape[0]: # object outside the cropped image + return None + + if (x is not None) and (x + w > im_new.shape[1]): # box outside the cropped image + w = im_new.shape[1] - x + + if (y is not None) and (y + h > im_new.shape[0]): # box outside the cropped image + h = im_new.shape[0] - y + + if (w / (h + 1.) > thresh_wh2) or (h / (w + 1.) > thresh_wh2): # object shape strange: too narrow + # tl.logging.info('xx', w, h) + return None + + if (w / (im_new.shape[1] * 1.) < thresh_wh) or (h / (im_new.shape[0] * 1.) < + thresh_wh): # object shape strange: too narrow + # tl.logging.info('yy', w, im_new.shape[1], h, im_new.shape[0]) + return None + + coord = [x, y, w, h] + + ## convert back if input format is center. + if is_center: + coord = obj_box_coord_upleft_to_centroid(coord) + + return coord + + coords_new = list() + classes_new = list() + for i, _ in enumerate(coords): + coord = coords[i] + + if len(coord) != 4: + raise AssertionError("coordinate should be 4 values : [x, y, w, h]") + + if is_rescale: + # for scaled coord, upscaled before process and scale back in the end. + coord = obj_box_coord_scale_to_pixelunit(coord, im.shape) + coord = _get_coord(coord) + if coord is not None: + coord = obj_box_coord_rescale(coord, im_new.shape) + coords_new.append(coord) + classes_new.append(classes[i]) + else: + coord = _get_coord(coord) + if coord is not None: + coords_new.append(coord) + classes_new.append(classes[i]) + return im_new, classes_new, coords_new + + +def obj_box_zoom( + im, classes=None, coords=None, zoom_range=(0.9, 1.1), row_index=0, col_index=1, channel_index=2, + fill_mode='nearest', cval=0., order=1, is_rescale=False, is_center=False, is_random=False, thresh_wh=0.02, + thresh_wh2=12. +): + """Zoom in and out of a single image, randomly or non-randomly, and compute the new bounding box coordinates. + Objects outside the cropped image will be removed. + + Parameters + ----------- + im : numpy.array + An image with dimension of [row, col, channel] (default). + classes : list of int or None + Class IDs. + coords : list of list of 4 int/float or None + Coordinates [[x, y, w, h], [x, y, w, h], ...]. + zoom_range row_index col_index channel_index is_random fill_mode cval and order : see ``tl.prepro.zoom``. + is_rescale : boolean + Set to True, if the input coordinates are rescaled to [0, 1]. Default is False. + is_center : boolean + Set to True, if the x and y of coordinates are the centroid. (i.e. darknet format). Default is False. + thresh_wh : float + Threshold, remove the box if its ratio of width(height) to image size less than the threshold. + thresh_wh2 : float + Threshold, remove the box if its ratio of width to height or vice verse higher than the threshold. + + Returns + ------- + numpy.array + A processed image + list of int + A list of classes + list of list of 4 numbers + A list of new bounding boxes. + + """ + if classes is None: + classes = [] + if coords is None: + coords = [] + + if len(zoom_range) != 2: + raise Exception('zoom_range should be a tuple or list of two floats. ' 'Received arg: ', zoom_range) + if is_random: + if zoom_range[0] == 1 and zoom_range[1] == 1: + zx, zy = 1, 1 + tl.logging.info(" random_zoom : not zoom in/out") + else: + zx, zy = np.random.uniform(zoom_range[0], zoom_range[1], 2) + else: + zx, zy = zoom_range + # tl.logging.info(zx, zy) + zoom_matrix = np.array([[zx, 0, 0], [0, zy, 0], [0, 0, 1]]) + + h, w = im.shape[row_index], im.shape[col_index] + transform_matrix = transform_matrix_offset_center(zoom_matrix, h, w) + im_new = affine_transform(im, transform_matrix, channel_index, fill_mode, cval, order) + + # modified from obj_box_crop + def _get_coord(coord): + """Input pixel-unit [x, y, w, h] format, then make sure [x, y] it is the up-left coordinates, + before getting the new coordinates. + Boxes outsides the cropped image will be removed. + + """ + if is_center: + coord = obj_box_coord_centroid_to_upleft(coord) + + # ======= pixel unit format and upleft, w, h ========== + x = (coord[0] - im.shape[1] / 2) / zy + im.shape[1] / 2 # only change this + y = (coord[1] - im.shape[0] / 2) / zx + im.shape[0] / 2 # only change this + w = coord[2] / zy # only change this + h = coord[3] / zx # only change thisS + + if x < 0: + if x + w <= 0: + return None + w = w + x + x = 0 + elif x > im_new.shape[1]: # object outside the cropped image + return None + + if y < 0: + if y + h <= 0: + return None + h = h + y + y = 0 + elif y > im_new.shape[0]: # object outside the cropped image + return None + + if (x is not None) and (x + w > im_new.shape[1]): # box outside the cropped image + w = im_new.shape[1] - x + + if (y is not None) and (y + h > im_new.shape[0]): # box outside the cropped image + h = im_new.shape[0] - y + + if (w / (h + 1.) > thresh_wh2) or (h / (w + 1.) > thresh_wh2): # object shape strange: too narrow + # tl.logging.info('xx', w, h) + return None + + if (w / (im_new.shape[1] * 1.) < thresh_wh) or (h / (im_new.shape[0] * 1.) < + thresh_wh): # object shape strange: too narrow + # tl.logging.info('yy', w, im_new.shape[1], h, im_new.shape[0]) + return None + + coord = [x, y, w, h] + + # convert back if input format is center. + if is_center: + coord = obj_box_coord_upleft_to_centroid(coord) + + return coord + + coords_new = list() + classes_new = list() + for i, _ in enumerate(coords): + coord = coords[i] + + if len(coord) != 4: + raise AssertionError("coordinate should be 4 values : [x, y, w, h]") + + if is_rescale: + # for scaled coord, upscaled before process and scale back in the end. + coord = obj_box_coord_scale_to_pixelunit(coord, im.shape) + coord = _get_coord(coord) + if coord is not None: + coord = obj_box_coord_rescale(coord, im_new.shape) + coords_new.append(coord) + classes_new.append(classes[i]) + else: + coord = _get_coord(coord) + if coord is not None: + coords_new.append(coord) + classes_new.append(classes[i]) + return im_new, classes_new, coords_new + + +def pad_sequences(sequences, maxlen=None, dtype='int32', padding='post', truncating='pre', value=0.): + """Pads each sequence to the same length: + the length of the longest sequence. + If maxlen is provided, any sequence longer + than maxlen is truncated to maxlen. + Truncation happens off either the beginning (default) or + the end of the sequence. + Supports post-padding and pre-padding (default). + + Parameters + ---------- + sequences : list of list of int + All sequences where each row is a sequence. + maxlen : int + Maximum length. + dtype : numpy.dtype or str + Data type to cast the resulting sequence. + padding : str + Either 'pre' or 'post', pad either before or after each sequence. + truncating : str + Either 'pre' or 'post', remove values from sequences larger than maxlen either in the beginning or in the end of the sequence + value : float + Value to pad the sequences to the desired value. + + Returns + ---------- + x : numpy.array + With dimensions (number_of_sequences, maxlen) + + Examples + ---------- + >>> sequences = [[1,1,1,1,1],[2,2,2],[3,3]] + >>> sequences = pad_sequences(sequences, maxlen=None, dtype='int32', + ... padding='post', truncating='pre', value=0.) + [[1 1 1 1 1] + [2 2 2 0 0] + [3 3 0 0 0]] + + """ + lengths = [len(s) for s in sequences] + + nb_samples = len(sequences) + if maxlen is None: + maxlen = np.max(lengths) + + # take the sample shape from the first non empty sequence + # checking for consistency in the main loop below. + sample_shape = tuple() + for s in sequences: + if len(s) > 0: + sample_shape = np.asarray(s).shape[1:] + break + + x = (np.ones((nb_samples, maxlen) + sample_shape) * value).astype(dtype) + for idx, s in enumerate(sequences): + if len(s) == 0: + continue # empty list was found + if truncating == 'pre': + trunc = s[-maxlen:] + elif truncating == 'post': + trunc = s[:maxlen] + else: + raise ValueError('Truncating type "%s" not understood' % truncating) + + # check `trunc` has expected shape + trunc = np.asarray(trunc, dtype=dtype) + if trunc.shape[1:] != sample_shape: + raise ValueError( + 'Shape of sample %s of sequence at position %s is different from expected shape %s' % + (trunc.shape[1:], idx, sample_shape) + ) + + if padding == 'post': + x[idx, :len(trunc)] = trunc + elif padding == 'pre': + x[idx, -len(trunc):] = trunc + else: + raise ValueError('Padding type "%s" not understood' % padding) + return x.tolist() + + +def remove_pad_sequences(sequences, pad_id=0): + """Remove padding. + + Parameters + ----------- + sequences : list of list of int + All sequences where each row is a sequence. + pad_id : int + The pad ID. + + Returns + ---------- + list of list of int + The processed sequences. + + Examples + ---------- + >>> sequences = [[2,3,4,0,0], [5,1,2,3,4,0,0,0], [4,5,0,2,4,0,0,0]] + >>> print(remove_pad_sequences(sequences, pad_id=0)) + [[2, 3, 4], [5, 1, 2, 3, 4], [4, 5, 0, 2, 4]] + + """ + sequences_out = copy.deepcopy(sequences) + + for i, _ in enumerate(sequences): + # for j in range(len(sequences[i])): + # if sequences[i][j] == pad_id: + # sequences_out[i] = sequences_out[i][:j] + # break + for j in range(1, len(sequences[i])): + if sequences[i][-j] != pad_id: + sequences_out[i] = sequences_out[i][0:-j + 1] + break + + return sequences_out + + +def process_sequences(sequences, end_id=0, pad_val=0, is_shorten=True, remain_end_id=False): + """Set all tokens(ids) after END token to the padding value, and then shorten (option) it to the maximum sequence length in this batch. + + Parameters + ----------- + sequences : list of list of int + All sequences where each row is a sequence. + end_id : int + The special token for END. + pad_val : int + Replace the `end_id` and the IDs after `end_id` to this value. + is_shorten : boolean + Shorten the sequences. Default is True. + remain_end_id : boolean + Keep an `end_id` in the end. Default is False. + + Returns + ---------- + list of list of int + The processed sequences. + + Examples + --------- + >>> sentences_ids = [[4, 3, 5, 3, 2, 2, 2, 2], <-- end_id is 2 + ... [5, 3, 9, 4, 9, 2, 2, 3]] <-- end_id is 2 + >>> sentences_ids = precess_sequences(sentences_ids, end_id=vocab.end_id, pad_val=0, is_shorten=True) + [[4, 3, 5, 3, 0], [5, 3, 9, 4, 9]] + + """ + max_length = 0 + for _, seq in enumerate(sequences): + is_end = False + for i_w, n in enumerate(seq): + if n == end_id and is_end == False: # 1st time to see end_id + is_end = True + if max_length < i_w: + max_length = i_w + if remain_end_id is False: + seq[i_w] = pad_val # set end_id to pad_val + elif is_end ==True: + seq[i_w] = pad_val + + if remain_end_id is True: + max_length += 1 + if is_shorten: + for i, seq in enumerate(sequences): + sequences[i] = seq[:max_length] + return sequences + + +def sequences_add_start_id(sequences, start_id=0, remove_last=False): + """Add special start token(id) in the beginning of each sequence. + + Parameters + ------------ + sequences : list of list of int + All sequences where each row is a sequence. + start_id : int + The start ID. + remove_last : boolean + Remove the last value of each sequences. Usually be used for removing the end ID. + + Returns + ---------- + list of list of int + The processed sequences. + + Examples + --------- + >>> sentences_ids = [[4,3,5,3,2,2,2,2], [5,3,9,4,9,2,2,3]] + >>> sentences_ids = sequences_add_start_id(sentences_ids, start_id=2) + [[2, 4, 3, 5, 3, 2, 2, 2, 2], [2, 5, 3, 9, 4, 9, 2, 2, 3]] + >>> sentences_ids = sequences_add_start_id(sentences_ids, start_id=2, remove_last=True) + [[2, 4, 3, 5, 3, 2, 2, 2], [2, 5, 3, 9, 4, 9, 2, 2]] + + For Seq2seq + + >>> input = [a, b, c] + >>> target = [x, y, z] + >>> decode_seq = [start_id, a, b] <-- sequences_add_start_id(input, start_id, True) + + """ + sequences_out = [[] for _ in range(len(sequences))] #[[]] * len(sequences) + for i, _ in enumerate(sequences): + if remove_last: + sequences_out[i] = [start_id] + sequences[i][:-1] + else: + sequences_out[i] = [start_id] + sequences[i] + return sequences_out + + +def sequences_add_end_id(sequences, end_id=888): + """Add special end token(id) in the end of each sequence. + + Parameters + ----------- + sequences : list of list of int + All sequences where each row is a sequence. + end_id : int + The end ID. + + Returns + ---------- + list of list of int + The processed sequences. + + Examples + --------- + >>> sequences = [[1,2,3],[4,5,6,7]] + >>> print(sequences_add_end_id(sequences, end_id=999)) + [[1, 2, 3, 999], [4, 5, 6, 999]] + + """ + sequences_out = [[] for _ in range(len(sequences))] #[[]] * len(sequences) + for i, _ in enumerate(sequences): + sequences_out[i] = sequences[i] + [end_id] + return sequences_out + + +def sequences_add_end_id_after_pad(sequences, end_id=888, pad_id=0): + """Add special end token(id) in the end of each sequence. + + Parameters + ----------- + sequences : list of list of int + All sequences where each row is a sequence. + end_id : int + The end ID. + pad_id : int + The pad ID. + + Returns + ---------- + list of list of int + The processed sequences. + + Examples + --------- + >>> sequences = [[1,2,0,0], [1,2,3,0], [1,2,3,4]] + >>> print(sequences_add_end_id_after_pad(sequences, end_id=99, pad_id=0)) + [[1, 2, 99, 0], [1, 2, 3, 99], [1, 2, 3, 4]] + + """ + # sequences_out = [[] for _ in range(len(sequences))]#[[]] * len(sequences) + + sequences_out = copy.deepcopy(sequences) + # # add a pad to all + # for i in range(len(sequences)): + # for j in range(len(sequences[i])): + # sequences_out[i].append(pad_id) + # # pad -- > end + # max_len = 0 + + for i, v in enumerate(sequences): + for j, _v2 in enumerate(v): + if sequences[i][j] == pad_id: + sequences_out[i][j] = end_id + # if j > max_len: + # max_len = j + break + + # # remove pad if too long + # for i in range(len(sequences)): + # for j in range(len(sequences[i])): + # sequences_out[i] = sequences_out[i][:max_len+1] + return sequences_out + + +def sequences_get_mask(sequences, pad_val=0): + """Return mask for sequences. + + Parameters + ----------- + sequences : list of list of int + All sequences where each row is a sequence. + pad_val : int + The pad value. + + Returns + ---------- + list of list of int + The mask. + + Examples + --------- + >>> sentences_ids = [[4, 0, 5, 3, 0, 0], + ... [5, 3, 9, 4, 9, 0]] + >>> mask = sequences_get_mask(sentences_ids, pad_val=0) + [[1 1 1 1 0 0] + [1 1 1 1 1 0]] + + """ + mask = np.ones_like(sequences) + for i, seq in enumerate(sequences): + for i_w in reversed(range(len(seq))): + if seq[i_w] == pad_val: + mask[i, i_w] = 0 + else: + break # <-- exit the for loop, prepcess next sequence + return mask + + +def keypoint_random_crop(image, annos, mask=None, size=(368, 368)): + """Randomly crop an image and corresponding keypoints without influence scales, given by ``keypoint_random_resize_shortestedge``. + + Parameters + ----------- + image : 3 channel image + The given image for augmentation. + annos : list of list of floats + The keypoints annotation of people. + mask : single channel image or None + The mask if available. + size : tuple of int + The size of returned image. + + Returns + ---------- + preprocessed image, annotation, mask + + """ + + _target_height = size[0] + _target_width = size[1] + target_size = (_target_width, _target_height) + + if len(np.shape(image)) == 2: + image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB) + height, width, _ = np.shape(image) + + for _ in range(50): + x = random.randrange(0, width - target_size[0]) if width > target_size[0] else 0 + y = random.randrange(0, height - target_size[1]) if height > target_size[1] else 0 + + # check whether any face is inside the box to generate a reasonably-balanced datasets + for joint in annos: + if x <= joint[0][0] < x + target_size[0] and y <= joint[0][1] < y + target_size[1]: + break + + def pose_crop(image, annos, mask, x, y, w, h): # TODO : speed up with affine transform + # adjust image + target_size = (w, h) + + img = image + resized = img[y:y + target_size[1], x:x + target_size[0], :] + resized_mask = mask[y:y + target_size[1], x:x + target_size[0]] + # adjust meta data + adjust_joint_list = [] + for joint in annos: + adjust_joint = [] + for point in joint: + if point[0] < -10 or point[1] < -10: + adjust_joint.append((-1000, -1000)) + continue + new_x, new_y = point[0] - x, point[1] - y + # should not crop outside the image + if new_x > w - 1 or new_y > h - 1: + adjust_joint.append((-1000, -1000)) + continue + adjust_joint.append((new_x, new_y)) + adjust_joint_list.append(adjust_joint) + + return resized, adjust_joint_list, resized_mask + + return pose_crop(image, annos, mask, x, y, target_size[0], target_size[1]) + + +def keypoint_resize_random_crop(image, annos, mask=None, size=(368, 368)): + """Reszie the image to make either its width or height equals to the given sizes. + Then randomly crop image without influence scales. + Resize the image match with the minimum size before cropping, this API will change the zoom scale of object. + + Parameters + ----------- + image : 3 channel image + The given image for augmentation. + annos : list of list of floats + The keypoints annotation of people. + mask : single channel image or None + The mask if available. + size : tuple of int + The size (height, width) of returned image. + + Returns + ---------- + preprocessed image, annos, mask + + """ + + if len(np.shape(image)) == 2: + image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB) + + def resize_image(image, annos, mask, target_width, target_height): + """Reszie image + + Parameters + ----------- + image : 3 channel image + The given image. + annos : list of list of floats + Keypoints of people + mask : single channel image or None + The mask if available. + target_width : int + Expected width of returned image. + target_height : int + Expected height of returned image. + + Returns + ---------- + preprocessed input image, annos, mask + + """ + y, x, _ = np.shape(image) + + ratio_y = target_height / y + ratio_x = target_width / x + + new_joints = [] + # update meta + for people in annos: + new_keypoints = [] + for keypoints in people: + if keypoints[0] < 0 or keypoints[1] < 0: + new_keypoints.append((-1000, -1000)) + continue + pts = (int(keypoints[0] * ratio_x + 0.5), int(keypoints[1] * ratio_y + 0.5)) + if pts[0] > target_width - 1 or pts[1] > target_height - 1: + new_keypoints.append((-1000, -1000)) + continue + + new_keypoints.append(pts) + new_joints.append(new_keypoints) + annos = new_joints + + new_image = cv2.resize(image, (target_width, target_height), interpolation=cv2.INTER_AREA) + if mask is not None: + new_mask = cv2.resize(mask, (target_width, target_height), interpolation=cv2.INTER_AREA) + return new_image, annos, new_mask + else: + return new_image, annos, None + + _target_height = size[0] + _target_width = size[1] + + if len(np.shape(image)) == 2: + image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB) + input_height, input_width, _ = np.shape(image) + + vertical_ratio = _target_height / input_height + horizontal_ratio = _target_width / input_width + + rescale_ratio = max(vertical_ratio, horizontal_ratio) + + image, annos, mask = resize_image( + image, annos, mask, round(input_width * rescale_ratio), round(input_height * rescale_ratio) + ) + + # At this point we should have input image which matches at least target + # height or target width, while the other dimensions larger than target. + new_height, new_width, _ = np.shape(image) + + if new_height > _target_height: + crop_range_y = np.random.randint(0, new_height - _target_height) + image = image[crop_range_y:crop_range_y + _target_height, :, :] + if mask is not None: + mask = mask[crop_range_y:crop_range_y + _target_height, :] + new_joints = [] + + for people in annos: # TODO : speed up with affine transform + new_keypoints = [] + for keypoints in people: + + # case orginal points are not usable + if keypoints[1] >= crop_range_y and keypoints[1] <= crop_range_y + _target_height - 1: + pts = (int(keypoints[0]), int(keypoints[1] - crop_range_y)) + else: + pts = (-1000, -1000) + new_keypoints.append(pts) + + new_joints.append(new_keypoints) + annos = new_joints + + elif new_width > _target_width: + crop_range_x = np.random.randint(0, new_width - _target_width) + image = image[:, crop_range_x:crop_range_x + _target_width, :] + if mask is not None: + mask = mask[:, crop_range_x:crop_range_x + _target_width] + new_joints = [] + + for people in annos: + new_keypoints = [] + for keypoints in people: + + # case orginal points are not usable + if keypoints[0] >= crop_range_x and keypoints[0] <= crop_range_x + _target_width - 1: + pts = (int(keypoints[0] - crop_range_x), int(keypoints[1])) + else: + pts = (-1000, -1000) + new_keypoints.append(pts) + + new_joints.append(new_keypoints) + annos = new_joints + + if mask is not None: + return image, annos, mask + else: + return image, annos, None + + +def keypoint_random_rotate(image, annos, mask=None, rg=15.): + """Rotate an image and corresponding keypoints. + + Parameters + ----------- + image : 3 channel image + The given image for augmentation. + annos : list of list of floats + The keypoints annotation of people. + mask : single channel image or None + The mask if available. + rg : int or float + Degree to rotate, usually 0 ~ 180. + + Returns + ---------- + preprocessed image, annos, mask + + """ + + def _rotate_coord(shape, newxy, point, angle): + angle = -1 * angle / 180.0 * math.pi + ox, oy = shape + px, py = point + ox /= 2 + oy /= 2 + qx = math.cos(angle) * (px - ox) - math.sin(angle) * (py - oy) + qy = math.sin(angle) * (px - ox) + math.cos(angle) * (py - oy) + new_x, new_y = newxy + qx += ox - new_x + qy += oy - new_y + return int(qx + 0.5), int(qy + 0.5) + + def _largest_rotated_rect(w, h, angle): + """ + Get largest rectangle after rotation. + http://stackoverflow.com/questions/16702966/rotate-image-and-crop-out-black-borders + """ + angle = angle / 180.0 * math.pi + if w <= 0 or h <= 0: + return 0, 0 + + width_is_longer = w >= h + side_long, side_short = (w, h) if width_is_longer else (h, w) + + # since the solutions for angle, -angle and 180-angle are all the same, + # if suffices to look at the first quadrant and the absolute values of sin,cos: + sin_a, cos_a = abs(math.sin(angle)), abs(math.cos(angle)) + if side_short <= 2. * sin_a * cos_a * side_long: + # half constrained case: two crop corners touch the longer side, + # the other two corners are on the mid-line parallel to the longer line + x = 0.5 * side_short + wr, hr = (x / sin_a, x / cos_a) if width_is_longer else (x / cos_a, x / sin_a) + else: + # fully constrained case: crop touches all 4 sides + cos_2a = cos_a * cos_a - sin_a * sin_a + wr, hr = (w * cos_a - h * sin_a) / cos_2a, (h * cos_a - w * sin_a) / cos_2a + return int(np.round(wr)), int(np.round(hr)) + + img_shape = np.shape(image) + height = img_shape[0] + width = img_shape[1] + deg = np.random.uniform(-rg, rg) + + img = image + center = (img.shape[1] * 0.5, img.shape[0] * 0.5) # x, y + rot_m = cv2.getRotationMatrix2D((int(center[0]), int(center[1])), deg, 1) + ret = cv2.warpAffine(img, rot_m, img.shape[1::-1], flags=cv2.INTER_AREA, borderMode=cv2.BORDER_CONSTANT) + if img.ndim == 3 and ret.ndim == 2: + ret = ret[:, :, np.newaxis] + neww, newh = _largest_rotated_rect(ret.shape[1], ret.shape[0], deg) + neww = min(neww, ret.shape[1]) + newh = min(newh, ret.shape[0]) + newx = int(center[0] - neww * 0.5) + newy = int(center[1] - newh * 0.5) + # print(ret.shape, deg, newx, newy, neww, newh) + img = ret[newy:newy + newh, newx:newx + neww] + # adjust meta data + adjust_joint_list = [] + for joint in annos: # TODO : speed up with affine transform + adjust_joint = [] + for point in joint: + if point[0] < -100 or point[1] < -100: + adjust_joint.append((-1000, -1000)) + continue + + x, y = _rotate_coord((width, height), (newx, newy), point, deg) + + if x > neww - 1 or y > newh - 1: + adjust_joint.append((-1000, -1000)) + continue + if x < 0 or y < 0: + adjust_joint.append((-1000, -1000)) + continue + + adjust_joint.append((x, y)) + adjust_joint_list.append(adjust_joint) + joint_list = adjust_joint_list + + if mask is not None: + msk = mask + center = (msk.shape[1] * 0.5, msk.shape[0] * 0.5) # x, y + rot_m = cv2.getRotationMatrix2D((int(center[0]), int(center[1])), deg, 1) + ret = cv2.warpAffine(msk, rot_m, msk.shape[1::-1], flags=cv2.INTER_AREA, borderMode=cv2.BORDER_CONSTANT) + if msk.ndim == 3 and msk.ndim == 2: + ret = ret[:, :, np.newaxis] + neww, newh = _largest_rotated_rect(ret.shape[1], ret.shape[0], deg) + neww = min(neww, ret.shape[1]) + newh = min(newh, ret.shape[0]) + newx = int(center[0] - neww * 0.5) + newy = int(center[1] - newh * 0.5) + # print(ret.shape, deg, newx, newy, neww, newh) + msk = ret[newy:newy + newh, newx:newx + neww] + return img, joint_list, msk + else: + return img, joint_list, None + + +def keypoint_random_flip( + image, annos, mask=None, prob=0.5, flip_list=(0, 1, 5, 6, 7, 2, 3, 4, 11, 12, 13, 8, 9, 10, 15, 14, 17, 16, 18) +): + """Flip an image and corresponding keypoints. + + Parameters + ----------- + image : 3 channel image + The given image for augmentation. + annos : list of list of floats + The keypoints annotation of people. + mask : single channel image or None + The mask if available. + prob : float, 0 to 1 + The probability to flip the image, if 1, always flip the image. + flip_list : tuple of int + Denotes how the keypoints number be changed after flipping which is required for pose estimation task. + The left and right body should be maintained rather than switch. + (Default COCO format). + Set to an empty tuple if you don't need to maintain left and right information. + + Returns + ---------- + preprocessed image, annos, mask + + """ + + _prob = np.random.uniform(0, 1.0) + if _prob < prob: + return image, annos, mask + + _, width, _ = np.shape(image) + image = cv2.flip(image, 1) + mask = cv2.flip(mask, 1) + new_joints = [] + for people in annos: # TODO : speed up with affine transform + new_keypoints = [] + for k in flip_list: + point = people[k] + if point[0] < 0 or point[1] < 0: + new_keypoints.append((-1000, -1000)) + continue + if point[0] > image.shape[1] - 1 or point[1] > image.shape[0] - 1: + new_keypoints.append((-1000, -1000)) + continue + if (width - point[0]) > image.shape[1] - 1: + new_keypoints.append((-1000, -1000)) + continue + new_keypoints.append((width - point[0], point[1])) + new_joints.append(new_keypoints) + annos = new_joints + + return image, annos, mask + + +def keypoint_random_resize(image, annos, mask=None, zoom_range=(0.8, 1.2)): + """Randomly resize an image and corresponding keypoints. + The height and width of image will be changed independently, so the scale will be changed. + + Parameters + ----------- + image : 3 channel image + The given image for augmentation. + annos : list of list of floats + The keypoints annotation of people. + mask : single channel image or None + The mask if available. + zoom_range : tuple of two floats + The minimum and maximum factor to zoom in or out, e.g (0.5, 1) means zoom out 1~2 times. + + Returns + ---------- + preprocessed image, annos, mask + + """ + height = image.shape[0] + width = image.shape[1] + _min, _max = zoom_range + scalew = np.random.uniform(_min, _max) + scaleh = np.random.uniform(_min, _max) + + neww = int(width * scalew) + newh = int(height * scaleh) + + dst = cv2.resize(image, (neww, newh), interpolation=cv2.INTER_AREA) + if mask is not None: + mask = cv2.resize(mask, (neww, newh), interpolation=cv2.INTER_AREA) + # adjust meta data + adjust_joint_list = [] + for joint in annos: # TODO : speed up with affine transform + adjust_joint = [] + for point in joint: + if point[0] < -100 or point[1] < -100: + adjust_joint.append((-1000, -1000)) + continue + adjust_joint.append((int(point[0] * scalew + 0.5), int(point[1] * scaleh + 0.5))) + adjust_joint_list.append(adjust_joint) + if mask is not None: + return dst, adjust_joint_list, mask + else: + return dst, adjust_joint_list, None + + +def keypoint_random_resize_shortestedge( + image, annos, mask=None, min_size=(368, 368), zoom_range=(0.8, 1.2), pad_val=(0, 0, np.random.uniform(0.0, 1.0)) +): + """Randomly resize an image and corresponding keypoints based on shorter edgeself. + If the resized image is smaller than `min_size`, uses padding to make shape matchs `min_size`. + The height and width of image will be changed together, the scale would not be changed. + + Parameters + ----------- + image : 3 channel image + The given image for augmentation. + annos : list of list of floats + The keypoints annotation of people. + mask : single channel image or None + The mask if available. + min_size : tuple of two int + The minimum size of height and width. + zoom_range : tuple of two floats + The minimum and maximum factor to zoom in or out, e.g (0.5, 1) means zoom out 1~2 times. + pad_val : int/float, or tuple of int or random function + The three padding values for RGB channels respectively. + + Returns + ---------- + preprocessed image, annos, mask + + """ + + _target_height = min_size[0] + _target_width = min_size[1] + + if len(np.shape(image)) == 2: + image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB) + height, width, _ = np.shape(image) + + ratio_w = _target_width / width + ratio_h = _target_height / height + ratio = min(ratio_w, ratio_h) + target_size = int(min(width * ratio + 0.5, height * ratio + 0.5)) + random_target = np.random.uniform(zoom_range[0], zoom_range[1]) + target_size = int(target_size * random_target) + + # target_size = int(min(_network_w, _network_h) * random.uniform(0.7, 1.5)) + + def pose_resize_shortestedge(image, annos, mask, target_size): + """ """ + # _target_height = 368 + # _target_width = 368 + # img = image + height, width, _ = np.shape(image) + + # adjust image + scale = target_size / min(height, width) + if height < width: + newh, neww = target_size, int(scale * width + 0.5) + else: + newh, neww = int(scale * height + 0.5), target_size + + dst = cv2.resize(image, (neww, newh), interpolation=cv2.INTER_AREA) + mask = cv2.resize(mask, (neww, newh), interpolation=cv2.INTER_AREA) + pw = ph = 0 + if neww < _target_width or newh < _target_height: + pw = max(0, (_target_width - neww) // 2) + ph = max(0, (_target_height - newh) // 2) + mw = (_target_width - neww) % 2 + mh = (_target_height - newh) % 2 + # color = np.random.uniform(0.0, 1.0) + dst = cv2.copyMakeBorder(dst, ph, ph + mh, pw, pw + mw, cv2.BORDER_CONSTANT, value=pad_val) #(0, 0, color)) + if mask is not None: + mask = cv2.copyMakeBorder(mask, ph, ph + mh, pw, pw + mw, cv2.BORDER_CONSTANT, value=1) + # adjust meta data + adjust_joint_list = [] + for joint in annos: # TODO : speed up with affine transform + adjust_joint = [] + for point in joint: + if point[0] < -100 or point[1] < -100: + adjust_joint.append((-1000, -1000)) + continue + # if point[0] <= 0 or point[1] <= 0 or int(point[0]*scale+0.5) > neww or int(point[1]*scale+0.5) > newh: + # adjust_joint.append((-1, -1)) + # continue + adjust_joint.append((int(point[0] * scale + 0.5) + pw, int(point[1] * scale + 0.5) + ph)) + adjust_joint_list.append(adjust_joint) + if mask is not None: + return dst, adjust_joint_list, mask + else: + return dst, adjust_joint_list, None + + return pose_resize_shortestedge(image, annos, mask, target_size) diff --git a/tensorlayer/rein.py b/tensorlayer/rein.py new file mode 100644 index 0000000..bd884d5 --- /dev/null +++ b/tensorlayer/rein.py @@ -0,0 +1,159 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import numpy as np +import tensorflow as tf +from six.moves import xrange + +__all__ = [ + 'discount_episode_rewards', + 'cross_entropy_reward_loss', + 'log_weight', + 'choice_action_by_probs', +] + + +def discount_episode_rewards(rewards=None, gamma=0.99, mode=0): + """Take 1D float array of rewards and compute discounted rewards for an + episode. When encount a non-zero value, consider as the end a of an episode. + + Parameters + ---------- + rewards : list + List of rewards + gamma : float + Discounted factor + mode : int + Mode for computing the discount rewards. + - If mode == 0, reset the discount process when encount a non-zero reward (Ping-pong game). + - If mode == 1, would not reset the discount process. + + Returns + -------- + list of float + The discounted rewards. + + Examples + ---------- + >>> rewards = np.asarray([0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1]) + >>> gamma = 0.9 + >>> discount_rewards = tl.rein.discount_episode_rewards(rewards, gamma) + >>> print(discount_rewards) + [ 0.72899997 0.81 0.89999998 1. 0.72899997 0.81 + 0.89999998 1. 0.72899997 0.81 0.89999998 1. ] + >>> discount_rewards = tl.rein.discount_episode_rewards(rewards, gamma, mode=1) + >>> print(discount_rewards) + [ 1.52110755 1.69011939 1.87791049 2.08656716 1.20729685 1.34144104 + 1.49048996 1.65610003 0.72899997 0.81 0.89999998 1. ] + + """ + if rewards is None: + raise Exception("rewards should be a list") + discounted_r = np.zeros_like(rewards, dtype=np.float32) + running_add = 0 + for t in reversed(xrange(0, rewards.size)): + if mode == 0: + if rewards[t] != 0: running_add = 0 + + running_add = running_add * gamma + rewards[t] + discounted_r[t] = running_add + return discounted_r + + +def cross_entropy_reward_loss(logits, actions, rewards, name=None): + """Calculate the loss for Policy Gradient Network. + + Parameters + ---------- + logits : tensor + The network outputs without softmax. This function implements softmax inside. + actions : tensor or placeholder + The agent actions. + rewards : tensor or placeholder + The rewards. + + Returns + -------- + Tensor + The TensorFlow loss function. + + Examples + ---------- + >>> states_batch_pl = tf.placeholder(tf.float32, shape=[None, D]) + >>> network = InputLayer(states_batch_pl, name='input') + >>> network = DenseLayer(network, n_units=H, act=tf.ops.relu, name='relu1') + >>> network = DenseLayer(network, n_units=3, name='out') + >>> probs = network.outputs + >>> sampling_prob = tf.ops.softmax(probs) + >>> actions_batch_pl = tf.placeholder(tf.int32, shape=[None]) + >>> discount_rewards_batch_pl = tf.placeholder(tf.float32, shape=[None]) + >>> loss = tl.rein.cross_entropy_reward_loss(probs, actions_batch_pl, discount_rewards_batch_pl) + >>> train_op = tf.train.RMSPropOptimizer(learning_rate, decay_rate).minimize(loss) + + """ + cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=actions, logits=logits, name=name) + + return tf.reduce_sum(tf.multiply(cross_entropy, rewards)) + + +def log_weight(probs, weights, name='log_weight'): + """Log weight. + + Parameters + ----------- + probs : tensor + If it is a network output, usually we should scale it to [0, 1] via softmax. + weights : tensor + The weights. + + Returns + -------- + Tensor + The Tensor after appling the log weighted expression. + + """ + with tf.variable_scope(name): + exp_v = tf.reduce_mean(tf.log(probs) * weights) + return exp_v + + +def choice_action_by_probs(probs=(0.5, 0.5), action_list=None): + """Choice and return an an action by given the action probability distribution. + + Parameters + ------------ + probs : list of float. + The probability distribution of all actions. + action_list : None or a list of int or others + A list of action in integer, string or others. If None, returns an integer range between 0 and len(probs)-1. + + Returns + -------- + float int or str + The chosen action. + + Examples + ---------- + >>> for _ in range(5): + >>> a = choice_action_by_probs([0.2, 0.4, 0.4]) + >>> print(a) + 0 + 1 + 1 + 2 + 1 + >>> for _ in range(3): + >>> a = choice_action_by_probs([0.5, 0.5], ['a', 'b']) + >>> print(a) + a + b + b + + """ + if action_list is None: + n_action = len(probs) + action_list = np.arange(n_action) + else: + if len(action_list) != len(probs): + raise Exception("number of actions should equal to number of probabilities.") + return np.random.choice(action_list, p=probs) diff --git a/tensorlayer/utils.py b/tensorlayer/utils.py new file mode 100644 index 0000000..508beb7 --- /dev/null +++ b/tensorlayer/utils.py @@ -0,0 +1,683 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import os +import random +import subprocess +import sys +import time +from collections import Counter +from sys import exit as _exit +from sys import platform as _platform + +import numpy as np +import tensorflow as tf +from sklearn.metrics import accuracy_score, confusion_matrix, f1_score + +import tensorlayer as tl + +__all__ = [ + 'fit', 'test', 'predict', 'evaluation', 'dict_to_one', 'flatten_list', 'class_balancing_oversample', + 'get_random_int', 'list_string_to_dict', 'exit_tensorflow', 'open_tensorboard', 'clear_all_placeholder_variables', + 'set_gpu_fraction', 'train_epoch', 'run_epoch' +] + + +def fit( + network, train_op, cost, X_train, y_train, acc=None, batch_size=100, n_epoch=100, print_freq=5, X_val=None, + y_val=None, eval_train=True, tensorboard_dir=None, tensorboard_epoch_freq=5, tensorboard_weight_histograms=True, + tensorboard_graph_vis=True +): + """Training a given non time-series network by the given cost function, training data, batch_size, n_epoch etc. + + - MNIST example click `here `_. + - In order to control the training details, the authors HIGHLY recommend ``tl.iterate`` see two MNIST examples `1 `_, `2 `_. + + Parameters + ---------- + network : TensorLayer Model + the network to be trained. + train_op : TensorFlow optimizer + The optimizer for training e.g. tf.optimizers.Adam(). + cost : TensorLayer or TensorFlow loss function + Metric for loss function, e.g tl.cost.cross_entropy. + X_train : numpy.array + The input of training data + y_train : numpy.array + The target of training data + acc : TensorFlow/numpy expression or None + Metric for accuracy or others. If None, would not print the information. + batch_size : int + The batch size for training and evaluating. + n_epoch : int + The number of training epochs. + print_freq : int + Print the training information every ``print_freq`` epochs. + X_val : numpy.array or None + The input of validation data. If None, would not perform validation. + y_val : numpy.array or None + The target of validation data. If None, would not perform validation. + eval_train : boolean + Whether to evaluate the model during training. + If X_val and y_val are not None, it reflects whether to evaluate the model on training data. + tensorboard_dir : string + path to log dir, if set, summary data will be stored to the tensorboard_dir/ directory for visualization with tensorboard. (default None) + tensorboard_epoch_freq : int + How many epochs between storing tensorboard checkpoint for visualization to log/ directory (default 5). + tensorboard_weight_histograms : boolean + If True updates tensorboard data in the logs/ directory for visualization + of the weight histograms every tensorboard_epoch_freq epoch (default True). + tensorboard_graph_vis : boolean + If True stores the graph in the tensorboard summaries saved to log/ (default True). + + Examples + -------- + See `tutorial_mnist_simple.py `_ + + >>> tl.utils.fit(network, train_op=tf.optimizers.Adam(learning_rate=0.0001), + ... cost=tl.cost.cross_entropy, X_train=X_train, y_train=y_train, acc=acc, + ... batch_size=64, n_epoch=20, _val=X_val, y_val=y_val, eval_train=True) + >>> tl.utils.fit(network, train_op, cost, X_train, y_train, + ... acc=acc, batch_size=500, n_epoch=200, print_freq=5, + ... X_val=X_val, y_val=y_val, eval_train=False, tensorboard=True) + + Notes + -------- + 'tensorboard_weight_histograms' and 'tensorboard_weight_histograms' are not supported now. + + """ + if X_train.shape[0] < batch_size: + raise AssertionError("Number of training examples should be bigger than the batch size") + + if tensorboard_dir is not None: + tl.logging.info("Setting up tensorboard ...") + #Set up tensorboard summaries and saver + tl.files.exists_or_mkdir(tensorboard_dir) + + #Only write summaries for more recent TensorFlow versions + if hasattr(tf, 'summary') and hasattr(tf.summary, 'create_file_writer'): + train_writer = tf.summary.create_file_writer(tensorboard_dir + '/train') + val_writer = tf.summary.create_file_writer(tensorboard_dir + '/validation') + if tensorboard_graph_vis: + # FIXME : not sure how to add tl network graph + pass + else: + train_writer = None + val_writer = None + + tl.logging.info("Finished! use `tensorboard --logdir=%s/` to start tensorboard" % tensorboard_dir) + + tl.logging.info("Start training the network ...") + start_time_begin = time.time() + for epoch in range(n_epoch): + start_time = time.time() + loss_ep, _, __ = train_epoch(network, X_train, y_train, cost=cost, train_op=train_op, batch_size=batch_size) + + train_loss, train_acc = None, None + val_loss, val_acc = None, None + if tensorboard_dir is not None and hasattr(tf, 'summary'): + if epoch + 1 == 1 or (epoch + 1) % tensorboard_epoch_freq == 0: + if eval_train is True: + train_loss, train_acc, _ = run_epoch( + network, X_train, y_train, cost=cost, acc=acc, batch_size=batch_size + ) + with train_writer.as_default(): + tf.compat.v2.summary.scalar('loss', train_loss, step=epoch) + if acc is not None: + tf.summary.scalar('acc', train_acc, step=epoch) + # FIXME : there seems to be an internal error in Tensorboard (misuse of tf.name_scope) + # if tensorboard_weight_histograms is not None: + # for param in network.all_weights: + # tf.summary.histogram(param.name, param, step=epoch) + + if (X_val is not None) and (y_val is not None): + val_loss, val_acc, _ = run_epoch(network, X_val, y_val, cost=cost, acc=acc, batch_size=batch_size) + with val_writer.as_default(): + tf.summary.scalar('loss', val_loss, step=epoch) + if acc is not None: + tf.summary.scalar('acc', val_acc, step=epoch) + # FIXME : there seems to be an internal error in Tensorboard (misuse of tf.name_scope) + # if tensorboard_weight_histograms is not None: + # for param in network.all_weights: + # tf.summary.histogram(param.name, param, step=epoch) + + if epoch + 1 == 1 or (epoch + 1) % print_freq == 0: + if (X_val is not None) and (y_val is not None): + tl.logging.info("Epoch %d of %d took %fs" % (epoch + 1, n_epoch, time.time() - start_time)) + if eval_train is True: + if train_loss is None: + train_loss, train_acc, _ = run_epoch( + network, X_train, y_train, cost=cost, acc=acc, batch_size=batch_size + ) + tl.logging.info(" train loss: %f" % train_loss) + if acc is not None: + tl.logging.info(" train acc: %f" % train_acc) + if val_loss is None: + val_loss, val_acc, _ = run_epoch(network, X_val, y_val, cost=cost, acc=acc, batch_size=batch_size) + + # tl.logging.info(" val loss: %f" % val_loss) + + if acc is not None: + pass + # tl.logging.info(" val acc: %f" % val_acc) + else: + tl.logging.info( + "Epoch %d of %d took %fs, loss %f" % (epoch + 1, n_epoch, time.time() - start_time, loss_ep) + ) + tl.logging.info("Total training time: %fs" % (time.time() - start_time_begin)) + + +def test(network, acc, X_test, y_test, batch_size, cost=None): + """ + Test a given non time-series network by the given test data and metric. + + Parameters + ---------- + network : TensorLayer Model + The network. + acc : TensorFlow/numpy expression or None + Metric for accuracy or others. + - If None, would not print the information. + X_test : numpy.array + The input of testing data. + y_test : numpy array + The target of testing data + batch_size : int or None + The batch size for testing, when dataset is large, we should use minibatche for testing; + if dataset is small, we can set it to None. + cost : TensorLayer or TensorFlow loss function + Metric for loss function, e.g tl.cost.cross_entropy. If None, would not print the information. + + Examples + -------- + See `tutorial_mnist_simple.py `_ + + >>> def acc(_logits, y_batch): + ... return np.mean(np.equal(np.argmax(_logits, 1), y_batch)) + >>> tl.utils.test(network, acc, X_test, y_test, batch_size=None, cost=tl.cost.cross_entropy) + + """ + tl.logging.info('Start testing the network ...') + network.eval() + if batch_size is None: + y_pred = network(X_test) + if cost is not None: + test_loss = cost(y_pred, y_test) + # tl.logging.info(" test loss: %f" % test_loss) + test_acc = acc(y_pred, y_test) + # tl.logging.info(" test acc: %f" % (test_acc / test_acc)) + return test_acc + else: + test_loss, test_acc, n_batch = run_epoch( + network, X_test, y_test, cost=cost, acc=acc, batch_size=batch_size, shuffle=False + ) + if cost is not None: + tl.logging.info(" test loss: %f" % test_loss) + tl.logging.info(" test acc: %f" % test_acc) + return test_acc + + +def predict(network, X, batch_size=None): + """ + Return the predict results of given non time-series network. + + Parameters + ---------- + network : TensorLayer Model + The network. + X : numpy.array + The inputs. + batch_size : int or None + The batch size for prediction, when dataset is large, we should use minibatche for prediction; + if dataset is small, we can set it to None. + + Examples + -------- + See `tutorial_mnist_simple.py `_ + + >>> _logits = tl.utils.predict(network, X_test) + >>> y_pred = np.argmax(_logits, 1) + + """ + network.eval() + if batch_size is None: + y_pred = network(X) + return y_pred + else: + result = None + for X_a, _ in tl.iterate.minibatches(X, X, batch_size, shuffle=False): + result_a = network(X_a) + if result is None: + result = result_a + else: + result = np.concatenate((result, result_a)) + if result is None: + if len(X) % batch_size == 0: + result_a = network(X[-(len(X) % batch_size):, :]) + result = result_a + else: + if len(X) != len(result) and len(X) % batch_size != 0: + result_a = network(X[-(len(X) % batch_size):, :]) + result = np.concatenate((result, result_a)) + return result + + +## Evaluation +def evaluation(y_test=None, y_predict=None, n_classes=None): + """ + Input the predicted results, targets results and + the number of class, return the confusion matrix, F1-score of each class, + accuracy and macro F1-score. + + Parameters + ---------- + y_test : list + The target results + y_predict : list + The predicted results + n_classes : int + The number of classes + + Examples + -------- + >>> c_mat, f1, acc, f1_macro = tl.utils.evaluation(y_test, y_predict, n_classes) + + """ + c_mat = confusion_matrix(y_test, y_predict, labels=[x for x in range(n_classes)]) + f1 = f1_score(y_test, y_predict, average=None, labels=[x for x in range(n_classes)]) + f1_macro = f1_score(y_test, y_predict, average='macro') + acc = accuracy_score(y_test, y_predict) + tl.logging.info('confusion matrix: \n%s' % c_mat) + tl.logging.info('f1-score : %s' % f1) + tl.logging.info('f1-score(macro) : %f' % f1_macro) # same output with > f1_score(y_true, y_pred, average='macro') + tl.logging.info('accuracy-score : %f' % acc) + return c_mat, f1, acc, f1_macro + + +def dict_to_one(dp_dict): + """Input a dictionary, return a dictionary that all items are set to one. + + Used for disable dropout, dropconnect layer and so on. + + Parameters + ---------- + dp_dict : dictionary + The dictionary contains key and number, e.g. keeping probabilities. + + """ + return {x: 1 for x in dp_dict} + + +def flatten_list(list_of_list): + """Input a list of list, return a list that all items are in a list. + + Parameters + ---------- + list_of_list : a list of list + + Examples + -------- + >>> tl.utils.flatten_list([[1, 2, 3],[4, 5],[6]]) + [1, 2, 3, 4, 5, 6] + + """ + return sum(list_of_list, []) + + +def class_balancing_oversample(X_train=None, y_train=None, printable=True): + """Input the features and labels, return the features and labels after oversampling. + + Parameters + ---------- + X_train : numpy.array + The inputs. + y_train : numpy.array + The targets. + + Examples + -------- + One X + + >>> X_train, y_train = class_balancing_oversample(X_train, y_train, printable=True) + + Two X + + >>> X, y = tl.utils.class_balancing_oversample(X_train=np.hstack((X1, X2)), y_train=y, printable=False) + >>> X1 = X[:, 0:5] + >>> X2 = X[:, 5:] + + """ + # ======== Classes balancing + if printable: + tl.logging.info("Classes balancing for training examples...") + + c = Counter(y_train) + + if printable: + tl.logging.info('the occurrence number of each stage: %s' % c.most_common()) + tl.logging.info('the least stage is Label %s have %s instances' % c.most_common()[-1]) + tl.logging.info('the most stage is Label %s have %s instances' % c.most_common(1)[0]) + + most_num = c.most_common(1)[0][1] + + if printable: + tl.logging.info('most num is %d, all classes tend to be this num' % most_num) + + locations = {} + number = {} + + for lab, num in c.most_common(): # find the index from y_train + number[lab] = num + locations[lab] = np.where(np.array(y_train) == lab)[0] + if printable: + tl.logging.info('convert list(np.array) to dict format') + X = {} # convert list to dict + for lab, num in number.items(): + X[lab] = X_train[locations[lab]] + + # oversampling + if printable: + tl.logging.info('start oversampling') + for key in X: + temp = X[key] + while True: + if len(X[key]) >= most_num: + break + X[key] = np.vstack((X[key], temp)) + if printable: + tl.logging.info('first features of label 0 > %d' % len(X[0][0])) + tl.logging.info('the occurrence num of each stage after oversampling') + for key in X: + tl.logging.info("%s %d" % (key, len(X[key]))) + if printable: + tl.logging.info('make each stage have same num of instances') + for key in X: + X[key] = X[key][0:most_num, :] + tl.logging.info("%s %d" % (key, len(X[key]))) + + # convert dict to list + if printable: + tl.logging.info('convert from dict to list format') + y_train = [] + X_train = np.empty(shape=(0, len(X[0][0]))) + for key in X: + X_train = np.vstack((X_train, X[key])) + y_train.extend([key for i in range(len(X[key]))]) + # tl.logging.info(len(X_train), len(y_train)) + c = Counter(y_train) + if printable: + tl.logging.info('the occurrence number of each stage after oversampling: %s' % c.most_common()) + # ================ End of Classes balancing + return X_train, y_train + + +## Random +def get_random_int(min_v=0, max_v=10, number=5, seed=None): + """Return a list of random integer by the given range and quantity. + + Parameters + ----------- + min_v : number + The minimum value. + max_v : number + The maximum value. + number : int + Number of value. + seed : int or None + The seed for random. + + Examples + --------- + >>> r = get_random_int(min_v=0, max_v=10, number=5) + [10, 2, 3, 3, 7] + + """ + rnd = random.Random() + if seed: + rnd = random.Random(seed) + # return [random.randint(min,max) for p in range(0, number)] + return [rnd.randint(min_v, max_v) for p in range(0, number)] + + +def list_string_to_dict(string): + """Inputs ``['a', 'b', 'c']``, returns ``{'a': 0, 'b': 1, 'c': 2}``.""" + dictionary = {} + for idx, c in enumerate(string): + dictionary.update({c: idx}) + return dictionary + + +def exit_tensorflow(port=6006): + """Close TensorBoard and Nvidia-process if available. + + Parameters + ---------- + port : int + TensorBoard port you want to close, `6006` as default. + + """ + text = "[TL] Close tensorboard and nvidia-process if available" + text2 = "[TL] Close tensorboard and nvidia-process not yet supported by this function (tl.ops.exit_tf) on " + + if _platform == "linux" or _platform == "linux2": + tl.logging.info('linux: %s' % text) + os.system('nvidia-smi') + os.system('fuser ' + str(port) + '/tcp -k') # kill tensorboard 6006 + os.system("nvidia-smi | grep python |awk '{print $3}'|xargs kill") # kill all nvidia-smi python process + _exit() + + elif _platform == "darwin": + tl.logging.info('OS X: %s' % text) + subprocess.Popen( + "lsof -i tcp:" + str(port) + " | grep -v PID | awk '{print $2}' | xargs kill", shell=True + ) # kill tensorboard + elif _platform == "win32": + raise NotImplementedError("this function is not supported on the Windows platform") + + else: + tl.logging.info(text2 + _platform) + + +def open_tensorboard(log_dir='/tmp/tensorflow', port=6006): + """Open Tensorboard. + + Parameters + ---------- + log_dir : str + Directory where your tensorboard logs are saved + port : int + TensorBoard port you want to open, 6006 is tensorboard default + + """ + text = "[TL] Open tensorboard, go to localhost:" + str(port) + " to access" + text2 = " not yet supported by this function (tl.ops.open_tb)" + + if not tl.files.exists_or_mkdir(log_dir, verbose=False): + tl.logging.info("[TL] Log reportory was created at %s" % log_dir) + + if _platform == "linux" or _platform == "linux2": + tl.logging.info('linux: %s' % text) + subprocess.Popen( + sys.prefix + " | python -m tensorflow.tensorboard --logdir=" + log_dir + " --port=" + str(port), shell=True + ) # open tensorboard in localhost:6006/ or whatever port you chose + elif _platform == "darwin": + tl.logging.info('OS X: %s' % text) + subprocess.Popen( + sys.prefix + " | python -m tensorflow.tensorboard --logdir=" + log_dir + " --port=" + str(port), shell=True + ) # open tensorboard in localhost:6006/ or whatever port you chose + elif _platform == "win32": + raise NotImplementedError("this function is not supported on the Windows platform") + else: + tl.logging.info(_platform + text2) + + +def clear_all_placeholder_variables(printable=True): + """Clears all the placeholder variables of keep prob, + including keeping probabilities of all dropout, denoising, dropconnect etc. + + Parameters + ---------- + printable : boolean + If True, print all deleted variables. + + """ + tl.logging.info('clear all .....................................') + gl = globals().copy() + for var in gl: + if var[0] == '_': continue + if 'func' in str(globals()[var]): continue + if 'module' in str(globals()[var]): continue + if 'class' in str(globals()[var]): continue + + if printable: + tl.logging.info(" clear_all ------- %s" % str(globals()[var])) + + del globals()[var] + + +def set_gpu_fraction(gpu_fraction=0.3): + """Set the GPU memory fraction for the application. + + Parameters + ---------- + gpu_fraction : None or float + Fraction of GPU memory, (0 ~ 1]. If None, allow gpu memory growth. + + References + ---------- + - `TensorFlow using GPU `__ + + """ + if gpu_fraction is None: + tl.logging.info("[TL]: ALLOW GPU MEM GROWTH") + tf.config.gpu.set_per_process_memory_growth(True) + else: + tl.logging.info("[TL]: GPU MEM Fraction %f" % gpu_fraction) + tf.config.gpu.set_per_process_memory_fraction(0.4) + # gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=gpu_fraction) + # sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options)) + # return sess + + +def train_epoch( + network, X, y, cost, train_op=tf.optimizers.Adam(learning_rate=0.0001), acc=None, batch_size=100, shuffle=True +): + """Training a given non time-series network by the given cost function, training data, batch_size etc. + for one epoch. + + Parameters + ---------- + network : TensorLayer Model + the network to be trained. + X : numpy.array + The input of training data + y : numpy.array + The target of training data + cost : TensorLayer or TensorFlow loss function + Metric for loss function, e.g tl.cost.cross_entropy. + train_op : TensorFlow optimizer + The optimizer for training e.g. tf.optimizers.Adam(). + acc : TensorFlow/numpy expression or None + Metric for accuracy or others. If None, would not print the information. + batch_size : int + The batch size for training and evaluating. + shuffle : boolean + Indicating whether to shuffle the dataset in training. + + Returns + ------- + loss_ep : Tensor. Average loss of this epoch. + acc_ep : Tensor or None. Average accuracy(metric) of this epoch. None if acc is not given. + n_step : int. Number of iterations taken in this epoch. + + """ + network.train() + loss_ep = 0 + acc_ep = 0 + n_step = 0 + for X_batch, y_batch in tl.iterate.minibatches(X, y, batch_size, shuffle=shuffle): + _loss, _acc = _train_step(network, X_batch, y_batch, cost=cost, train_op=train_op, acc=acc) + + loss_ep += _loss + if acc is not None: + acc_ep += _acc + n_step += 1 + + loss_ep = loss_ep / n_step + acc_ep = acc_ep / n_step if acc is not None else None + + return loss_ep, acc_ep, n_step + + +def run_epoch(network, X, y, cost=None, acc=None, batch_size=100, shuffle=False): + """Run a given non time-series network by the given cost function, test data, batch_size etc. + for one epoch. + + Parameters + ---------- + network : TensorLayer Model + the network to be trained. + X : numpy.array + The input of training data + y : numpy.array + The target of training data + cost : TensorLayer or TensorFlow loss function + Metric for loss function, e.g tl.cost.cross_entropy. + acc : TensorFlow/numpy expression or None + Metric for accuracy or others. If None, would not print the information. + batch_size : int + The batch size for training and evaluating. + shuffle : boolean + Indicating whether to shuffle the dataset in training. + + Returns + ------- + loss_ep : Tensor. Average loss of this epoch. None if 'cost' is not given. + acc_ep : Tensor. Average accuracy(metric) of this epoch. None if 'acc' is not given. + n_step : int. Number of iterations taken in this epoch. + """ + network.eval() + loss_ep = 0 + acc_ep = 0 + n_step = 0 + for X_batch, y_batch in tl.iterate.minibatches(X, y, batch_size, shuffle=shuffle): + _loss, _acc = _run_step(network, X_batch, y_batch, cost=cost, acc=acc) + if cost is not None: + loss_ep += _loss + if acc is not None: + acc_ep += _acc + n_step += 1 + + loss_ep = loss_ep / n_step if cost is not None else None + acc_ep = acc_ep / n_step if acc is not None else None + + return loss_ep, acc_ep, n_step + + +@tf.function +def _train_step(network, X_batch, y_batch, cost, train_op=tf.optimizers.Adam(learning_rate=0.0001), acc=None): + """Train for one step""" + with tf.GradientTape() as tape: + y_pred = network(X_batch) + _loss = cost(y_pred, y_batch) + + grad = tape.gradient(_loss, network.trainable_weights) + train_op.apply_gradients(zip(grad, network.trainable_weights)) + + if acc is not None: + _acc = acc(y_pred, y_batch) + return _loss, _acc + else: + return _loss, None + + +# @tf.function # FIXME : enable tf.function will cause some bugs in numpy, need fixing +def _run_step(network, X_batch, y_batch, cost=None, acc=None): + """Run for one step""" + y_pred = network(X_batch) + _loss, _acc = None, None + if cost is not None: + _loss = cost(y_pred, y_batch) + if acc is not None: + _acc = acc(y_pred, y_batch) + return _loss, _acc diff --git a/tensorlayer/visualize.py b/tensorlayer/visualize.py new file mode 100644 index 0000000..72c1b18 --- /dev/null +++ b/tensorlayer/visualize.py @@ -0,0 +1,664 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import os + +import imageio +import numpy as np + +import tensorlayer as tl +from tensorlayer.lazy_imports import LazyImport + +cv2 = LazyImport("cv2") + +# Uncomment the following line if you got: _tkinter.TclError: no display name and no $DISPLAY environment variable +# import matplotlib +# matplotlib.use('Agg') + +__all__ = [ + 'read_image', + 'read_images', + 'save_image', + 'save_images', + 'draw_boxes_and_labels_to_image', + 'draw_mpii_people_to_image', + 'frame', + 'CNN2d', + 'images2d', + 'tsne_embedding', + 'draw_weights', + 'W', +] + + +def read_image(image, path=''): + """Read one image. + + Parameters + ----------- + image : str + The image file name. + path : str + The image folder path. + + Returns + ------- + numpy.array + The image. + + """ + return imageio.imread(os.path.join(path, image)) + + +def read_images(img_list, path='', n_threads=10, printable=True): + """Returns all images in list by given path and name of each image file. + + Parameters + ------------- + img_list : list of str + The image file names. + path : str + The image folder path. + n_threads : int + The number of threads to read image. + printable : boolean + Whether to print information when reading images. + + Returns + ------- + list of numpy.array + The images. + + """ + imgs = [] + for idx in range(0, len(img_list), n_threads): + b_imgs_list = img_list[idx:idx + n_threads] + b_imgs = tl.prepro.threading_data(b_imgs_list, fn=read_image, path=path) + # tl.logging.info(b_imgs.shape) + imgs.extend(b_imgs) + if printable: + tl.logging.info('read %d from %s' % (len(imgs), path)) + return imgs + + +def save_image(image, image_path='_temp.png'): + """Save a image. + + Parameters + ----------- + image : numpy array + [w, h, c] + image_path : str + path + + """ + try: # RGB + imageio.imwrite(image_path, image) + except Exception: # Greyscale + imageio.imwrite(image_path, image[:, :, 0]) + + +def save_images(images, size, image_path='_temp.png'): + """Save multiple images into one single image. + + Parameters + ----------- + images : numpy array + (batch, w, h, c) + size : list of 2 ints + row and column number. + number of images should be equal or less than size[0] * size[1] + image_path : str + save path + + Examples + --------- + >>> import numpy as np + >>> import tensorlayer as tl + >>> images = np.random.rand(64, 100, 100, 3) + >>> tl.visualize.save_images(images, [8, 8], 'temp.png') + + """ + if len(images.shape) == 3: # Greyscale [batch, h, w] --> [batch, h, w, 1] + images = images[:, :, :, np.newaxis] + + def merge(images, size): + h, w = images.shape[1], images.shape[2] + img = np.zeros((h * size[0], w * size[1], 3), dtype=images.dtype) + for idx, image in enumerate(images): + i = idx % size[1] + j = idx // size[1] + img[j * h:j * h + h, i * w:i * w + w, :] = image + return img + + def imsave(images, size, path): + if np.max(images) <= 1 and (-1 <= np.min(images) < 0): + images = ((images + 1) * 127.5).astype(np.uint8) + elif np.max(images) <= 1 and np.min(images) >= 0: + images = (images * 255).astype(np.uint8) + + return imageio.imwrite(path, merge(images, size)) + + if len(images) > size[0] * size[1]: + raise AssertionError("number of images should be equal or less than size[0] * size[1] {}".format(len(images))) + + return imsave(images, size, image_path) + + +def draw_boxes_and_labels_to_image( + image, classes, coords, scores, classes_list, is_center=True, is_rescale=True, save_name=None +): + """Draw bboxes and class labels on image. Return or save the image with bboxes, example in the docs of ``tl.prepro``. + + Parameters + ----------- + image : numpy.array + The RGB image [height, width, channel]. + classes : list of int + A list of class ID (int). + coords : list of int + A list of list for coordinates. + - Should be [x, y, x2, y2] (up-left and botton-right format) + - If [x_center, y_center, w, h] (set is_center to True). + scores : list of float + A list of score (float). (Optional) + classes_list : list of str + for converting ID to string on image. + is_center : boolean + Whether the coordinates is [x_center, y_center, w, h] + - If coordinates are [x_center, y_center, w, h], set it to True for converting it to [x, y, x2, y2] (up-left and botton-right) internally. + - If coordinates are [x1, x2, y1, y2], set it to False. + is_rescale : boolean + Whether to rescale the coordinates from pixel-unit format to ratio format. + - If True, the input coordinates are the portion of width and high, this API will scale the coordinates to pixel unit internally. + - If False, feed the coordinates with pixel unit format. + save_name : None or str + The name of image file (i.e. image.png), if None, not to save image. + + Returns + ------- + numpy.array + The saved image. + + References + ----------- + - OpenCV rectangle and putText. + - `scikit-image `__. + + """ + if len(coords) != len(classes): + raise AssertionError("number of coordinates and classes are equal") + + if len(scores) > 0 and len(scores) != len(classes): + raise AssertionError("number of scores and classes are equal") + + # don't change the original image, and avoid error https://stackoverflow.com/questions/30249053/python-opencv-drawing-errors-after-manipulating-array-with-numpy + image = image.copy() + + imh, imw = image.shape[0:2] + thick = int((imh + imw) // 430) + + for i, _v in enumerate(coords): + if is_center: + x, y, x2, y2 = tl.prepro.obj_box_coord_centroid_to_upleft_butright(coords[i]) + else: + x, y, x2, y2 = coords[i] + + if is_rescale: # scale back to pixel unit if the coords are the portion of width and high + x, y, x2, y2 = tl.prepro.obj_box_coord_scale_to_pixelunit([x, y, x2, y2], (imh, imw)) + + cv2.rectangle( + image, + (int(x), int(y)), + (int(x2), int(y2)), # up-left and botton-right + [0, 255, 0], + thick + ) + + cv2.putText( + image, + classes_list[classes[i]] + ((" %.2f" % (scores[i])) if (len(scores) != 0) else " "), + (int(x), int(y)), # button left + 0, + 1.5e-3 * imh, # bigger = larger font + [0, 0, 256], # self.meta['colors'][max_indx], + int(thick / 2) + 1 + ) # bold + + if save_name is not None: + # cv2.imwrite('_my.png', image) + save_image(image, save_name) + # if len(coords) == 0: + # tl.logging.info("draw_boxes_and_labels_to_image: no bboxes exist, cannot draw !") + return image + + +def draw_mpii_pose_to_image(image, poses, save_name='image.png'): + """Draw people(s) into image using MPII dataset format as input, return or save the result image. + + This is an experimental API, can be changed in the future. + + Parameters + ----------- + image : numpy.array + The RGB image [height, width, channel]. + poses : list of dict + The people(s) annotation in MPII format, see ``tl.files.load_mpii_pose_dataset``. + save_name : None or str + The name of image file (i.e. image.png), if None, not to save image. + + Returns + -------- + numpy.array + The saved image. + + Examples + -------- + >>> import pprint + >>> import tensorlayer as tl + >>> img_train_list, ann_train_list, img_test_list, ann_test_list = tl.files.load_mpii_pose_dataset() + >>> image = tl.vis.read_image(img_train_list[0]) + >>> tl.vis.draw_mpii_pose_to_image(image, ann_train_list[0], 'image.png') + >>> pprint.pprint(ann_train_list[0]) + + References + ----------- + - `MPII Keyponts and ID `__ + """ + # import skimage + # don't change the original image, and avoid error https://stackoverflow.com/questions/30249053/python-opencv-drawing-errors-after-manipulating-array-with-numpy + image = image.copy() + + imh, imw = image.shape[0:2] + thick = int((imh + imw) // 430) + # radius = int(image.shape[1] / 500) + 1 + radius = int(thick * 1.5) + + if image.max() < 1: + image = image * 255 + + for people in poses: + # Pose Keyponts + joint_pos = people['joint_pos'] + # draw sketch + # joint id (0 - r ankle, 1 - r knee, 2 - r hip, 3 - l hip, 4 - l knee, + # 5 - l ankle, 6 - pelvis, 7 - thorax, 8 - upper neck, + # 9 - head top, 10 - r wrist, 11 - r elbow, 12 - r shoulder, + # 13 - l shoulder, 14 - l elbow, 15 - l wrist) + # + # 9 + # 8 + # 12 ** 7 ** 13 + # * * * + # 11 * 14 + # * * * + # 10 2 * 6 * 3 15 + # * * + # 1 4 + # * * + # 0 5 + + lines = [ + [(0, 1), [100, 255, 100]], + [(1, 2), [50, 255, 50]], + [(2, 6), [0, 255, 0]], # right leg + [(3, 4), [100, 100, 255]], + [(4, 5), [50, 50, 255]], + [(6, 3), [0, 0, 255]], # left leg + [(6, 7), [255, 255, 100]], + [(7, 8), [255, 150, 50]], # body + [(8, 9), [255, 200, 100]], # head + [(10, 11), [255, 100, 255]], + [(11, 12), [255, 50, 255]], + [(12, 8), [255, 0, 255]], # right hand + [(8, 13), [0, 255, 255]], + [(13, 14), [100, 255, 255]], + [(14, 15), [200, 255, 255]] # left hand + ] + for line in lines: + start, end = line[0] + if (start in joint_pos) and (end in joint_pos): + cv2.line( + image, + (int(joint_pos[start][0]), int(joint_pos[start][1])), + (int(joint_pos[end][0]), int(joint_pos[end][1])), # up-left and botton-right + line[1], + thick + ) + # rr, cc, val = skimage.draw.line_aa(int(joint_pos[start][1]), int(joint_pos[start][0]), int(joint_pos[end][1]), int(joint_pos[end][0])) + # image[rr, cc] = line[1] + # draw circles + for pos in joint_pos.items(): + _, pos_loc = pos # pos_id, pos_loc + pos_loc = (int(pos_loc[0]), int(pos_loc[1])) + cv2.circle(image, center=pos_loc, radius=radius, color=(200, 200, 200), thickness=-1) + # rr, cc = skimage.draw.circle(int(pos_loc[1]), int(pos_loc[0]), radius) + # image[rr, cc] = [0, 255, 0] + + # Head + head_rect = people['head_rect'] + if head_rect: # if head exists + cv2.rectangle( + image, + (int(head_rect[0]), int(head_rect[1])), + (int(head_rect[2]), int(head_rect[3])), # up-left and botton-right + [0, 180, 0], + thick + ) + + if save_name is not None: + # cv2.imwrite(save_name, image) + save_image(image, save_name) + return image + + +draw_mpii_people_to_image = draw_mpii_pose_to_image + + +def frame(I=None, second=5, saveable=True, name='frame', cmap=None, fig_idx=12836): + """Display a frame. Make sure OpenAI Gym render() is disable before using it. + + Parameters + ---------- + I : numpy.array + The image. + second : int + The display second(s) for the image(s), if saveable is False. + saveable : boolean + Save or plot the figure. + name : str + A name to save the image, if saveable is True. + cmap : None or str + 'gray' for greyscale, None for default, etc. + fig_idx : int + matplotlib figure index. + + Examples + -------- + >>> env = gym.make("Pong-v0") + >>> observation = env.reset() + >>> tl.visualize.frame(observation) + + """ + import matplotlib.pyplot as plt + if saveable is False: + plt.ion() + plt.figure(fig_idx) # show all feature images + + if len(I.shape) and I.shape[-1] == 1: # (10,10,1) --> (10,10) + I = I[:, :, 0] + + plt.imshow(I, cmap) + plt.title(name) + # plt.gca().xaxis.set_major_locator(plt.NullLocator()) # distable tick + # plt.gca().yaxis.set_major_locator(plt.NullLocator()) + + if saveable: + plt.savefig(name + '.pdf', format='pdf') + else: + plt.draw() + plt.pause(second) + + +def CNN2d(CNN=None, second=10, saveable=True, name='cnn', fig_idx=3119362): + """Display a group of RGB or Greyscale CNN masks. + + Parameters + ---------- + CNN : numpy.array + The image. e.g: 64 5x5 RGB images can be (5, 5, 3, 64). + second : int + The display second(s) for the image(s), if saveable is False. + saveable : boolean + Save or plot the figure. + name : str + A name to save the image, if saveable is True. + fig_idx : int + The matplotlib figure index. + + Examples + -------- + >>> tl.visualize.CNN2d(network.all_params[0].eval(), second=10, saveable=True, name='cnn1_mnist', fig_idx=2012) + + """ + import matplotlib.pyplot as plt + # tl.logging.info(CNN.shape) # (5, 5, 3, 64) + # exit() + n_mask = CNN.shape[3] + n_row = CNN.shape[0] + n_col = CNN.shape[1] + n_color = CNN.shape[2] + row = int(np.sqrt(n_mask)) + col = int(np.ceil(n_mask / row)) + plt.ion() # active mode + fig = plt.figure(fig_idx) + count = 1 + for _ir in range(1, row + 1): + for _ic in range(1, col + 1): + if count > n_mask: + break + fig.add_subplot(col, row, count) + # tl.logging.info(CNN[:,:,:,count-1].shape, n_row, n_col) # (5, 1, 32) 5 5 + # exit() + # plt.imshow( + # np.reshape(CNN[count-1,:,:,:], (n_row, n_col)), + # cmap='gray', interpolation="nearest") # theano + if n_color == 1: + plt.imshow(np.reshape(CNN[:, :, :, count - 1], (n_row, n_col)), cmap='gray', interpolation="nearest") + elif n_color == 3: + plt.imshow( + np.reshape(CNN[:, :, :, count - 1], (n_row, n_col, n_color)), cmap='gray', interpolation="nearest" + ) + else: + raise Exception("Unknown n_color") + plt.gca().xaxis.set_major_locator(plt.NullLocator()) # distable tick + plt.gca().yaxis.set_major_locator(plt.NullLocator()) + count = count + 1 + if saveable: + plt.savefig(name + '.pdf', format='pdf') + else: + plt.draw() + plt.pause(second) + + +def images2d(images=None, second=10, saveable=True, name='images', dtype=None, fig_idx=3119362): + """Display a group of RGB or Greyscale images. + + Parameters + ---------- + images : numpy.array + The images. + second : int + The display second(s) for the image(s), if saveable is False. + saveable : boolean + Save or plot the figure. + name : str + A name to save the image, if saveable is True. + dtype : None or numpy data type + The data type for displaying the images. + fig_idx : int + matplotlib figure index. + + Examples + -------- + >>> X_train, y_train, X_test, y_test = tl.files.load_cifar10_dataset(shape=(-1, 32, 32, 3), plotable=False) + >>> tl.visualize.images2d(X_train[0:100,:,:,:], second=10, saveable=False, name='cifar10', dtype=np.uint8, fig_idx=20212) + + """ + import matplotlib.pyplot as plt + # tl.logging.info(images.shape) # (50000, 32, 32, 3) + # exit() + if dtype: + images = np.asarray(images, dtype=dtype) + n_mask = images.shape[0] + n_row = images.shape[1] + n_col = images.shape[2] + n_color = images.shape[3] + row = int(np.sqrt(n_mask)) + col = int(np.ceil(n_mask / row)) + plt.ion() # active mode + fig = plt.figure(fig_idx) + count = 1 + for _ir in range(1, row + 1): + for _ic in range(1, col + 1): + if count > n_mask: + break + fig.add_subplot(col, row, count) + # tl.logging.info(images[:,:,:,count-1].shape, n_row, n_col) # (5, 1, 32) 5 5 + # plt.imshow( + # np.reshape(images[count-1,:,:,:], (n_row, n_col)), + # cmap='gray', interpolation="nearest") # theano + if n_color == 1: + plt.imshow(np.reshape(images[count - 1, :, :], (n_row, n_col)), cmap='gray', interpolation="nearest") + # plt.title(name) + elif n_color == 3: + plt.imshow(images[count - 1, :, :], cmap='gray', interpolation="nearest") + # plt.title(name) + else: + raise Exception("Unknown n_color") + plt.gca().xaxis.set_major_locator(plt.NullLocator()) # distable tick + plt.gca().yaxis.set_major_locator(plt.NullLocator()) + count = count + 1 + if saveable: + plt.savefig(name + '.pdf', format='pdf') + else: + plt.draw() + plt.pause(second) + + +def tsne_embedding(embeddings, reverse_dictionary, plot_only=500, second=5, saveable=False, name='tsne', fig_idx=9862): + """Visualize the embeddings by using t-SNE. + + Parameters + ---------- + embeddings : numpy.array + The embedding matrix. + reverse_dictionary : dictionary + id_to_word, mapping id to unique word. + plot_only : int + The number of examples to plot, choice the most common words. + second : int + The display second(s) for the image(s), if saveable is False. + saveable : boolean + Save or plot the figure. + name : str + A name to save the image, if saveable is True. + fig_idx : int + matplotlib figure index. + + Examples + -------- + >>> see 'tutorial_word2vec_basic.py' + >>> final_embeddings = normalized_embeddings.eval() + >>> tl.visualize.tsne_embedding(final_embeddings, labels, reverse_dictionary, + ... plot_only=500, second=5, saveable=False, name='tsne') + + """ + import matplotlib.pyplot as plt + + def plot_with_labels(low_dim_embs, labels, figsize=(18, 18), second=5, saveable=True, name='tsne', fig_idx=9862): + + if low_dim_embs.shape[0] < len(labels): + raise AssertionError("More labels than embeddings") + + if saveable is False: + plt.ion() + plt.figure(fig_idx) + + plt.figure(figsize=figsize) # in inches + + for i, label in enumerate(labels): + x, y = low_dim_embs[i, :] + plt.scatter(x, y) + plt.annotate(label, xy=(x, y), xytext=(5, 2), textcoords='offset points', ha='right', va='bottom') + + if saveable: + plt.savefig(name + '.pdf', format='pdf') + else: + plt.draw() + plt.pause(second) + + try: + from sklearn.manifold import TSNE + from six.moves import xrange + + tsne = TSNE(perplexity=30, n_components=2, init='pca', n_iter=5000) + # plot_only = 500 + low_dim_embs = tsne.fit_transform(embeddings[:plot_only, :]) + labels = [reverse_dictionary[i] for i in xrange(plot_only)] + plot_with_labels(low_dim_embs, labels, second=second, saveable=saveable, name=name, fig_idx=fig_idx) + + except ImportError: + _err = "Please install sklearn and matplotlib to visualize embeddings." + tl.logging.error(_err) + raise ImportError(_err) + + +def draw_weights(W=None, second=10, saveable=True, shape=None, name='mnist', fig_idx=2396512): + """Visualize every columns of the weight matrix to a group of Greyscale img. + + Parameters + ---------- + W : numpy.array + The weight matrix + second : int + The display second(s) for the image(s), if saveable is False. + saveable : boolean + Save or plot the figure. + shape : a list with 2 int or None + The shape of feature image, MNIST is [28, 80]. + name : a string + A name to save the image, if saveable is True. + fig_idx : int + matplotlib figure index. + + Examples + -------- + >>> tl.visualize.draw_weights(network.all_params[0].eval(), second=10, saveable=True, name='weight_of_1st_layer', fig_idx=2012) + + """ + if shape is None: + shape = [28, 28] + + import matplotlib.pyplot as plt + if saveable is False: + plt.ion() + fig = plt.figure(fig_idx) # show all feature images + n_units = W.shape[1] + + num_r = int(np.sqrt(n_units)) # 每行显示的个数 若25个hidden unit -> 每行显示5个 + num_c = int(np.ceil(n_units / num_r)) + count = int(1) + for _row in range(1, num_r + 1): + for _col in range(1, num_c + 1): + if count > n_units: + break + fig.add_subplot(num_r, num_c, count) + # ------------------------------------------------------------ + # plt.imshow(np.reshape(W[:,count-1],(28,28)), cmap='gray') + # ------------------------------------------------------------ + feature = W[:, count - 1] / np.sqrt((W[:, count - 1]**2).sum()) + # feature[feature<0.0001] = 0 # value threshold + # if count == 1 or count == 2: + # print(np.mean(feature)) + # if np.std(feature) < 0.03: # condition threshold + # feature = np.zeros_like(feature) + # if np.mean(feature) < -0.015: # condition threshold + # feature = np.zeros_like(feature) + plt.imshow( + np.reshape(feature, (shape[0], shape[1])), cmap='gray', interpolation="nearest" + ) # , vmin=np.min(feature), vmax=np.max(feature)) + # plt.title(name) + # ------------------------------------------------------------ + # plt.imshow(np.reshape(W[:,count-1] ,(np.sqrt(size),np.sqrt(size))), cmap='gray', interpolation="nearest") + plt.gca().xaxis.set_major_locator(plt.NullLocator()) # distable tick + plt.gca().yaxis.set_major_locator(plt.NullLocator()) + count = count + 1 + if saveable: + plt.savefig(name + '.pdf', format='pdf') + else: + plt.draw() + plt.pause(second) + + +W = draw_weights diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/files/model_basic.h5 b/tests/files/model_basic.h5 new file mode 100644 index 0000000..7d7be22 Binary files /dev/null and b/tests/files/model_basic.h5 differ diff --git a/tests/files/test_utils_saveload.py b/tests/files/test_utils_saveload.py new file mode 100644 index 0000000..58a1d37 --- /dev/null +++ b/tests/files/test_utils_saveload.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import numpy as np +import tensorflow as tf +import tensorlayer as tl +from tensorlayer.layers import * +from tensorlayer.models import * + +from tests.utils import CustomTestCase + + +def basic_static_model(): + ni = Input((None, 24, 24, 3)) + nn = Conv2d(16, (5, 5), (1, 1), padding='SAME', act=tf.nn.relu, name="conv1")(ni) + nn = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool1')(nn) + + nn = Conv2d(16, (5, 5), (1, 1), padding='SAME', act=tf.nn.relu, name="conv2")(nn) + nn = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool2')(nn) + + nn = Flatten(name='flatten')(nn) + nn = Dense(100, act=None, name="dense1")(nn) + nn = Dense(10, act=None, name="dense2")(nn) + M = Model(inputs=ni, outputs=nn, name='basic_static') + return M + + +class basic_dynamic_model(Model): + + def __init__(self): + super(basic_dynamic_model, self).__init__(name="basic_dynamic") + self.conv1 = Conv2d(16, (5, 5), (1, 1), padding='SAME', act=tf.nn.relu, in_channels=3, name="conv1") + self.pool1 = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool1') + + self.conv2 = Conv2d(16, (5, 5), (1, 1), padding='SAME', act=tf.nn.relu, in_channels=16, name="conv2") + self.pool2 = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool2') + + self.flatten = Flatten(name='flatten') + self.dense1 = Dense(100, act=None, in_channels=576, name="dense1") + self.dense2 = Dense(10, act=None, in_channels=100, name="dense2") + + def forward(self, x): + x = self.conv1(x) + x = self.pool1(x) + x = self.conv2(x) + x = self.pool2(x) + x = self.flatten(x) + x = self.dense1(x) + x = self.dense2(x) + return x + + +class Model_Core_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + cls.static_model = basic_static_model() + cls.dynamic_model = basic_dynamic_model() + + @classmethod + def tearDownClass(cls): + pass + + def test_hdf5(self): + modify_val = np.zeros_like(self.static_model.all_weights[-2].numpy()) + ori_val = self.static_model.all_weights[-2].numpy() + tl.files.save_weights_to_hdf5("./model_basic.h5", self.static_model) + + self.static_model.all_weights[-2].assign(modify_val) + tl.files.load_hdf5_to_weights_in_order("./model_basic.h5", self.static_model) + self.assertLess(np.max(np.abs(ori_val - self.static_model.all_weights[-2].numpy())), 1e-7) + + self.static_model.all_weights[-2].assign(modify_val) + tl.files.load_hdf5_to_weights("./model_basic.h5", self.static_model) + self.assertLess(np.max(np.abs(ori_val - self.static_model.all_weights[-2].numpy())), 1e-7) + + ori_weights = self.static_model._all_weights + self.static_model._all_weights = self.static_model._all_weights[1:] + self.static_model.all_weights[-2].assign(modify_val) + tl.files.load_hdf5_to_weights("./model_basic.h5", self.static_model, skip=True) + self.assertLess(np.max(np.abs(ori_val - self.static_model.all_weights[-2].numpy())), 1e-7) + self.static_model._all_weights = ori_weights + + def test_npz(self): + modify_val = np.zeros_like(self.dynamic_model.all_weights[-2].numpy()) + ori_val = self.dynamic_model.all_weights[-2].numpy() + tl.files.save_npz(self.dynamic_model.all_weights, "./model_basic.npz") + + self.dynamic_model.all_weights[-2].assign(modify_val) + tl.files.load_and_assign_npz("./model_basic.npz", self.dynamic_model) + self.assertLess(np.max(np.abs(ori_val - self.dynamic_model.all_weights[-2].numpy())), 1e-7) + + def test_npz_dict(self): + modify_val = np.zeros_like(self.dynamic_model.all_weights[-2].numpy()) + ori_val = self.dynamic_model.all_weights[-2].numpy() + tl.files.save_npz_dict(self.dynamic_model.all_weights, "./model_basic.npz") + + self.dynamic_model.all_weights[-2].assign(modify_val) + tl.files.load_and_assign_npz_dict("./model_basic.npz", self.dynamic_model) + self.assertLess(np.max(np.abs(ori_val - self.dynamic_model.all_weights[-2].numpy())), 1e-7) + + ori_weights = self.dynamic_model._all_weights + self.dynamic_model._all_weights = self.static_model._all_weights[1:] + self.dynamic_model.all_weights[-2].assign(modify_val) + tl.files.load_and_assign_npz_dict("./model_basic.npz", self.dynamic_model, skip=True) + self.assertLess(np.max(np.abs(ori_val - self.dynamic_model.all_weights[-2].numpy())), 1e-7) + self.dynamic_model._all_weights = ori_weights + + +if __name__ == '__main__': + + unittest.main() diff --git a/tests/layers/__init__.py b/tests/layers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/layers/test_layers_activation.py b/tests/layers/test_layers_activation.py new file mode 100644 index 0000000..fcbe690 --- /dev/null +++ b/tests/layers/test_layers_activation.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class Activation_Layer_Test(CustomTestCase): + + @classmethod + def setUpClass(self): + self.inputs = tl.layers.Input([10, 5]) + + @classmethod + def tearDownClass(self): + pass + + def test_prelu_1(self): + prelulayer = tl.layers.PRelu(channel_shared=True) + class prelu_model(tl.layers.Module): + def __init__(self): + super(prelu_model, self).__init__() + self.prelu = prelulayer + + def forward(self, inputs): + return self.prelu(inputs) + net = prelu_model() + + self.assertTrue(tl.get_tensor_shape(net(self.inputs)), [10, 5]) + + def test_prelu_2(self): + prelulayer = tl.layers.PRelu(in_channels=5) + prelu = prelulayer(self.inputs) + + self.assertTrue(tl.get_tensor_shape(prelu), [10, 5]) + + def test_prelu6_1(self): + prelu6layer = tl.layers.PRelu6(in_channels=5) + prelu6 = prelu6layer(self.inputs) + + self.assertTrue(tl.get_tensor_shape(prelu6), [10, 5]) + + + def test_prelu6_2(self): + prelu6layer = tl.layers.PRelu6(channel_shared=True) + + class prelu6_model(tl.layers.Module): + def __init__(self): + super(prelu6_model, self).__init__() + self.prelu = prelu6layer + + def forward(self, inputs): + return self.prelu(inputs) + + net = prelu6_model() + + self.assertTrue(tl.get_tensor_shape(net(self.inputs)), [10, 5]) + + def test_ptrelu6_1(self): + ptrelu6layer = tl.layers.PTRelu6(channel_shared=True) + ptrelu6 = ptrelu6layer(self.inputs) + + self.assertTrue(tl.get_tensor_shape(ptrelu6), [10, 5]) + + def test_ptrelu6_2(self): + ptrelu6layer = tl.layers.PTRelu6(in_channels=5) + + class ptrelu6_model(tl.layers.Module): + def __init__(self): + super(ptrelu6_model, self).__init__() + self.prelu = ptrelu6layer + + def forward(self, inputs): + return self.prelu(inputs) + + net = ptrelu6_model() + + self.assertTrue(tl.get_tensor_shape(net(self.inputs)), [10, 5]) + + def test_lrelu(self): + lrelulayer = tl.layers.LeakyReLU(alpha=0.5) + lrelu = lrelulayer(self.inputs) + + self.assertTrue(tl.get_tensor_shape(lrelu), [5, 10]) + + def test_lrelu6(self): + lrelu6layer = tl.layers.LeakyReLU6(alpha=0.5) + lrelu6 = lrelu6layer(self.inputs) + + self.assertTrue(tl.get_tensor_shape(lrelu6), [5, 10]) + + def test_ltrelu6(self): + ltrelu6layer = tl.layers.LeakyTwiceRelu6() + ltrelu6 = ltrelu6layer(self.inputs) + + self.assertTrue(tl.get_tensor_shape(ltrelu6), [5, 10]) + + def test_swish(self): + swishlayer = tl.layers.Swish() + swish = swishlayer(self.inputs) + + self.assertTrue(tl.get_tensor_shape(swish), [5, 10]) + + def test_hardtanh(self): + hardtanhlayer = tl.layers.HardTanh() + hardtanh = hardtanhlayer(self.inputs) + + self.assertTrue(tl.get_tensor_shape(hardtanh), [5, 10]) + + def test_mish(self): + mishlayer = tl.layers.Mish() + mish = mishlayer(self.inputs) + + self.assertTrue(tl.get_tensor_shape(mish), [5, 10]) + + +if __name__ == '__main__': + + unittest.main() diff --git a/tests/layers/test_layers_convolution.py b/tests/layers/test_layers_convolution.py new file mode 100644 index 0000000..20fb15a --- /dev/null +++ b/tests/layers/test_layers_convolution.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class Layer_Convolution_1D_Test(CustomTestCase): + + @classmethod + def setUpClass(self): + + self.batch_size = 8 + self.inputs_shape = [self.batch_size, 100, 1] + self.input_layer = tl.layers.Input(self.inputs_shape, name='input_layer') + + self.conv1dlayer1 = tl.layers.Conv1d(in_channels=1, n_filter=32, filter_size=5, stride=2) + self.n1 = self.conv1dlayer1(self.input_layer) + + self.conv1dlayer2 = tl.layers.Conv1d(in_channels=32, n_filter=32, filter_size=5, stride=2) + self.n2 = self.conv1dlayer2(self.n1) + + self.dconv1dlayer1 = tl.layers.DeConv1d(n_filter=64, in_channels=32, filter_size=5, name='deconv1dlayer') + self.n3 = self.dconv1dlayer1(self.n2) + + @classmethod + def tearDownClass(self): + pass + + def test_layer_n1(self): + self.assertEqual(len(self.conv1dlayer1.all_weights), 2) + self.assertEqual(tl.get_tensor_shape(self.n1), [self.batch_size, 50, 32]) + + def test_layer_n2(self): + self.assertEqual(len(self.conv1dlayer2.all_weights), 2) + self.assertEqual(tl.get_tensor_shape(self.n2), [self.batch_size, 25, 32]) + + def test_layer_n3(self): + self.assertEqual(len(self.dconv1dlayer1.all_weights), 2) + self.assertEqual(tl.get_tensor_shape(self.n3), [self.batch_size, 25, 64]) + + +class Layer_Convolution_2D_Test(CustomTestCase): + + @classmethod + def setUpClass(self): + + self.batch_size = 5 + self.inputs_shape = [self.batch_size, 400, 400, 3] + self.input_layer = tl.layers.Input(self.inputs_shape, name='input_layer') + + self.conv2dlayer1 = tl.layers.Conv2d(n_filter=32, in_channels=3, strides=(2, 2), filter_size=(5, 5), + padding='SAME', b_init=tl.initializers.truncated_normal(0.01), name='conv2dlayer' + ) + self.n1 = self.conv2dlayer1(self.input_layer) + + self.conv2dlayer2 = tl.layers.Conv2d(n_filter=32, in_channels=32, filter_size=(3, 3), + strides=(2, 2), act=None, name='conv2d') + self.n2 = self.conv2dlayer2(self.n1) + + self.conv2dlayer3 = tl.layers.Conv2d(in_channels=32, n_filter=32, filter_size=(3, 3), strides=(2, 2), + act=tl.ReLU, b_init=None, name='conv2d_no_bias' + ) + self.n3 = self.conv2dlayer3(self.n2) + + self.dconv2dlayer = tl.layers.DeConv2d(n_filter=32, in_channels=32, filter_size=(5, 5), strides=(2, 2), + name='deconv2dlayer' + ) + self.n4 = self.dconv2dlayer(self.n3) + + self.dwconv2dlayer = tl.layers.DepthwiseConv2d(in_channels=32, filter_size=(3, 3), strides=(1, 1), + dilation_rate=(2, 2), act=tl.ReLU, depth_multiplier=2,name='depthwise') + self.n5 = self.dwconv2dlayer(self.n4) + + @classmethod + def tearDownClass(cls): + pass + # tf.reset_default_graph() + + def test_layer_n1(self): + self.assertEqual(len(self.conv2dlayer1.all_weights), 2) + self.assertEqual(tl.get_tensor_shape(self.n1), [self.batch_size, 200, 200, 32]) + + def test_layer_n2(self): + self.assertEqual(len(self.conv2dlayer2.all_weights), 2) + self.assertEqual(tl.get_tensor_shape(self.n2), [self.batch_size, 100, 100, 32]) + + def test_layer_n3(self): + self.assertEqual(len(self.conv2dlayer3.all_weights), 1) # b_init is None + self.assertEqual(tl.get_tensor_shape(self.n3), [self.batch_size, 50, 50, 32]) + + def test_layer_n4(self): + self.assertEqual(len(self.dconv2dlayer.all_weights), 2) + self.assertEqual(tl.get_tensor_shape(self.n4), [self.batch_size, 100, 100, 32]) + + def test_layer_n5(self): + self.assertEqual(len(self.dwconv2dlayer.all_weights), 2) + self.assertEqual(tl.get_tensor_shape(self.n5), [self.batch_size, 100, 100, 64]) + + +class Layer_Convolution_3D_Test(CustomTestCase): + + @classmethod + def setUpClass(self): + print("\n#################################") + + self.batch_size = 5 + self.inputs_shape = [self.batch_size, 20, 20, 20, 3] + self.input_layer = tl.layers.Input(self.inputs_shape, name='input_layer') + + self.conv3dlayer1 = tl.layers.Conv3d(n_filter=32, in_channels=3, filter_size=(2, 2, 2), strides=(2, 2, 2)) + self.n1 = self.conv3dlayer1(self.input_layer) + + self.deconv3dlayer = tl.layers.DeConv3d(n_filter=128, in_channels=32, filter_size=(2, 2, 2), strides=(2, 2, 2) + ) + self.n2 = self.deconv3dlayer(self.n1) + + self.conv3dlayer2 = tl.layers.Conv3d(n_filter=64, in_channels=128,filter_size=(3, 3, 3), strides=(3, 3, 3), + act=tl.ReLU, b_init=None, name='conv3d_no_bias') + self.n3 = self.conv3dlayer2(self.n2) + + @classmethod + def tearDownClass(self): + pass + + def test_layer_n1(self): + self.assertEqual(len(self.conv3dlayer1.all_weights), 2) + self.assertEqual(tl.get_tensor_shape(self.n1), [self.batch_size, 10, 10, 10, 32]) + + def test_layer_n2(self): + self.assertEqual(len(self.deconv3dlayer.all_weights), 2) + self.assertEqual(tl.get_tensor_shape(self.n2), [self.batch_size, 20, 20, 20, 128]) + + def test_layer_n3(self): + self.assertEqual(len(self.conv3dlayer2.all_weights), 1) # b_init is None + self.assertEqual(tl.get_tensor_shape(self.n3), [self.batch_size, 7, 7, 7, 64]) + + +if __name__ == '__main__': + + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/layers/test_layers_core_act.py b/tests/layers/test_layers_core_act.py new file mode 100644 index 0000000..71a85d7 --- /dev/null +++ b/tests/layers/test_layers_core_act.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class Layer_Convolution_2D_Test(CustomTestCase): + + @classmethod + def setUpClass(self): + self.batch_size = 5 + self.inputs_shape = [self.batch_size, 400, 400, 3] + self.input_layer = tl.layers.Input(self.inputs_shape, name='input_layer') + + self.conv2dlayer1 = tl.layers.Conv2d(n_filter=32, in_channels=3, act=tl.ReLU, filter_size=(5, 5), + strides=(2, 2), + padding='SAME', b_init=tl.initializers.constant(value=0.0), + name='conv2dlayer' + ) + self.n1 = self.conv2dlayer1(self.input_layer) + + self.conv2dlayer2 = tl.layers.Conv2d(n_filter=32, in_channels=32, filter_size=(3, 3), strides=(2, 2), + act="relu", name='conv2d') + self.n2 = self.conv2dlayer2(self.n1) + + self.conv2dlayer3 = tl.layers.Conv2d(n_filter=32, in_channels=32, filter_size=(3, 3), strides=(2, 2), + act="leaky_relu", b_init=None) + self.n3 = self.conv2dlayer3(self.n2) + + self.conv2dlayer4 = tl.layers.Conv2d(n_filter=32, in_channels=32, filter_size=(3, 3), strides=(2, 2), + act="lrelu", b_init=None) + self.n4 = self.conv2dlayer4(self.n3) + + self.conv2dlayer5 = tl.layers.Conv2d(n_filter=32, in_channels=32, filter_size=(3, 3), strides=(2, 2), + act="sigmoid") + self.n5 = self.conv2dlayer5(self.n4) + + self.conv2dlayer6 = tl.layers.Conv2d(n_filter=32, in_channels=32, filter_size=(3, 3), strides=(2, 2), + act="tanh") + self.n6 = self.conv2dlayer6(self.n5) + + self.conv2dlayer7 = tl.layers.Conv2d( + n_filter=32, filter_size=(3, 3), strides=(2, 2), act="leaky_relu0.22", in_channels=32 + ) + self.n7 = self.conv2dlayer7(self.n6) + + self.conv2dlayer8 = tl.layers.Conv2d(n_filter=32, filter_size=(3, 3), strides=(2, 2), act="lrelu0.22", + in_channels=32) + self.n8 = self.conv2dlayer8(self.n7) + + self.conv2dlayer9 = tl.layers.Conv2d(n_filter=32, filter_size=(3, 3), strides=(2, 2), act="softplus", + in_channels=32) + self.n9 = self.conv2dlayer9(self.n8) + + self.conv2dlayer10 = tl.layers.Conv2d(n_filter=32, filter_size=(3, 3), strides=(2, 2), act="relu6", + in_channels=32) + self.n10 = self.conv2dlayer10(self.n9) + + @classmethod + def tearDownClass(self): + pass + + def test_relu(self): + self.assertEqual(tl.get_tensor_shape(self.n1), [5, 200, 200, 32]) + + def test_relu_str(self): + self.assertEqual(tl.get_tensor_shape(self.n2), [5, 100, 100, 32]) + + def test_leaky_relu_str(self): + self.assertEqual(tl.get_tensor_shape(self.n3), [5, 50, 50, 32]) + + def test_lrelu_str(self): + self.assertEqual(tl.get_tensor_shape(self.n4), [5, 25, 25, 32]) + + def test_sigmoid_str(self): + self.assertEqual(tl.get_tensor_shape(self.n5), [5, 13, 13, 32]) + + def test_tanh_str(self): + self.assertEqual(tl.get_tensor_shape(self.n6), [5, 7, 7, 32]) + + def test_leaky_relu_float_str(self): + self.assertEqual(tl.get_tensor_shape(self.n7), [5, 4, 4, 32]) + + def test_lrelu_float_str(self): + self.assertEqual(tl.get_tensor_shape(self.n8), [5, 2, 2, 32]) + + def test_softplus_str(self): + self.assertEqual(tl.get_tensor_shape(self.n9), [5, 1, 1, 32]) + + def test_relu6_str(self): + self.assertEqual(tl.get_tensor_shape(self.n10), [5, 1, 1, 32]) + + +if __name__ == '__main__': + + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/layers/test_layers_core_basedense_dropout.py b/tests/layers/test_layers_core_basedense_dropout.py new file mode 100644 index 0000000..a926a74 --- /dev/null +++ b/tests/layers/test_layers_core_basedense_dropout.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class Layer_Core_Test(CustomTestCase): + + @classmethod + def setUpClass(self): + + self.batch_size = 8 + + self.inputs_shape = [self.batch_size, 784] + self.input = tl.layers.Input(self.inputs_shape) + self.dense1 = tl.layers.Dense(n_units=800, act=tl.ReLU, in_channels=784, name='test_dense') + self.n1 = self.dense1(self.input) + + self.dropout1 = tl.layers.Dropout(keep=0.8) + self.n2 = self.dropout1(self.n1) + + self.dense2 = tl.layers.Dense(n_units=10, act='relu', b_init=None, in_channels=800) + self.n3 = self.dense2(self.n2) + + self.dense3 = tl.layers.Dense(n_units=10, act='relu', b_init=None, in_channels=10) + self.n4 = self.dense3(self.n3) + + self.concat = tl.layers.Concat(concat_dim=-1)([self.n2, self.n3]) + + class get_model(tl.layers.Module): + def __init__(self): + super(get_model, self).__init__() + self.layer1 = tl.layers.Dense(n_units=800, act=tl.ReLU, in_channels=784, name='test_dense') + self.dp = tl.layers.Dropout(keep=0.8) + self.layer2 = tl.layers.Dense(n_units=10, act='relu', b_init=None, in_channels=800) + self.layer3 = tl.layers.Dense(n_units=10, act='relu', b_init=None, in_channels=10) + + def forward(self, inputs): + z = self.layer1(inputs) + z = self.dp(z) + z = self.layer2(z) + z = self.layer3(z) + return z + + self.net = get_model() + + + @classmethod + def tearDownClass(cls): + pass + + def test_dense(self): + self.assertEqual(tl.get_tensor_shape(self.n1), [self.batch_size, 800]) + + def test_dense_nonbias(self): + self.assertEqual(len(self.dense2.all_weights), 1) + + def test_dropout(self): + self.assertEqual(len(self.dropout1.all_weights), 0) + + def test_model(self): + self.assertEqual(len(self.net.all_weights), 4) + + +if __name__ == '__main__': + + unittest.main() diff --git a/tests/layers/test_layers_core_nested.py b/tests/layers/test_layers_core_nested.py new file mode 100644 index 0000000..167d7af --- /dev/null +++ b/tests/layers/test_layers_core_nested.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*-\ +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorlayer as tl +import numpy as np + +from tests.utils import CustomTestCase + + +class Layer_nested(CustomTestCase): + + @classmethod + def setUpClass(cls): + print("##### begin testing nested layer #####") + + @classmethod + def tearDownClass(cls): + pass + + def test_nested_layer_with_inchannels(cls): + + class MyLayer(tl.layers.Module): + + def __init__(self, name=None): + super(MyLayer, self).__init__(name=name) + self.input_layer = tl.layers.Dense(in_channels=50, n_units=20) + self.build(None) + self._built = True + + def build(self, inputs_shape=None): + self.W = self._get_weights('weights', shape=(20, 10)) + + def forward(self, inputs): + inputs = self.input_layer(inputs) + output = tl.ops.matmul(inputs, self.W) + return output + + class model(tl.layers.Module): + + def __init__(self, name=None): + super(model, self).__init__(name=name) + self.layer = MyLayer() + + def forward(self, inputs): + return self.layer(inputs) + + input = tl.layers.Input(shape=(100, 50)) + model_dynamic = model() + model_dynamic.set_train() + cls.assertEqual(model_dynamic(input).shape, (100, 10)) + cls.assertEqual(len(model_dynamic.all_weights), 3) + cls.assertEqual(len(model_dynamic.trainable_weights), 3) + + model_dynamic.layer.input_layer.b.assign_add(tl.ops.ones((20, ))) + cls.assertEqual(np.sum(model_dynamic.all_weights[-1].numpy() - tl.ops.ones(20, ).numpy()), 0) + + +if __name__ == '__main__': + + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/layers/test_layers_deformable_convolution.py b/tests/layers/test_layers_deformable_convolution.py new file mode 100644 index 0000000..a449b42 --- /dev/null +++ b/tests/layers/test_layers_deformable_convolution.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorlayer as tl +from tests.utils import CustomTestCase + +class Layer_Convolution_2D_Test(CustomTestCase): + + @classmethod + def setUpClass(self): + print("\n#################################") + + self.batch_size = 5 + self.inputs_shape = [self.batch_size, 10, 10, 16] + self.input_layer = tl.layers.Input(self.inputs_shape, name='input_layer') + + self.offset1 = tl.layers.Conv2d(n_filter=18, filter_size=(3, 3), strides=(1, 1), padding='SAME', + name='offset1')(self.input_layer) + self.init_deformconv1 = tl.layers.DeformableConv2d( + offset_layer=self.offset1, n_filter=32, filter_size=(3, 3), act='relu', name='deformable1' + ) + self.deformconv1 = self.init_deformconv1(self.input_layer) + self.offset2 = tl.layers.Conv2d(n_filter=18, filter_size=(3, 3), strides=(1, 1), padding='SAME', + name='offset2')(self.deformconv1) + self.deformconv2 = tl.layers.DeformableConv2d( + offset_layer=self.offset2, n_filter=64, filter_size=(3, 3), act='relu', name='deformable2' + )(self.deformconv1) + + @classmethod + def tearDownClass(self): + pass + + def test_layer_n1(self): + + self.assertEqual(len(self.init_deformconv1.all_weights), 2) + self.assertEqual(tl.get_tensor_shape(self.deformconv1)[1:], [10, 10, 32]) + + def test_layer_n2(self): + self.assertEqual(tl.get_tensor_shape(self.deformconv2)[1:], [10, 10, 64]) + + +if __name__ == '__main__': + + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/layers/test_layers_dense.py b/tests/layers/test_layers_dense.py new file mode 100644 index 0000000..c8cc326 --- /dev/null +++ b/tests/layers/test_layers_dense.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorlayer as tl + +from tests.utils import CustomTestCase +import numpy as np + + +class Layer_BinaryDense_Test(CustomTestCase): + + @classmethod + def setUpClass(self): + print("-" * 20, "Layer_BinaryDense_Test", "-" * 20) + self.batch_size = 4 + self.inputs_shape = [self.batch_size, 10] + + self.ni = tl.layers.Input(self.inputs_shape, name='input_layer') + self.layer1 = tl.layers.BinaryDense(n_units=5) + + self.layer2 = tl.layers.BinaryDense(n_units=5, in_channels=10) + + self.n1 = self.layer1(self.ni) + self.n2 = self.layer2(self.ni) + + @classmethod + def tearDownClass(cls): + pass + + def test_layer_n1(self): + print(self.n1[0]) + self.assertEqual(tl.ops.ReduceSum()(self.n1).numpy() % 1, 0.0) # should be integer + + def test_layer_n2(self): + print(self.n2[0]) + self.assertEqual(tl.ops.ReduceSum()(self.n2).numpy() % 1, 0.0) # should be integer + + +class Layer_DorefaDense_Test(CustomTestCase): + + @classmethod + def setUpClass(self): + print("-" * 20, "Layer_DorefaDense_Test", "-" * 20) + self.batch_size = 4 + self.inputs_shape = [self.batch_size, 10] + + self.ni = tl.layers.Input(self.inputs_shape, name='input_layer') + self.layer1 = tl.layers.DorefaDense(n_units=5) + self.layer2 = tl.layers.DorefaDense(n_units=5, in_channels=10) + + self.n1 = self.layer1(self.ni) + self.n2 = self.layer2(self.ni) + + @classmethod + def tearDownClass(cls): + pass + + def test_layer_n1(self): + print(self.n1[0]) + + def test_layer_n2(self): + print(self.n2[0]) + + +class Layer_DropconnectDense_Test(CustomTestCase): + + @classmethod + def setUpClass(self): + print("-" * 20, "Layer_DropconnectDense_Test", "-" * 20) + self.batch_size = 4 + self.inputs_shape = [self.batch_size, 10] + + self.ni = tl.layers.Input(self.inputs_shape, name='input_layer') + self.layer1 = tl.layers.DropconnectDense(n_units=5, keep=1.0) + + self.layer2 = tl.layers.DropconnectDense(n_units=5, in_channels=10, keep=0.01) + + self.n1 = self.layer1(self.ni) + self.n2 = self.layer2(self.ni) + + @classmethod + def tearDownClass(cls): + pass + + def test_layer_n1(self): + print(self.n1[0]) + + def test_layer_n2(self): + print(self.n2[0]) + + +class Layer_QuanDense_Test(CustomTestCase): + + @classmethod + def setUpClass(self): + print("-" * 20, "Layer_QuanDense_Test", "-" * 20) + self.batch_size = 4 + self.inputs_shape = [self.batch_size, 10] + + self.ni = tl.layers.Input(self.inputs_shape, name='input_layer') + self.layer1 = tl.layers.QuanDense(n_units=5) + + self.layer2 = tl.layers.QuanDense(n_units=5, in_channels=10) + + self.n1 = self.layer1(self.ni) + self.n2 = self.layer2(self.ni) + + @classmethod + def tearDownClass(cls): + pass + + def test_layer_n1(self): + print(self.n1[0]) + + def test_layer_n2(self): + print(self.n2[0]) + + +class Layer_QuanDenseWithBN_Test(CustomTestCase): + + @classmethod + def setUpClass(self): + print("-" * 20, "Layer_QuanDenseWithBN_Test", "-" * 20) + self.batch_size = 4 + self.inputs_shape = [self.batch_size, 10] + + self.inputs = tl.initializers.TruncatedNormal()(shape=self.inputs_shape) + self.layer1 = tl.layers.QuanDenseWithBN(n_units=5) + self.layer2 = tl.layers.QuanDenseWithBN(n_units=5, in_channels=10) + + self.n1 = self.layer1(self.inputs) + self.n2 = self.layer2(self.inputs) + + @classmethod + def tearDownClass(cls): + pass + + def test_layer_n1(self): + print(self.n1[0]) + + def test_layer_n2(self): + print(self.n2[0]) + + +class Layer_TernaryDense_Test(CustomTestCase): + + @classmethod + def setUpClass(self): + print("-" * 20, "Layer_BinaryDense_Test", "-" * 20) + self.batch_size = 4 + self.inputs_shape = [self.batch_size, 10] + + self.inputs = tl.layers.Input(self.inputs_shape, name='input_layer') + self.layer1 = tl.layers.TernaryDense(n_units=5) + self.layer2 = tl.layers.TernaryDense(n_units=5, in_channels=10) + + self.n1 = self.layer1(self.inputs) + self.n2 = self.layer2(self.inputs) + + @classmethod + def tearDownClass(cls): + pass + + def test_layer_n1(self): + print(np.unique(self.n1.numpy().reshape(-1))) + print(self.n1[0]) + + def test_layer_n2(self): + print(np.unique(self.n2.numpy().reshape(-1))) + print(self.n2[0]) + + +if __name__ == '__main__': + + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/layers/test_layers_embedding.py b/tests/layers/test_layers_embedding.py new file mode 100644 index 0000000..832e47b --- /dev/null +++ b/tests/layers/test_layers_embedding.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorlayer as tl +import numpy as np + +from tests.utils import CustomTestCase + + +class Layer_Embed_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + pass + + @classmethod + def tearDownClass(cls): + pass + + def test_onehot(self): + input = tl.layers.Input([32], dtype=tl.int32) + onehot = tl.layers.OneHot(depth=8, on_value=1, off_value=0, axis=-1) + print(onehot) + tensor = tl.layers.OneHot(depth=8)(input) + self.assertEqual(tensor.get_shape().as_list(), [32, 8]) + + def test_embed(self): + input = tl.layers.Input([8, 100], dtype=tl.int32) + embed = tl.layers.Embedding(vocabulary_size=1000, embedding_size=50, name='embed') + print(embed) + tensor = embed(input) + self.assertEqual(tensor.get_shape().as_list(), [8, 100, 50]) + + def test_avg_embed(self): + batch_size = 8 + length = 5 + input = tl.layers.Input([batch_size, length], dtype=tl.int32) + avgembed = tl.layers.AverageEmbedding(vocabulary_size=1000, embedding_size=50, name='avg') + print(avgembed) + tensor = avgembed(input) + # print(tensor) + self.assertEqual(tensor.get_shape().as_list(), [batch_size, 50]) + + def test_word2vec_nce(self): + batch_size = 8 + embedding_size = 50 + inputs = tl.layers.Input([batch_size], dtype=tl.int32) + labels = tl.layers.Input([batch_size, 1], dtype=tl.int32) + emb_net = tl.layers.Word2vecEmbedding( + vocabulary_size=10000, + embedding_size=embedding_size, + num_sampled=100, + activate_nce_loss=True, # the nce loss is activated + nce_loss_args={}, + E_init=tl.initializers.random_uniform(minval=-1.0, maxval=1.0), + nce_W_init=tl.initializers.truncated_normal(stddev=float(1.0 / np.sqrt(embedding_size))), + nce_b_init=tl.initializers.constant(value=0.0), + ) + print(emb_net) + + embed_tensor = emb_net([inputs, labels], use_nce_loss=False) + embed_tensor, embed_nce_loss = emb_net([inputs, labels], use_nce_loss=True) + embed_tensor, embed_nce_loss = emb_net([inputs, labels]) + self.assertEqual(embed_tensor.get_shape().as_list(), [batch_size, embedding_size]) + + def test_word2vec_no_nce(self): + batch_size = 8 + embedding_size = 50 + inputs = tl.layers.Input([batch_size], dtype=tl.int32) + emb_net = tl.layers.Word2vecEmbedding( + vocabulary_size=10000, + embedding_size=embedding_size, + num_sampled=100, + activate_nce_loss=False, + nce_loss_args={}, + E_init=tl.initializers.random_uniform(minval=-1.0, maxval=1.0), + nce_W_init=tl.initializers.truncated_normal(stddev=float(1.0 / np.sqrt(embedding_size))), + nce_b_init=tl.initializers.constant(value=0.0), + ) + print(emb_net) + embed_tensor = emb_net(inputs) + embed_tensor = emb_net(inputs, use_nce_loss=False) + try: + embed_tensor = emb_net(inputs, use_nce_loss=True) + except AttributeError as e: + print(e) + self.assertEqual(embed_tensor.get_shape().as_list(), [batch_size, embedding_size]) + + +if __name__ == '__main__': + + unittest.main() diff --git a/tests/layers/test_layers_extend.py b/tests/layers/test_layers_extend.py new file mode 100644 index 0000000..22b6856 --- /dev/null +++ b/tests/layers/test_layers_extend.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class Layer_Extend_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + pass + + @classmethod + def tearDownClass(cls): + pass + + def test_expand_dims(self): + x = tl.layers.Input([8, 3]) + expandlayer = tl.layers.ExpandDims(axis=-1) + y = expandlayer(x) + self.assertEqual(tl.get_tensor_shape(y), [8, 3, 1]) + + def test_tile(self): + x = tl.layers.Input([8, 3]) + tilelayer = tl.layers.Tile(multiples=[2, 3]) + y = tilelayer(x) + self.assertEqual(tl.get_tensor_shape(y), [16, 9]) + + +if __name__ == '__main__': + + unittest.main() diff --git a/tests/layers/test_layers_lambda.py b/tests/layers/test_layers_lambda.py new file mode 100644 index 0000000..d2ab4a1 --- /dev/null +++ b/tests/layers/test_layers_lambda.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest +import numpy as np + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class Layer_Lambda_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + cls.data_x = np.random.random([100, 1]).astype(np.float32) + cls.data_y = cls.data_x**3 + np.random.random() * cls.data_x**2 + np.random.random() * cls.data_x + + @classmethod + def tearDownClass(cls): + pass + + def test_lambda_keras(self): + layers = [ + tf.keras.layers.Dense(10, activation=tf.nn.relu), + tf.keras.layers.Dense(5, activation=tf.nn.sigmoid), + tf.keras.layers.Dense(1, activation=tf.identity) + ] + perceptron = tf.keras.Sequential(layers) + # in order to get trainable_variables of keras + _ = perceptron(np.random.random([100, 5]).astype(np.float32)) + + class CustomizeModel(tl.layers.Module): + + def __init__(self): + super(CustomizeModel, self).__init__() + self.dense = tl.layers.Dense(in_channels=1, n_units=5) + self.lambdalayer = tl.layers.Lambda(perceptron, perceptron.trainable_variables) + + def forward(self, x): + z = self.dense(x) + z = self.lambdalayer(z) + return z + + optimizer = tf.optimizers.Adam(learning_rate=0.1) + + model = CustomizeModel() + print(model.lambdalayer) + + model.set_train() + + for epoch in range(10): + with tf.GradientTape() as tape: + pred_y = model(self.data_x) + loss = tl.cost.mean_squared_error(pred_y, self.data_y) + + gradients = tape.gradient(loss, model.trainable_weights) + optimizer.apply_gradients(zip(gradients, model.trainable_weights)) + + print("epoch %d, loss %f" % (epoch, loss)) + + def test_lambda_func_with_args(self): + + def customize_func(x, foo=42): + if foo == 0: + return tf.nn.relu(x) + elif foo == 1: + return tf.nn.sigmoid(x) + else: + return tf.identity(x) + + class CustomizeModel(tl.layers.Module): + + def __init__(self): + super(CustomizeModel, self).__init__() + self.dense = tl.layers.Dense(in_channels=1, n_units=5) + self.lambdalayer = tl.layers.Lambda(customize_func, fn_weights=[], fn_args={'foo': 0}) + + def forward(self, x, bar): + z = self.dense(x) + if bar == -1: + zf = self.lambdalayer(z) + else: + zf = self.lambdalayer(z, foo=bar) + return z, zf + + model = CustomizeModel() + print(model.lambdalayer) + model.set_train() + + out, out2 = model(self.data_x, bar=-1) + self.assertTrue(np.array_equal(out2.numpy(), tf.nn.relu(out).numpy())) + out, out2 = model(self.data_x, bar=0) + self.assertTrue(np.array_equal(out2.numpy(), tf.nn.relu(out).numpy())) + out, out2 = model(self.data_x, bar=1) + self.assertTrue(np.array_equal(out2.numpy(), tf.nn.sigmoid(out).numpy())) + out, out2 = model(self.data_x, bar=2) + self.assertTrue(np.array_equal(out2.numpy(), out.numpy())) + + def test_lambda_func_with_weight(self): + + a = tf.Variable(1.0) + + def customize_fn(x): + return x + a + + class CustomizeModel(tl.layers.Module): + + def __init__(self): + super(CustomizeModel, self).__init__() + self.dense = tl.layers.Dense(in_channels=1, n_units=5) + self.lambdalayer = tl.layers.Lambda(customize_fn, fn_weights=[a]) + + def forward(self, x): + z = self.dense(x) + z = self.lambdalayer(z) + return z + + model = CustomizeModel() + print(model.lambdalayer) + model.set_train() + + out = model(self.data_x) + print(out.shape) + + def test_lambda_func_without_args(self): + + class CustomizeModel(tl.layers.Module): + + def __init__(self): + super(CustomizeModel, self).__init__() + self.dense = tl.layers.Dense(in_channels=1, n_units=5) + self.lambdalayer = tl.layers.Lambda(lambda x: 2 * x) + + def forward(self, x): + z = self.dense(x) + zf = self.lambdalayer(z) + return z, zf + + model = CustomizeModel() + print(model.lambdalayer) + model.set_train() + + out, out2 = model(self.data_x) + self.assertTrue(np.array_equal(out2.numpy(), out.numpy() * 2)) + + def test_elementwiselambda_func_with_args(self): + + def customize_func(noise, mean, std, foo=42): + return mean + noise * tf.exp(std * 0.5) + foo + + class CustomizeModel(tl.layers.Module): + + def __init__(self): + super(CustomizeModel, self).__init__() + self.dense1 = tl.layers.Dense(in_channels=1, n_units=5) + self.dense2 = tl.layers.Dense(in_channels=1, n_units=5) + self.dense3 = tl.layers.Dense(in_channels=1, n_units=5) + self.lambdalayer = tl.layers.ElementwiseLambda(customize_func, fn_args={'foo': 1024}) + + def forward(self, x, bar=None): + noise = self.dense1(x) + mean = self.dense2(x) + std = self.dense3(x) + if bar is None: + out = self.lambdalayer([noise, mean, std]) + else: + out = self.lambdalayer([noise, mean, std], foo=bar) + return noise, mean, std, out + + model = CustomizeModel() + print(model.lambdalayer) + model.set_train() + + noise, mean, std, out = model(self.data_x) + self.assertTrue(np.allclose(out.numpy(), customize_func(noise, mean, std, foo=1024).numpy())) + noise, mean, std, out = model(self.data_x, bar=2048) + self.assertTrue(np.allclose(out.numpy(), customize_func(noise, mean, std, foo=2048).numpy())) + + def test_elementwiselambda_func_without_args(self): + + def customize_func(noise, mean, std): + return mean + noise * tf.exp(std * 0.5) + + class CustomizeModel(tl.layers.Module): + + def __init__(self): + super(CustomizeModel, self).__init__() + self.dense1 = tl.layers.Dense(in_channels=1, n_units=5) + self.dense2 = tl.layers.Dense(in_channels=1, n_units=5) + self.dense3 = tl.layers.Dense(in_channels=1, n_units=5) + self.lambdalayer = tl.layers.ElementwiseLambda(customize_func, fn_weights=[]) + + def forward(self, x): + noise = self.dense1(x) + mean = self.dense2(x) + std = self.dense3(x) + out = self.lambdalayer([noise, mean, std]) + return noise, mean, std, out + + model = CustomizeModel() + print(model.lambdalayer) + model.set_train() + + noise, mean, std, out = model(self.data_x) + self.assertTrue(np.array_equal(out.numpy(), customize_func(noise, mean, std).numpy())) + + +if __name__ == '__main__': + + unittest.main() diff --git a/tests/layers/test_layers_merge.py b/tests/layers/test_layers_merge.py new file mode 100644 index 0000000..0aeb763 --- /dev/null +++ b/tests/layers/test_layers_merge.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import numpy as np +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class Layer_Merge_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + pass + + @classmethod + def tearDownClass(cls): + pass + + def test_concat(self): + + class CustomModel(tl.layers.Module): + + def __init__(self): + super(CustomModel, self).__init__() + self.dense1 = tl.layers.Dense(in_channels=20, n_units=10, act=tl.ReLU, name='relu1_1') + self.dense2 = tl.layers.Dense(in_channels=20, n_units=10, act=tl.ReLU, name='relu2_1') + self.concat = tl.layers.Concat(concat_dim=1, name='concat_layer') + + def forward(self, inputs): + d1 = self.dense1(inputs) + d2 = self.dense2(inputs) + outputs = self.concat([d1, d2]) + return outputs + + model = CustomModel() + model.set_train() + inputs = tl.ops.convert_to_tensor(np.random.random([4, 20]).astype(np.float32)) + outputs = model(inputs) + print(model) + + self.assertEqual(outputs.get_shape().as_list(), [4, 20]) + + def test_elementwise(self): + + class CustomModel(tl.layers.Module): + + def __init__(self): + super(CustomModel, self).__init__() + self.dense1 = tl.layers.Dense(in_channels=20, n_units=10, act=tl.ReLU, name='relu1_1') + self.dense2 = tl.layers.Dense(in_channels=20, n_units=10, act=tl.ReLU, name='relu2_1') + self.element = tl.layers.Elementwise(combine_fn=tl.minimum, name='minimum', act=None) + + def forward(self, inputs): + d1 = self.dense1(inputs) + d2 = self.dense2(inputs) + outputs = self.element([d1, d2]) + return outputs, d1, d2 + + model = CustomModel() + model.set_train() + inputs = tl.ops.convert_to_tensor(np.random.random([4, 20]).astype(np.float32)) + outputs, d1, d2 = model(inputs) + print(model) + + min = tl.ops.minimum(d1, d2) + self.assertEqual(outputs.get_shape().as_list(), [4, 10]) + self.assertTrue(np.array_equal(min.numpy(), outputs.numpy())) + + +if __name__ == '__main__': + + unittest.main() diff --git a/tests/layers/test_layers_noise.py b/tests/layers/test_layers_noise.py new file mode 100644 index 0000000..2a8b4fe --- /dev/null +++ b/tests/layers/test_layers_noise.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorlayer as tl +from tensorlayer.layers import * + +from tests.utils import CustomTestCase + + +class Layer_Convolution_1D_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + print("\n#################################") + + cls.batch_size = 8 + cls.inputs_shape = [cls.batch_size, 200] + cls.input_layer = Input(cls.inputs_shape, name='input_layer') + + cls.dense = tl.layers.Dense(n_units=100, act=tl.ReLU, in_channels=200)(cls.input_layer) + + cls.noiselayer = tl.layers.GaussianNoise(name='gaussian')(cls.dense) + + @classmethod + def tearDownClass(cls): + pass + + def test_layer_n1(self): + self.assertEqual(self.noiselayer.get_shape().as_list()[1:], [100]) + + +if __name__ == '__main__': + + # tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/layers/test_layers_normalization.py b/tests/layers/test_layers_normalization.py new file mode 100644 index 0000000..51cd413 --- /dev/null +++ b/tests/layers/test_layers_normalization.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorlayer as tl +from tensorlayer.layers import * +from tests.utils import CustomTestCase + + +class Laye_BatchNorm_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + + x_0_input_shape = [None, 10] + x_1_input_shape = [None, 100, 1] + x_2_input_shape = [None, 100, 100, 3] + x_3_input_shape = [None, 100, 100, 100, 3] + batchsize = 2 + + cls.x0 = tl.ops.truncated_normal(shape=[batchsize] + x_0_input_shape[1:]) + cls.x1 = tl.ops.truncated_normal([batchsize] + x_1_input_shape[1:]) + cls.x2 = tl.ops.truncated_normal([batchsize] + x_2_input_shape[1:]) + cls.x3 = tl.ops.truncated_normal([batchsize] + x_3_input_shape[1:]) + + ## Base + ni_1 = Input(x_1_input_shape, name='test_ni1') + nn_1 = Conv1d(n_filter=32, filter_size=5, stride=2, name='test_conv1d')(ni_1) + n1_b = BatchNorm(name='test_conv')(nn_1) + cls.n1_b = n1_b + + ni_2 = Input(x_2_input_shape, name='test_ni2') + nn_2 = Conv2d(n_filter=32, filter_size=(3, 3), strides=(2, 2), name='test_conv2d')(ni_2) + n2_b = BatchNorm(name='test_bn2d')(nn_2) + cls.n2_b = n2_b + + ni_3 = Input(x_3_input_shape, name='test_ni2') + nn_3 = Conv3d(n_filter=32, filter_size=(3, 3, 3), strides=(2, 2, 2), name='test_conv3d')(ni_3) + n3_b = BatchNorm(name='test_bn3d')(nn_3) + cls.n3_b = n3_b + + class bn_0d_model(tl.layers.Module): + + def __init__(self): + super(bn_0d_model, self).__init__() + self.fc = Dense(32, in_channels=10) + self.bn = BatchNorm(num_features=32, name='test_bn1d') + + def forward(self, x): + x = self.bn(self.fc(x)) + return x + + dynamic_base = bn_0d_model() + cls.n0_b = dynamic_base(cls.x0) + + ## 0D ======================================================================== + + nin_0 = Input(x_0_input_shape, name='test_in1') + + n0 = Dense(32)(nin_0) + n0 = BatchNorm1d(name='test_bn0d')(n0) + + cls.n0 = n0 + + class bn_0d_model(tl.layers.Module): + + def __init__(self): + super(bn_0d_model, self).__init__(name='test_bn_0d_model') + self.fc = Dense(32, in_channels=10) + self.bn = BatchNorm1d(num_features=32, name='test_bn1d') + + def forward(self, x): + x = self.bn(self.fc(x)) + return x + + cls.dynamic_0d = bn_0d_model() + + ## 1D ======================================================================== + + nin_1 = Input(x_1_input_shape, name='test_in1') + + n1 = Conv1d(n_filter=32, filter_size=5, stride=2, name='test_conv1d')(nin_1) + n1 = BatchNorm1d(name='test_bn1d')(n1) + + cls.n1 = n1 + + class bn_1d_model(tl.layers.Module): + + def __init__(self): + super(bn_1d_model, self).__init__(name='test_bn_1d_model') + self.conv = Conv1d(n_filter=32, filter_size=5, stride=2, name='test_conv1d', in_channels=1) + self.bn = BatchNorm1d(num_features=32, name='test_bn1d') + + def forward(self, x): + x = self.bn(self.conv(x)) + return x + + cls.dynamic_1d = bn_1d_model() + + ## 2D ======================================================================== + + nin_2 = Input(x_2_input_shape, name='test_in2') + + n2 = Conv2d(n_filter=32, filter_size=(3, 3), strides=(2, 2), name='test_conv2d')(nin_2) + n2 = BatchNorm2d(name='test_bn2d')(n2) + + cls.n2 = n2 + + class bn_2d_model(tl.layers.Module): + + def __init__(self): + super(bn_2d_model, self).__init__(name='test_bn_2d_model') + self.conv = Conv2d(n_filter=32, filter_size=(3, 3), strides=(2, 2), name='test_conv2d', in_channels=3) + self.bn = BatchNorm2d(num_features=32, name='test_bn2d') + + def forward(self, x): + x = self.bn(self.conv(x)) + return x + + cls.dynamic_2d = bn_2d_model() + + ## 3D ======================================================================== + + nin_3 = Input(x_3_input_shape, name='test_in3') + + n3 = Conv3d(n_filter=32, filter_size=(3, 3, 3), strides=(2, 2, 2), name='test_conv3d')(nin_3) + n3 = BatchNorm3d(name='test_bn3d', act=tl.ReLU)(n3) + + cls.n3 = n3 + + class bn_3d_model(tl.layers.Module): + + def __init__(self): + super(bn_3d_model, self).__init__(name='test_bn_3d_model') + self.conv = Conv3d( + n_filter=32, filter_size=(3, 3, 3), strides=(2, 2, 2), name='test_conv3d', in_channels=3 + ) + self.bn = BatchNorm3d(num_features=32, name='test_bn3d') + + def forward(self, x): + x = self.bn(self.conv(x)) + return x + + cls.dynamic_3d = bn_3d_model() + + + @classmethod + def tearDownClass(cls): + pass + # tf.reset_default_graph() + + def test_BatchNorm(self): + self.assertEqual(self.n1_b.shape[1:], (50, 32)) + + self.assertEqual(self.n2_b.shape[1:], (50, 50, 32)) + + self.assertEqual(self.n3_b.shape[1:], (50, 50, 50, 32)) + + self.assertEqual(self.n0_b.shape[1:], (32)) + print("test_BatchNorm OK") + + def test_BatchNorm0d(self): + self.assertEqual(self.n0.shape[1:], (32)) + + def test_BatchNorm1d(self): + self.assertEqual(self.n1.shape[1:], (50, 32)) + + def test_BatchNorm2d(self): + self.assertEqual(self.n2.shape[1:], (50, 50, 32)) + + def test_BatchNorm3d(self): + self.assertEqual(self.n3.shape[1:], (50, 50, 50, 32)) + + def test_dataformat(self): + bn1d = BatchNorm1d(data_format='channels_first', num_features=32) + bn2d = BatchNorm2d(data_format='channels_first', num_features=32) + bn3d = BatchNorm3d(data_format='channels_first', num_features=32) + bn = BatchNorm(data_format='channels_first') + + try: + bn_fail = BatchNorm1d(data_format='xyz', num_features=32) + except Exception as e: + self.assertIsInstance(e, ValueError) + print(e) + + def test_exception(self): + try: + bn = BatchNorm(num_features=32) + except Exception as e: + self.assertIsInstance(e, ValueError) + print(e) + + try: + ni = Input([None, 100, 1], name='test_ni1') + bn = BatchNorm(decay=1.5)(ni) + except Exception as e: + self.assertIsInstance(e, ValueError) + print(e) + + +if __name__ == '__main__': + + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/layers/test_layers_padding.py b/tests/layers/test_layers_padding.py new file mode 100644 index 0000000..fa3e13b --- /dev/null +++ b/tests/layers/test_layers_padding.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class Layer_Padding_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + + ## 1D + cls.input_layer1 = tl.layers.Input([None, 100, 1], name='input_layer1') + + n1 = tl.layers.ZeroPad1d(padding=1)(cls.input_layer1) + n2 = tl.layers.ZeroPad1d(padding=(2, 3))(cls.input_layer1) + + cls.n1_shape = n1.get_shape().as_list() + cls.n2_shape = n2.get_shape().as_list() + + ## 2D + cls.input_layer2 = tl.layers.Input([None, 100, 100, 3], name='input_layer2') + + n0 = tl.layers.PadLayer([[0, 0], [3, 3], [3, 3], [0, 0]], "REFLECT", name='inpad')(cls.input_layer2) + n3 = tl.layers.ZeroPad2d(padding=2)(cls.input_layer2) + n4 = tl.layers.ZeroPad2d(padding=(2, 3))(cls.input_layer2) + n5 = tl.layers.ZeroPad2d(padding=((3, 3), (4, 4)))(cls.input_layer2) + + cls.n0_shape = n0.get_shape().as_list() + cls.n3_shape = n3.get_shape().as_list() + cls.n4_shape = n4.get_shape().as_list() + cls.n5_shape = n5.get_shape().as_list() + + ## 3D + cls.input_layer3 = tl.layers.Input([None, 100, 100, 100, 3], name='input_layer3') + + n6 = tl.layers.ZeroPad3d(padding=2)(cls.input_layer3) + n7 = tl.layers.ZeroPad3d(padding=(2, 3, 4))(cls.input_layer3) + n8 = tl.layers.ZeroPad3d(padding=((3, 3), (4, 4), (5, 5)))(cls.input_layer3) + + cls.n6_shape = n6.get_shape().as_list() + cls.n7_shape = n7.get_shape().as_list() + cls.n8_shape = n8.get_shape().as_list() + + @classmethod + def tearDownClass(cls): + pass + + def test_n0_shape(self): + self.assertEqual(self.n0_shape[1:], [106, 106, 3]) + + def test_n1_shape(self): + self.assertEqual(self.n1_shape[1:], [102, 1]) + + def test_n2_shape(self): + self.assertEqual(self.n2_shape[1:], [105, 1]) + + def test_n3_shape(self): + self.assertEqual(self.n3_shape[1:], [104, 104, 3]) + + def test_n4_shape(self): + self.assertEqual(self.n4_shape[1:], [104, 106, 3]) + + def test_n5_shape(self): + self.assertEqual(self.n5_shape[1:], [106, 108, 3]) + + def test_n6_shape(self): + self.assertEqual(self.n6_shape[1:], [104, 104, 104, 3]) + + def test_n7_shape(self): + self.assertEqual(self.n7_shape[1:], [104, 106, 108, 3]) + + def test_n8_shape(self): + self.assertEqual(self.n8_shape[1:], [106, 108, 110, 3]) + + +if __name__ == '__main__': + + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/layers/test_layers_pooling.py b/tests/layers/test_layers_pooling.py new file mode 100644 index 0000000..39582aa --- /dev/null +++ b/tests/layers/test_layers_pooling.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorlayer as tl +from tensorlayer.layers import * + +from tests.utils import CustomTestCase + + +class Layer_Pooling_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + + ## 1D ======================================================================== + + x_1_input_shape = [None, 100, 1] + nin_1 = Input(x_1_input_shape, name='test_in1') + + n1 = tl.layers.Conv1d(n_filter=32, filter_size=5, stride=2, name='test_conv1d')(nin_1) + n2 = tl.layers.MaxPool1d(filter_size=3, strides=2, padding='SAME', name='test_maxpool1d')(n1) + n3 = tl.layers.MeanPool1d(filter_size=3, strides=2, padding='SAME', name='test_meanpool1d')(n1) + n4 = tl.layers.GlobalMaxPool1d(name='test_maxpool1d')(n1) + n5 = tl.layers.GlobalMeanPool1d(name='test_meanpool1d')(n1) + n16 = tl.layers.MaxPool1d(filter_size=3, strides=1, padding='VALID', dilation_rate=2, name='test_maxpool1d')(n1) + n17 = tl.layers.MeanPool1d(filter_size=3, strides=1, padding='VALID', dilation_rate=2, + name='test_meanpool1d')(n1) + + cls.n1_shape = n1.get_shape().as_list() + cls.n2_shape = n2.get_shape().as_list() + cls.n3_shape = n3.get_shape().as_list() + cls.n4_shape = n4.get_shape().as_list() + cls.n5_shape = n5.get_shape().as_list() + cls.n16_shape = n16.get_shape().as_list() + cls.n17_shape = n17.get_shape().as_list() + + ## 2D ======================================================================== + + x_2_input_shape = [None, 100, 100, 3] + nin_2 = Input(x_2_input_shape, name='test_in2') + + n6 = tl.layers.Conv2d(n_filter=32, filter_size=(3, 3), strides=(2, 2), name='test_conv2d')(nin_2) + n7 = tl.layers.MaxPool2d(filter_size=(3, 3), strides=(2, 2), padding='SAME', name='test_maxpool2d')(n6) + n8 = tl.layers.MeanPool2d(filter_size=(3, 3), strides=(2, 2), padding='SAME', name='test_meanpool2d')(n6) + n9 = tl.layers.GlobalMaxPool2d(name='test_maxpool2d')(n6) + n10 = tl.layers.GlobalMeanPool2d(name='test_meanpool2d')(n6) + n15 = tl.layers.PoolLayer(name='test_pool2d')(n6) + # n18 = tl.layers.CornerPool2d('TopLeft', name='test_cornerpool2d')(n6) + + cls.n6_shape = n6.get_shape().as_list() + cls.n7_shape = n7.get_shape().as_list() + cls.n8_shape = n8.get_shape().as_list() + cls.n9_shape = n9.get_shape().as_list() + cls.n10_shape = n10.get_shape().as_list() + cls.n15_shape = n15.get_shape().as_list() + # cls.n18_shape = n18.get_shape().as_list() + + + ## 3D ======================================================================== + + x_3_input_shape = [None, 100, 100, 100, 3] + nin_3 = Input(x_3_input_shape, name='test_in3') + + n11 = tl.layers.MeanPool3d(filter_size=(3, 3, 3), strides=(2, 2, 2), padding='SAME', + name='test_meanpool3d')(nin_3) + n12 = tl.layers.GlobalMaxPool3d(name='test_maxpool3d')(nin_3) + n13 = tl.layers.GlobalMeanPool3d(name='test_meanpool3d')(nin_3) + n14 = tl.layers.MaxPool3d(filter_size=(3, 3, 3), strides=(2, 2, 2), padding='SAME', + name='test_maxpool3d')(nin_3) + + cls.n11_shape = n11.get_shape().as_list() + cls.n12_shape = n12.get_shape().as_list() + cls.n13_shape = n13.get_shape().as_list() + cls.n14_shape = n14.get_shape().as_list() + + @classmethod + def tearDownClass(cls): + pass + # tf.reset_default_graph() + + def test_n1_shape(self): + self.assertEqual(self.n1_shape[1:3], [50, 32]) + + def test_n2_shape(self): + self.assertEqual(self.n2_shape[1:3], [25, 32]) + + def test_n3_shape(self): + self.assertEqual(self.n3_shape[1:3], [25, 32]) + + def test_n4_shape(self): + self.assertEqual(self.n4_shape[-1], 32) + + def test_n5_shape(self): + self.assertEqual(self.n5_shape[-1], 32) + + def test_n6_shape(self): + self.assertEqual(self.n6_shape[1:4], [50, 50, 32]) + + def test_n7_shape(self): + self.assertEqual(self.n7_shape[1:4], [25, 25, 32]) + + def test_n8_shape(self): + self.assertEqual(self.n8_shape[1:4], [25, 25, 32]) + + def test_n9_shape(self): + self.assertEqual(self.n9_shape[-1], 32) + + def test_n10_shape(self): + self.assertEqual(self.n10_shape[-1], 32) + + def test_n11_shape(self): + self.assertEqual(self.n11_shape[1:5], [50, 50, 50, 3]) + + def test_n12_shape(self): + self.assertEqual(self.n12_shape[-1], 3) + + def test_n13_shape(self): + self.assertEqual(self.n13_shape[-1], 3) + + def test_n14_shape(self): + self.assertEqual(self.n14_shape[1:5], [50, 50, 50, 3]) + + def test_n15_shape(self): + self.assertEqual(self.n15_shape[1:4], [25, 25, 32]) + + def test_n16_shape(self): + self.assertEqual(self.n16_shape[1:4], [46, 32]) + + def test_n17_shape(self): + self.assertEqual(self.n17_shape[1:4], [46, 32]) + + # def test_n18_shape(self): + # self.assertEqual(self.n18_shape[1:], [50, 50, 32]) + + +if __name__ == '__main__': + + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/layers/test_layers_recurrent.py b/tests/layers/test_layers_recurrent.py new file mode 100644 index 0000000..011327e --- /dev/null +++ b/tests/layers/test_layers_recurrent.py @@ -0,0 +1,926 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import numpy as np +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class Layer_RNN_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + + cls.batch_size = 2 + + cls.vocab_size = 20 + cls.embedding_size = 4 + + cls.hidden_size = 8 + cls.num_steps = 6 + + cls.data_n_steps = np.random.randint(low=cls.num_steps // 2, high=cls.num_steps + 1, size=cls.batch_size) + cls.data_x = np.random.random([cls.batch_size, cls.num_steps, cls.embedding_size]).astype(np.float32) + + for i in range(cls.batch_size): + for j in range(cls.data_n_steps[i], cls.num_steps): + cls.data_x[i][j][:] = 0 + + cls.data_y = np.zeros([cls.batch_size, 1]).astype(np.float32) + cls.data_y2 = np.zeros([cls.batch_size, cls.num_steps]).astype(np.float32) + + map1 = np.random.random([1, cls.num_steps]) + map2 = np.random.random([cls.embedding_size, 1]) + for i in range(cls.batch_size): + cls.data_y[i] = np.matmul(map1, np.matmul(cls.data_x[i], map2)) + cls.data_y2[i] = np.matmul(cls.data_x[i], map2)[:, 0] + + @classmethod + def tearDownClass(cls): + pass + + def test_basic_simplernn(self): + + inputs = tl.layers.Input([self.batch_size, self.num_steps, self.embedding_size]) + rnnlayer = tl.layers.RNN( + cell=tf.keras.layers.SimpleRNNCell(units=self.hidden_size, dropout=0.1), return_last_output=True, + return_seq_2d=False, return_last_state=True + ) + rnn, rnn_state = rnnlayer(inputs) + outputs = tl.layers.Dense(n_units=1)(rnn) + rnn_model = tl.models.Model(inputs=inputs, outputs=[outputs, rnn_state[0]]) + print(rnn_model) + + optimizer = tf.optimizers.Adam(learning_rate=0.01) + + rnn_model.train() + assert rnnlayer.is_train + + for epoch in range(50): + with tf.GradientTape() as tape: + pred_y, final_state = rnn_model(self.data_x) + loss = tl.cost.mean_squared_error(pred_y, self.data_y) + + gradients = tape.gradient(loss, rnn_model.trainable_weights) + optimizer.apply_gradients(zip(gradients, rnn_model.trainable_weights)) + + if (epoch + 1) % 10 == 0: + print("epoch %d, loss %f" % (epoch, loss)) + + def test_basic_simplernn_class(self): + + inputs = tl.layers.Input([self.batch_size, self.num_steps, self.embedding_size]) + rnnlayer = tl.layers.SimpleRNN( + units=self.hidden_size, dropout=0.1, return_last_output=True, return_seq_2d=False, return_last_state=True + ) + rnn, rnn_state = rnnlayer(inputs) + outputs = tl.layers.Dense(n_units=1)(rnn) + rnn_model = tl.models.Model(inputs=inputs, outputs=[outputs, rnn_state[0]]) + print(rnn_model) + + optimizer = tf.optimizers.Adam(learning_rate=0.01) + + rnn_model.train() + assert rnnlayer.is_train + + for epoch in range(50): + with tf.GradientTape() as tape: + pred_y, final_state = rnn_model(self.data_x) + loss = tl.cost.mean_squared_error(pred_y, self.data_y) + + gradients = tape.gradient(loss, rnn_model.trainable_weights) + optimizer.apply_gradients(zip(gradients, rnn_model.trainable_weights)) + + if (epoch + 1) % 10 == 0: + print("epoch %d, loss %f" % (epoch, loss)) + + def test_basic_simplernn2(self): + + inputs = tl.layers.Input([self.batch_size, self.num_steps, self.embedding_size]) + rnnlayer = tl.layers.RNN( + cell=tf.keras.layers.SimpleRNNCell(units=self.hidden_size, dropout=0.1), return_last_output=False, + return_seq_2d=True, return_last_state=False + ) + rnn = rnnlayer(inputs) + outputs = tl.layers.Dense(n_units=1)(rnn) + rnn_model = tl.models.Model(inputs=inputs, outputs=[outputs, rnn]) + print(rnn_model) + + rnn_model.eval() + assert not rnnlayer.is_train + + pred_y, rnn_y = rnn_model(self.data_x) + self.assertEqual(pred_y.get_shape().as_list(), [self.batch_size * self.num_steps, 1]) + self.assertEqual(rnn_y.get_shape().as_list(), [self.batch_size * self.num_steps, self.hidden_size]) + + def test_basic_simplernn3(self): + + inputs = tl.layers.Input([self.batch_size, self.num_steps, self.embedding_size]) + rnnlayer = tl.layers.RNN( + cell=tf.keras.layers.SimpleRNNCell(units=self.hidden_size, dropout=0.1), return_last_output=False, + return_seq_2d=False, return_last_state=False + ) + rnn = rnnlayer(inputs) + rnn_model = tl.models.Model(inputs=inputs, outputs=rnn) + print(rnn_model) + + rnn_model.eval() + assert not rnnlayer.is_train + + rnn_y = rnn_model(self.data_x) + self.assertEqual(rnn_y.get_shape().as_list(), [self.batch_size, self.num_steps, self.hidden_size]) + + def test_basic_simplernn_dynamic(self): + + class CustomisedModel(tl.models.Model): + + def __init__(self): + super(CustomisedModel, self).__init__() + self.rnnlayer = tl.layers.RNN( + cell=tf.keras.layers.SimpleRNNCell(units=8, dropout=0.1), in_channels=4, return_last_output=False, + return_seq_2d=False, return_last_state=False + ) + self.dense = tl.layers.Dense(in_channels=8, n_units=1) + + def forward(self, x): + z = self.rnnlayer(x) + z = self.dense(z[:, -1, :]) + return z + + rnn_model = CustomisedModel() + print(rnn_model) + optimizer = tf.optimizers.Adam(learning_rate=0.01) + rnn_model.train() + + for epoch in range(50): + with tf.GradientTape() as tape: + pred_y = rnn_model(self.data_x) + loss = tl.cost.mean_squared_error(pred_y, self.data_y) + + gradients = tape.gradient(loss, rnn_model.trainable_weights) + optimizer.apply_gradients(zip(gradients, rnn_model.trainable_weights)) + + if (epoch + 1) % 10 == 0: + print("epoch %d, loss %f" % (epoch, loss)) + + def test_basic_simplernn_dynamic_class(self): + + class CustomisedModel(tl.models.Model): + + def __init__(self): + super(CustomisedModel, self).__init__() + self.rnnlayer = tl.layers.SimpleRNN( + units=8, dropout=0.1, in_channels=4, return_last_output=False, return_seq_2d=False, + return_last_state=False + ) + self.dense = tl.layers.Dense(in_channels=8, n_units=1) + + def forward(self, x): + z = self.rnnlayer(x) + z = self.dense(z[:, -1, :]) + return z + + rnn_model = CustomisedModel() + print(rnn_model) + optimizer = tf.optimizers.Adam(learning_rate=0.01) + rnn_model.train() + + for epoch in range(50): + with tf.GradientTape() as tape: + pred_y = rnn_model(self.data_x) + loss = tl.cost.mean_squared_error(pred_y, self.data_y) + + gradients = tape.gradient(loss, rnn_model.trainable_weights) + optimizer.apply_gradients(zip(gradients, rnn_model.trainable_weights)) + + if (epoch + 1) % 10 == 0: + print("epoch %d, loss %f" % (epoch, loss)) + + def test_basic_simplernn_dynamic_2(self): + + class CustomisedModel(tl.models.Model): + + def __init__(self): + super(CustomisedModel, self).__init__() + self.rnnlayer = tl.layers.RNN( + cell=tf.keras.layers.SimpleRNNCell(units=8, dropout=0.1), in_channels=4, return_last_output=False, + return_seq_2d=False, return_last_state=False + ) + self.dense = tl.layers.Dense(in_channels=8, n_units=1) + + def forward(self, x): + z = self.rnnlayer(x, return_seq_2d=True) + z = self.dense(z[-2:, :]) + return z + + rnn_model = CustomisedModel() + print(rnn_model) + optimizer = tf.optimizers.Adam(learning_rate=0.01) + rnn_model.train() + assert rnn_model.rnnlayer.is_train + + for epoch in range(50): + with tf.GradientTape() as tape: + pred_y = rnn_model(self.data_x) + loss = tl.cost.mean_squared_error(pred_y, self.data_y) + + gradients = tape.gradient(loss, rnn_model.trainable_weights) + optimizer.apply_gradients(zip(gradients, rnn_model.trainable_weights)) + + if (epoch + 1) % 10 == 0: + print("epoch %d, loss %f" % (epoch, loss)) + + def test_basic_simplernn_dynamic_3(self): + + class CustomisedModel(tl.models.Model): + + def __init__(self): + super(CustomisedModel, self).__init__() + self.rnnlayer1 = tl.layers.RNN( + cell=tf.keras.layers.SimpleRNNCell(units=8, dropout=0.1), in_channels=4, return_last_output=True, + return_last_state=True + ) + self.rnnlayer2 = tl.layers.RNN( + cell=tf.keras.layers.SimpleRNNCell(units=8, dropout=0.1), in_channels=4, return_last_output=True, + return_last_state=False + ) + self.dense = tl.layers.Dense(in_channels=8, n_units=1) + + def forward(self, x): + _, state = self.rnnlayer1(x[:, :2, :]) + z = self.rnnlayer2(x[:, 2:, :], initial_state=state) + z = self.dense(z) + return z + + rnn_model = CustomisedModel() + print(rnn_model) + optimizer = tf.optimizers.Adam(learning_rate=0.01) + rnn_model.train() + assert rnn_model.rnnlayer1.is_train + assert rnn_model.rnnlayer2.is_train + + for epoch in range(50): + with tf.GradientTape() as tape: + pred_y = rnn_model(self.data_x) + loss = tl.cost.mean_squared_error(pred_y, self.data_y) + + gradients = tape.gradient(loss, rnn_model.trainable_weights) + optimizer.apply_gradients(zip(gradients, rnn_model.trainable_weights)) + + if (epoch + 1) % 10 == 0: + print("epoch %d, loss %f" % (epoch, loss)) + + def test_basic_lstmrnn(self): + + inputs = tl.layers.Input([self.batch_size, self.num_steps, self.embedding_size]) + rnnlayer = tl.layers.RNN( + cell=tf.keras.layers.LSTMCell(units=self.hidden_size, dropout=0.1), return_last_output=True, + return_seq_2d=False, return_last_state=True + ) + rnn, rnn_state = rnnlayer(inputs) + outputs = tl.layers.Dense(n_units=1)(rnn) + rnn_model = tl.models.Model(inputs=inputs, outputs=[outputs, rnn_state[0], rnn_state[1]]) + print(rnn_model) + + optimizer = tf.optimizers.Adam(learning_rate=0.01) + + rnn_model.train() + + for epoch in range(50): + with tf.GradientTape() as tape: + pred_y, final_h, final_c = rnn_model(self.data_x) + loss = tl.cost.mean_squared_error(pred_y, self.data_y) + + gradients = tape.gradient(loss, rnn_model.trainable_weights) + optimizer.apply_gradients(zip(gradients, rnn_model.trainable_weights)) + + if (epoch + 1) % 10 == 0: + print("epoch %d, loss %f" % (epoch, loss)) + + def test_basic_lstmrnn_class(self): + + inputs = tl.layers.Input([self.batch_size, self.num_steps, self.embedding_size]) + rnnlayer = tl.layers.LSTMRNN( + units=self.hidden_size, dropout=0.1, return_last_output=True, return_seq_2d=False, return_last_state=True + ) + rnn, rnn_state = rnnlayer(inputs) + outputs = tl.layers.Dense(n_units=1)(rnn) + rnn_model = tl.models.Model(inputs=inputs, outputs=[outputs, rnn_state[0], rnn_state[1]]) + print(rnn_model) + + optimizer = tf.optimizers.Adam(learning_rate=0.01) + + rnn_model.train() + + for epoch in range(50): + with tf.GradientTape() as tape: + pred_y, final_h, final_c = rnn_model(self.data_x) + loss = tl.cost.mean_squared_error(pred_y, self.data_y) + + gradients = tape.gradient(loss, rnn_model.trainable_weights) + optimizer.apply_gradients(zip(gradients, rnn_model.trainable_weights)) + + if (epoch + 1) % 10 == 0: + print("epoch %d, loss %f" % (epoch, loss)) + + def test_basic_grurnn(self): + + inputs = tl.layers.Input([self.batch_size, self.num_steps, self.embedding_size]) + rnnlayer = tl.layers.RNN( + cell=tf.keras.layers.GRUCell(units=self.hidden_size, dropout=0.1), return_last_output=True, + return_seq_2d=False, return_last_state=True + ) + rnn, rnn_state = rnnlayer(inputs) + outputs = tl.layers.Dense(n_units=1)(rnn) + rnn_model = tl.models.Model(inputs=inputs, outputs=[outputs, rnn_state[0]]) + print(rnn_model) + + optimizer = tf.optimizers.Adam(learning_rate=0.01) + + rnn_model.train() + + for epoch in range(50): + with tf.GradientTape() as tape: + pred_y, final_h = rnn_model(self.data_x) + loss = tl.cost.mean_squared_error(pred_y, self.data_y) + + gradients = tape.gradient(loss, rnn_model.trainable_weights) + optimizer.apply_gradients(zip(gradients, rnn_model.trainable_weights)) + + if (epoch + 1) % 10 == 0: + print("epoch %d, loss %f" % (epoch, loss)) + + def test_basic_grurnn_class(self): + + inputs = tl.layers.Input([self.batch_size, self.num_steps, self.embedding_size]) + rnnlayer = tl.layers.GRURNN( + units=self.hidden_size, dropout=0.1, return_last_output=True, return_seq_2d=False, return_last_state=True + ) + rnn, rnn_state = rnnlayer(inputs) + outputs = tl.layers.Dense(n_units=1)(rnn) + rnn_model = tl.models.Model(inputs=inputs, outputs=[outputs, rnn_state[0]]) + print(rnn_model) + + optimizer = tf.optimizers.Adam(learning_rate=0.01) + + rnn_model.train() + + for epoch in range(50): + with tf.GradientTape() as tape: + pred_y, final_h = rnn_model(self.data_x) + loss = tl.cost.mean_squared_error(pred_y, self.data_y) + + gradients = tape.gradient(loss, rnn_model.trainable_weights) + optimizer.apply_gradients(zip(gradients, rnn_model.trainable_weights)) + + if (epoch + 1) % 10 == 0: + print("epoch %d, loss %f" % (epoch, loss)) + + def test_basic_birnn_simplernncell(self): + + inputs = tl.layers.Input([self.batch_size, self.num_steps, self.embedding_size]) + rnnlayer = tl.layers.BiRNN( + fw_cell=tf.keras.layers.SimpleRNNCell(units=self.hidden_size, dropout=0.1), + bw_cell=tf.keras.layers.SimpleRNNCell(units=self.hidden_size + 1, + dropout=0.1), return_seq_2d=True, return_last_state=True + ) + rnn, rnn_fw_state, rnn_bw_state = rnnlayer(inputs) + dense = tl.layers.Dense(n_units=1)(rnn) + outputs = tl.layers.Reshape([-1, self.num_steps])(dense) + rnn_model = tl.models.Model(inputs=inputs, outputs=[outputs, rnn, rnn_fw_state[0], rnn_bw_state[0]]) + print(rnn_model) + + optimizer = tf.optimizers.Adam(learning_rate=0.01) + + rnn_model.train() + assert rnnlayer.is_train + + for epoch in range(50): + with tf.GradientTape() as tape: + pred_y, r, rfw, rbw = rnn_model(self.data_x) + loss = tl.cost.mean_squared_error(pred_y, self.data_y2) + + self.assertEqual( + r.get_shape().as_list(), [self.batch_size * self.num_steps, self.hidden_size + self.hidden_size + 1] + ) + gradients = tape.gradient(loss, rnn_model.trainable_weights) + optimizer.apply_gradients(zip(gradients, rnn_model.trainable_weights)) + + if (epoch + 1) % 10 == 0: + print("epoch %d, loss %f" % (epoch, loss)) + + def test_basic_birnn_lstmcell(self): + + inputs = tl.layers.Input([self.batch_size, self.num_steps, self.embedding_size]) + rnnlayer = tl.layers.BiRNN( + fw_cell=tf.keras.layers.LSTMCell(units=self.hidden_size, dropout=0.1), + bw_cell=tf.keras.layers.LSTMCell(units=self.hidden_size + 1, + dropout=0.1), return_seq_2d=False, return_last_state=True + ) + rnn, rnn_fw_state, rnn_bw_state = rnnlayer(inputs) + din = tl.layers.Reshape([-1, self.hidden_size + self.hidden_size + 1])(rnn) + dense = tl.layers.Dense(n_units=1)(din) + outputs = tl.layers.Reshape([-1, self.num_steps])(dense) + rnn_model = tl.models.Model(inputs=inputs, outputs=[outputs, rnn, rnn_fw_state[0], rnn_bw_state[0]]) + print(rnn_model) + + optimizer = tf.optimizers.Adam(learning_rate=0.01) + + rnn_model.train() + assert rnnlayer.is_train + + for epoch in range(50): + with tf.GradientTape() as tape: + pred_y, r, rfw, rbw = rnn_model(self.data_x) + loss = tl.cost.mean_squared_error(pred_y, self.data_y2) + + self.assertEqual( + r.get_shape().as_list(), [self.batch_size, self.num_steps, self.hidden_size + self.hidden_size + 1] + ) + gradients = tape.gradient(loss, rnn_model.trainable_weights) + optimizer.apply_gradients(zip(gradients, rnn_model.trainable_weights)) + + if (epoch + 1) % 10 == 0: + print("epoch %d, loss %f" % (epoch, loss)) + + def test_basic_birnn_grucell(self): + + class CustomisedModel(tl.models.Model): + + def __init__(self): + super(CustomisedModel, self).__init__() + self.rnnlayer = tl.layers.BiRNN( + fw_cell=tf.keras.layers.GRUCell(units=8, + dropout=0.1), bw_cell=tf.keras.layers.GRUCell(units=8, dropout=0.1), + in_channels=4, return_seq_2d=False, return_last_state=False + ) + self.dense = tl.layers.Dense(in_channels=16, n_units=1) + self.reshape = tl.layers.Reshape([-1, 6]) + + def forward(self, x): + z = self.rnnlayer(x, return_seq_2d=True) + z = self.dense(z) + z = self.reshape(z) + return z + + rnn_model = CustomisedModel() + print(rnn_model) + optimizer = tf.optimizers.Adam(learning_rate=0.01) + rnn_model.train() + + for epoch in range(50): + with tf.GradientTape() as tape: + pred_y = rnn_model(self.data_x) + loss = tl.cost.mean_squared_error(pred_y, self.data_y) + + gradients = tape.gradient(loss, rnn_model.trainable_weights) + optimizer.apply_gradients(zip(gradients, rnn_model.trainable_weights)) + + if (epoch + 1) % 10 == 0: + print("epoch %d, loss %f" % (epoch, loss)) + + def test_stack_simplernn(self): + + inputs = tl.layers.Input([self.batch_size, self.num_steps, self.embedding_size]) + rnnlayer1 = tl.layers.RNN( + cell=tf.keras.layers.SimpleRNNCell(units=self.hidden_size, dropout=0.1), return_last_output=False, + return_seq_2d=False, return_last_state=False + ) + rnn1 = rnnlayer1(inputs) + rnnlayer2 = tl.layers.RNN( + cell=tf.keras.layers.SimpleRNNCell(units=self.hidden_size, dropout=0.1), return_last_output=True, + return_seq_2d=False, return_last_state=False + ) + rnn2 = rnnlayer2(rnn1) + outputs = tl.layers.Dense(n_units=1)(rnn2) + rnn_model = tl.models.Model(inputs=inputs, outputs=outputs) + print(rnn_model) + + optimizer = tf.optimizers.Adam(learning_rate=0.01) + + rnn_model.train() + assert rnnlayer1.is_train + assert rnnlayer2.is_train + + for epoch in range(50): + with tf.GradientTape() as tape: + pred_y = rnn_model(self.data_x) + loss = tl.cost.mean_squared_error(pred_y, self.data_y) + + gradients = tape.gradient(loss, rnn_model.trainable_weights) + optimizer.apply_gradients(zip(gradients, rnn_model.trainable_weights)) + + if (epoch + 1) % 10 == 0: + print("epoch %d, loss %f" % (epoch, loss)) + + def test_stack_birnn_simplernncell(self): + + inputs = tl.layers.Input([self.batch_size, self.num_steps, self.embedding_size]) + rnnlayer = tl.layers.BiRNN( + fw_cell=tf.keras.layers.SimpleRNNCell(units=self.hidden_size, dropout=0.1), + bw_cell=tf.keras.layers.SimpleRNNCell(units=self.hidden_size + 1, + dropout=0.1), return_seq_2d=False, return_last_state=False + ) + rnn = rnnlayer(inputs) + rnnlayer2 = tl.layers.BiRNN( + fw_cell=tf.keras.layers.SimpleRNNCell(units=self.hidden_size, dropout=0.1), + bw_cell=tf.keras.layers.SimpleRNNCell(units=self.hidden_size + 1, + dropout=0.1), return_seq_2d=True, return_last_state=False + ) + rnn2 = rnnlayer2(rnn) + dense = tl.layers.Dense(n_units=1)(rnn2) + outputs = tl.layers.Reshape([-1, self.num_steps])(dense) + rnn_model = tl.models.Model(inputs=inputs, outputs=outputs) + print(rnn_model) + + optimizer = tf.optimizers.Adam(learning_rate=0.01) + + rnn_model.train() + assert rnnlayer.is_train + assert rnnlayer2.is_train + + for epoch in range(50): + with tf.GradientTape() as tape: + pred_y = rnn_model(self.data_x) + loss = tl.cost.mean_squared_error(pred_y, self.data_y2) + + gradients = tape.gradient(loss, rnn_model.trainable_weights) + optimizer.apply_gradients(zip(gradients, rnn_model.trainable_weights)) + + if (epoch + 1) % 10 == 0: + print("epoch %d, loss %f" % (epoch, loss)) + + def test_basic_simplernn_dropout_1(self): + + inputs = tl.layers.Input([self.batch_size, self.num_steps, self.embedding_size]) + rnnlayer = tl.layers.RNN( + cell=tf.keras.layers.SimpleRNNCell(units=self.hidden_size, dropout=0.5), return_last_output=True, + return_seq_2d=False, return_last_state=False + ) + rnn = rnnlayer(inputs) + outputs = tl.layers.Dense(n_units=1)(rnn) + rnn_model = tl.models.Model(inputs=inputs, outputs=[outputs, rnn]) + print(rnn_model) + + rnn_model.train() + assert rnnlayer.is_train + + pred_y, rnn_1 = rnn_model(self.data_x) + pred_y, rnn_2 = rnn_model(self.data_x) + self.assertFalse(np.allclose(rnn_1, rnn_2)) + + rnn_model.eval() + assert not rnnlayer.is_train + + pred_y_1, rnn_1 = rnn_model(self.data_x) + pred_y_2, rnn_2 = rnn_model(self.data_x) + self.assertTrue(np.allclose(rnn_1, rnn_2)) + + def test_basic_simplernn_dropout_2(self): + + inputs = tl.layers.Input([self.batch_size, self.num_steps, self.embedding_size]) + rnnlayer = tl.layers.RNN( + cell=tf.keras.layers.SimpleRNNCell(units=self.hidden_size, recurrent_dropout=0.5), return_last_output=True, + return_seq_2d=False, return_last_state=False + ) + rnn = rnnlayer(inputs) + outputs = tl.layers.Dense(n_units=1)(rnn) + rnn_model = tl.models.Model(inputs=inputs, outputs=[outputs, rnn]) + print(rnn_model) + + rnn_model.train() + assert rnnlayer.is_train + + pred_y, rnn_1 = rnn_model(self.data_x) + pred_y, rnn_2 = rnn_model(self.data_x) + self.assertFalse(np.allclose(rnn_1, rnn_2)) + + rnn_model.eval() + assert not rnnlayer.is_train + + pred_y_1, rnn_1 = rnn_model(self.data_x) + pred_y_2, rnn_2 = rnn_model(self.data_x) + self.assertTrue(np.allclose(rnn_1, rnn_2)) + + def test_basic_birnn_simplernn_dropout_1(self): + + inputs = tl.layers.Input([self.batch_size, self.num_steps, self.embedding_size]) + rnnlayer = tl.layers.BiRNN( + fw_cell=tf.keras.layers.SimpleRNNCell(units=self.hidden_size, dropout=0.5), + bw_cell=tf.keras.layers.SimpleRNNCell(units=self.hidden_size, + dropout=0.5), return_seq_2d=True, return_last_state=False + ) + rnn = rnnlayer(inputs) + outputs = tl.layers.Dense(n_units=1)(rnn) + rnn_model = tl.models.Model(inputs=inputs, outputs=[outputs, rnn]) + print(rnn_model) + + rnn_model.train() + assert rnnlayer.is_train + + pred_y, rnn_1 = rnn_model(self.data_x) + pred_y, rnn_2 = rnn_model(self.data_x) + self.assertFalse(np.allclose(rnn_1, rnn_2)) + + rnn_model.eval() + assert not rnnlayer.is_train + + pred_y_1, rnn_1 = rnn_model(self.data_x) + pred_y_2, rnn_2 = rnn_model(self.data_x) + self.assertTrue(np.allclose(rnn_1, rnn_2)) + + def test_basic_birnn_simplernn_dropout_2(self): + + inputs = tl.layers.Input([self.batch_size, self.num_steps, self.embedding_size]) + rnnlayer = tl.layers.BiRNN( + fw_cell=tf.keras.layers.SimpleRNNCell(units=self.hidden_size, recurrent_dropout=0.5), + bw_cell=tf.keras.layers.SimpleRNNCell(units=self.hidden_size, + recurrent_dropout=0.5), return_seq_2d=True, return_last_state=False + ) + rnn = rnnlayer(inputs) + outputs = tl.layers.Dense(n_units=1)(rnn) + rnn_model = tl.models.Model(inputs=inputs, outputs=[outputs, rnn]) + print(rnn_model) + + rnn_model.train() + assert rnnlayer.is_train + + pred_y, rnn_1 = rnn_model(self.data_x) + pred_y, rnn_2 = rnn_model(self.data_x) + self.assertFalse(np.allclose(rnn_1, rnn_2)) + + rnn_model.eval() + assert not rnnlayer.is_train + + pred_y_1, rnn_1 = rnn_model(self.data_x) + pred_y_2, rnn_2 = rnn_model(self.data_x) + self.assertTrue(np.allclose(rnn_1, rnn_2)) + + def test_sequence_length(self): + data = [[[1], [2], [0], [0], [0]], [[1], [2], [3], [0], [0]], [[1], [2], [6], [1], [0]]] + data = tf.convert_to_tensor(data, dtype=tf.float32) + length = tl.layers.retrieve_seq_length_op(data) + print(length) + data = [ + [[1, 2], [2, 2], [1, 2], [1, 2], [0, 0]], [[2, 3], [2, 4], [3, 2], [0, 0], [0, 0]], + [[3, 3], [2, 2], [5, 3], [1, 2], [0, 0]] + ] + data = tf.convert_to_tensor(data, dtype=tf.float32) + length = tl.layers.retrieve_seq_length_op(data) + print(length) + + def test_sequence_length2(self): + data = [[1, 2, 0, 0, 0], [1, 2, 3, 0, 0], [1, 2, 6, 1, 0]] + data = tf.convert_to_tensor(data, dtype=tf.float32) + length = tl.layers.retrieve_seq_length_op2(data) + print(length) + + def test_sequence_length3(self): + data = [[[1], [2], [0], [0], [0]], [[1], [2], [3], [0], [0]], [[1], [2], [6], [1], [0]]] + data = tf.convert_to_tensor(data, dtype=tf.float32) + length = tl.layers.retrieve_seq_length_op3(data) + print(length) + data = [ + [[1, 2], [2, 2], [1, 2], [1, 2], [0, 0]], [[2, 3], [2, 4], [3, 2], [0, 0], [0, 0]], + [[3, 3], [2, 2], [5, 3], [1, 2], [0, 0]] + ] + data = tf.convert_to_tensor(data, dtype=tf.float32) + length = tl.layers.retrieve_seq_length_op3(data) + print(length) + data = [[1, 2, 0, 0, 0], [1, 2, 3, 0, 0], [1, 2, 6, 1, 0]] + data = tf.convert_to_tensor(data, dtype=tf.float32) + length = tl.layers.retrieve_seq_length_op3(data) + print(length) + data = [ + ['hello', 'world', '', '', ''], ['hello', 'world', 'tensorlayer', '', ''], + ['hello', 'world', 'tensorlayer', '2.0', ''] + ] + data = tf.convert_to_tensor(data, dtype=tf.string) + length = tl.layers.retrieve_seq_length_op3(data, pad_val='') + print(length) + + try: + data = [1, 2, 0, 0, 0] + data = tf.convert_to_tensor(data, dtype=tf.float32) + length = tl.layers.retrieve_seq_length_op3(data) + print(length) + except Exception as e: + print(e) + + try: + data = np.random.random([4, 2, 6, 2]) + data = tf.convert_to_tensor(data, dtype=tf.float32) + length = tl.layers.retrieve_seq_length_op3(data) + print(length) + except Exception as e: + print(e) + + def test_target_mask_op(self): + fail_flag = False + data = [ + ['hello', 'world', '', '', ''], ['hello', 'world', 'tensorlayer', '', ''], + ['hello', 'world', 'tensorlayer', '2.0', ''] + ] + try: + tl.layers.target_mask_op(data, pad_val='') + fail_flag = True + except AttributeError as e: + print(e) + if fail_flag: + self.fail("Type error not raised") + + data = tf.convert_to_tensor(data, dtype=tf.string) + mask = tl.layers.target_mask_op(data, pad_val='') + print(mask) + + data = [[[1], [0], [0], [0], [0]], [[1], [2], [3], [0], [0]], [[1], [2], [0], [1], [0]]] + data = tf.convert_to_tensor(data, dtype=tf.float32) + mask = tl.layers.target_mask_op(data) + print(mask) + + data = [ + [[0, 0], [2, 2], [1, 2], [1, 2], [0, 0]], [[2, 3], [2, 4], [3, 2], [1, 0], [0, 0]], + [[3, 3], [0, 1], [5, 3], [1, 2], [0, 0]] + ] + data = tf.convert_to_tensor(data, dtype=tf.float32) + mask = tl.layers.target_mask_op(data) + print(mask) + + fail_flag = False + try: + data = [1, 2, 0, 0, 0] + data = tf.convert_to_tensor(data, dtype=tf.float32) + tl.layers.target_mask_op(data) + fail_flag = True + except ValueError as e: + print(e) + if fail_flag: + self.fail("Wrong data shape not detected.") + + fail_flag = False + try: + data = np.random.random([4, 2, 6, 2]) + data = tf.convert_to_tensor(data, dtype=tf.float32) + tl.layers.target_mask_op(data) + fail_flag = True + except ValueError as e: + print(e) + if fail_flag: + self.fail("Wrong data shape not detected.") + + def test_dynamic_rnn(self): + batch_size = 3 + num_steps = 5 + embedding_size = 6 + + hidden_size = 4 + inputs = tl.layers.Input([batch_size, num_steps, embedding_size]) + + rnn_layer = tl.layers.RNN( + cell=tf.keras.layers.LSTMCell(units=hidden_size, dropout=0.1), in_channels=embedding_size, + return_last_output=True, return_last_state=True + ) + + rnn_layer.is_train = False + + print(tl.layers.retrieve_seq_length_op3(inputs)) + _ = rnn_layer(inputs, sequence_length=tl.layers.retrieve_seq_length_op3(inputs)) + _ = rnn_layer(inputs, sequence_length=np.array([5, 5, 5])) + + # test exceptions + except_flag = False + try: + _ = rnn_layer(inputs, sequence_length=1) + except_flag = True + except TypeError as e: + print(e) + + try: + _ = rnn_layer(inputs, sequence_length=["str", 1, 2]) + except_flag = True + except TypeError as e: + print(e) + + try: + _ = rnn_layer(inputs, sequence_length=[10, 2, 2]) + except_flag = True + except ValueError as e: + print(e) + + try: + _ = rnn_layer(inputs, sequence_length=[1]) + except_flag = True + except ValueError as e: + print(e) + + if except_flag: + self.fail("Exception not detected.") + + # test warning + for _ in range(5): + _ = rnn_layer(inputs, sequence_length=[5, 5, 5], return_last_output=False, return_last_state=True) + _ = rnn_layer(inputs, sequence_length=[5, 5, 5], return_last_output=True, return_last_state=False) + + x = rnn_layer(inputs, sequence_length=None, return_last_output=True, return_last_state=True) + y = rnn_layer(inputs, sequence_length=[5, 5, 5], return_last_output=True, return_last_state=True) + + assert len(x) == 2 + assert len(y) == 2 + + for i, j in zip(x, y): + self.assertTrue(np.allclose(i, j)) + + def test_dynamic_rnn_with_seq_len_op2(self): + data = [[[1], [2], [0], [0], [0]], [[1], [2], [3], [0], [0]], [[1], [2], [6], [1], [1]]] + data = tf.convert_to_tensor(data, dtype=tf.float32) + + class DynamicRNNExample(tl.models.Model): + + def __init__(self): + super(DynamicRNNExample, self).__init__() + + self.rnnlayer = tl.layers.RNN( + cell=tf.keras.layers.SimpleRNNCell(units=6, dropout=0.1), in_channels=1, return_last_output=True, + return_last_state=True + ) + + def forward(self, x): + z0, s0 = self.rnnlayer(x, sequence_length=None) + z1, s1 = self.rnnlayer(x, sequence_length=tl.layers.retrieve_seq_length_op3(x)) + z2, s2 = self.rnnlayer(x, sequence_length=tl.layers.retrieve_seq_length_op3(x), initial_state=s1) + print(z0) + print(z1) + print(z2) + print("===") + print(s0) + print(s1) + print(s2) + return z2, s2 + + model = DynamicRNNExample() + model.eval() + + output, state = model(data) + print(output.shape) + print(state) + + def test_dynamic_rnn_with_fake_data(self): + + class CustomisedModel(tl.models.Model): + + def __init__(self): + super(CustomisedModel, self).__init__() + self.rnnlayer = tl.layers.LSTMRNN( + units=8, dropout=0.1, in_channels=4, return_last_output=True, return_last_state=False + ) + self.dense = tl.layers.Dense(in_channels=8, n_units=1) + + def forward(self, x): + z = self.rnnlayer(x, sequence_length=tl.layers.retrieve_seq_length_op3(x)) + z = self.dense(z[:, :]) + return z + + rnn_model = CustomisedModel() + print(rnn_model) + optimizer = tf.optimizers.Adam(learning_rate=0.01) + rnn_model.train() + + for epoch in range(50): + with tf.GradientTape() as tape: + pred_y = rnn_model(self.data_x) + loss = tl.cost.mean_squared_error(pred_y, self.data_y) + + gradients = tape.gradient(loss, rnn_model.trainable_weights) + optimizer.apply_gradients(zip(gradients, rnn_model.trainable_weights)) + + if (epoch + 1) % 10 == 0: + print("epoch %d, loss %f" % (epoch, loss)) + + filename = "dynamic_rnn.h5" + rnn_model.save_weights(filename) + + # Testing saving and restoring of RNN weights + rnn_model2 = CustomisedModel() + rnn_model2.eval() + pred_y = rnn_model2(self.data_x) + loss = tl.cost.mean_squared_error(pred_y, self.data_y) + print("MODEL INIT loss %f" % (loss)) + + rnn_model2.load_weights(filename) + pred_y = rnn_model2(self.data_x) + loss = tl.cost.mean_squared_error(pred_y, self.data_y) + print("MODEL RESTORE W loss %f" % (loss)) + + import os + os.remove(filename) + + +if __name__ == '__main__': + + unittest.main() diff --git a/tests/layers/test_layers_resampling.py b/tests/layers/test_layers_resampling.py new file mode 100644 index 0000000..4f0bbc9 --- /dev/null +++ b/tests/layers/test_layers_resampling.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import sys +sys.path.append("/home/wurundi/workspace/tensorlayer2") + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorlayer as tl +from tensorlayer.layers import * + +from tests.utils import CustomTestCase + + +class Layer_Pooling_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + + ## 1D ======================================================================== + + ## 2D ======================================================================== + + x_2_input_shape = [None, 100, 100, 3] + nin_2 = Input(x_2_input_shape) + + n6 = tl.layers.Conv2d(n_filter=32, filter_size=(3, 3), strides=(2, 2), name='test_conv2d')(nin_2) + + n7 = tl.layers.UpSampling2d(scale=(2, 2), name='test_UpSampling2d_1')(n6) + + n8 = tl.layers.UpSampling2d(scale=3, name='test_UpSampling2d_2')(n6) + + n9 = tl.layers.DownSampling2d(scale=(2, 2), name='test_DownSampling2d_1')(n6) + + n10 = tl.layers.DownSampling2d(scale=5, name='test_DownSampling2d_2')(n6) + + cls.n6_shape = n6.get_shape().as_list() + cls.n7_shape = n7.get_shape().as_list() + cls.n8_shape = n8.get_shape().as_list() + cls.n9_shape = n9.get_shape().as_list() + cls.n10_shape = n10.get_shape().as_list() + + @classmethod + def tearDownClass(cls): + pass + # tf.reset_default_graph() + + def test_UpSampling2d(self): + self.assertEqual(self.n7_shape[1:3], [100, 100]) + self.assertEqual(self.n8_shape[1:3], [150, 150]) + + try: + layer = tl.layers.UpSampling2d(scale=(2, 2, 2)) + except Exception as e: + print(e) + + def test_DownSampling2d(self): + self.assertEqual(self.n9_shape[1:3], [25, 25]) + self.assertEqual(self.n10_shape[1:3], [10, 10]) + + try: + layer = tl.layers.DownSampling2d(scale=(2, 2, 2)) + except Exception as e: + print(e) + + +if __name__ == '__main__': + + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/layers/test_layers_scale.py b/tests/layers/test_layers_scale.py new file mode 100644 index 0000000..c87e560 --- /dev/null +++ b/tests/layers/test_layers_scale.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class Layer_Scale_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + pass + + @classmethod + def tearDownClass(cls): + pass + + def test_scale(self): + + class model(tl.layers.Module): + def __init__(self): + super(model, self).__init__() + self.dense = tl.layers.Dense(n_units=10) + self.scalelayer = tl.layers.Scale(init_scale=0.5) + + def forward(self, inputs): + output1 = self.dense(inputs) + output2 = self.scalelayer(output1) + return output1, output2 + + input = tl.layers.Input((8, 3), init=tl.initializers.random_normal()) + net = model() + net.set_train() + dout, fout = net(input) + + for i in range(len(dout)): + for j in range(len(dout[i])): + self.assertEqual(dout[i][j].numpy() * 0.5, fout[i][j].numpy()) + + +if __name__ == '__main__': + + unittest.main() diff --git a/tests/layers/test_layers_shape.py b/tests/layers/test_layers_shape.py new file mode 100644 index 0000000..139a3f8 --- /dev/null +++ b/tests/layers/test_layers_shape.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest +import numpy as np + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class Layer_Shape_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + cls.data = tl.layers.Input(shape=(8, 4, 3), init=tl.initializers.random_normal()) + cls.imgdata = tl.layers.Input(shape=(2, 16, 16, 8), init=tl.initializers.random_normal()) + + @classmethod + def tearDownClass(cls): + pass + + def test_flatten(self): + + class CustomizeModel(tl.layers.Module): + + def __init__(self): + super(CustomizeModel, self).__init__() + self.flatten = tl.layers.Flatten() + + def forward(self, x): + return self.flatten(x) + + model = CustomizeModel() + print(model.flatten) + model.set_train() + out = model(self.data) + self.assertEqual(out.get_shape().as_list(), [8, 12]) + + def test_reshape(self): + + class CustomizeModel(tl.layers.Module): + + def __init__(self): + super(CustomizeModel, self).__init__() + self.reshape1 = tl.layers.Reshape(shape=(8, 12)) + self.reshape2 = tl.layers.Reshape(shape=(-1, 12)) + self.reshape3 = tl.layers.Reshape(shape=()) + + def forward(self, x): + return self.reshape1(x), self.reshape2(x), self.reshape3(x[0][0][0]) + + model = CustomizeModel() + print(model.reshape1) + print(model.reshape2) + print(model.reshape3) + model.set_train() + out1, out2, out3 = model(self.data) + self.assertEqual(out1.get_shape().as_list(), [8, 12]) + self.assertEqual(out2.get_shape().as_list(), [8, 12]) + self.assertEqual(out3.get_shape().as_list(), []) + + def test_transpose(self): + + class CustomizeModel(tl.layers.Module): + + def __init__(self): + super(CustomizeModel, self).__init__() + self.transpose1 = tl.layers.Transpose() + self.transpose2 = tl.layers.Transpose([2, 1, 0]) + self.transpose3 = tl.layers.Transpose([0, 2, 1]) + self.transpose4 = tl.layers.Transpose(conjugate=True) + + def forward(self, x): + return self.transpose1(x), self.transpose2(x), self.transpose3(x), self.transpose4(x) + + real = tl.layers.Input(shape=(8, 4, 3), init=tl.initializers.random_normal()) + comp = tl.layers.Input(shape=(8, 4, 3), init=tl.initializers.random_normal()) + import tensorflow as tf + complex_data = tf.dtypes.complex(real, comp) + model = CustomizeModel() + print(model.transpose1) + print(model.transpose2) + print(model.transpose3) + print(model.transpose4) + model.set_train() + out1, out2, out3, out4 = model(self.data) + self.assertEqual(out1.get_shape().as_list(), [3, 4, 8]) + self.assertEqual(out2.get_shape().as_list(), [3, 4, 8]) + self.assertEqual(out3.get_shape().as_list(), [8, 3, 4]) + self.assertEqual(out4.get_shape().as_list(), [3, 4, 8]) + self.assertTrue(np.array_equal(out1.numpy(), out4.numpy())) + + out1, out2, out3, out4 = model(complex_data) + self.assertEqual(out1.get_shape().as_list(), [3, 4, 8]) + self.assertEqual(out2.get_shape().as_list(), [3, 4, 8]) + self.assertEqual(out3.get_shape().as_list(), [8, 3, 4]) + self.assertEqual(out4.get_shape().as_list(), [3, 4, 8]) + self.assertTrue(np.array_equal(np.conj(out1.numpy()), out4.numpy())) + + def test_shuffle(self): + + class CustomizeModel(tl.layers.Module): + + def __init__(self, x): + super(CustomizeModel, self).__init__() + self.shuffle = tl.layers.Shuffle(x) + + def forward(self, x): + return self.shuffle(x) + + model = CustomizeModel(2) + print(model.shuffle) + model.set_train() + out = model(self.imgdata) + self.assertEqual(out.get_shape().as_list(), [2, 16, 16, 8]) + + +if __name__ == '__main__': + + unittest.main() diff --git a/tests/layers/test_layers_stack.py b/tests/layers/test_layers_stack.py new file mode 100644 index 0000000..6a703cb --- /dev/null +++ b/tests/layers/test_layers_stack.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorlayer as tl +from tensorlayer.layers import * + +from tests.utils import CustomTestCase + + +class Layer_Stack_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + print("-" * 20, "Layer_Stack_Test", "-" * 20) + cls.batch_size = 4 + cls.inputs_shape = [cls.batch_size, 10] + + cls.ni = Input(cls.inputs_shape, name='input_layer') + class model(tl.layers.Module): + def __init__(self): + super(model, self).__init__() + self.a = Dense(n_units=5) + self.b = Dense(n_units=5) + self.stack = Stack(axis=1) + + def forward(self, inputs): + output1 = self.a(inputs) + output2 = self.b(inputs) + output = self.stack([output1, output2]) + return output + + a = Dense(n_units=5)(cls.ni) + b = Dense(n_units=5)(cls.ni) + cls.layer1 = Stack(axis=1) + cls.n1 = cls.layer1([a, b]) + + net = model() + net.set_train() + cls.inputs = Input(cls.inputs_shape) + cls.n2 = net(cls.inputs) + + + @classmethod + def tearDownClass(cls): + pass + + def test_layer_n1(self): + self.assertEqual(self.n1.shape, (4, 2, 5)) + + def test_layer_n2(self): + self.assertEqual(self.n2.shape, (4, 2, 5)) + + +class Layer_UnStack_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + print("-" * 20, "Layer_UnStack_Test", "-" * 20) + cls.batch_size = 4 + cls.inputs_shape = [cls.batch_size, 10] + + cls.ni = Input(cls.inputs_shape, name='input_layer') + a = Dense(n_units=5)(cls.ni) + cls.layer1 = UnStack(axis=1) + cls.n1 = cls.layer1(a) + + class model(tl.layers.Module): + def __init__(self): + super(model, self).__init__() + self.a = Dense(n_units=5) + self.unstack = UnStack(axis=1) + + def forward(self, inputs): + output1 = self.a(inputs) + output = self.unstack(output1) + return output + + + cls.inputs = Input(cls.inputs_shape) + net = model() + net.set_train() + cls.n2 = net(cls.inputs) + + print(cls.layer1) + + @classmethod + def tearDownClass(cls): + pass + + def test_layer_n1(self): + self.assertEqual(len(self.n1), 5) + self.assertEqual(self.n1[0].shape, (self.batch_size, )) + + def test_layer_n2(self): + self.assertEqual(len(self.n2), 5) + self.assertEqual(self.n1[0].shape, (self.batch_size, )) + + +if __name__ == '__main__': + + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/models/test_auto_naming.py b/tests/models/test_auto_naming.py new file mode 100644 index 0000000..fb8f037 --- /dev/null +++ b/tests/models/test_auto_naming.py @@ -0,0 +1,283 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import numpy as np +import tensorflow as tf +import tensorlayer as tl +from tensorlayer.layers import * +from tensorlayer.models import * + +from tests.utils import CustomTestCase + + +def basic_static_model(name=None, conv1_name="conv1", conv2_name="conv2"): + ni = Input((None, 24, 24, 3)) + nn = Conv2d(16, (5, 5), (1, 1), padding='SAME', act=tf.nn.relu, name=conv1_name)(ni) + nn = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool1')(nn) + + nn = Conv2d(16, (5, 5), (1, 1), padding='SAME', act=tf.nn.relu, name=conv2_name)(nn) + nn = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool2')(nn) + + M = Model(inputs=ni, outputs=nn, name=name) + return M + + +def nested_static_model(name=None, inner_model_name=None): + ni = Input((None, 24, 24, 3)) + nn = ModelLayer(basic_static_model(inner_model_name))(ni) + M = Model(inputs=ni, outputs=nn, name=name) + return M + + +class basic_dynamic_model(Model): + + def __init__(self, name=None, conv1_name="conv1", conv2_name="conv2"): + super(basic_dynamic_model, self).__init__(name=name) + self.conv1 = Conv2d(16, (5, 5), (1, 1), padding='SAME', act=tf.nn.relu, in_channels=3, name=conv1_name) + self.pool1 = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool1') + + self.conv2 = Conv2d(16, (5, 5), (1, 1), padding='SAME', act=tf.nn.relu, in_channels=16, name=conv2_name) + self.pool2 = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool2') + + def forward(self, x): + x = self.conv1(x) + x = self.pool1(x) + x = self.conv2(x) + x = self.pool2(x) + return x + + +class nested_dynamic_model(Model): + + def __init__(self, name=None, inner_model_name_1=None, inner_model_name_2=None): + super(nested_dynamic_model, self).__init__(name=name) + + self.inner_model_1 = basic_dynamic_model(name=inner_model_name_1) + self.inner_model_2 = basic_dynamic_model(name=inner_model_name_2) + + def forward(self, x): + x = self.inner_model_1(x) + x = self.inner_model_2(x) + return x + + +class Auto_Naming_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + pass + + @classmethod + def tearDownClass(cls): + pass + + def test_dynamic_model_auto_naming(self): + print('-' * 20, 'test_dynamic_model_auto_naming', '-' * 20) + test_flag = True + + model_basic = basic_dynamic_model() + model_basic_1 = basic_dynamic_model() + model_basic_2 = basic_dynamic_model("basic_dynamic_model_2") + model_basic_3 = basic_dynamic_model() + model_basic_given_name = basic_dynamic_model("a_dynamic_model") + + self.assertEqual(model_basic.name, "basic_dynamic_model") + self.assertEqual(model_basic.conv1.name, "conv1") + self.assertEqual(model_basic_1.name, "basic_dynamic_model_1") + self.assertEqual(model_basic_1.conv1.name, "conv1") + self.assertEqual(model_basic_2.name, "basic_dynamic_model_2") + self.assertEqual(model_basic_3.name, "basic_dynamic_model_3") + self.assertEqual(model_basic_given_name.name, "a_dynamic_model") + + try: + model_basic_given_repeat_name = basic_dynamic_model("basic_dynamic_model_1") + test_flag = False + except Exception as e: + print(e) + if not test_flag: + self.fail("Failed to detect repeat user given names") + + model_nested = nested_dynamic_model() + model_nested_1 = nested_dynamic_model(inner_model_name_1="a_inner_dynamic_model") + + self.assertEqual(model_nested.name, "nested_dynamic_model") + self.assertEqual(model_nested.inner_model_1.name, "basic_dynamic_model_4") + self.assertEqual(model_nested.inner_model_2.name, "basic_dynamic_model_5") + self.assertEqual(model_nested_1.name, "nested_dynamic_model_1") + self.assertEqual(model_nested_1.inner_model_1.name, "a_inner_dynamic_model") + self.assertEqual(model_nested_1.inner_model_2.name, "basic_dynamic_model_6") + + try: + model_nested_given_repeat_name = nested_dynamic_model(inner_model_name_2="basic_dynamic_model_1") + test_flag = False + except Exception as e: + print(e) + if not test_flag: + self.fail("Failed to detect nested repeat user given names") + + try: + model_nested_given_repeat_name_1 = nested_dynamic_model(name="basic_dynamic_model_5") + test_flag = False + except Exception as e: + print(e) + if not test_flag: + self.fail("Failed to detect nested repeat user given names") + + def test_static_model_auto_naming(self): + print('-' * 20, 'test_static_model_auto_naming', '-' * 20) + test_flag = True + + model_basic = basic_static_model() + model_basic_1 = basic_static_model() + assname = "model_%d" % (int(model_basic_1.name.split("_")[-1]) + 1) + model_basic_2 = basic_static_model(name=assname) + model_basic_3 = basic_static_model() + model_basic_given_name = basic_static_model("a_static_model") + + # self.assertEqual(model_basic.name, "model") + basename = model_basic.name + bnum = basename.split("_")[-1] + try: + bnum = int(bnum) + except: + bnum = 0 + self.assertEqual(model_basic_1.name, "model_%d" % (bnum + 1)) + self.assertEqual(model_basic_2.name, assname) + self.assertEqual(model_basic_3.name, "model_%d" % (int(assname.split("_")[-1]) + 1)) + self.assertEqual(model_basic_given_name.name, "a_static_model") + + try: + model_basic_given_repeat_name = basic_static_model("model_1") + test_flag = False + except Exception as e: + print(e) + if not test_flag: + self.fail("Failed to detect repeat user given names") + + model_nested = nested_static_model() + model_nested_1 = nested_static_model(inner_model_name="a_inner_static_model") + + # self.assertEqual(model_nested.name, "model_5") + self.assertEqual(model_nested_1.name, "model_%d" % (int(model_nested.name.split("_")[-1]) + 1)) + + try: + model_nested_given_repeat_name = nested_static_model(inner_model_name="a_inner_static_model") + test_flag = False + except Exception as e: + print(e) + if not test_flag: + self.fail("Failed to detect repeat user given names") + + def test_layer_name_uniqueness(self): + print('-' * 20, 'test_layer_name_uniqueness', '-' * 20) + test_flag = True + + # dynamic + try: + model_dynamic = basic_dynamic_model(conv1_name="conv", conv2_name="conv", name="test_layer_name_dynamic") + # dynamic mode check uniqueness when self.all_layers is called + all_layers = model_dynamic.all_layers + test_flag = False + except Exception as e: + print(e) + if not test_flag: + self.fail("Failed to detect that layers inside a model have the same name in dynamic mode") + + # static + try: + model_static = basic_static_model(conv1_name="conv", conv2_name="conv", name="test_layer_name_static") + test_flag = False + except Exception as e: + print(e) + if not test_flag: + self.fail("Failed to detect that layers inside a model have the same name in static mode") + + def test_vgg_auto_naming(self): + print('-' * 20, 'test_vgg_auto_naming', '-' * 20) + test_flag = True + + vgg = vgg16() + vgg_1 = vgg16() + vgg_2 = vgg16(name="vgg16_2") + vgg_3 = vgg16() + vgg_given_name = vgg16(name="a_vgg_model") + + # self.assertEqual(vgg.name, "vgg16") + # self.assertEqual(vgg_1.name, "vgg16_1") + # self.assertEqual(vgg_2.name, "vgg16_2") + # self.assertEqual(vgg_3.name, "vgg16_3") + # self.assertEqual(vgg_given_name.name, "a_vgg_model") + + # try: + # vgg_given_repeat_name = vgg16(name="vgg16_1") + # test_flag = False + # except Exception as e: + # print(e) + # if not test_flag: + # self.fail("Failed to detect repeat user given names") + + def test_layerlist(self): + print('-' * 20, 'test_layerlist', '-' * 20) + test_flag = True + + try: + inputs = tl.layers.Input([10, 5]) + layer1 = tl.layers.LayerList( + [tl.layers.Dense(n_units=4, name='dense1'), + tl.layers.Dense(n_units=3, name='dense1')] + )(inputs) + model = tl.models.Model(inputs=inputs, outputs=layer1, name='layerlistmodel') + print([w.name for w in model.all_weights]) + test_flag = False + except Exception as e: + print(e) + if not test_flag: + self.fail("Fail to detect duplicate name in LayerList") + + def test_modellayer(self): + print('-' * 20, 'test_modellayer', '-' * 20) + test_flag = True + + try: + + class inner_model(Model): + + def __init__(self): + super(inner_model, self).__init__() + self.layer1 = tl.layers.Dense(n_units=4, in_channels=5, name='dense1') + self.layer2 = tl.layers.Dense(n_units=4, in_channels=4, name='dense1') + + def forward(self, x): + return self.layer2(self.layer1(x)) + + inputs = tl.layers.Input([10, 5]) + model_layer = tl.layers.ModelLayer(inner_model())(inputs) + model = tl.models.Model(inputs=inputs, outputs=model_layer, name='modellayermodel') + print(model) + print([w.name for w in model.all_weights]) + test_flag = False + except Exception as e: + print(e) + if not test_flag: + self.fail("Fail to detect duplicate name in ModelLayer") + + def test_layerlist(self): + try: + inputs = tl.layers.Input([10, 5]) + layer1 = tl.layers.LayerList( + [tl.layers.Dense(n_units=4, name='dense1'), + tl.layers.Dense(n_units=3, name='dense1')] + )(inputs) + model = tl.models.Model(inputs=inputs, outputs=layer1, name='layerlistmodel') + print([w.name for w in model.all_weights]) + self.fail("Fail to detect duplicate name in layerlist") + except Exception as e: + print(e) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/models/test_keras_save.py b/tests/models/test_keras_save.py new file mode 100644 index 0000000..2d40b31 --- /dev/null +++ b/tests/models/test_keras_save.py @@ -0,0 +1,46 @@ +from tensorflow.python.keras.applications import VGG16 +from tensorflow.python.keras.layers import Dense, Conv2D +from tensorflow.python.keras import Model +from tensorflow.python.training import saver +import tensorflow as tf + +# get the whole model +# vgg = VGG16(weights=None) +# print([x.name for x in vgg.weights]) + + +class Nested_VGG(Model): + + def __init__(self): + super(Nested_VGG, self).__init__() + self.vgg1 = VGG16(weights=None) + # print([x.name for x in self.vgg1.weights]) + self.vgg2 = VGG16(weights=None) + self.dense = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv1') + + def call(self, inputs, training=None, mask=None): + pass + + +class MyModel(Model): + + def __init__(self): + super(MyModel, self).__init__() + self.inner = Nested_VGG() + + def call(self, inputs, training=None, mask=None): + pass + + +model = MyModel() +print([x.name for x in model.layers]) +# print([x.name for x in model.inner.weights]) +print('vgg1:') +print([x.name for x in model.inner.vgg1.weights]) +print([x.name for x in model.inner.vgg1.layers]) + +print('vgg2') +print(model.inner.vgg2.get_layer('block1_conv1').kernel.name) +print([x.name for x in model.inner.vgg2.weights]) +print([x.name for x in model.inner.vgg2.layers]) +model.save_weights('./keras_model.h5') diff --git a/tests/models/test_model_core.py b/tests/models/test_model_core.py new file mode 100644 index 0000000..3db470f --- /dev/null +++ b/tests/models/test_model_core.py @@ -0,0 +1,426 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import numpy as np +import tensorflow as tf +import tensorlayer as tl +from tensorlayer.layers import * +from tensorlayer.models import * + +from tests.utils import CustomTestCase + + +def basic_static_model(): + ni = Input((None, 24, 24, 3)) + nn = Conv2d(16, (5, 5), (1, 1), padding='SAME', act=tf.nn.relu, name="conv1")(ni) + nn = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool1')(nn) + + nn = Conv2d(16, (5, 5), (1, 1), padding='SAME', act=tf.nn.relu, name="conv2")(nn) + nn = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool2')(nn) + + nn = Flatten(name='flatten')(nn) + nn = Dense(100, act=None, name="dense1")(nn) + nn = Dense(10, act=None, name="dense2")(nn) + M = Model(inputs=ni, outputs=nn) + return M + + +class basic_dynamic_model(Model): + + def __init__(self): + super(basic_dynamic_model, self).__init__() + self.conv1 = Conv2d(16, (5, 5), (1, 1), padding='SAME', act=tf.nn.relu, in_channels=3, name="conv1") + self.pool1 = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool1') + + self.conv2 = Conv2d(16, (5, 5), (1, 1), padding='SAME', act=tf.nn.relu, in_channels=16, name="conv2") + self.pool2 = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool2') + + self.flatten = Flatten(name='flatten') + self.dense1 = Dense(100, act=None, in_channels=576, name="dense1") + self.dense2 = Dense(10, act=None, in_channels=100, name="dense2") + + def forward(self, x): + x = self.conv1(x) + x = self.pool1(x) + x = self.conv2(x) + x = self.pool2(x) + x = self.flatten(x) + x = self.dense1(x) + x = self.dense2(x) + return x + + +class Model_Core_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + pass + + @classmethod + def tearDownClass(cls): + pass + + def test_dynamic_basic(self): + print('-' * 20, 'test_dynamic_basic', '-' * 20) + model_basic = basic_dynamic_model() + + # test empty model before calling + self.assertEqual(model_basic.is_train, None) + self.assertEqual(model_basic._all_weights, None) + self.assertEqual(model_basic._inputs, None) + self.assertEqual(model_basic._outputs, None) + self.assertEqual(model_basic._model_layer, None) + self.assertEqual(model_basic._all_layers, None) + self.assertEqual(model_basic._nodes_fixed, False) + + # test layer and weights access + all_layers = model_basic.all_layers + self.assertEqual(len(model_basic.all_layers), 7) + self.assertEqual(model_basic._all_weights, None) + + self.assertIsNotNone(model_basic.all_weights) + print([w.name for w in model_basic.all_weights]) + + # test model mode + model_basic.train() + self.assertEqual(model_basic.is_train, True) + model_basic.eval() + self.assertEqual(model_basic.is_train, False) + model_basic.test() + self.assertEqual(model_basic.is_train, False) + model_basic.infer() + self.assertEqual(model_basic.is_train, False) + + # test as_layer + try: + model_basic.as_layer() + except Exception as e: + print(e) + self.assertIsNone(model_basic._model_layer) + + # test print + try: + print(model_basic) + except Exception as e: + print(e) + + # test forwarding + inputs = np.random.normal(size=[2, 24, 24, 3]).astype(np.float32) + outputs1 = model_basic(inputs) + self.assertEqual(model_basic._nodes_fixed, True) + self.assertEqual(model_basic.is_train, False) + + try: + outputs2 = model_basic(inputs, is_train=True) + except Exception as e: + print(e) + outputs2 = model_basic(inputs, is_train=False) + self.assertEqual(model_basic.is_train, False) + + self.assertLess(np.max(np.abs(outputs1.numpy() - outputs2.numpy())), 1e-7) + + # test layer node + self.assertEqual(len(model_basic.all_layers[-1]._nodes), 0) + self.assertEqual(model_basic.all_layers[-2]._nodes_fixed, True) + + # test release_memory + try: + model_basic.release_memory() + except Exception as e: + print(e) + + def test_static_basic(self): + print('-' * 20, 'test_static_basic', '-' * 20) + model_basic = basic_static_model() + + # test empty model before calling + self.assertEqual(model_basic.is_train, None) + self.assertEqual(model_basic._all_weights, None) + self.assertIsNotNone(model_basic._inputs) + self.assertIsNotNone(model_basic._outputs) + self.assertEqual(model_basic._model_layer, None) + self.assertIsNotNone(model_basic._all_layers) + self.assertIsNotNone(model_basic._nodes_fixed) + + # test layer and weights access + all_layers = model_basic.all_layers + self.assertEqual(len(model_basic.all_layers), 8) + self.assertEqual(model_basic._all_weights, None) + + self.assertIsNotNone(model_basic.all_weights) + print([w.name for w in model_basic.all_weights]) + + # test model mode + model_basic.train() + self.assertEqual(model_basic.is_train, True) + model_basic.eval() + self.assertEqual(model_basic.is_train, False) + model_basic.test() + self.assertEqual(model_basic.is_train, False) + model_basic.infer() + self.assertEqual(model_basic.is_train, False) + + # test as_layer + self.assertIsInstance(model_basic.as_layer(), tl.layers.Layer) + self.assertIsNotNone(model_basic._model_layer) + + # test print + try: + print(model_basic) + except Exception as e: + print(e) + + # test forwarding + inputs = np.random.normal(size=[2, 24, 24, 3]).astype(np.float32) + outputs1 = model_basic(inputs) + self.assertEqual(model_basic._nodes_fixed, True) + self.assertEqual(model_basic.is_train, False) + + try: + outputs2 = model_basic(inputs, is_train=True) + except Exception as e: + print(e) + outputs2 = model_basic(inputs, is_train=False) + self.assertEqual(model_basic.is_train, False) + + self.assertLess(np.max(np.abs(outputs1.numpy() - outputs2.numpy())), 1e-7) + + # test layer node + self.assertEqual(len(model_basic.all_layers[-1]._nodes), 1) + self.assertEqual(model_basic.all_layers[-2]._nodes_fixed, True) + + # test release_memory + try: + model_basic.release_memory() + except Exception as e: + print(e) + + def test_deprecated_function(self): + print('-' * 20, 'test_deprecated_function', '-' * 20) + model = basic_dynamic_model() + + try: + model.print_all_layers() + except Exception as e: + print(e) + + try: + model.count_params() + except Exception as e: + print(e) + + try: + model.print_params() + except Exception as e: + print(e) + + try: + model.all_params() + except Exception as e: + print(e) + + try: + model.all_drop() + except Exception as e: + print(e) + + def test_exceptions(self): + print('-' * 20, 'test exceptions', '-' * 20) + np_arr = np.random.normal(size=[4, 784]).astype(np.float32) + tf_tensor = tf.random.normal(shape=[4, 784]) + ni = Input(shape=[4, 784]) + + try: + model = Model(inputs=[], outputs=[]) + except Exception as e: + self.assertIsInstance(e, ValueError) + print(e) + + try: + model = Model(inputs=np_arr, outputs=np_arr + 1) + except Exception as e: + self.assertIsInstance(e, TypeError) + print(e) + + try: + model = Model(inputs=[np_arr], outputs=[np_arr + 1]) + except Exception as e: + self.assertIsInstance(e, TypeError) + print(e) + + try: + model = Model(inputs=[tf_tensor], outputs=[tf_tensor + 1]) + except Exception as e: + self.assertIsInstance(e, TypeError) + print(e) + + try: + model = Model(inputs=tf_tensor, outputs=[tf_tensor + 1]) + except Exception as e: + self.assertIsInstance(e, TypeError) + print(e) + + try: + model = Model(inputs=ni, outputs=[tf_tensor + 1]) + except Exception as e: + self.assertIsInstance(e, TypeError) + print(e) + + try: + + class ill_model(Model): + + def __init__(self): + super(ill_model, self).__init__() + self.dense2 = Dense(10, act=None) + + def forward(self, x): + x = self.dense2(x) + return x + + model = ill_model() + weights = model.all_weights + except Exception as e: + self.assertIsInstance(e, AttributeError) + print(e) + + try: + ni = Input([4, 784]) + nn = Dense(10)(ni) + model = Model(inputs=ni, outputs=nn) + outputs = model(np_arr) + except Exception as e: + self.assertIsInstance(e, ValueError) + print(e) + + try: + ni = Input([4, 784]) + model = Model(inputs=ni, outputs=ni) + model.save_weights('./empty_model.h5') + except Exception as e: + print(e) + + try: + ni = Input([4, 784]) + nn = Dense(10)(ni) + model = Model(inputs=ni, outputs=nn) + model._outputs = None + outputs = model(np_arr, is_train=True) + except Exception as e: + self.assertIsInstance(e, ValueError) + print(e) + + def test_list_inputs_outputs(self): + print('-' * 20, 'test_list_inputs_outputs', '-' * 20) + ni_1 = Input(shape=[4, 16]) + ni_2 = Input(shape=[4, 32]) + a_1 = Dense(80)(ni_1) + b_1 = Dense(160)(ni_2) + concat = Concat()([a_1, b_1]) + a_2 = Dense(10)(concat) + b_2 = Dense(20)(concat) + + model = Model(inputs=[ni_1, ni_2], outputs=[a_2, b_2]) + + model.train() + np_arr1 = np.random.normal(size=[4, 16]).astype(np.float32) + np_arr2 = np.random.normal(size=[4, 32]).astype(np.float32) + + try: + outputs = model(np_arr1) + except Exception as e: + self.assertIsInstance(e, ValueError) + print(e) + + try: + outputs = model([np_arr1]) + except Exception as e: + self.assertIsInstance(e, ValueError) + print(e) + + out_a, out_b = model([np_arr1, np_arr2]) + self.assertEqual(out_a.shape, [4, 10]) + self.assertEqual(out_b.shape, [4, 20]) + + def test_special_case(self): + print('-' * 20, 'test_special_case', '-' * 20) + + class my_model(Model): + + def __init__(self): + super(my_model, self).__init__() + self.dense = Dense(64, in_channels=3) + self.vgg = tl.models.vgg16() + + def forward(self, x): + return x + + model = my_model() + weights = model.all_weights + self.assertGreater(len(weights), 2) + print(len(weights)) + + def test_get_layer(self): + print('-' * 20, 'test_get_layer', '-' * 20) + model_basic = basic_dynamic_model() + self.assertIsInstance(model_basic.get_layer('conv2'), tl.layers.Conv2d) + try: + model_basic.get_layer('abc') + except Exception as e: + print(e) + + try: + model_basic.get_layer(index=99) + except Exception as e: + print(e) + + model_basic = basic_static_model() + self.assertIsInstance(model_basic.get_layer('conv2'), tl.layers.Conv2d) + self.assertIsInstance(model_basic.get_layer(index=2), tl.layers.MaxPool2d) + print([w.name for w in model_basic.get_layer(index=-1).all_weights]) + try: + model_basic.get_layer('abc') + except Exception as e: + print(e) + + try: + model_basic.get_layer(index=99) + except Exception as e: + print(e) + + def test_model_weights_copy(self): + print('-' * 20, 'test_model_weights_copy', '-' * 20) + model_basic = basic_static_model() + model_weights = model_basic.trainable_weights + ori_len = len(model_weights) + model_weights.append(np.arange(5)) + new_len = len(model_weights) + self.assertEqual(new_len - 1, ori_len) + + def test_inchannels_exception(self): + print('-' * 20, 'test_inchannels_exception', '-' * 20) + + class my_model(Model): + + def __init__(self): + super(my_model, self).__init__() + self.dense = Dense(64) + self.vgg = tl.models.vgg16() + + def forward(self, x): + return x + + try: + M = my_model() + except Exception as e: + self.assertIsInstance(e, AttributeError) + print(e) + + +if __name__ == '__main__': + + unittest.main() diff --git a/tests/models/test_model_save.py b/tests/models/test_model_save.py new file mode 100644 index 0000000..ba224ee --- /dev/null +++ b/tests/models/test_model_save.py @@ -0,0 +1,288 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import numpy as np +import tensorflow as tf +import tensorlayer as tl +from tensorlayer.layers import * +from tensorlayer.models import * + +from tests.utils import CustomTestCase + + +def basic_static_model(include_top=True): + ni = Input((None, 24, 24, 3)) + nn = Conv2d(16, (5, 5), (1, 1), padding='SAME', act=tf.nn.relu, name="conv1")(ni) + nn = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool1')(nn) + + nn = Conv2d(16, (5, 5), (1, 1), padding='SAME', act=tf.nn.relu, name="conv2")(nn) + nn = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool2')(nn) + + nn = Flatten(name='flatten')(nn) + nn = Dense(100, act=None, name="dense1")(nn) + if include_top is True: + nn = Dense(10, act=None, name="dense2")(nn) + M = Model(inputs=ni, outputs=nn) + return M + + +class basic_dynamic_model(Model): + + def __init__(self, include_top=True): + super(basic_dynamic_model, self).__init__() + self.include_top = include_top + self.conv1 = Conv2d(16, (5, 5), (1, 1), padding='SAME', act=tf.nn.relu, in_channels=3, name="conv1") + self.pool1 = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool1') + + self.conv2 = Conv2d(16, (5, 5), (1, 1), padding='SAME', act=tf.nn.relu, in_channels=16, name="conv2") + self.pool2 = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool2') + + self.flatten = Flatten(name='flatten') + self.dense1 = Dense(100, act=None, in_channels=576, name="dense1") + if include_top is True: + self.dense2 = Dense(10, act=None, in_channels=100, name="dense2") + + def forward(self, x): + x = self.conv1(x) + x = self.pool1(x) + x = self.conv2(x) + x = self.pool2(x) + x = self.flatten(x) + x = self.dense1(x) + if self.include_top: + x = self.dense2(x) + return x + + +class Nested_VGG(Model): + + def __init__(self): + super(Nested_VGG, self).__init__() + self.vgg1 = tl.models.vgg16() + self.vgg2 = tl.models.vgg16() + + def forward(self, x): + pass + + +class Model_Save_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + cls.static_basic = basic_static_model() + cls.dynamic_basic = basic_dynamic_model() + cls.static_basic_skip = basic_static_model(include_top=False) + cls.dynamic_basic_skip = basic_dynamic_model(include_top=False) + + print([l.name for l in cls.dynamic_basic.all_layers]) + print([l.name for l in cls.dynamic_basic_skip.all_layers]) + pass + + @classmethod + def tearDownClass(cls): + pass + + def normal_save(self, model_basic): + # Default save + model_basic.save_weights('./model_basic.none') + + # hdf5 + print('testing hdf5 saving...') + modify_val = np.zeros_like(model_basic.all_weights[-2].numpy()) + ori_val = model_basic.all_weights[-2].numpy() + model_basic.save_weights("./model_basic.h5") + model_basic.all_weights[-2].assign(modify_val) + model_basic.load_weights("./model_basic.h5") + self.assertLess(np.max(np.abs(ori_val - model_basic.all_weights[-2].numpy())), 1e-7) + + model_basic.all_weights[-2].assign(modify_val) + model_basic.load_weights("./model_basic.h5", format="hdf5") + self.assertLess(np.max(np.abs(ori_val - model_basic.all_weights[-2].numpy())), 1e-7) + + model_basic.all_weights[-2].assign(modify_val) + model_basic.load_weights("./model_basic.h5", format="hdf5", in_order=False) + self.assertLess(np.max(np.abs(ori_val - model_basic.all_weights[-2].numpy())), 1e-7) + + # npz + print('testing npz saving...') + model_basic.save_weights("./model_basic.npz", format='npz') + model_basic.all_weights[-2].assign(modify_val) + model_basic.load_weights("./model_basic.npz") + + model_basic.all_weights[-2].assign(modify_val) + model_basic.load_weights("./model_basic.npz", format='npz') + model_basic.save_weights("./model_basic.npz") + self.assertLess(np.max(np.abs(ori_val - model_basic.all_weights[-2].numpy())), 1e-7) + + # npz_dict + print('testing npz_dict saving...') + model_basic.save_weights("./model_basic.npz", format='npz_dict') + model_basic.all_weights[-2].assign(modify_val) + model_basic.load_weights("./model_basic.npz", format='npz_dict') + self.assertLess(np.max(np.abs(ori_val - model_basic.all_weights[-2].numpy())), 1e-7) + + # ckpt + try: + model_basic.save_weights('./model_basic.ckpt', format='ckpt') + except Exception as e: + self.assertIsInstance(e, NotImplementedError) + + # other cases + try: + model_basic.save_weights('./model_basic.xyz', format='xyz') + except Exception as e: + self.assertIsInstance(e, ValueError) + try: + model_basic.load_weights('./model_basic.xyz', format='xyz') + except Exception as e: + self.assertIsInstance(e, FileNotFoundError) + try: + model_basic.load_weights('./model_basic.h5', format='xyz') + except Exception as e: + self.assertIsInstance(e, ValueError) + + def test_normal_save(self): + print('-' * 20, 'test save weights', '-' * 20) + + self.normal_save(self.static_basic) + self.normal_save(self.dynamic_basic) + + print('testing save dynamic and load static...') + try: + self.dynamic_basic.save_weights("./model_basic.h5") + self.static_basic.load_weights("./model_basic.h5", in_order=False) + except Exception as e: + print(e) + + def test_skip(self): + print('-' * 20, 'test skip save/load', '-' * 20) + + print("testing dynamic skip load...") + self.dynamic_basic.save_weights("./model_basic.h5") + ori_weights = self.dynamic_basic_skip.all_weights + ori_val = ori_weights[1].numpy() + modify_val = np.zeros_like(ori_val) + self.dynamic_basic_skip.all_weights[1].assign(modify_val) + self.dynamic_basic_skip.load_weights("./model_basic.h5", skip=True) + self.assertLess(np.max(np.abs(ori_val - self.dynamic_basic_skip.all_weights[1].numpy())), 1e-7) + + try: + self.dynamic_basic_skip.load_weights("./model_basic.h5", in_order=False, skip=False) + except Exception as e: + print(e) + + print("testing static skip load...") + self.static_basic.save_weights("./model_basic.h5") + ori_weights = self.static_basic_skip.all_weights + ori_val = ori_weights[1].numpy() + modify_val = np.zeros_like(ori_val) + self.static_basic_skip.all_weights[1].assign(modify_val) + self.static_basic_skip.load_weights("./model_basic.h5", skip=True) + self.assertLess(np.max(np.abs(ori_val - self.static_basic_skip.all_weights[1].numpy())), 1e-7) + + try: + self.static_basic_skip.load_weights("./model_basic.h5", in_order=False, skip=False) + except Exception as e: + print(e) + + def test_nested_vgg(self): + print('-' * 20, 'test nested vgg', '-' * 20) + nested_vgg = Nested_VGG() + print([l.name for l in nested_vgg.all_layers]) + nested_vgg.save_weights("nested_vgg.h5") + + # modify vgg1 weight val + tar_weight1 = nested_vgg.vgg1.layers[0].all_weights[0] + print(tar_weight1.name) + ori_val1 = tar_weight1.numpy() + modify_val1 = np.zeros_like(ori_val1) + tar_weight1.assign(modify_val1) + # modify vgg2 weight val + tar_weight2 = nested_vgg.vgg2.layers[1].all_weights[0] + print(tar_weight2.name) + ori_val2 = tar_weight2.numpy() + modify_val2 = np.zeros_like(ori_val2) + tar_weight2.assign(modify_val2) + + nested_vgg.load_weights("nested_vgg.h5") + + self.assertLess(np.max(np.abs(ori_val1 - tar_weight1.numpy())), 1e-7) + self.assertLess(np.max(np.abs(ori_val2 - tar_weight2.numpy())), 1e-7) + + def test_double_nested_vgg(self): + print('-' * 20, 'test_double_nested_vgg', '-' * 20) + + class mymodel(Model): + + def __init__(self): + super(mymodel, self).__init__() + self.inner = Nested_VGG() + self.list = LayerList( + [ + tl.layers.Dense(n_units=4, in_channels=10, name='dense1'), + tl.layers.Dense(n_units=3, in_channels=4, name='dense2') + ] + ) + + def forward(self, *inputs, **kwargs): + pass + + net = mymodel() + net.save_weights("double_nested.h5") + print([x.name for x in net.all_layers]) + + # modify vgg1 weight val + tar_weight1 = net.inner.vgg1.layers[0].all_weights[0] + ori_val1 = tar_weight1.numpy() + modify_val1 = np.zeros_like(ori_val1) + tar_weight1.assign(modify_val1) + # modify vgg2 weight val + tar_weight2 = net.inner.vgg2.layers[1].all_weights[0] + ori_val2 = tar_weight2.numpy() + modify_val2 = np.zeros_like(ori_val2) + tar_weight2.assign(modify_val2) + + net.load_weights("double_nested.h5") + self.assertLess(np.max(np.abs(ori_val1 - tar_weight1.numpy())), 1e-7) + self.assertLess(np.max(np.abs(ori_val2 - tar_weight2.numpy())), 1e-7) + + def test_layerlist(self): + print('-' * 20, 'test_layerlist', '-' * 20) + + # simple modellayer + ni = tl.layers.Input([10, 4]) + nn = tl.layers.Dense(n_units=3, name='dense1')(ni) + modellayer = tl.models.Model(inputs=ni, outputs=nn, name='modellayer').as_layer() + + # nested layerlist with modellayer + inputs = tl.layers.Input([10, 5]) + layer1 = tl.layers.LayerList([tl.layers.Dense(n_units=4, name='dense1'), modellayer])(inputs) + model = tl.models.Model(inputs=inputs, outputs=layer1, name='layerlistmodel') + + model.save_weights("layerlist.h5") + tar_weight = model.get_layer(index=-1)[0].all_weights[0] + print(tar_weight.name) + ori_val = tar_weight.numpy() + modify_val = np.zeros_like(ori_val) + tar_weight.assign(modify_val) + + model.load_weights("layerlist.h5") + self.assertLess(np.max(np.abs(ori_val - tar_weight.numpy())), 1e-7) + + def test_exceptions(self): + print('-' * 20, 'test_exceptions', '-' * 20) + try: + ni = Input([4, 784]) + model = Model(inputs=ni, outputs=ni) + model.save_weights('./empty_model.h5') + except Exception as e: + print(e) + + +if __name__ == '__main__': + + unittest.main() diff --git a/tests/models/test_model_save_graph.py b/tests/models/test_model_save_graph.py new file mode 100644 index 0000000..3e52715 --- /dev/null +++ b/tests/models/test_model_save_graph.py @@ -0,0 +1,547 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import numpy as np +import tensorflow as tf +import tensorlayer as tl +from tensorlayer.layers import * +from tensorlayer.models import * + +from tests.utils import CustomTestCase + + +def RemoveDateInConfig(config): + config["version_info"]["save_date"] = None + return config + + +def basic_static_model(): + ni = Input((None, 24, 24, 3)) + nn = Conv2d(16, (5, 5), (1, 1), padding='SAME', act=tf.nn.relu, name="conv1")(ni) + nn = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool1')(nn) + + nn = Conv2d(16, (5, 5), (1, 1), padding='SAME', act=tf.nn.relu, name="conv2")(nn) + nn = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool2')(nn) + + nn = Flatten(name='flatten')(nn) + nn = Dense(100, act=None, name="dense1")(nn) + M = Model(inputs=ni, outputs=nn) + return M + + +class Model_Save_and_Load_without_weights(CustomTestCase): + + @classmethod + def setUpClass(cls): + print("##### begin testing save_graph, load_graph, without weights #####") + + def test_save(self): + M1 = basic_static_model() + print("Model config = \n", M1.config) + print("Model = \n", M1) + M1.save(filepath="basic_model_without_weights.hdf5", save_weights=False) + M2 = Model.load(filepath="basic_model_without_weights.hdf5", load_weights=False) + + M1_config = RemoveDateInConfig(M1.config) + M2_config = RemoveDateInConfig(M2.config) + + self.assertEqual(M1_config, M2_config) + + +def get_model(inputs_shape): + ni = Input(inputs_shape) + nn = Dropout(keep=0.8)(ni) + nn = Dense(n_units=800, act=tf.nn.relu, + in_channels=784)(nn) # in_channels is optional in this case as it can be inferred by the previous layer + nn = Dropout(keep=0.8)(nn) + nn = Dense(n_units=800, act=tf.nn.relu, + in_channels=800)(nn) # in_channels is optional in this case as it can be inferred by the previous layer + nn = Dropout(keep=0.8)(nn) + nn = Dense(n_units=10, act=tf.nn.relu, + in_channels=800)(nn) # in_channels is optional in this case as it can be inferred by the previous layer + M = Model(inputs=ni, outputs=nn) + return M + + +class Model_Save_with_weights(CustomTestCase): + + @classmethod + def setUpClass(cls): + print("##### begin testing save_graph, after training, with weights #####") + + def test_save(self): + tl.logging.set_verbosity(tl.logging.DEBUG) + X_train, y_train, X_val, y_val, X_test, y_test = tl.files.load_mnist_dataset(shape=(-1, 784)) + MLP = get_model([None, 784]) + print(MLP) + n_epoch = 3 + batch_size = 500 + train_weights = MLP.trainable_weights + optimizer = tf.optimizers.Adam(lr=0.0001) + + for epoch in range(n_epoch): ## iterate the dataset n_epoch times + print("epoch = ", epoch) + + for X_batch, y_batch in tl.iterate.minibatches(X_train, y_train, batch_size, shuffle=True): + MLP.train() # enable dropout + + with tf.GradientTape() as tape: + ## compute outputs + _logits = MLP(X_batch) # alternatively, you can use MLP(x, is_train=True) and remove MLP.train() + ## compute loss and update model + _loss = tl.cost.cross_entropy(_logits, y_batch, name='train_loss') + + grad = tape.gradient(_loss, train_weights) + optimizer.apply_gradients(zip(grad, train_weights)) + + MLP.eval() + + val_loss, val_acc, n_iter = 0, 0, 0 + for X_batch, y_batch in tl.iterate.minibatches(X_val, y_val, batch_size, shuffle=False): + _logits = MLP(X_batch) # is_train=False, disable dropout + val_loss += tl.cost.cross_entropy(_logits, y_batch, name='eval_loss') + val_acc += np.mean(np.equal(np.argmax(_logits, 1), y_batch)) + n_iter += 1 + print(" val loss: {}".format(val_loss / n_iter)) + print(" val acc: {}".format(val_acc / n_iter)) + + MLP.save("MLP.hdf5") + + +class Model_Load_with_weights_and_train(CustomTestCase): + + @classmethod + def setUpClass(cls): + print("##### begin testing load_graph, after training, with weights, and train again #####") + + def test_save(self): + MLP = Model.load("MLP.hdf5", ) + + MLP.eval() + + n_epoch = 3 + batch_size = 500 + train_weights = MLP.trainable_weights + optimizer = tf.optimizers.Adam(lr=0.0001) + X_train, y_train, X_val, y_val, X_test, y_test = tl.files.load_mnist_dataset(shape=(-1, 784)) + val_loss, val_acc, n_iter = 0, 0, 0 + for X_batch, y_batch in tl.iterate.minibatches(X_val, y_val, batch_size, shuffle=False): + _logits = MLP(X_batch) # is_train=False, disable dropout + val_loss += tl.cost.cross_entropy(_logits, y_batch, name='eval_loss') + val_acc += np.mean(np.equal(np.argmax(_logits, 1), y_batch)) + n_iter += 1 + print(" val loss: {}".format(val_loss / n_iter)) + print(" val acc: {}".format(val_acc / n_iter)) + assert val_acc > 0.7 + + for epoch in range(n_epoch): ## iterate the dataset n_epoch times + print("epoch = ", epoch) + + for X_batch, y_batch in tl.iterate.minibatches(X_train, y_train, batch_size, shuffle=True): + MLP.train() # enable dropout + + with tf.GradientTape() as tape: + ## compute outputs + _logits = MLP(X_batch) # alternatively, you can use MLP(x, is_train=True) and remove MLP.train() + ## compute loss and update model + _loss = tl.cost.cross_entropy(_logits, y_batch, name='train_loss') + + grad = tape.gradient(_loss, train_weights) + optimizer.apply_gradients(zip(grad, train_weights)) + + MLP.save("MLP.hdf5") + + +def create_base_network(input_shape): + '''Base network to be shared (eq. to feature extraction). + ''' + input = Input(shape=input_shape) + x = Flatten()(input) + x = Dense(128, act=tf.nn.relu)(x) + x = Dropout(0.9)(x) + x = Dense(128, act=tf.nn.relu)(x) + x = Dropout(0.9)(x) + x = Dense(128, act=tf.nn.relu)(x) + return Model(input, x) + + +def get_siamese_network(input_shape): + """Create siamese network with shared base network as layer + """ + base_layer = create_base_network(input_shape).as_layer() + + ni_1 = Input(input_shape) + ni_2 = Input(input_shape) + nn_1 = base_layer(ni_1) + nn_2 = base_layer(ni_2) + return Model(inputs=[ni_1, ni_2], outputs=[nn_1, nn_2]) + + +class Reuse_ModelLayer_test(CustomTestCase): + + @classmethod + def setUpClass(cls): + print("##### begin testing save_graph, load_graph, including ModelLayer and reuse #####") + + def test_save(self): + input_shape = (None, 784) + M1 = get_siamese_network(input_shape) + print("Model config = \n", M1.config) + print("Model = \n", M1) + M1.save(filepath="siamese.hdf5", save_weights=False) + M2 = Model.load(filepath="siamese.hdf5", load_weights=False) + + M1_config = RemoveDateInConfig(M1.config) + M2_config = RemoveDateInConfig(M2.config) + + self.assertEqual(M1_config, M2_config) + + +class Vgg_LayerList_test(CustomTestCase): + + @classmethod + def setUpClass(cls): + print("##### begin testing save_graph, load_graph, including LayerList #####") + + def test_save(self): + M1 = tl.models.vgg16(mode='static') + print("Model config = \n", M1.config) + print("Model = \n", M1) + M1.save(filepath="vgg.hdf5", save_weights=False) + M2 = Model.load(filepath="vgg.hdf5", load_weights=False) + + M1_config = RemoveDateInConfig(M1.config) + M2_config = RemoveDateInConfig(M2.config) + + self.assertEqual(M1_config, M2_config) + + +class List_inputs_outputs_test(CustomTestCase): + + @classmethod + def setUpClass(cls): + print("##### begin testing model with list inputs and outputs #####") + + def test_list_inputs_outputs(self): + ni_1 = Input(shape=[4, 16]) + ni_2 = Input(shape=[4, 32]) + a_1 = Dense(80)(ni_1) + b_1 = Dense(160)(ni_2) + concat = Concat()([a_1, b_1]) + a_2 = Dense(10)(concat) + b_2 = Dense(20)(concat) + + M1 = Model(inputs=[ni_1, ni_2], outputs=[a_2, b_2]) + print("Model config = \n", M1.config) + print("Model = \n", M1) + M1.save(filepath="list.hdf5", save_weights=False) + M2 = Model.load(filepath="list.hdf5", load_weights=False) + + M1_config = RemoveDateInConfig(M1.config) + M2_config = RemoveDateInConfig(M2.config) + + self.assertEqual(M1_config, M2_config) + + +class Lambda_layer_test(CustomTestCase): + + @classmethod + def setUpClass(cls): + print("##### begin testing lambda layer #####") + + def test_lambda_layer_no_para_no_args(self): + x = tl.layers.Input([8, 3], name='input') + y = tl.layers.Lambda(lambda x: 2 * x, name='lambda')(x) + M1 = tl.models.Model(x, y) + M1.save("lambda_no_para_no_args.hdf5") + M2 = tl.models.Model.load("lambda_no_para_no_args.hdf5") + print(M1) + print(M2) + M1.eval() + M2.eval() + npInput = np.zeros((8, 3)) + 3 + output1 = M1(npInput).numpy() + output2 = M1(npInput).numpy() + + M1_config = RemoveDateInConfig(M1.config) + M2_config = RemoveDateInConfig(M2.config) + + self.assertEqual((output1 == output2).all(), True) + self.assertEqual(M1_config, M2_config) + + def test_lambda_layer_no_para_with_args(self): + + def customize_func(x, foo=42): # x is the inputs, foo is an argument + return foo * x + + x = tl.layers.Input([8, 3], name='input') + y = tl.layers.Lambda(customize_func, fn_args={'foo': 3}, name='lambda')(x) + M1 = tl.models.Model(x, y) + M1.save("lambda_no_para_with_args.hdf5") + M2 = tl.models.Model.load("lambda_no_para_with_args.hdf5") + print(M1) + print(M2) + M1.eval() + M2.eval() + npInput = np.zeros((8, 3)) + 3 + output1 = M1(npInput).numpy() + output2 = M2(npInput).numpy() + + M1_config = RemoveDateInConfig(M1.config) + M2_config = RemoveDateInConfig(M2.config) + + self.assertEqual((output1 == output2).all(), True) + self.assertEqual((output1 == (np.zeros((8, 3)) + 9)).all(), True) + self.assertEqual(M1_config, M2_config) + + def test_lambda_layer_keras_model(self): + input_shape = [100, 5] + in_2 = tl.layers.Input(input_shape, name='input') + layers = [ + tf.keras.layers.Dense(10, activation=tf.nn.relu), + tf.keras.layers.Dense(5, activation=tf.nn.sigmoid), + tf.keras.layers.Dense(1, activation=tf.nn.relu) + ] + perceptron = tf.keras.Sequential(layers) + # in order to compile keras model and get trainable_variables of the keras model + _ = perceptron(np.random.random(input_shape).astype(np.float32)) + plambdalayer = tl.layers.Lambda(perceptron, perceptron.trainable_variables)(in_2) + M2 = tl.models.Model(inputs=in_2, outputs=plambdalayer) + + M2.save('M2_keras.hdf5') + M4 = Model.load('M2_keras.hdf5') + + M2.eval() + M4.eval() + npInput = np.zeros(input_shape) + 3 + output2 = M2(npInput).numpy() + output4 = M4(npInput).numpy() + + M2_config = RemoveDateInConfig(M2.config) + M4_config = RemoveDateInConfig(M4.config) + + self.assertEqual((output2 == output4).all(), True) + self.assertEqual(M2_config, M4_config) + + ori_weights = M4.all_weights + ori_val = ori_weights[1].numpy() + modify_val = np.zeros_like(ori_val) + 10 + M4.all_weights[1].assign(modify_val) + M4 = Model.load('M2_keras.hdf5') + + self.assertLess(np.max(np.abs(ori_val - M4.all_weights[1].numpy())), 1e-7) + + def test_lambda_layer_keras_layer(self): + input_shape = [100, 5] + in_1 = tl.layers.Input(input_shape, name='input') + denselayer = tf.keras.layers.Dense(10, activation=tf.nn.relu) + # in order to compile keras model and get trainable_variables of the keras model + _ = denselayer(np.random.random(input_shape).astype(np.float32)) + dlambdalayer = tl.layers.Lambda(denselayer, denselayer.trainable_variables)(in_1) + M1 = tl.models.Model(inputs=in_1, outputs=dlambdalayer) + + M1.save('M1_keras.hdf5') + M3 = Model.load('M1_keras.hdf5') + + M1.eval() + M3.eval() + npInput = np.zeros(input_shape) + 3 + output1 = M1(npInput).numpy() + output3 = M3(npInput).numpy() + + M1_config = RemoveDateInConfig(M1.config) + M3_config = RemoveDateInConfig(M3.config) + + self.assertEqual((output1 == output3).all(), True) + self.assertEqual(M1_config, M3_config) + + ori_weights = M3.all_weights + ori_val = ori_weights[1].numpy() + modify_val = np.zeros_like(ori_val) + 10 + M3.all_weights[1].assign(modify_val) + M3 = Model.load('M1_keras.hdf5') + + self.assertLess(np.max(np.abs(ori_val - M3.all_weights[1].numpy())), 1e-7) + + +class ElementWise_lambda_test(CustomTestCase): + + @classmethod + def setUpClass(cls): + print("##### begin testing elementwise lambda layer #####") + + def test_elementwise_no_para_with_args(self): + # z = mean + noise * tf.exp(std * 0.5) + foo + def func(noise, mean, std, foo=42): + return mean + noise * tf.exp(std * 0.5) + foo + + noise = tl.layers.Input([100, 1]) + mean = tl.layers.Input([100, 1]) + std = tl.layers.Input([100, 1]) + out = tl.layers.ElementwiseLambda(fn=func, fn_args={'foo': 84}, name='elementwiselambda')([noise, mean, std]) + M1 = Model(inputs=[noise, mean, std], outputs=out) + M1.save("elementwise_npwa.hdf5") + M2 = Model.load("elementwise_npwa.hdf5") + + M1.eval() + M2.eval() + ipt = [np.zeros((100, 1)) + 11, np.zeros((100, 1)) + 21, np.zeros((100, 1)) + 31] + output1 = M1(ipt).numpy() + output2 = M2(ipt).numpy() + + M1_config = RemoveDateInConfig(M1.config) + M2_config = RemoveDateInConfig(M2.config) + + self.assertEqual((output1 == output2).all(), True) + self.assertEqual(M1_config, M2_config) + + def test_elementwise_no_para_no_args(self): + # z = mean + noise * tf.exp(std * 0.5) + foo + def func(noise, mean, std, foo=42): + return mean + noise * tf.exp(std * 0.5) + foo + + noise = tl.layers.Input([100, 1]) + mean = tl.layers.Input([100, 1]) + std = tl.layers.Input([100, 1]) + out = tl.layers.ElementwiseLambda(fn=func, name='elementwiselambda')([noise, mean, std]) + M1 = Model(inputs=[noise, mean, std], outputs=out) + M1.save("elementwise_npna.hdf5") + M2 = Model.load("elementwise_npna.hdf5") + + M1.eval() + M2.eval() + ipt = [np.zeros((100, 1)) + 11, np.zeros((100, 1)) + 21, np.zeros((100, 1)) + 31] + output1 = M1(ipt).numpy() + output2 = M2(ipt).numpy() + + M1_config = RemoveDateInConfig(M1.config) + M2_config = RemoveDateInConfig(M2.config) + + self.assertEqual((output1 == output2).all(), True) + self.assertEqual(M1_config, M2_config) + + def test_elementwise_lambda_func(self): + # z = mean + noise * tf.exp(std * 0.5) + noise = tl.layers.Input([100, 1]) + mean = tl.layers.Input([100, 1]) + std = tl.layers.Input([100, 1]) + out = tl.layers.ElementwiseLambda(fn=lambda x, y, z: x + y * tf.exp(z * 0.5), + name='elementwiselambda')([noise, mean, std]) + M1 = Model(inputs=[noise, mean, std], outputs=out) + M1.save("elementwise_lambda.hdf5") + M2 = Model.load("elementwise_lambda.hdf5") + + M1.eval() + M2.eval() + ipt = [ + (np.zeros((100, 1)) + 11).astype(np.float32), (np.zeros((100, 1)) + 21).astype(np.float32), + (np.zeros((100, 1)) + 31).astype(np.float32) + ] + output1 = M1(ipt).numpy() + output2 = M2(ipt).numpy() + + M1_config = RemoveDateInConfig(M1.config) + M2_config = RemoveDateInConfig(M2.config) + + self.assertEqual((output1 == output2).all(), True) + self.assertEqual(M1_config, M2_config) + + # # ElementwiseLambda does not support keras layer/model func yet + # def test_elementwise_keras_model(self): + # kerasinput1 = tf.keras.layers.Input(shape=(100, )) + # kerasinput2 = tf.keras.layers.Input(shape=(100, )) + # kerasconcate = tf.keras.layers.concatenate(inputs=[kerasinput1, kerasinput2]) + # kerasmodel = tf.keras.models.Model(inputs=[kerasinput1, kerasinput2], outputs=kerasconcate) + # _ = kerasmodel([np.random.random([100,]).astype(np.float32), np.random.random([100,]).astype(np.float32)]) + # + # input1 = tl.layers.Input([100, 1]) + # input2 = tl.layers.Input([100, 1]) + # out = tl.layers.ElementwiseLambda(fn=kerasmodel, name='elementwiselambda')([input1, input2]) + # M1 = Model(inputs=[input1, input2], outputs=out) + # M1.save("elementwise_keras_model.hdf5") + # M2 = Model.load("elementwise_keras_model.hdf5") + # + # M1.eval() + # M2.eval() + # ipt = [np.zeros((100, 1)) + 11, np.zeros((100, 1)) + 21, np.zeros((100, 1)) + 31] + # output1 = M1(ipt).numpy() + # output2 = M2(ipt).numpy() + # + # M1_config = RemoveDateInConfig(M1.config) + # M2_config = RemoveDateInConfig(M2.config) + # + # self.assertEqual((output1 == output2).all(), True) + # self.assertEqual(M1_config, M2_config) + + +class basic_dynamic_model(Model): + + def __init__(self): + super(basic_dynamic_model, self).__init__() + self.conv1 = Conv2d(16, (5, 5), (1, 1), padding='SAME', act=tf.nn.relu, in_channels=3, name="conv1") + self.pool1 = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool1') + + self.conv2 = Conv2d(16, (5, 5), (1, 1), padding='SAME', act=tf.nn.relu, in_channels=16, name="conv2") + self.pool2 = MaxPool2d((3, 3), (2, 2), padding='SAME', name='pool2') + + self.flatten = Flatten(name='flatten') + self.dense1 = Dense(100, act=None, in_channels=576, name="dense1") + self.dense2 = Dense(10, act=None, in_channels=100, name="dense2") + + def forward(self, x): + x = self.conv1(x) + x = self.pool1(x) + x = self.conv2(x) + x = self.pool2(x) + x = self.flatten(x) + x = self.dense1(x) + x = self.dense2(x) + return x + + +class Dynamic_config_test(CustomTestCase): + + @classmethod + def setUpClass(cls): + print("##### begin testing exception in dynamic mode #####") + + def test_dynamic_config(self): + M1 = basic_dynamic_model() + print(M1.config) + for layer in M1.all_layers: + print(layer.config) + + +class Exception_test(CustomTestCase): + + @classmethod + def setUpClass(cls): + print("##### begin testing exception in dynamic mode #####") + + def test_exception(self): + M1 = basic_dynamic_model() + try: + M1.save("dynamic.hdf5", save_weights=False) + except Exception as e: + self.assertIsInstance(e, RuntimeError) + print(e) + + M2 = basic_static_model() + M2.save("basic_static_mode.hdf5", save_weights=False) + try: + M3 = Model.load("basic_static_mode.hdf5") + except Exception as e: + self.assertIsInstance(e, RuntimeError) + print(e) + + +if __name__ == '__main__': + + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/models/test_seq2seq_model.py b/tests/models/test_seq2seq_model.py new file mode 100644 index 0000000..d77aa47 --- /dev/null +++ b/tests/models/test_seq2seq_model.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import numpy as np +import tensorflow as tf +import tensorlayer as tl +from tqdm import tqdm +from sklearn.utils import shuffle +from tensorlayer.models.seq2seq import Seq2seq +from tests.utils import CustomTestCase +from tensorlayer.cost import cross_entropy_seq + + +class Model_SEQ2SEQ_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + + cls.batch_size = 16 + + cls.vocab_size = 20 + cls.embedding_size = 32 + cls.dec_seq_length = 5 + cls.trainX = np.random.randint(20, size=(50, 6)) + cls.trainY = np.random.randint(20, size=(50, cls.dec_seq_length + 1)) + cls.trainY[:, 0] = 0 # start_token == 0 + + # Parameters + cls.src_len = len(cls.trainX) + cls.tgt_len = len(cls.trainY) + + assert cls.src_len == cls.tgt_len + + cls.num_epochs = 100 + cls.n_step = cls.src_len // cls.batch_size + + @classmethod + def tearDownClass(cls): + pass + + def test_basic_simpleSeq2Seq(self): + model_ = Seq2seq( + decoder_seq_length=5, + cell_enc=tf.keras.layers.GRUCell, + cell_dec=tf.keras.layers.GRUCell, + n_layer=3, + n_units=128, + embedding_layer=tl.layers.Embedding(vocabulary_size=self.vocab_size, embedding_size=self.embedding_size), + ) + + optimizer = tf.optimizers.Adam(learning_rate=0.001) + + for epoch in range(self.num_epochs): + model_.train() + trainX, trainY = shuffle(self.trainX, self.trainY) + total_loss, n_iter = 0, 0 + for X, Y in tqdm(tl.iterate.minibatches(inputs=trainX, targets=trainY, batch_size=self.batch_size, + shuffle=False), total=self.n_step, + desc='Epoch[{}/{}]'.format(epoch + 1, self.num_epochs), leave=False): + + dec_seq = Y[:, :-1] + target_seq = Y[:, 1:] + + with tf.GradientTape() as tape: + ## compute outputs + output = model_(inputs=[X, dec_seq]) + + output = tf.reshape(output, [-1, self.vocab_size]) + + loss = cross_entropy_seq(logits=output, target_seqs=target_seq) + + grad = tape.gradient(loss, model_.all_weights) + optimizer.apply_gradients(zip(grad, model_.all_weights)) + + total_loss += loss + n_iter += 1 + + model_.eval() + test_sample = trainX[0:2, :].tolist() + + top_n = 1 + for i in range(top_n): + prediction = model_([test_sample], seq_length=self.dec_seq_length, start_token=0, top_n=1) + print("Prediction: >>>>> ", prediction, "\n Target: >>>>> ", trainY[0:2, 1:], "\n\n") + + # printing average loss after every epoch + print('Epoch [{}/{}]: loss {:.4f}'.format(epoch + 1, self.num_epochs, total_loss / n_iter)) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/models/test_seq2seq_with_attention.py b/tests/models/test_seq2seq_with_attention.py new file mode 100644 index 0000000..d7dbeae --- /dev/null +++ b/tests/models/test_seq2seq_with_attention.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import numpy as np +import tensorflow as tf +import tensorlayer as tl +from tqdm import tqdm +from sklearn.utils import shuffle +from tensorlayer.models.seq2seq_with_attention import Seq2seqLuongAttention +from tests.utils import CustomTestCase +from tensorlayer.cost import cross_entropy_seq + + +class Model_SEQ2SEQ_WITH_ATTENTION_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + + cls.batch_size = 16 + + cls.vocab_size = 200 + cls.embedding_size = 32 + cls.dec_seq_length = 5 + cls.pure_time = np.linspace(-1, 1, 21) + cls.pure_signal = 100 * np.sin(cls.pure_time) + cls.dataset = np.zeros((100, 21)) + for i in range(100): + noise = 100 + 1 * np.random.normal(0, 1, cls.pure_signal.shape) + cls.dataset[i] = cls.pure_signal + noise + cls.dataset = cls.dataset.astype(int) + np.random.shuffle(cls.dataset) + cls.trainX = cls.dataset[:80, :15] + cls.trainY = cls.dataset[:80, 15:] + cls.testX = cls.dataset[80:, :15] + cls.testY = cls.dataset[80:, 15:] + + cls.trainY[:, 0] = 0 # start_token == 0 + cls.testY[:, 0] = 0 # start_token == 0 + + # Parameters + cls.src_len = len(cls.trainX) + cls.tgt_len = len(cls.trainY) + + assert cls.src_len == cls.tgt_len + + cls.num_epochs = 500 + cls.n_step = cls.src_len // cls.batch_size + + @classmethod + def tearDownClass(cls): + pass + + def test_basic_simpleSeq2Seq(self): + + model_ = Seq2seqLuongAttention( + hidden_size=128, cell=tf.keras.layers.SimpleRNNCell, + embedding_layer=tl.layers.Embedding(vocabulary_size=self.vocab_size, + embedding_size=self.embedding_size), method='dot' + ) + optimizer = tf.optimizers.Adam(learning_rate=0.001) + + for epoch in range(self.num_epochs): + model_.train() + trainX, trainY = shuffle(self.trainX, self.trainY) + total_loss, n_iter = 0, 0 + for X, Y in tqdm(tl.iterate.minibatches(inputs=trainX, targets=trainY, batch_size=self.batch_size, + shuffle=False), total=self.n_step, + desc='Epoch[{}/{}]'.format(epoch + 1, self.num_epochs), leave=False): + dec_seq = Y[:, :-1] + target_seq = Y[:, 1:] + + with tf.GradientTape() as tape: + ## compute outputs + output = model_(inputs=[X, dec_seq]) + # print(output) + output = tf.reshape(output, [-1, self.vocab_size]) + + loss = cross_entropy_seq(logits=output, target_seqs=target_seq) + grad = tape.gradient(loss, model_.trainable_weights) + optimizer.apply_gradients(zip(grad, model_.trainable_weights)) + + total_loss += loss + n_iter += 1 + + model_.eval() + test_sample = self.testX[:5, :].tolist() # Can't capture the sequence. + top_n = 1 + for i in range(top_n): + prediction = model_([test_sample], seq_length=self.dec_seq_length, sos=0) + print("Prediction: >>>>> ", prediction, "\n Target: >>>>> ", self.testY[:5, 1:], "\n\n") + + # printing average loss after every epoch + print('Epoch [{}/{}]: loss {:.4f}'.format(epoch + 1, self.num_epochs, total_loss / n_iter)) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/pending/__init__.py b/tests/pending/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/pending/test_array_ops.py b/tests/pending/test_array_ops.py new file mode 100644 index 0000000..56b80d4 --- /dev/null +++ b/tests/pending/test_array_ops.py @@ -0,0 +1,244 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl + +import numpy as np + +from tests.utils import CustomTestCase + + +class Array_Op_Alphas_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + + b1 = tl.alphas([4, 3, 2, 1], 0.5431) + b2 = tl.alphas([4, 3, 2], 5) + b3 = tl.alphas([1, 2, 3, 4], -5) + b4 = tl.alphas([2, 3, 4], True) + + with tf.Session() as sess: + cls._b1, cls._b2, cls._b3, cls._b4 = sess.run([b1, b2, b3, b4]) + + @classmethod + def tearDownClass(cls): + tf.reset_default_graph() + + def test_b1(self): + self.assertEqual(self._b1.shape, (4, 3, 2, 1)) + + b1 = np.array( + [ + [ + [ + [0.5431], + [0.5431], + ], + [ + [0.5431], + [0.5431], + ], + [ + [0.5431], + [0.5431], + ], + ], [ + [ + [0.5431], + [0.5431], + ], + [ + [0.5431], + [0.5431], + ], + [ + [0.5431], + [0.5431], + ], + ], [ + [ + [0.5431], + [0.5431], + ], + [ + [0.5431], + [0.5431], + ], + [ + [0.5431], + [0.5431], + ], + ], [ + [ + [0.5431], + [0.5431], + ], + [ + [0.5431], + [0.5431], + ], + [ + [0.5431], + [0.5431], + ], + ] + ] + ) + + np.array_equal(self._b1, b1) + + def test_b2(self): + self.assertEqual(self._b2.shape, (4, 3, 2)) + + b2 = np.array( + [ + [ + [ + 5, + 5, + ], + [ + 5, + 5, + ], + [ + 5, + 5, + ], + ], [ + [ + 5, + 5, + ], + [ + 5, + 5, + ], + [ + 5, + 5, + ], + ], [ + [ + 5, + 5, + ], + [ + 5, + 5, + ], + [ + 5, + 5, + ], + ], [ + [ + 5, + 5, + ], + [ + 5, + 5, + ], + [ + 5, + 5, + ], + ] + ] + ) + + np.array_equal(self._b2, b2) + + def test_b3(self): + self.assertEqual(self._b3.shape, (1, 2, 3, 4)) + + b3 = np.array( + [ + [ + [[-5, -5, -5, -5], [-5, -5, -5, -5], [-5, -5, -5, -5]], + [[-5, -5, -5, -5], [-5, -5, -5, -5], [-5, -5, -5, -5]], + ] + ] + ) + + np.array_equal(self._b3, b3) + + def test_b4(self): + self.assertEqual(self._b4.shape, (2, 3, 4)) + + b4 = np.array( + [ + [[True, True, True, True], [True, True, True, True], [True, True, True, True]], + [[True, True, True, True], [True, True, True, True], [True, True, True, True]], + ] + ) + + np.array_equal(self._b4, b4) + + +class Array_Op_Alphas_Like_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + a = tf.constant([[[4, 5, 6], [1, 2, 3]], [[4, 5, 6], [1, 2, 3]]]) + + b1 = tl.alphas_like(a, 0.5431) + b2 = tl.alphas_like(a, 5) + b3 = tl.alphas_like(a, -5) + b4 = tl.alphas_like(a, True) + + with tf.Session() as sess: + cls._b1, cls._b2, cls._b3, cls._b4 = sess.run([b1, b2, b3, b4]) + + @classmethod + def tearDownClass(cls): + tf.reset_default_graph() + + def test_b1(self): + self.assertEqual(self._b1.shape, (2, 2, 3)) + + b1 = np.array( + [ + [[0.5431, 0.5431, 0.5431], [0.5431, 0.5431, 0.5431]], + [[0.5431, 0.5431, 0.5431], [0.5431, 0.5431, 0.5431]] + ] + ) + + np.array_equal(self._b1, b1) + + def test_b2(self): + self.assertEqual(self._b2.shape, (2, 2, 3)) + + b2 = np.array([[[5, 5, 5], [5, 5, 5]], [[5, 5, 5], [5, 5, 5]]]) + + np.array_equal(self._b2, b2) + + def test_b3(self): + self.assertEqual(self._b3.shape, (2, 2, 3)) + + b3 = np.array([[[-5, -5, -5], [-5, -5, -5]], [[-5, -5, -5], [-5, -5, -5]]]) + + np.array_equal(self._b3, b3) + + def test_b4(self): + self.assertEqual(self._b4.shape, (2, 2, 3)) + + b4 = np.array([[[True, True, True], [True, True, True]], [[True, True, True], [True, True, True]]]) + + np.array_equal(self._b4, b4) + + +if __name__ == '__main__': + + tf.logging.set_verbosity(tf.logging.DEBUG) + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/pending/test_decorators.py b/tests/pending/test_decorators.py new file mode 100644 index 0000000..cc88785 --- /dev/null +++ b/tests/pending/test_decorators.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl + +from tensorlayer.decorators import private_method + +from tests.utils import CustomTestCase + + +class Layer_Pooling_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + + class MyClass(object): + + @private_method + def _private_func(self): + tl.logging.debug("I am private") + + def public_func(self): + tl.logging.debug("I am public and calling now the private func") + self._private_func() + + cls.my_object = MyClass() + + def test_call_from_public_method(self): + with self.assertNotRaises(RuntimeError): + self.my_object.public_func() + + def test_call_private_method(self): + with self.assertRaises(RuntimeError): + self.my_object._private_func() + + +if __name__ == '__main__': + + tf.logging.set_verbosity(tf.logging.DEBUG) + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/pending/test_documentation.py b/tests/pending/test_documentation.py new file mode 100644 index 0000000..211142e --- /dev/null +++ b/tests/pending/test_documentation.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +from sphinx.application import Sphinx + + +class DocTest(unittest.TestCase): + source_dir = u'docs/' + config_dir = u'docs/' + output_dir = u'docs/test_build' + doctree_dir = u'docs/test_build/doctrees' + + all_files = True + + @classmethod + def setUpClass(cls): + os.environ["SPHINXBUILD"] = "1" + + def test_html_documentation(self): + app = Sphinx( + self.source_dir, + self.config_dir, + self.output_dir, + self.doctree_dir, + buildername='html', + warningiserror=True, + ) + app.build(force_all=self.all_files) + # TODO: additional checks here if needed + + def test_text_documentation(self): + # The same, but with different buildername + app = Sphinx( + self.source_dir, + self.config_dir, + self.output_dir, + self.doctree_dir, + buildername='text', + warningiserror=False, + ) + app.build(force_all=self.all_files) + # TODO: additional checks if needed + + def tearDown(self): + # TODO: clean up the output directory + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/pending/test_layers_basic.py b/tests/pending/test_layers_basic.py new file mode 100644 index 0000000..2771f96 --- /dev/null +++ b/tests/pending/test_layers_basic.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class Layer_Basic_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + + x = tf.placeholder(tf.float32, [None, 100]) + + n = tl.layers.InputLayer(x, name='in') + n = tl.layers.DenseLayer(n, n_units=80, name='d1') + n = tl.layers.DenseLayer(n, n_units=80, name='d2') + + n.print_layers() + n.print_params(False) + + n2 = n[:, :30] + n2.print_layers() + + cls.n_params = n.count_params() + cls.all_layers = n.all_layers + cls.all_params = n.all_params + cls.shape_n = n.outputs.get_shape().as_list() + + cls.shape_n2 = n2.outputs.get_shape().as_list() + + @classmethod + def tearDownClass(cls): + tf.reset_default_graph() + + def test_n_params(self): + self.assertEqual(self.n_params, 14560) + + def test_shape_n(self): + self.assertEqual(self.shape_n[-1], 80) + + def test_all_layers(self): + self.assertEqual(len(self.all_layers), 3) + + def test_all_params(self): + self.assertEqual(len(self.all_params), 4) + + def test_shape_n2(self): + self.assertEqual(self.shape_n2[-1], 30) + + +if __name__ == '__main__': + + tf.logging.set_verbosity(tf.logging.DEBUG) + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/pending/test_layers_flow_control.py b/tests/pending/test_layers_flow_control.py new file mode 100644 index 0000000..d86eb21 --- /dev/null +++ b/tests/pending/test_layers_flow_control.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class Layer_Flow_Control_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + x = tf.placeholder(tf.float32, shape=(None, 784), name='x') + + # define the network + net_in = tl.layers.InputLayer(x, name='in') + net_in = tl.layers.DropoutLayer(net_in, keep=0.8, name='in/drop') + # net 0 + net_0 = tl.layers.DenseLayer(net_in, n_units=800, act=tf.nn.relu, name='net0/relu1') + net_0 = tl.layers.DropoutLayer(net_0, keep=0.5, name='net0/drop1') + net_0 = tl.layers.DenseLayer(net_0, n_units=800, act=tf.nn.relu, name='net0/relu2') + # net 1 + net_1 = tl.layers.DenseLayer(net_in, n_units=800, act=tf.nn.relu, name='net1/relu1') + net_1 = tl.layers.DropoutLayer(net_1, keep=0.8, name='net1/drop1') + net_1 = tl.layers.DenseLayer(net_1, n_units=800, act=tf.nn.relu, name='net1/relu2') + net_1 = tl.layers.DropoutLayer(net_1, keep=0.8, name='net1/drop2') + net_1 = tl.layers.DenseLayer(net_1, n_units=800, act=tf.nn.relu, name='net1/relu3') + # multiplexer + net_mux = tl.layers.MultiplexerLayer(layers=[net_0, net_1], name='mux') + network = tl.layers.ReshapeLayer(net_mux, shape=(-1, 800), name='reshape') + network = tl.layers.DropoutLayer(network, keep=0.5, name='drop3') + # output layer + network = tl.layers.DenseLayer(network, n_units=10, name='output') + + network.print_layers() + network.print_params(False) + + cls.net_shape = network.outputs.get_shape().as_list() + cls.net_layers = network.all_layers + cls.net_params = network.all_params + cls.net_all_drop = network.all_drop + cls.net_n_params = network.count_params() + + @classmethod + def tearDownClass(cls): + tf.reset_default_graph() + + def test_net_shape(self): + self.assertEqual(self.net_shape[-1], 10) + + def test_net_layers(self): + self.assertEqual(len(self.net_layers), 14) + + def test_net_params(self): + self.assertEqual(len(self.net_params), 12) + + def test_net_all_drop(self): + self.assertEqual(len(self.net_all_drop), 5) + + def test_net_n_params(self): + self.assertEqual(self.net_n_params, 3186410) + + +if __name__ == '__main__': + + tf.logging.set_verbosity(tf.logging.DEBUG) + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/pending/test_layers_importer.py b/tests/pending/test_layers_importer.py new file mode 100644 index 0000000..1c1321a --- /dev/null +++ b/tests/pending/test_layers_importer.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf + +from tensorflow.contrib.slim.python.slim.nets.inception_v3 import inception_v3 +from tensorflow.contrib.slim.python.slim.nets.inception_v3 import inception_v3_arg_scope + +slim = tf.contrib.slim +keras = tf.keras + +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class Layer_Importer_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + + cls.net_in = dict() + + # ============================= # + # LambdaLayer + # ============================= # + x = tf.placeholder(tf.float32, shape=[None, 784]) + cls.net_in["lambda"] = tl.layers.InputLayer(x, name='input') + + # ============================= # + # SlimNetsLayer + # ============================= # + x = tf.placeholder(tf.float32, shape=[None, 299, 299, 3]) + cls.net_in["slim"] = tl.layers.InputLayer(x, name='input_layer') + + @classmethod + def tearDownClass(cls): + tf.reset_default_graph() + + def test_lambda_layer(self): + + def keras_block(x): + x = keras.layers.Dropout(0.8)(x) + x = keras.layers.Dense(100, activation='relu')(x) + # x = keras.layers.Dropout(0.8)(x) + # x = keras.layers.Dense(100, activation='relu')(x) + x = keras.layers.Dropout(0.5)(x) + logits = keras.layers.Dense(10, activation='linear')(x) + + return logits + + with self.assertNotRaises(Exception): + tl.layers.LambdaLayer(self.net_in["lambda"], fn=keras_block, name='keras') + + def test_slim_layer(self): + + with self.assertNotRaises(Exception): + with slim.arg_scope(inception_v3_arg_scope()): + # Alternatively, you should implement inception_v3 without TensorLayer as follow. + # logits, end_points = inception_v3(X, num_classes=1001, + # is_training=False) + tl.layers.SlimNetsLayer( + self.net_in["slim"], + slim_layer=inception_v3, + slim_args={ + 'num_classes': 1001, + 'is_training': False, + # 'dropout_keep_prob' : 0.8, # for training + # 'min_depth' : 16, + # 'depth_multiplier' : 1.0, + # 'prediction_fn' : slim.softmax, + # 'spatial_squeeze' : True, + # 'reuse' : None, + # 'scope' : 'InceptionV3' + }, + name='InceptionV3' # <-- the name should be the same with the ckpt model + ) + + +if __name__ == '__main__': + + tf.logging.set_verbosity(tf.logging.DEBUG) + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/pending/test_layers_normalization.py b/tests/pending/test_layers_normalization.py new file mode 100644 index 0000000..d0891ab --- /dev/null +++ b/tests/pending/test_layers_normalization.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +def model(x, is_train=True, reuse=False): + with tf.variable_scope("model", reuse=reuse): + n = tl.layers.InputLayer(x, name='in') + n = tl.layers.Conv2d(n, n_filter=80, name='conv2d_1') + n = tl.layers.BatchNormLayer(n, is_train=is_train, name='norm_batch') + n = tl.layers.Conv2d(n, n_filter=80, name='conv2d_2') + n = tl.layers.LocalResponseNormLayer(n, name='norm_local') + n = tl.layers.LayerNormLayer(n, reuse=reuse, name='norm_layer') + n = tl.layers.InstanceNormLayer(n, name='norm_instance') + # n = tl.layers.GroupNormLayer(n, groups=40, name='groupnorm') + n.outputs = tf.reshape(n.outputs, [-1, 80, 100, 100]) + n = tl.layers.GroupNormLayer(n, groups=40, data_format='channels_first', name='groupnorm') + n.outputs = tf.reshape(n.outputs, [-1, 100, 100, 80]) + n = tl.layers.SwitchNormLayer(n, name='switchnorm') + n = tl.layers.QuanConv2dWithBN(n, n_filter=3, is_train=is_train, name='quan_cnn_with_bn') + n = tl.layers.FlattenLayer(n, name='flatten') + n = tl.layers.QuanDenseLayerWithBN(n, n_units=10, name='quan_dense_with_bn') + return n + + +class Layer_Normalization_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + + x = tf.placeholder(tf.float32, [None, 100, 100, 3]) + + net_train = model(x, is_train=True, reuse=False) + net_eval = model(x, is_train=False, reuse=True) + + net_train.print_layers() + net_train.print_params(False) + + cls.data = dict() + cls.data["train_network"] = dict() + cls.data["eval_network"] = dict() + + cls.data["train_network"]["layers"] = net_train.all_layers + cls.data["eval_network"]["layers"] = net_eval.all_layers + + cls.data["train_network"]["params"] = net_train.all_params + + cls.data["train_network"]["n_params"] = net_train.count_params() + + print(net_train.count_params()) + + @classmethod + def tearDownClass(cls): + tf.reset_default_graph() + + def test_all_layers(self): + self.assertEqual(len(self.data["train_network"]["layers"]), 12) + self.assertEqual(len(self.data["eval_network"]["layers"]), 12) + + def test_all_params(self): + self.assertEqual(len(self.data["train_network"]["params"]), 28) + + def test_n_params(self): + self.assertEqual(self.data["train_network"]["n_params"], 363098) + + +if __name__ == '__main__': + + tf.logging.set_verbosity(tf.logging.DEBUG) + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/pending/test_layers_padding.py b/tests/pending/test_layers_padding.py new file mode 100644 index 0000000..ab6f6b5 --- /dev/null +++ b/tests/pending/test_layers_padding.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class Layer_Padding_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + + ## 1D + x = tf.placeholder(tf.float32, (None, 100, 1)) + n = tl.layers.InputLayer(x) + + n1 = tl.layers.ZeroPad1d(n, padding=1) + n2 = tl.layers.ZeroPad1d(n, padding=(2, 3)) + + n1.print_layers() + n2.print_layers() + + cls.n1_shape = n1.outputs.get_shape().as_list() + cls.n2_shape = n2.outputs.get_shape().as_list() + + ## 2D + x = tf.placeholder(tf.float32, (None, 100, 100, 3)) + n = tl.layers.InputLayer(x) + + n3 = tl.layers.ZeroPad2d(n, padding=2) + n4 = tl.layers.ZeroPad2d(n, padding=(2, 3)) + n5 = tl.layers.ZeroPad2d(n, padding=((3, 3), (4, 4))) + + n3.print_layers() + n4.print_layers() + n5.print_layers() + + cls.n3_shape = n3.outputs.get_shape().as_list() + cls.n4_shape = n4.outputs.get_shape().as_list() + cls.n5_shape = n5.outputs.get_shape().as_list() + + ## 3D + x = tf.placeholder(tf.float32, (None, 100, 100, 100, 3)) + n = tl.layers.InputLayer(x) + + n6 = tl.layers.ZeroPad3d(n, padding=2) + n7 = tl.layers.ZeroPad3d(n, padding=(2, 3, 4)) + n8 = tl.layers.ZeroPad3d(n, padding=((3, 3), (4, 4), (5, 5))) + + n6.print_layers() + n7.print_layers() + n8.print_layers() + + cls.n6_shape = n6.outputs.get_shape().as_list() + cls.n7_shape = n7.outputs.get_shape().as_list() + cls.n8_shape = n8.outputs.get_shape().as_list() + + @classmethod + def tearDownClass(cls): + tf.reset_default_graph() + + def test_n1_shape(self): + self.assertEqual(self.n1_shape[1:], [102, 1]) + + def test_n2_shape(self): + self.assertEqual(self.n2_shape[1:], [105, 1]) + + def test_n3_shape(self): + self.assertEqual(self.n3_shape[1:], [104, 104, 3]) + + def test_n4_shape(self): + self.assertEqual(self.n4_shape[1:], [104, 106, 3]) + + def test_n5_shape(self): + self.assertEqual(self.n5_shape[1:], [106, 108, 3]) + + def test_n6_shape(self): + self.assertEqual(self.n6_shape[1:], [104, 104, 104, 3]) + + def test_n7_shape(self): + self.assertEqual(self.n7_shape[1:], [104, 106, 108, 3]) + + def test_n8_shape(self): + self.assertEqual(self.n8_shape[1:], [106, 108, 110, 3]) + + +if __name__ == '__main__': + + tf.logging.set_verbosity(tf.logging.DEBUG) + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/pending/test_layers_spatial_transformer.py b/tests/pending/test_layers_spatial_transformer.py new file mode 100644 index 0000000..4c6d81b --- /dev/null +++ b/tests/pending/test_layers_spatial_transformer.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +def model(x, is_train, reuse): + with tf.variable_scope("STN", reuse=reuse): + nin = tl.layers.InputLayer(x, name='in') + ## 1. Localisation network + # use MLP as the localisation net + nt = tl.layers.FlattenLayer(nin, name='flatten') + nt = tl.layers.DenseLayer(nt, n_units=20, act=tf.nn.tanh, name='dense1') + nt = tl.layers.DropoutLayer(nt, keep=0.8, is_fix=True, is_train=is_train, name='drop1') + # you can also use CNN instead for MLP as the localisation net + # nt = Conv2d(nin, 16, (3, 3), (2, 2), act=tf.ops.relu, padding='SAME', name='tc1') + # nt = Conv2d(nt, 8, (3, 3), (2, 2), act=tf.ops.relu, padding='SAME', name='tc2') + ## 2. Spatial transformer module (sampler) + n = tl.layers.SpatialTransformer2dAffineLayer(nin, theta_layer=nt, out_size=(40, 40), name='spatial') + s = n + ## 3. Classifier + n = tl.layers.Conv2d( + n, n_filter=16, filter_size=(3, 3), strides=(2, 2), act=tf.nn.relu, padding='SAME', name='conv1' + ) + + n = tl.layers.Conv2d( + n, n_filter=16, filter_size=(3, 3), strides=(2, 2), act=tf.nn.relu, padding='SAME', name='conv2' + ) + n = tl.layers.FlattenLayer(n, name='flatten2') + n = tl.layers.DenseLayer(n, n_units=1024, act=tf.nn.relu, name='out1') + n = tl.layers.DenseLayer(n, n_units=10, name='out2') + return n, s + + +class Layer_Spatial_Transformer_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + cls.x = tf.placeholder(tf.float32, shape=[None, 28, 28, 1]) + + net, s = model(cls.x, is_train=True, reuse=False) + + net.print_layers() + net.print_params(False) + + cls.s_shape = s.outputs.get_shape().as_list() + cls.net_layers = net.all_layers + cls.net_params = net.all_params + cls.net_n_params = net.count_params() + + @classmethod + def tearDownClass(cls): + tf.reset_default_graph() + + def test_reuse(self): + + with self.assertNotRaises(Exception): + _, _ = model(self.x, is_train=True, reuse=True) + + def test_net_shape(self): + self.assertEqual(self.s_shape[1:], [40, 40, 1]) + + def test_net_layers(self): + self.assertEqual(len(self.net_layers), 10) + + def test_net_params(self): + self.assertEqual(len(self.net_params), 12) + + def test_net_n_params(self): + self.assertEqual(self.net_n_params, 1667980) + + +if __name__ == '__main__': + + tf.logging.set_verbosity(tf.logging.DEBUG) + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/pending/test_layers_stack.py b/tests/pending/test_layers_stack.py new file mode 100644 index 0000000..0745a83 --- /dev/null +++ b/tests/pending/test_layers_stack.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class Layer_Stack_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + + x = tf.placeholder(tf.float32, shape=[None, 30]) + net_in = tl.layers.InputLayer(x, name='input') + + net_d1 = tl.layers.DenseLayer(net_in, n_units=10, name='dense1') + net_d2 = tl.layers.DenseLayer(net_in, n_units=10, name='dense2') + net_d3 = tl.layers.DenseLayer(net_in, n_units=10, name='dense3') + + cls.net_stack = tl.layers.StackLayer([net_d1, net_d2, net_d3], axis=1, name='stack') + + cls.net_stack.print_layers() + cls.net_stack.print_params(False) + + cls.net_unstack = tl.layers.UnStackLayer(cls.net_stack, axis=1, name='unstack') + + cls.net_unstack.print_layers() + cls.net_unstack.print_params(False) + + @classmethod + def tearDownClass(cls): + tf.reset_default_graph() + + def test_StackLayer(self): + self.assertEqual(self.net_stack.outputs.get_shape().as_list()[-1], 10) + self.assertEqual(len(self.net_stack.all_layers), 5) + self.assertEqual(len(self.net_stack.all_params), 6) + self.assertEqual(self.net_stack.count_params(), 930) + + def test_UnStackLayer(self): + + for n in self.net_unstack.outputs: + shape = n.outputs.get_shape().as_list() + + self.assertEqual(shape[-1], 10) + self.assertEqual(len(n.all_layers), 5) + self.assertEqual(len(n.all_params), 6) + self.assertEqual(n.count_params(), 930) + + +if __name__ == '__main__': + + tf.logging.set_verbosity(tf.logging.DEBUG) + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/pending/test_layers_super_resolution.py b/tests/pending/test_layers_super_resolution.py new file mode 100644 index 0000000..9b359cb --- /dev/null +++ b/tests/pending/test_layers_super_resolution.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class Layer_Super_Resolution_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + t_signal = tf.placeholder('float32', [10, 100, 4], name='x') + n = tl.layers.InputLayer(t_signal, name='in') + n = tl.layers.Conv1d(n, n_filter=32, filter_size=3, stride=1, padding='SAME', name='conv1d') + net1 = tl.layers.SubpixelConv1d(n, scale=2, name='subpixel') + + net1.print_layers() + net1.print_params(False) + + cls.net1_shape = net1.outputs.get_shape().as_list() + cls.net1_layers = net1.all_layers + cls.net1_params = net1.all_params + cls.net1_n_params = net1.count_params() + + ## 2D + x = tf.placeholder('float32', [10, 100, 100, 3], name='x') + n = tl.layers.InputLayer(x, name='in') + n = tl.layers.Conv2d(n, n_filter=32, filter_size=(3, 2), strides=(1, 1), padding='SAME', name='conv2d') + net2 = tl.layers.SubpixelConv2d(n, scale=2, name='subpixel2d') + + net2.print_layers() + net2.print_params(False) + + cls.net2_shape = net2.outputs.get_shape().as_list() + cls.net2_layers = net2.all_layers + cls.net2_params = net2.all_params + cls.net2_n_params = net2.count_params() + + @classmethod + def tearDownClass(cls): + tf.reset_default_graph() + + def test_net1_shape(self): + self.assertEqual(self.net1_shape, [10, 200, 16]) + self.assertEqual(len(self.net1_layers), 3) + self.assertEqual(len(self.net1_params), 2) + self.assertEqual(self.net1_n_params, 416) + + def test_net2_shape(self): + self.assertEqual(self.net2_shape, [10, 200, 200, 8]) + self.assertEqual(len(self.net2_layers), 3) + self.assertEqual(len(self.net2_params), 2) + self.assertEqual(self.net2_n_params, 608) + + +if __name__ == '__main__': + + tf.logging.set_verbosity(tf.logging.DEBUG) + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/pending/test_layers_time_distributed.py b/tests/pending/test_layers_time_distributed.py new file mode 100644 index 0000000..a97c511 --- /dev/null +++ b/tests/pending/test_layers_time_distributed.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +def model(x, is_train=True, reuse=False, name_scope="env1"): + with tf.variable_scope(name_scope, reuse=reuse): + net = tl.layers.InputLayer(x, name='input') + net = tl.layers.TimeDistributedLayer( + net, layer_class=tl.layers.DenseLayer, args={ + 'n_units': 50, + 'name': 'dense' + }, name='time_dense' + ) + return net + + +class Layer_Time_Distributed_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + + batch_size = 32 + timestep = 20 + input_dim = 100 + + cls.x = tf.placeholder(dtype=tf.float32, shape=[batch_size, timestep, input_dim], name="encode_seqs") + net = model(cls.x, is_train=True, reuse=False) + + cls.net_shape = net.outputs.get_shape().as_list() + cls.n_params = net.count_params() + net.print_params(False) + + @classmethod + def tearDownClass(cls): + tf.reset_default_graph() + + def test_net_shape(self): + self.assertEqual(self.net_shape, [32, 20, 50]) + + def test_net_n_params(self): + self.assertEqual(self.n_params, 5050) + + def test_reuse(self): + + with self.assertNotRaises(Exception): + model(self.x, is_train=True, reuse=False, name_scope="env2") + model(self.x, is_train=False, reuse=True, name_scope="env2") + + with self.assertRaises(Exception): + model(self.x, is_train=True, reuse=False) # Already defined model with the same var_scope + + +if __name__ == '__main__': + + tf.logging.set_verbosity(tf.logging.DEBUG) + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/pending/test_logging.py b/tests/pending/test_logging.py new file mode 100644 index 0000000..fffdf7c --- /dev/null +++ b/tests/pending/test_logging.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class TL_Logger_Test(CustomTestCase): + + def test_debug(self): + with self.assertNotRaises(Exception): + tl.logging.debug("This is a debug message") + + def test_error(self): + with self.assertNotRaises(Exception): + tl.logging.error("This is an error message") + + def test_fatal(self): + with self.assertNotRaises(Exception): + tl.logging.fatal("This is a fatal error message") + + def test_info(self): + with self.assertNotRaises(Exception): + tl.logging.info("This is an information message") + + def test_warn(self): + with self.assertNotRaises(Exception): + tl.logging.warn("This is a warning message") + + def test_set_verbosity(self): + with self.assertNotRaises(Exception): + tl.logging.set_verbosity(tl.logging.DEBUG) + tl.logging.set_verbosity(tl.logging.INFO) + tl.logging.set_verbosity(tl.logging.WARN) + tl.logging.set_verbosity(tl.logging.ERROR) + tl.logging.set_verbosity(tl.logging.FATAL) + + def test_get_verbosity(self): + with self.assertNotRaises(Exception): + tl.logging.get_verbosity() + + +if __name__ == '__main__': + + tf.logging.set_verbosity(tf.logging.DEBUG) + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/pending/test_logging_hyperdash.py b/tests/pending/test_logging_hyperdash.py new file mode 100644 index 0000000..c39e661 --- /dev/null +++ b/tests/pending/test_logging_hyperdash.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +import time + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl + +from tensorlayer.logging.contrib import hyperdash as hd + +from tests.utils import CustomTestCase + + +class TL_Logger_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + cls.apikey = os.getenv('HYPERDASH_APIKEY', "test_api_key") + + def test_apikey_unset(self): + + with self.assertRaises(ValueError): + hd.HyperDashHandler.reset_apikey() + hd.HyperDashHandler.get_apikey() + + def test_apikey_set(self): + + with self.assertNotRaises(ValueError): + hd.HyperDashHandler.set_apikey(self.apikey) + hd.HyperDashHandler.get_apikey() + + def test_monitor(self): + + with self.assertNotRaises(Exception): + + hd.HyperDashHandler.set_apikey(self.apikey) + + @hd.monitor("TRAVIS 1 - dogs vs. cats") + def train_dogs_vs_cats(exp=None): + + # Record the value of hyperparameter gamma for this experiment + lr = exp.param("learning rate", 0.005) + tl.logging.debug("Learning Rate: %f" % lr) + + for epoch, accuracy in enumerate([10, 30, 50, 70, 80, 90, 95, 100]): + tl.logging.debug("Epoch %d - Accuracy %d%%" % (epoch + 1, accuracy)) + + # Record a numerical performance metric + exp.metric(name="accuracy", value=accuracy) + + time.sleep(0.1) + + train_dogs_vs_cats() + + def test_monitor_variant(self): + + with self.assertNotRaises(Exception): + + @hd.monitor("TRAVIS 2 - dogs vs. cats", api_key=self.apikey) + def train_dogs_vs_cats(exp=None): + + # Record the value of hyperparameter gamma for this experiment + lr = exp.param("learning rate", 0.005) + tl.logging.debug("Learning Rate: %f" % lr) + + for epoch, accuracy in enumerate([10, 30, 50, 70, 80, 90, 95, 100]): + tl.logging.debug("Epoch %d - Accuracy %d%%" % (epoch + 1, accuracy)) + + # Record a numerical performance metric + exp.metric(name="accuracy", value=accuracy) + + time.sleep(0.1) + + train_dogs_vs_cats() + + def test_Experiment(self): + + hd.HyperDashHandler.set_apikey(self.apikey) + + with self.assertNotRaises(Exception): + + def train_dogs_vs_cats(): + + # Create an experiment with a model name, then autostart + exp = hd.Experiment("TRAVIS 3 - dogs vs. cats") + + # Record the value of hyperparameter gamma for this experiment + lr = exp.param("learning rate", 0.005) + tl.logging.debug("Learning Rate: %f" % lr) + + for epoch, accuracy in enumerate([10, 30, 50, 70, 80, 90, 95, 100]): + tl.logging.debug("Epoch %d - Accuracy %d%%" % (epoch + 1, accuracy)) + + # Record a numerical performance metric + exp.metric(name="accuracy", value=accuracy) + + time.sleep(0.1) + + # Cleanup and mark that the experiment successfully completed + exp.end() + + train_dogs_vs_cats() + + def test_Experiment_variant(self): + + with self.assertNotRaises(Exception): + + def train_dogs_vs_cats(): + + # Create an experiment with a model name, then autostart + exp = hd.Experiment("TRAVIS 4 - dogs vs. cats", api_key=self.apikey) + + # Record the value of hyperparameter gamma for this experiment + lr = exp.param("learning rate", 0.005) + tl.logging.debug("Learning Rate: %f" % lr) + + for epoch, accuracy in enumerate([10, 30, 50, 70, 80, 90, 95, 100]): + tl.logging.debug("Epoch %d - Accuracy %d%%" % (epoch + 1, accuracy)) + + # Record a numerical performance metric + exp.metric(name="accuracy", value=accuracy) + + time.sleep(0.1) + + # Cleanup and mark that the experiment successfully completed + exp.end() + + train_dogs_vs_cats() + + +if __name__ == '__main__': + + tf.logging.set_verbosity(tf.logging.DEBUG) + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/pending/test_mnist_simple.py b/tests/pending/test_mnist_simple.py new file mode 100644 index 0000000..ec14c39 --- /dev/null +++ b/tests/pending/test_mnist_simple.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class Simple_MNIST_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + + # define placeholders + cls.x = tf.placeholder(tf.float32, shape=[None, 784], name='x') + cls.y_ = tf.placeholder(tf.int64, shape=[None], name='y_') + + # define the network + network = tl.layers.InputLayer(cls.x, name='input') + network = tl.layers.DropoutLayer(network, keep=0.8, name='drop1') + network = tl.layers.DenseLayer(network, n_units=100, act=tf.nn.relu, name='relu1') + network = tl.layers.DropoutLayer(network, keep=0.8, name='drop2') + network = tl.layers.DenseLayer(network, n_units=100, act=tf.nn.relu, name='relu2') + network = tl.layers.DropoutLayer(network, keep=0.8, name='drop3') + + # the softmax is implemented internally in tl.cost.cross_entropy(y, y_) to + # speed up computation, so we use identity here. + # see tf.ops.sparse_softmax_cross_entropy_with_logits() + cls.network = tl.layers.DenseLayer(network, n_units=10, name='output') + + # define cost function and metric. + y = cls.network.outputs + cls.cost = tl.cost.cross_entropy(y, cls.y_, name='cost') + + correct_prediction = tf.equal(tf.argmax(y, 1), cls.y_) + + cls.acc = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) + # y_op = tf.argmax(tf.ops.softmax(y), 1) + + # define the optimizer + train_params = cls.network.trainable_weights + cls.train_op = tf.train.AdamOptimizer(learning_rate=0.0001).minimize(cls.cost, var_list=train_params) + + @classmethod + def tearDownClass(cls): + tf.reset_default_graph() + + def test_reuse_vgg(self): + + # prepare data + X_train, y_train, X_val, y_val, X_test, y_test = tl.files.load_mnist_dataset(shape=(-1, 784)) + + # for fashion_MNIST dataset test + # X_train, y_train, X_val, y_val, X_test, y_test = tl.files.load_fashion_mnist_dataset(shape=(-1, 784)) + + with self.assertNotRaises(Exception): + with tf.Session() as sess: + + # initialize all variables in the session + tl.layers.initialize_global_variables(sess) + + # print network information + self.network.print_params() + self.network.print_layers() + + # train the network + tl.utils.fit( + sess, self.network, self.train_op, self.cost, X_train, y_train, self.x, self.y_, acc=self.acc, + batch_size=500, n_epoch=1, print_freq=1, X_val=X_val, y_val=y_val, eval_train=False + ) + + # evaluation + tl.utils.test( + sess, self.network, self.acc, X_test, y_test, self.x, self.y_, batch_size=None, cost=self.cost + ) + + # save the network to .npz file + tl.files.save_npz(self.network.all_params, name='model.npz') + sess.close() + + +if __name__ == '__main__': + + tf.logging.set_verbosity(tf.logging.DEBUG) + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/pending/test_models.py b/tests/pending/test_models.py new file mode 100644 index 0000000..ecaf036 --- /dev/null +++ b/tests/pending/test_models.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class VGG_Model_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + + with tf.Graph().as_default(): + # - Classify ImageNet classes with VGG16, see `tutorial_models_vgg16.py __` + x = tf.placeholder(tf.float32, [None, 224, 224, 3]) + # get the whole model + vgg1 = tl.models.VGG16(x) + # restore pre-trained VGG parameters + # sess = tf.InteractiveSession() + # vgg.restore_params(sess) + # use for inferencing + # probs = tf.ops.softmax(vgg1.outputs) + + cls.vgg1_layers = vgg1.all_layers + cls.vgg1_params = vgg1.all_params + + with tf.Graph().as_default(): + # - Extract features with VGG16 and Train a classifier with 100 classes + x = tf.placeholder(tf.float32, [None, 224, 224, 3]) + # get VGG without the last layer + vgg2 = tl.models.VGG16(x, end_with='fc2_relu') + + cls.vgg2_layers = vgg2.all_layers + cls.vgg2_params = vgg2.all_params + + print("TYPE:", type(vgg2)) + + # add one more layer + _ = tl.layers.DenseLayer(vgg2, n_units=100, name='out') + # initialize all parameters + # sess = tf.InteractiveSession() + # tl.layers.initialize_global_variables(sess) + # restore pre-trained VGG parameters + # vgg.restore_params(sess) + # train your own classifier (only update the last layer) + + cls.vgg2_train_params = tl.layers.get_variables_with_name('out') + + with tf.Graph().as_default() as graph: + # - Reuse model + x = tf.placeholder(tf.float32, [None, 224, 224, 3]) + # get VGG without the last layer + vgg3 = tl.models.VGG16(x, end_with='fc2_relu') + # reuse the parameters of vgg1 with different input + # restore pre-trained VGG parameters (as they share parameters, we don’t need to restore vgg2) + # sess = tf.InteractiveSession() + # vgg1.restore_params(sess) + + cls.vgg3_layers = vgg3.all_layers + cls.vgg3_params = vgg3.all_params + cls.vgg3_graph = graph + + @classmethod + def tearDownClass(cls): + tf.reset_default_graph() + + def test_vgg1_layers(self): + self.assertEqual(len(self.vgg1_layers), 23) + + def test_vgg2_layers(self): + self.assertEqual(len(self.vgg2_layers), 22) + + def test_vgg3_layers(self): + self.assertEqual(len(self.vgg3_layers), 22) + + def test_vgg1_params(self): + self.assertEqual(len(self.vgg1_params), 32) + + def test_vgg2_params(self): + self.assertEqual(len(self.vgg2_params), 30) + + def test_vgg3_params(self): + self.assertEqual(len(self.vgg3_params), 30) + + def test_vgg2_train_params(self): + self.assertEqual(len(self.vgg2_train_params), 2) + + def test_reuse_vgg(self): + + with self.assertNotRaises(Exception): + with self.vgg3_graph.as_default(): + x = tf.placeholder(tf.float32, [None, 224, 224, 3]) + _ = tl.models.VGG16(x, end_with='fc2_relu', reuse=True) + + +if __name__ == '__main__': + + tf.logging.set_verbosity(tf.logging.DEBUG) + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/pending/test_optimizer_amsgrad.py b/tests/pending/test_optimizer_amsgrad.py new file mode 100644 index 0000000..0ceb8b3 --- /dev/null +++ b/tests/pending/test_optimizer_amsgrad.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class Layer_Pooling_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + cls.x = tf.placeholder(tf.float32, shape=[None, 784], name='x') + cls.y_ = tf.placeholder(tf.int64, shape=[None], name='y_') + + # define the network + cls.network = tl.layers.InputLayer(cls.x, name='input') + cls.network = tl.layers.DropoutLayer(cls.network, keep=0.8, name='drop1') + cls.network = tl.layers.DenseLayer(cls.network, 800, tf.nn.relu, name='relu1') + cls.network = tl.layers.DropoutLayer(cls.network, keep=0.5, name='drop2') + cls.network = tl.layers.DenseLayer(cls.network, 800, tf.nn.relu, name='relu2') + cls.network = tl.layers.DropoutLayer(cls.network, keep=0.5, name='drop3') + + cls.network = tl.layers.DenseLayer(cls.network, n_units=10, name='output') + + # define cost function and metric. + cls.y = cls.network.outputs + cls.cost = tl.cost.cross_entropy(cls.y, cls.y_, name='cost') + + correct_prediction = tf.equal(tf.argmax(cls.y, 1), cls.y_) + + cls.acc = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) + + # define the optimizer + train_params = cls.network.all_params + optimizer = tl.optimizers.AMSGrad(learning_rate=1e-4, beta1=0.9, beta2=0.999, epsilon=1e-8) + cls.train_op = optimizer.minimize(cls.cost, var_list=train_params) + + @classmethod + def tearDownClass(cls): + tf.reset_default_graph() + + def test_training(self): + + with self.assertNotRaises(Exception): + + X_train, y_train, X_val, y_val, _, _ = tl.files.load_mnist_dataset(shape=(-1, 784)) + + with tf.Session() as sess: + # initialize all variables in the session + tl.layers.initialize_global_variables(sess) + + # print network information + self.network.print_params() + self.network.print_layers() + + # train the network + tl.utils.fit( + sess, self.network, self.train_op, self.cost, X_train, y_train, self.x, self.y_, acc=self.acc, + batch_size=500, n_epoch=1, print_freq=1, X_val=X_val, y_val=y_val, eval_train=False + ) + + +if __name__ == '__main__': + + tf.logging.set_verbosity(tf.logging.DEBUG) + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/pending/test_pydocstyle.py b/tests/pending/test_pydocstyle.py new file mode 100644 index 0000000..b93bf74 --- /dev/null +++ b/tests/pending/test_pydocstyle.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +from tests.utils import list_all_py_files + +from pydocstyle.checker import check +from pydocstyle.checker import violations + +registry = violations.ErrorRegistry + + +def lookup_error_params(code): + for group in registry.groups: + for error_params in group.errors: + if error_params.code == code: + return error_params + + +class PyDOC_Style_Test(unittest.TestCase): + + @classmethod + def setUpClass(cls): + + cls.violations = list() + + # TODO: fix all violations to make it empty + _disabled_checks = [ + 'D205', # 1 blank line required between summary line and description + 'D102', # Missing docstring in public method + 'D400', # First line should end with a period + 'D100', # Missing docstring in public module + 'D107', # Missing docstring in __init__ + 'D103', # Missing docstring in public function + 'D401', # First line should be in imperative mood + 'D101', # Missing docstring in public class + 'D413', # Missing blank line after last section + 'D105', # Missing docstring in magic method + 'D104', # Missing docstring in public package + 'D302', # Use u"""for Unicode docstrings + + # Rules that has conflict with yapf + 'D202', # No blank lines allowed after function docstring + ] + + for filename in list_all_py_files(): + print(filename) + for err in check([filename]): + if not err.code in _disabled_checks: + cls.violations.append(err) + + def test_violations(self): + if self.violations: + counts = dict() + + for err in self.violations: + counts[err.code] = counts.get(err.code, 0) + 1 + print(err) + + for n, code in sorted([(n, code) for code, n in counts.items()], reverse=True): + p = lookup_error_params(code) + print('%s %8d %s' % (code, n, p.short_desc)) + + raise Exception('PyDoc Coding Style: %d violations have been found' % (len(self.violations))) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/pending/test_reuse_mlp.py b/tests/pending/test_reuse_mlp.py new file mode 100644 index 0000000..3ca435b --- /dev/null +++ b/tests/pending/test_reuse_mlp.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +# define the network +def mlp(x, is_train=True, reuse=False): + with tf.variable_scope("MLP", reuse=reuse): + tl.layers.set_name_reuse(reuse) # print warning + network = tl.layers.InputLayer(x, name='input') + network = tl.layers.DropoutLayer(network, keep=0.8, is_fix=True, is_train=is_train, name='drop1') + network = tl.layers.DenseLayer(network, n_units=800, act=tf.nn.relu, name='relu1') + network = tl.layers.DropoutLayer(network, keep=0.5, is_fix=True, is_train=is_train, name='drop2') + network = tl.layers.DenseLayer(network, n_units=800, act=tf.nn.relu, name='relu2') + network = tl.layers.DropoutLayer(network, keep=0.5, is_fix=True, is_train=is_train, name='drop3') + network = tl.layers.DenseLayer(network, n_units=10, name='output') + return network + + +class MLP_Reuse_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + + # define placeholder + cls.x = tf.placeholder(tf.float32, shape=[None, 784], name='x') + + # define inferences + mlp(cls.x, is_train=True, reuse=False) + mlp(cls.x, is_train=False, reuse=True) + + @classmethod + def tearDownClass(cls): + tf.reset_default_graph() + + def test_reuse(self): + + with self.assertRaises(Exception): + mlp(self.x, is_train=False, reuse=False) # Already defined model with the same var_scope + + +if __name__ == '__main__': + + tf.logging.set_verbosity(tf.logging.DEBUG) + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/pending/test_tf_layers.py b/tests/pending/test_tf_layers.py new file mode 100644 index 0000000..dc04a06 --- /dev/null +++ b/tests/pending/test_tf_layers.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class Layer_Convolution_1D_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + + def get_network_1d(inputs, reuse=False): + + with tf.variable_scope("1D_network", reuse=reuse): + net = tl.layers.InputLayer(inputs) + + net1 = tl.layers.Conv1d(net, name="Conv1d") # 2 params + net2 = tl.layers.SeparableConv1d(net1, name="SeparableConv1d") # 3 params + net3 = tl.layers.MaxPool1d(net2, (1, ), name="MaxPool1d") # 0 params + net4 = tl.layers.MeanPool1d(net3, (1, ), name="MeanPool1d") # 0 params + + # HAO Test + net5 = tl.layers.Conv1d(net4, name="Conv1d1") # 2 params + net6 = tl.layers.SeparableConv1d(net5, name="SeparableConv1d1") # 3 params + net7 = tl.layers.SeparableConv1d(net6, name="SeparableConv1d2") # 3 params + + return [net, net1, net2, net3, net4, net5, net6, net7] + + input_pl_train = tf.placeholder(tf.float32, [None, 32, 3]) + input_plh_test = tf.placeholder(tf.float32, [None, 32, 3]) + + cls.network_1 = get_network_1d(input_pl_train, reuse=False) + cls.network_2 = get_network_1d(input_plh_test, reuse=True) + + @classmethod + def tearDownClass(cls): + tf.reset_default_graph() + + def test_layer_net0(self): + self.assertEqual(len(self.network_1[0].all_params), 0) + self.assertEqual(len(self.network_2[0].all_params), 0) + + def test_layer_net1(self): + self.assertEqual(len(self.network_1[1].all_params), 2) + self.assertEqual(len(self.network_2[1].all_params), 2) + + def test_layer_net2(self): + self.assertEqual(len(self.network_1[2].all_params), 5) + self.assertEqual(len(self.network_2[2].all_params), 5) + + def test_layer_net3(self): + self.assertEqual(len(self.network_1[3].all_params), 5) + self.assertEqual(len(self.network_2[3].all_params), 5) + + def test_layer_net4(self): + self.assertEqual(len(self.network_1[4].all_params), 5) + self.assertEqual(len(self.network_2[4].all_params), 5) + + def test_layer_net5(self): + self.assertEqual(len(self.network_1[5].all_params), 7) + self.assertEqual(len(self.network_2[5].all_params), 7) + + def test_layer_net6(self): + self.assertEqual(len(self.network_1[6].all_params), 10) + self.assertEqual(len(self.network_2[6].all_params), 10) + + def test_layer_net7(self): + self.assertEqual(len(self.network_1[7].all_params), 13) + self.assertEqual(len(self.network_2[7].all_params), 13) + + +class Layer_Convolution_2D_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + + def get_network_2d(inputs, reuse=False): + + with tf.variable_scope("2D_network", reuse=reuse): + net = tl.layers.InputLayer(inputs) + + net1 = tl.layers.Conv2d(net, name="Conv2d") # 2 params + net2 = tl.layers.DeConv2d(net1, name="DeConv2d") # 2 params + net3 = tl.layers.SeparableConv2d(net2, name="SeparableConv2d") # 3 params + net4 = tl.layers.MaxPool2d(net3, (1, 1), name="MaxPool2d") # 0 params + net5 = tl.layers.MeanPool2d(net4, (1, 1), name="MeanPool2d") # 0 params + + # HAO Test + net6 = tl.layers.Conv2d(net5, name="Conv2d1") # 2 params + net7 = tl.layers.DeConv2d(net6, name="DeConv2d1") # 2 params + net8 = tl.layers.DeConv2d(net7, name="DeConv2d2") # 2 params + net9 = tl.layers.SeparableConv2d(net8, name="SeparableConv2d1") # 3 params + + return [net, net1, net2, net3, net4, net5, net6, net7, net8, net9] + + input_pl_train = tf.placeholder(tf.float32, [None, 32, 32, 3]) + input_plh_test = tf.placeholder(tf.float32, [None, 32, 32, 3]) + + cls.network_1 = get_network_2d(input_pl_train, reuse=False) + cls.network_2 = get_network_2d(input_plh_test, reuse=True) + + def test_layer_net0(self): + self.assertEqual(len(self.network_1[0].all_params), 0) + self.assertEqual(len(self.network_2[0].all_params), 0) + + def test_layer_net1(self): + self.assertEqual(len(self.network_1[1].all_params), 2) + self.assertEqual(len(self.network_2[1].all_params), 2) + + def test_layer_net2(self): + self.assertEqual(len(self.network_1[2].all_params), 4) + self.assertEqual(len(self.network_2[2].all_params), 4) + + def test_layer_net3(self): + self.assertEqual(len(self.network_1[3].all_params), 7) + self.assertEqual(len(self.network_2[3].all_params), 7) + + def test_layer_net4(self): + self.assertEqual(len(self.network_1[4].all_params), 7) + self.assertEqual(len(self.network_2[4].all_params), 7) + + def test_layer_net5(self): + self.assertEqual(len(self.network_1[5].all_params), 7) + self.assertEqual(len(self.network_2[5].all_params), 7) + + def test_layer_net6(self): + self.assertEqual(len(self.network_1[6].all_params), 9) + self.assertEqual(len(self.network_2[6].all_params), 9) + + def test_layer_net7(self): + self.assertEqual(len(self.network_1[7].all_params), 11) + self.assertEqual(len(self.network_2[7].all_params), 11) + + def test_layer_net8(self): + self.assertEqual(len(self.network_1[8].all_params), 13) + self.assertEqual(len(self.network_2[8].all_params), 13) + + def test_layer_net9(self): + self.assertEqual(len(self.network_1[9].all_params), 16) + self.assertEqual(len(self.network_2[9].all_params), 16) + + @classmethod + def tearDownClass(cls): + tf.reset_default_graph() + + +class Layer_Convolution_3D_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + + def get_network_3d(inputs, reuse=False): + + with tf.variable_scope("3D_network", reuse=reuse): + net = tl.layers.InputLayer(inputs) + + net1 = tl.layers.Conv3dLayer( + net, shape=(2, 2, 2, 3, 32), strides=(1, 2, 2, 2, 1), name="Conv3dLayer" + ) # 2 params + net2 = tl.layers.DeConv3d(net1, name="DeConv3d") # 2 params + net3 = tl.layers.MaxPool3d(net2, (1, 1, 1), name="MaxPool3d") # 0 params + net4 = tl.layers.MeanPool3d(net3, (1, 1, 1), name="MeanPool3d") # 0 params + + # HAO Test + net5 = tl.layers.DeConv3d(net4, name="DeConv3d1") # 2 params + + return [net, net1, net2, net3, net4, net5] + + input_pl_train = tf.placeholder(tf.float32, [None, 32, 32, 32, 3]) + input_plh_test = tf.placeholder(tf.float32, [None, 32, 32, 32, 3]) + + cls.network_1 = get_network_3d(input_pl_train, reuse=False) + cls.network_2 = get_network_3d(input_plh_test, reuse=True) + + @classmethod + def tearDownClass(cls): + tf.reset_default_graph() + + def test_layer_net0(self): + self.assertEqual(len(self.network_1[0].all_params), 0) + self.assertEqual(len(self.network_2[0].all_params), 0) + + def test_layer_net1(self): + self.assertEqual(len(self.network_1[1].all_params), 2) + self.assertEqual(len(self.network_2[1].all_params), 2) + + def test_layer_net2(self): + self.assertEqual(len(self.network_1[2].all_params), 4) + self.assertEqual(len(self.network_2[2].all_params), 4) + + def test_layer_net3(self): + self.assertEqual(len(self.network_1[3].all_params), 4) + self.assertEqual(len(self.network_2[3].all_params), 4) + + def test_layer_net4(self): + self.assertEqual(len(self.network_1[4].all_params), 4) + self.assertEqual(len(self.network_2[4].all_params), 4) + + def test_layer_net5(self): + self.assertEqual(len(self.network_1[5].all_params), 6) + self.assertEqual(len(self.network_2[5].all_params), 6) + + +if __name__ == '__main__': + + tf.logging.set_verbosity(tf.logging.DEBUG) + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/pending/test_timeout.py b/tests/pending/test_timeout.py new file mode 100644 index 0000000..9b5dda6 --- /dev/null +++ b/tests/pending/test_timeout.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import time + +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl + +from tests.utils import WindowsError +from tests.utils import TimeoutError + +from tests.utils import TimeoutContext +from tests.utils import CustomTestCase + +from tests.utils.custom_networks import InceptionV4_Network + +if os.getenv("TRAVIS", None) is not None: + NETWORK_CREATION_TIMEOUT = 120 # Seconds before timeout +else: + NETWORK_CREATION_TIMEOUT = 40 # Seconds before timeout + +###################################################################################### +# # +# UNITTEST TIMEOUT # +# # +###################################################################################### + + +class Layer_Timeoutt_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + + ####################################################################### + #### ============= Placeholders Declaration ============= #### + ####################################################################### + + cls.input_plh = tf.placeholder(tf.float32, [None, 299, 299, 3], name='input_placeholder') + + ####################################################################### + #### ============= Model Declaration ============= #### + ####################################################################### + + cls.inception_v4_net = InceptionV4_Network(include_FC_head=True, flatten_output=False) + + @classmethod + def tearDownClass(cls): + tf.reset_default_graph() + + def test_timeout_not_reuse(self): + + with self.assertNotRaises(TimeoutError): + try: + with TimeoutContext(NETWORK_CREATION_TIMEOUT): + start_time = time.time() + + _ = self.inception_v4_net(self.input_plh, reuse=False, is_train=False) + + tl.logging.info("Seconds Elapsed [Not Reused]: %d" % int(time.time() - start_time)) + + except WindowsError: + tl.logging.warning("This unittest can not run on Windows") + + def test_timeout_reuse(self): + + with self.assertNotRaises(TimeoutError): + try: + with TimeoutContext(NETWORK_CREATION_TIMEOUT): + start_time = time.time() + + _ = self.inception_v4_net(self.input_plh, reuse=True, is_train=False) + + tl.logging.info("Seconds Elapsed [Reused Model]: %d" % int(time.time() - start_time)) + + except WindowsError: + tl.logging.warning("This unittest can not run on Windows") + + +if __name__ == '__main__': + + tf.logging.set_verbosity(tf.logging.DEBUG) + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/pending/test_utils_predict.py b/tests/pending/test_utils_predict.py new file mode 100644 index 0000000..ec751e2 --- /dev/null +++ b/tests/pending/test_utils_predict.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import numpy as np + +import tensorflow as tf +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class Util_Predict_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + cls.x1 = tf.placeholder(tf.float32, [None, 5, 5, 3]) + cls.x2 = tf.placeholder(tf.float32, [8, 5, 5, 3]) + cls.X1 = np.ones([127, 5, 5, 3]) + cls.X2 = np.ones([7, 5, 5, 3]) + cls.batch_size = 8 + + @classmethod + def tearDownClass(cls): + tf.reset_default_graph() + + def test_case1(self): + with self.assertNotRaises(Exception): + with tf.Session() as sess: + n = tl.layers.InputLayer(self.x1) + y = n.outputs + y_op = tf.nn.softmax(y) + tl.utils.predict(sess, n, self.X1, self.x1, y_op, batch_size=self.batch_size) + sess.close() + + def test_case2(self): + with self.assertRaises(Exception): + with tf.Session() as sess: + n = tl.layers.InputLayer(self.x2) + y = n.outputs + y_op = tf.nn.softmax(y) + tl.utils.predict(sess, n, self.X2, self.x2, y_op, batch_size=self.batch_size) + sess.close() + + +if __name__ == '__main__': + + tf.logging.set_verbosity(tf.logging.DEBUG) + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/pending/test_yapf_format.py b/tests/pending/test_yapf_format.py new file mode 100644 index 0000000..05ff6f6 --- /dev/null +++ b/tests/pending/test_yapf_format.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import sys +import unittest + +from tests.utils import list_all_py_files +from tests.utils import CustomTestCase + +from yapf.yapflib.yapf_api import FormatCode + + +def _read_utf_8_file(filename): + if sys.version_info.major == 2: ## Python 2 specific + with open(filename, 'rb') as f: + return unicode(f.read(), 'utf-8') + else: + with open(filename, encoding='utf-8') as f: + return f.read() + + +class YAPF_Style_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + + cls.badly_formatted_files = list() + cls.files_2_test = list_all_py_files() + + def test_files_format(self): + + for file in list_all_py_files(): + + try: + + print(file) + code = _read_utf_8_file(file) + + # https://pypi.python.org/pypi/yapf/0.20.2#example-as-a-module + diff, changed = FormatCode(code, filename=file, style_config='setup.cfg', print_diff=True) + + if changed: + print(diff) + self.badly_formatted_files.append(file) + except Exception as e: + print("Error while processing file: `%s`\n" "Error: %s" % (file, str(e))) + + with self.assertNotRaises(Exception): + + str_err = "" + + if self.badly_formatted_files: + for filename in self.badly_formatted_files: + str_err += 'yapf -i --style=setup.cfg %s\n' % filename + + str_err = "\n======================================================================================\n" \ + "Bad Coding Style: %d file(s) need to be formatted, run the following commands to fix: \n%s" \ + "======================================================================================" % ( + len(self.badly_formatted_files), str_err) + + raise Exception(str_err) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/performance_test/__init__.py b/tests/performance_test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/performance_test/vgg/README.md b/tests/performance_test/vgg/README.md new file mode 100644 index 0000000..62b0fa8 --- /dev/null +++ b/tests/performance_test/vgg/README.md @@ -0,0 +1,34 @@ +# Performance Test (VGG16) + +### Introduction + +This test compares performance of the following libraries: + +1. TensorLayer v. 2.0 +2. TensorFlow ([tf.keras](https://www.tensorflow.org/api_docs/python/tf/keras)) v. 2.0.0-alpha +3. [Keras](https://keras.io/) v. 2.2.4 + + + +### With GPU + +__Hardware:__ + +- CPU: Intel(R) Xeon(R) CPU E5-2630 v4 @ 2.20GHz 40 core +- GPU: TITAN Xp + +__Experiment Settings:__ +- Model: [VGG16](http://www.robots.ox.ac.uk/~vgg/research/very_deep/) +- Batch size: 32 +- Number of iterations: 300 + +__Results:__ + +| Mode | Lib | Data Format | Max GPU Memory Usage(MB) |Max CPU Memory Usage(MB) | Avg CPU Memory Usage(MB) | Runtime (sec) | +| :-------: | :-------------: | :-----------: | :-----------------: | :-----------------: | :-----------------: | :-----------: | +| AutoGraph | TensorFlow 2.0 | channel last | 11833 | 2161 | 2136 | 74 | +| | Tensorlayer 2.0 | channel last | 11833 | 2187 | 2169 | 76 | +| Graph | Keras | channel last | 8677 | 2580 | 2576 | 101 | +| Eager | TensorFlow 2.0 | channel last | 8723 | 2052 | 2024 | 97 | +| | TensorLayer 2.0 | channel last | 8723 | 2010 | 2007 | 95 | + diff --git a/tests/performance_test/vgg/__init__.py b/tests/performance_test/vgg/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/performance_test/vgg/exp_config.py b/tests/performance_test/vgg/exp_config.py new file mode 100644 index 0000000..53ffe35 --- /dev/null +++ b/tests/performance_test/vgg/exp_config.py @@ -0,0 +1,16 @@ +import numpy as np + + +def random_input_generator(num, batchsize=32, format='NHWC'): + input_shape = (batchsize, 224, 224, 3) if format == 'NHWC' else (batchsize, 3, 224, 224) + rng = np.random.RandomState(1234) + for i in range(num): + x = rng.uniform(0.0, 1.0, size=input_shape).astype(np.float32) + y = rng.randint(0, 1000, size=(batchsize, )) + yield (x, y) + + +MONITOR_INTERVAL = 50 +NUM_ITERS = 300 +BATCH_SIZE = 32 +LERANING_RATE = 0.0001 diff --git a/tests/performance_test/vgg/keras_test.py b/tests/performance_test/vgg/keras_test.py new file mode 100644 index 0000000..4b77cbe --- /dev/null +++ b/tests/performance_test/vgg/keras_test.py @@ -0,0 +1,66 @@ +import time +import os +import psutil +import keras +from keras.applications.vgg16 import VGG16 +from keras.backend.tensorflow_backend import set_session +from keras.utils import to_categorical +import tensorflow as tf +from exp_config import random_input_generator, MONITOR_INTERVAL, NUM_ITERS, BATCH_SIZE, LERANING_RATE + +config = tf.ConfigProto() +config.gpu_options.allow_growth = True +sess = tf.Session(config=config) +set_session(sess) + +# get the whole model +vgg = VGG16(weights=None) + +# system monitor +info = psutil.virtual_memory() +monitor_interval = MONITOR_INTERVAL +avg_mem_usage = 0 +max_mem_usage = 0 +count = 0 +total_time = 0 + +# training setting +num_iter = NUM_ITERS +batch_size = BATCH_SIZE +vgg.compile(loss=keras.losses.categorical_crossentropy, optimizer=keras.optimizers.Adam(lr=LERANING_RATE)) + +# data generator +gen = random_input_generator(num_iter, batch_size) + +# begin training + +for idx, data in enumerate(gen): + x_batch = data[0] + y_batch = to_categorical(data[1], num_classes=1000) + + start_time = time.time() + + # forward + backward + vgg.train_on_batch(x_batch, y_batch) + + end_time = time.time() + consume_time = end_time - start_time + total_time += consume_time + + if idx % monitor_interval == 0: + cur_usage = psutil.Process(os.getpid()).memory_info().rss + max_mem_usage = max(cur_usage, max_mem_usage) + avg_mem_usage += cur_usage + count += 1 + print( + "[*] {} iteration: memory usage {:.2f}MB, consume time {:.4f}s".format( + idx, cur_usage / (1024 * 1024), consume_time + ) + ) + +print('consumed time:', total_time) + +avg_mem_usage = avg_mem_usage / count / (1024 * 1024) +max_mem_usage = max_mem_usage / (1024 * 1024) +print('average memory usage: {:.2f}MB'.format(avg_mem_usage)) +print('maximum memory usage: {:.2f}MB'.format(max_mem_usage)) diff --git a/tests/performance_test/vgg/pytorch_test.py b/tests/performance_test/vgg/pytorch_test.py new file mode 100644 index 0000000..a81aa0b --- /dev/null +++ b/tests/performance_test/vgg/pytorch_test.py @@ -0,0 +1,75 @@ +import torch +import torch.nn.functional as F +import torch.optim as optim +from torchvision.models import vgg16 +import time +import os +import psutil +import numpy as np +from exp_config import random_input_generator, MONITOR_INTERVAL, NUM_ITERS, BATCH_SIZE, LERANING_RATE + +# set gpu_id 0 +device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + +# system monitor +info = psutil.virtual_memory() +monitor_interval = MONITOR_INTERVAL +avg_mem_usage = 0 +max_mem_usage = 0 +count = 0 +total_time = 0 + +# get the whole model +vgg = vgg16() + +start_time = time.time() +vgg = vgg.to(device) +total_time += time.time() - start_time + +# training setting +num_iter = NUM_ITERS +batch_size = BATCH_SIZE +optimizer = optim.Adam(vgg.parameters(), lr=LERANING_RATE) + +# data generator +gen = random_input_generator(num_iter, batch_size, format='NCHW') + +# begin training + +for idx, data in enumerate(gen): + + start_time = time.time() + + x_batch = torch.Tensor(data[0]) + y_batch = torch.Tensor(data[1]).long() + x_batch = x_batch.to(device) + y_batch = y_batch.to(device) + + # forward + backward + outputs = vgg(x_batch) + loss = F.cross_entropy(outputs, y_batch) + optimizer.zero_grad() + loss.backward() + optimizer.step() + + end_time = time.time() + consume_time = end_time - start_time + total_time += consume_time + + if idx % monitor_interval == 0: + cur_usage = psutil.Process(os.getpid()).memory_info().rss + max_mem_usage = max(cur_usage, max_mem_usage) + avg_mem_usage += cur_usage + count += 1 + print( + "[*] {} iteration: memory usage {:.2f}MB, consume time {:.4f}s".format( + idx, cur_usage / (1024 * 1024), consume_time + ) + ) + +print('consumed time:', total_time) + +avg_mem_usage = avg_mem_usage / count / (1024 * 1024) +max_mem_usage = max_mem_usage / (1024 * 1024) +print('average memory usage: {:.2f}MB'.format(avg_mem_usage)) +print('maximum memory usage: {:.2f}MB'.format(max_mem_usage)) diff --git a/tests/performance_test/vgg/tf2-autograph.py b/tests/performance_test/vgg/tf2-autograph.py new file mode 100644 index 0000000..90d2ccf --- /dev/null +++ b/tests/performance_test/vgg/tf2-autograph.py @@ -0,0 +1,77 @@ +import time +import os +import psutil +from tensorflow.python.keras.applications import VGG16 +import tensorflow as tf +from exp_config import random_input_generator, MONITOR_INTERVAL, NUM_ITERS, BATCH_SIZE, LERANING_RATE + +gpus = tf.config.experimental.list_physical_devices('GPU') +if gpus: + for gpu in gpus: + tf.config.experimental.set_memory_growth(gpu, True) + +# get the whole model +vgg = VGG16(weights=None) + +# system monitor +info = psutil.virtual_memory() +monitor_interval = MONITOR_INTERVAL +avg_mem_usage = 0 +max_mem_usage = 0 +count = 0 +total_time = 0 + +# training setting +num_iter = NUM_ITERS +batch_size = BATCH_SIZE +train_weights = vgg.trainable_variables +optimizer = tf.optimizers.Adam(learning_rate=LERANING_RATE) +loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True) + +# data generator +gen = random_input_generator(num_iter, batch_size) + + +# training function +@tf.function +def train_step(x_batch, y_batch): + # forward + backward + with tf.GradientTape() as tape: + ## compute outputs + _logits = vgg(x_batch, training=True) + ## compute loss and update model + _loss = loss_object(y_batch, _logits) + + grad = tape.gradient(_loss, train_weights) + optimizer.apply_gradients(zip(grad, train_weights)) + + +# begin training +for idx, data in enumerate(gen): + start_time = time.time() + + x_batch = tf.convert_to_tensor(data[0]) + y_batch = tf.convert_to_tensor(data[1]) + train_step(x_batch, y_batch) + + end_time = time.time() + consume_time = end_time - start_time + total_time += consume_time + + if idx % monitor_interval == 0: + cur_usage = psutil.Process(os.getpid()).memory_info().rss + max_mem_usage = max(cur_usage, max_mem_usage) + avg_mem_usage += cur_usage + count += 1 + tf.print( + "[*] {} iteration: memory usage {:.2f}MB, consume time {:.4f}s".format( + idx, cur_usage / (1024 * 1024), consume_time + ) + ) + +print('consumed time:', total_time) + +avg_mem_usage = avg_mem_usage / count / (1024 * 1024) +max_mem_usage = max_mem_usage / (1024 * 1024) +print('average memory usage: {:.2f}MB'.format(avg_mem_usage)) +print('maximum memory usage: {:.2f}MB'.format(max_mem_usage)) diff --git a/tests/performance_test/vgg/tf2-eager.py b/tests/performance_test/vgg/tf2-eager.py new file mode 100644 index 0000000..d4c7808 --- /dev/null +++ b/tests/performance_test/vgg/tf2-eager.py @@ -0,0 +1,78 @@ +import time +import os +import psutil +from tensorflow.python.keras.applications import VGG16 +import tensorflow as tf +from exp_config import random_input_generator, MONITOR_INTERVAL, NUM_ITERS, BATCH_SIZE, LERANING_RATE + +gpus = tf.config.experimental.list_physical_devices('GPU') +if gpus: + for gpu in gpus: + tf.config.experimental.set_memory_growth(gpu, True) + +# get the whole model +vgg = VGG16(weights=None) + +# system monitor +info = psutil.virtual_memory() +monitor_interval = MONITOR_INTERVAL +avg_mem_usage = 0 +max_mem_usage = 0 +count = 0 +total_time = 0 + +# training setting +num_iter = NUM_ITERS +batch_size = BATCH_SIZE +train_weights = vgg.trainable_variables +optimizer = tf.optimizers.Adam(learning_rate=LERANING_RATE) +loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True) + +# data generator +gen = random_input_generator(num_iter, batch_size) + + +# training function +def train_step(x_batch, y_batch): + # forward + backward + with tf.GradientTape() as tape: + ## compute outputs + _logits = vgg(x_batch, training=True) + ## compute loss and update model + _loss = loss_object(y_batch, _logits) + + grad = tape.gradient(_loss, train_weights) + optimizer.apply_gradients(zip(grad, train_weights)) + + return _loss + + +# begin training +for idx, data in enumerate(gen): + start_time = time.time() + + x_batch = tf.convert_to_tensor(data[0]) + y_batch = tf.convert_to_tensor(data[1]) + loss = train_step(x_batch, y_batch) + + end_time = time.time() + consume_time = end_time - start_time + total_time += consume_time + + if idx % monitor_interval == 0: + cur_usage = psutil.Process(os.getpid()).memory_info().rss + max_mem_usage = max(cur_usage, max_mem_usage) + avg_mem_usage += cur_usage + count += 1 + tf.print( + "[*] {} iteration: memory usage {:.2f}MB, consume time {:.4f}s, loss {:.4f}".format( + idx, cur_usage / (1024 * 1024), consume_time, loss + ) + ) + +print('consumed time:', total_time) + +avg_mem_usage = avg_mem_usage / count / (1024 * 1024) +max_mem_usage = max_mem_usage / (1024 * 1024) +print('average memory usage: {:.2f}MB'.format(avg_mem_usage)) +print('maximum memory usage: {:.2f}MB'.format(max_mem_usage)) diff --git a/tests/performance_test/vgg/tl2-autograph.py b/tests/performance_test/vgg/tl2-autograph.py new file mode 100644 index 0000000..63f5539 --- /dev/null +++ b/tests/performance_test/vgg/tl2-autograph.py @@ -0,0 +1,79 @@ +import time +import os +import psutil +import tensorflow as tf +import tensorlayer as tl +from exp_config import random_input_generator, MONITOR_INTERVAL, NUM_ITERS, BATCH_SIZE, LERANING_RATE + +gpus = tf.config.experimental.list_physical_devices('GPU') +if gpus: + for gpu in gpus: + tf.config.experimental.set_memory_growth(gpu, True) + +tl.logging.set_verbosity(tl.logging.DEBUG) + +# get the whole model +vgg = tl.models.vgg16() + +# system monitor +info = psutil.virtual_memory() +monitor_interval = MONITOR_INTERVAL +avg_mem_usage = 0 +max_mem_usage = 0 +count = 0 +total_time = 0 + +# training setting +num_iter = NUM_ITERS +batch_size = BATCH_SIZE +train_weights = vgg.trainable_weights +optimizer = tf.optimizers.Adam(learning_rate=LERANING_RATE) +loss_object = tl.cost.cross_entropy + +# data generator +gen = random_input_generator(num_iter, batch_size) + + +# training function +@tf.function +def train_step(x_batch, y_batch): + # forward + backward + with tf.GradientTape() as tape: + ## compute outputs + _logits = vgg(x_batch) + ## compute loss and update model + _loss = loss_object(_logits, y_batch) + + grad = tape.gradient(_loss, train_weights) + optimizer.apply_gradients(zip(grad, train_weights)) + + +# begin training +vgg.train() + +for idx, data in enumerate(gen): + start_time = time.time() + + train_step(data[0], data[1]) + + end_time = time.time() + consume_time = end_time - start_time + total_time += consume_time + + if idx % monitor_interval == 0: + cur_usage = psutil.Process(os.getpid()).memory_info().rss + max_mem_usage = max(cur_usage, max_mem_usage) + avg_mem_usage += cur_usage + count += 1 + tl.logging.info( + "[*] {} iteration: memory usage {:.2f}MB, consume time {:.4f}s".format( + idx, cur_usage / (1024 * 1024), consume_time + ) + ) + +print('consumed time:', total_time) + +avg_mem_usage = avg_mem_usage / count / (1024 * 1024) +max_mem_usage = max_mem_usage / (1024 * 1024) +print('average memory usage: {:.2f}MB'.format(avg_mem_usage)) +print('maximum memory usage: {:.2f}MB'.format(max_mem_usage)) diff --git a/tests/performance_test/vgg/tl2-eager.py b/tests/performance_test/vgg/tl2-eager.py new file mode 100644 index 0000000..fd2ef40 --- /dev/null +++ b/tests/performance_test/vgg/tl2-eager.py @@ -0,0 +1,79 @@ +import time +import os +import psutil +import tensorflow as tf +import tensorlayer as tl +from exp_config import random_input_generator, MONITOR_INTERVAL, NUM_ITERS, BATCH_SIZE, LERANING_RATE + +gpus = tf.config.experimental.list_physical_devices('GPU') +if gpus: + for gpu in gpus: + tf.config.experimental.set_memory_growth(gpu, True) + +tl.logging.set_verbosity(tl.logging.DEBUG) + +# get the whole model +vgg = tl.models.vgg16() + +# system monitor +info = psutil.virtual_memory() +monitor_interval = MONITOR_INTERVAL +avg_mem_usage = 0 +max_mem_usage = 0 +count = 0 +total_time = 0 + +# training setting +num_iter = NUM_ITERS +batch_size = BATCH_SIZE +train_weights = vgg.trainable_weights +optimizer = tf.optimizers.Adam(learning_rate=LERANING_RATE) +loss_object = tl.cost.cross_entropy + +# data generator +gen = random_input_generator(num_iter, batch_size) + + +# training function +def train_step(x_batch, y_batch): + # forward + backward + with tf.GradientTape() as tape: + ## compute outputs + _logits = vgg(x_batch) + ## compute loss and update model + _loss = loss_object(_logits, y_batch) + + grad = tape.gradient(_loss, train_weights) + optimizer.apply_gradients(zip(grad, train_weights)) + return _loss + + +# begin training +vgg.train() + +for idx, data in enumerate(gen): + start_time = time.time() + + loss = train_step(data[0], data[1]) + + end_time = time.time() + consume_time = end_time - start_time + total_time += consume_time + + if idx % monitor_interval == 0: + cur_usage = psutil.Process(os.getpid()).memory_info().rss + max_mem_usage = max(cur_usage, max_mem_usage) + avg_mem_usage += cur_usage + count += 1 + tl.logging.info( + "[*] {} iteration: memory usage {:.2f}MB, consume time {:.4f}s, loss {:.4f}".format( + idx, cur_usage / (1024 * 1024), consume_time, loss + ) + ) + +print('consumed time:', total_time) + +avg_mem_usage = avg_mem_usage / count / (1024 * 1024) +max_mem_usage = max_mem_usage / (1024 * 1024) +print('average memory usage: {:.2f}MB'.format(avg_mem_usage)) +print('maximum memory usage: {:.2f}MB'.format(max_mem_usage)) diff --git a/tests/performance_test/vgg/tl2-static-autograph.py b/tests/performance_test/vgg/tl2-static-autograph.py new file mode 100644 index 0000000..0af20ad --- /dev/null +++ b/tests/performance_test/vgg/tl2-static-autograph.py @@ -0,0 +1,79 @@ +import time +import os +import psutil +import tensorflow as tf +import tensorlayer as tl +from exp_config import random_input_generator, MONITOR_INTERVAL, NUM_ITERS, BATCH_SIZE, LERANING_RATE + +gpus = tf.config.experimental.list_physical_devices('GPU') +if gpus: + for gpu in gpus: + tf.config.experimental.set_memory_growth(gpu, True) + +tl.logging.set_verbosity(tl.logging.DEBUG) + +# get the whole model +vgg = tl.models.vgg16(mode='static') + +# system monitor +info = psutil.virtual_memory() +monitor_interval = MONITOR_INTERVAL +avg_mem_usage = 0 +max_mem_usage = 0 +count = 0 +total_time = 0 + +# training setting +num_iter = NUM_ITERS +batch_size = BATCH_SIZE +train_weights = vgg.trainable_weights +optimizer = tf.optimizers.Adam(learning_rate=LERANING_RATE) +loss_object = tl.cost.cross_entropy + +# data generator +gen = random_input_generator(num_iter, batch_size) + + +# training function +@tf.function +def train_step(x_batch, y_batch): + # forward + backward + with tf.GradientTape() as tape: + ## compute outputs + _logits = vgg(x_batch) + ## compute loss and update model + _loss = loss_object(_logits, y_batch) + + grad = tape.gradient(_loss, train_weights) + optimizer.apply_gradients(zip(grad, train_weights)) + + +# begin training +vgg.train() + +for idx, data in enumerate(gen): + start_time = time.time() + + train_step(data[0], data[1]) + + end_time = time.time() + consume_time = end_time - start_time + total_time += consume_time + + if idx % monitor_interval == 0: + cur_usage = psutil.Process(os.getpid()).memory_info().rss + max_mem_usage = max(cur_usage, max_mem_usage) + avg_mem_usage += cur_usage + count += 1 + tl.logging.info( + "[*] {} iteration: memory usage {:.2f}MB, consume time {:.4f}s".format( + idx, cur_usage / (1024 * 1024), consume_time + ) + ) + +print('consumed time:', total_time) + +avg_mem_usage = avg_mem_usage / count / (1024 * 1024) +max_mem_usage = max_mem_usage / (1024 * 1024) +print('average memory usage: {:.2f}MB'.format(avg_mem_usage)) +print('maximum memory usage: {:.2f}MB'.format(max_mem_usage)) diff --git a/tests/performance_test/vgg/tl2-static-eager.py b/tests/performance_test/vgg/tl2-static-eager.py new file mode 100644 index 0000000..b6d5287 --- /dev/null +++ b/tests/performance_test/vgg/tl2-static-eager.py @@ -0,0 +1,79 @@ +import time +import os +import psutil +import tensorflow as tf +import tensorlayer as tl +from exp_config import random_input_generator, MONITOR_INTERVAL, NUM_ITERS, BATCH_SIZE, LERANING_RATE + +gpus = tf.config.experimental.list_physical_devices('GPU') +if gpus: + for gpu in gpus: + tf.config.experimental.set_memory_growth(gpu, True) + +tl.logging.set_verbosity(tl.logging.DEBUG) + +# get the whole model +vgg = tl.models.vgg16(mode='static') + +# system monitor +info = psutil.virtual_memory() +monitor_interval = MONITOR_INTERVAL +avg_mem_usage = 0 +max_mem_usage = 0 +count = 0 +total_time = 0 + +# training setting +num_iter = NUM_ITERS +batch_size = BATCH_SIZE +train_weights = vgg.trainable_weights +optimizer = tf.optimizers.Adam(learning_rate=LERANING_RATE) +loss_object = tl.cost.cross_entropy + +# data generator +gen = random_input_generator(num_iter, batch_size) + + +# training function +def train_step(x_batch, y_batch): + # forward + backward + with tf.GradientTape() as tape: + ## compute outputs + _logits = vgg(x_batch) + ## compute loss and update model + _loss = loss_object(_logits, y_batch) + + grad = tape.gradient(_loss, train_weights) + optimizer.apply_gradients(zip(grad, train_weights)) + return _loss + + +# begin training +vgg.train() + +for idx, data in enumerate(gen): + start_time = time.time() + + loss = train_step(data[0], data[1]) + + end_time = time.time() + consume_time = end_time - start_time + total_time += consume_time + + if idx % monitor_interval == 0: + cur_usage = psutil.Process(os.getpid()).memory_info().rss + max_mem_usage = max(cur_usage, max_mem_usage) + avg_mem_usage += cur_usage + count += 1 + tl.logging.info( + "[*] {} iteration: memory usage {:.2f}MB, consume time {:.4f}s, loss {:.4f}".format( + idx, cur_usage / (1024 * 1024), consume_time, loss + ) + ) + +print('consumed time:', total_time) + +avg_mem_usage = avg_mem_usage / count / (1024 * 1024) +max_mem_usage = max_mem_usage / (1024 * 1024) +print('average memory usage: {:.2f}MB'.format(avg_mem_usage)) +print('maximum memory usage: {:.2f}MB'.format(max_mem_usage)) diff --git a/tests/test_activations.py b/tests/test_activations.py new file mode 100644 index 0000000..39097a6 --- /dev/null +++ b/tests/test_activations.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl + +from tests.utils import CustomTestCase + + +class Test_Leaky_ReLUs(CustomTestCase): + + @classmethod + def setUpClass(cls): + cls.alpha = 0.2 + + cls.vmin = 0 + cls.vmax = 10 + + @classmethod + def tearDownClass(cls): + pass + + def test_lrelu(self): + for i in range(-5, 15): + + if i > 0: + good_output = i + else: + good_output = self.alpha * i + + computed_output = tl.act.leaky_relu(float(i), alpha=self.alpha) + + self.assertAlmostEqual(computed_output.numpy(), good_output, places=5) + + net = tl.layers.Input([10, 2]) + net = tl.layers.Dense(n_units=100, act=lambda x: tl.act.lrelu(x, 0.2), name='dense')(net) + print(net) + + def test_lrelu6(self): + for i in range(-5, 15): + + if i < 0: + good_output = self.alpha * i + else: + good_output = min(6, i) + + computed_output = tl.act.leaky_relu6(float(i), alpha=self.alpha) + + self.assertAlmostEqual(computed_output.numpy(), good_output, places=5) + + net = tl.layers.Input([10, 2]) + net = tl.layers.Dense(n_units=100, act=lambda x: tl.act.leaky_relu6(x, 0.2), name='dense')(net) + print(net) + + def test_ltrelu6(self): + for i in range(-5, 15): + + if i < 0: + good_output = self.alpha * i + elif i < 6: + good_output = i + else: + good_output = 6 + (self.alpha * (i - 6)) + + computed_output = tl.act.leaky_twice_relu6(float(i), alpha_low=self.alpha, alpha_high=self.alpha) + + self.assertAlmostEqual(computed_output.numpy(), good_output, places=5) + + net = tl.layers.Input([10, 200]) + net = tl.layers.Dense(n_units=100, act=lambda x: tl.act.leaky_twice_relu6(x, 0.2, 0.2), name='dense')(net) + print(net) + + def test_ramp(self): + + for i in range(-5, 15): + + if i < self.vmin: + good_output = self.vmin + elif i > self.vmax: + good_output = self.vmax + else: + good_output = i + + computed_output = tl.act.ramp(float(i), v_min=self.vmin, v_max=self.vmax) + + self.assertAlmostEqual(computed_output.numpy(), good_output, places=5) + + def test_sign(self): + + for i in range(-5, 15): + + if i < 0: + good_output = -1 + elif i == 0: + good_output = 0 + else: + good_output = 1 + + computed_output = tl.act.sign(float(i)) + + self.assertAlmostEqual(computed_output.numpy(), good_output, places=5) + + def test_swish(self): + import numpy as np + + for i in range(-5, 15): + + good_output = i / (1 + np.math.exp(-i)) + + computed_output = tl.act.swish(float(i)) + + self.assertAlmostEqual(computed_output.numpy(), good_output, places=5) + + +if __name__ == '__main__': + + unittest.main() diff --git a/tests/test_initializers.py b/tests/test_initializers.py new file mode 100644 index 0000000..df86fd8 --- /dev/null +++ b/tests/test_initializers.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl +import numpy as np + +from tests.utils import CustomTestCase + + +class Test_Leaky_ReLUs(CustomTestCase): + + @classmethod + def setUpClass(cls): + cls.ni = tl.layers.Input(shape=[16, 10]) + cls.w_shape = (10, 5) + cls.eps = 0.0 + + @classmethod + def tearDownClass(cls): + pass + + def init_dense(self, w_init): + return tl.layers.Dense(n_units=self.w_shape[1], in_channels=self.w_shape[0], W_init=w_init) + + def test_zeros(self): + dense = self.init_dense(tl.initializers.zeros()) + self.assertEqual(np.sum(dense.all_weights[0].numpy() - np.zeros(shape=self.w_shape)), self.eps) + nn = dense(self.ni) + + def test_ones(self): + dense = self.init_dense(tl.initializers.ones()) + self.assertEqual(np.sum(dense.all_weights[0].numpy() - np.ones(shape=self.w_shape)), self.eps) + nn = dense(self.ni) + + def test_constant(self): + dense = self.init_dense(tl.initializers.constant(value=5.0)) + self.assertEqual(np.sum(dense.all_weights[0].numpy() - np.ones(shape=self.w_shape) * 5.0), self.eps) + nn = dense(self.ni) + + # test with numpy arr + arr = np.random.uniform(size=self.w_shape).astype(np.float32) + dense = self.init_dense(tl.initializers.constant(value=arr)) + self.assertEqual(np.sum(dense.all_weights[0].numpy() - arr), self.eps) + nn = dense(self.ni) + + def test_RandomUniform(self): + dense = self.init_dense(tl.initializers.random_uniform(minval=-0.1, maxval=0.1, seed=1234)) + print(dense.all_weights[0].numpy()) + nn = dense(self.ni) + + def test_RandomNormal(self): + dense = self.init_dense(tl.initializers.random_normal(mean=0.0, stddev=0.1)) + print(dense.all_weights[0].numpy()) + nn = dense(self.ni) + + def test_TruncatedNormal(self): + dense = self.init_dense(tl.initializers.truncated_normal(mean=0.0, stddev=0.1)) + print(dense.all_weights[0].numpy()) + nn = dense(self.ni) + + def test_deconv2d_bilinear_upsampling_initializer(self): + rescale_factor = 2 + imsize = 128 + num_channels = 3 + num_in_channels = 3 + num_out_channels = 3 + filter_shape = (5, 5, num_out_channels, num_in_channels) + ni = tl.layers.Input(shape=(1, imsize, imsize, num_channels)) + bilinear_init = tl.initializers.deconv2d_bilinear_upsampling_initializer(shape=filter_shape) + deconv_layer = tl.layers.DeConv2dLayer( + shape=filter_shape, outputs_shape=(1, imsize * rescale_factor, imsize * rescale_factor, num_out_channels), + strides=(1, rescale_factor, rescale_factor, 1), W_init=bilinear_init, padding='SAME', act=None, + name='g/h1/decon2d' + ) + nn = deconv_layer(ni) + + def test_config(self): + init = tl.initializers.constant(value=5.0) + new_init = tl.initializers.Constant.from_config(init.get_config()) + + +if __name__ == '__main__': + + unittest.main() diff --git a/tests/test_nlp.py b/tests/test_nlp.py new file mode 100644 index 0000000..680eeb8 --- /dev/null +++ b/tests/test_nlp.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl + +from tensorflow.python.platform import gfile +from tests.utils import CustomTestCase +import nltk +nltk.download('punkt') + + +class Test_Leaky_ReLUs(CustomTestCase): + + @classmethod + def setUpClass(cls): + pass + + @classmethod + def tearDownClass(cls): + pass + + def test_as_bytes(self): + origin_str = "str" + origin_bytes = b'bytes' + converted_str = tl.nlp.as_bytes(origin_str) + converted_bytes = tl.nlp.as_bytes(origin_bytes) + print('str after using as_bytes:', converted_str) + print('bytes after using as_bytes:', converted_bytes) + + def test_as_text(self): + origin_str = "str" + origin_bytes = b'bytes' + converted_str = tl.nlp.as_text(origin_str) + converted_bytes = tl.nlp.as_text(origin_bytes) + print('str after using as_text:', converted_str) + print('bytes after using as_text:', converted_bytes) + + def test_save_vocab(self): + words = tl.files.load_matt_mahoney_text8_dataset() + vocabulary_size = 50000 + data, count, dictionary, reverse_dictionary = tl.nlp.build_words_dataset(words, vocabulary_size, True) + tl.nlp.save_vocab(count, name='vocab_text8.txt') + + def test_basic_tokenizer(self): + c = "how are you?" + tokens = tl.nlp.basic_tokenizer(c) + print(tokens) + + def test_generate_skip_gram_batch(self): + data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + batch, labels, data_index = tl.nlp.generate_skip_gram_batch( + data=data, batch_size=8, num_skips=2, skip_window=1, data_index=0 + ) + print(batch) + print(labels) + + def test_process_sentence(self): + c = "how are you?" + c = tl.nlp.process_sentence(c) + print(c) + + def test_words_to_word_id(self): + words = tl.files.load_matt_mahoney_text8_dataset() + vocabulary_size = 50000 + data, count, dictionary, reverse_dictionary = tl.nlp.build_words_dataset(words, vocabulary_size, True) + ids = tl.nlp.words_to_word_ids(words, dictionary) + context = tl.nlp.word_ids_to_words(ids, reverse_dictionary) + # print(ids) + # print(context) + + +if __name__ == '__main__': + + unittest.main() diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py new file mode 100644 index 0000000..15d4814 --- /dev/null +++ b/tests/utils/__init__.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from tests.utils.custom_testcase import * +from tests.utils.list_py_files import * +from tests.utils.timeout_utils import * + +from tests.utils.custom_layers import * +from tests.utils.custom_networks import * \ No newline at end of file diff --git a/tests/utils/custom_layers/__init__.py b/tests/utils/custom_layers/__init__.py new file mode 100644 index 0000000..995a053 --- /dev/null +++ b/tests/utils/custom_layers/__init__.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from tests.utils.custom_layers.basic_layers import * +from tests.utils.custom_layers.inception_blocks import * \ No newline at end of file diff --git a/tests/utils/custom_layers/basic_layers.py b/tests/utils/custom_layers/basic_layers.py new file mode 100644 index 0000000..83f320a --- /dev/null +++ b/tests/utils/custom_layers/basic_layers.py @@ -0,0 +1,136 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorflow as tf +import tensorlayer as tl + +__all__ = [ + 'activation_module', + 'conv_module', + 'dense_module', +] + + +def activation_module(layer, activation_fn, leaky_relu_alpha=0.2, name=None): + + act_name = name + "/activation" if name is not None else "activation" + + if activation_fn not in ["ReLU", "ReLU6", "Leaky_ReLU", "PReLU", "PReLU6", "PTReLU6", "CReLU", "ELU", "SELU", + "tanh", "sigmoid", "softmax", None]: + raise Exception("Unknown 'activation_fn': %s" % activation_fn) + + elif activation_fn == "ReLU": + layer = tl.layers.LambdaLayer(prev_layer=layer, fn=tf.nn.relu, name=act_name) + + elif activation_fn == "ReLU6": + layer = tl.layers.LambdaLayer(prev_layer=layer, fn=tf.nn.relu6, name=act_name) + + elif activation_fn == "Leaky_ReLU": + layer = tl.layers.LambdaLayer( + prev_layer=layer, fn=tf.nn.leaky_relu, fn_args={'alpha': leaky_relu_alpha}, name=act_name + ) + + elif activation_fn == "PReLU": + layer = tl.layers.PReluLayer(prev_layer=layer, channel_shared=False, name=act_name) + + elif activation_fn == "PReLU6": + layer = tl.layers.PRelu6Layer(prev_layer=layer, channel_shared=False, name=act_name) + + elif activation_fn == "PTReLU6": + layer = tl.layers.PTRelu6Layer(prev_layer=layer, channel_shared=False, name=act_name) + + elif activation_fn == "CReLU": + layer = tl.layers.LambdaLayer(prev_layer=layer, fn=tf.nn.crelu, name=act_name) + + elif activation_fn == "ELU": + layer = tl.layers.LambdaLayer(prev_layer=layer, fn=tf.nn.elu, name=act_name) + + elif activation_fn == "SELU": + layer = tl.layers.LambdaLayer(prev_layer=layer, fn=tf.nn.selu, name=act_name) + + elif activation_fn == "tanh": + layer = tl.layers.LambdaLayer(prev_layer=layer, fn=tf.nn.tanh, name=act_name) + + elif activation_fn == "sigmoid": + layer = tl.layers.LambdaLayer(prev_layer=layer, fn=tf.nn.sigmoid, name=act_name) + + elif activation_fn == "softmax": + layer = tl.layers.LambdaLayer(prev_layer=layer, fn=tf.nn.softmax, name=act_name) + + return layer + + +def conv_module( + prev_layer, n_out_channel, filter_size, strides, padding, is_train=True, use_batchnorm=True, activation_fn=None, + conv_init=tl.initializers.random_uniform(), + batch_norm_init=tl.initializers.truncated_normal(mean=1., + stddev=0.02), bias_init=tf.zeros_initializer(), name=None +): + + if activation_fn not in ["ReLU", "ReLU6", "Leaky_ReLU", "PReLU", "PReLU6", "PTReLU6", "CReLU", "ELU", "SELU", + "tanh", "sigmoid", "softmax", None]: + raise Exception("Unknown 'activation_fn': %s" % activation_fn) + + conv_name = 'conv2d' if name is None else name + bn_name = 'batch_norm' if name is None else name + '/BatchNorm' + + layer = tl.layers.Conv2d( + prev_layer, + n_filter=n_out_channel, + filter_size=filter_size, + strides=strides, + padding=padding, + act=None, + W_init=conv_init, + b_init=None if use_batchnorm else bias_init, # Not useful as the convolutions are batch normalized + name=conv_name + ) + + if use_batchnorm: + + layer = tl.layers.BatchNormLayer(layer, act=None, is_train=is_train, gamma_init=batch_norm_init, name=bn_name) + + logits = layer.outputs + + layer = activation_module(layer, activation_fn, name=conv_name) + + return layer, logits + + +def dense_module( + prev_layer, n_units, is_train, use_batchnorm=True, activation_fn=None, + dense_init=tl.initializers.random_uniform(), + batch_norm_init=tl.initializers.truncated_normal(mean=1., + stddev=0.02), bias_init=tf.zeros_initializer(), name=None +): + + if activation_fn not in ["ReLU", "ReLU6", "Leaky_ReLU", "PReLU", "PReLU6", "PTReLU6", "CReLU", "ELU", "SELU", + "tanh", "sigmoid", "softmax", None]: + raise Exception("Unknown 'activation_fn': %s" % activation_fn) + + # Flatten: Conv to FC + if prev_layer.outputs.get_shape().__len__() != 2: # The input dimension must be rank 2 + layer = tl.layers.FlattenLayer(prev_layer, name='flatten') + + else: + layer = prev_layer + + layer = tl.layers.DenseLayer( + layer, + n_units=n_units, + act=None, + W_init=dense_init, + b_init=None if use_batchnorm else bias_init, # Not useful as the convolutions are batch normalized + name='dense' if name is None else name + ) + + if use_batchnorm: + layer = tl.layers.BatchNormLayer( + layer, act=None, is_train=is_train, gamma_init=batch_norm_init, name='batch_norm' + ) + + logits = layer.outputs + + layer = activation_module(layer, activation_fn) + + return layer, logits diff --git a/tests/utils/custom_layers/inception_blocks.py b/tests/utils/custom_layers/inception_blocks.py new file mode 100644 index 0000000..89d2640 --- /dev/null +++ b/tests/utils/custom_layers/inception_blocks.py @@ -0,0 +1,279 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import tensorflow as tf +import tensorlayer as tl + +from tests.utils.custom_layers.basic_layers import conv_module + +__all__ = [ + 'block_inception_a', + 'block_reduction_a', + 'block_inception_b', + 'block_reduction_b', + 'block_inception_c', + 'block_reduction_b', +] + + +def block_inception_a(inputs, scope=None, is_train=False): + """Builds Inception-A block for Inception v4 network.""" + # By default use stride=1 and SAME padding + + with tf.variable_scope(name_or_scope=scope, default_name='BlockInceptionA', values=[inputs]): + with tf.variable_scope('Branch_0'): + branch_0, _ = conv_module( + inputs, n_out_channel=96, filter_size=(1, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0a_1x1' + ) + + with tf.variable_scope('Branch_1'): + branch_1, _ = conv_module( + inputs, n_out_channel=64, filter_size=(1, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0a_1x1' + ) + + branch_1, _ = conv_module( + branch_1, n_out_channel=96, filter_size=(3, 3), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0b_3x3' + ) + + with tf.variable_scope('Branch_2'): + branch_2, _ = conv_module( + inputs, n_out_channel=64, filter_size=(1, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0a_1x1' + ) + + branch_2, _ = conv_module( + branch_2, n_out_channel=96, filter_size=(3, 3), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0b_3x3' + ) + + branch_2, _ = conv_module( + branch_2, n_out_channel=96, filter_size=(3, 3), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0c_3x3' + ) + + with tf.variable_scope('Branch_3'): + branch_3 = tl.layers.MeanPool2d( + inputs, filter_size=(3, 3), strides=(1, 1), padding='SAME', name='AvgPool_0a_3x3' + ) + + branch_3, _ = conv_module( + branch_3, n_out_channel=96, filter_size=(1, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0b_1x1' + ) + + return tl.layers.ConcatLayer([branch_0, branch_1, branch_2, branch_3], concat_dim=3, name='concat_layer') + + +def block_reduction_a(inputs, scope=None, is_train=False): + """Builds Reduction-A block for Inception v4 network.""" + # By default use stride=1 and SAME padding + + with tf.variable_scope(scope, 'BlockReductionA', [inputs]): + with tf.variable_scope('Branch_0'): + branch_0, _ = conv_module( + inputs, n_out_channel=384, filter_size=(3, 3), strides=(2, 2), padding='VALID', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_1a_3x3' + ) + + with tf.variable_scope('Branch_1'): + branch_1, _ = conv_module( + inputs, n_out_channel=192, filter_size=(1, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0a_1x1' + ) + + branch_1, _ = conv_module( + branch_1, n_out_channel=224, filter_size=(3, 3), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0b_3x3' + ) + + branch_1, _ = conv_module( + branch_1, n_out_channel=256, filter_size=(3, 3), strides=(2, 2), padding='VALID', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_1a_3x3' + ) + + with tf.variable_scope('Branch_2'): + branch_2 = tl.layers.MaxPool2d(inputs, (3, 3), strides=(2, 2), padding='VALID', name='MaxPool_1a_3x3') + + return tl.layers.ConcatLayer([branch_0, branch_1, branch_2], concat_dim=3, name='concat_layer') + + +def block_inception_b(inputs, scope=None, is_train=False): + """Builds Inception-B block for Inception v4 network.""" + # By default use stride=1 and SAME padding + + with tf.variable_scope(scope, 'BlockInceptionB', [inputs]): + with tf.variable_scope('Branch_0'): + branch_0, _ = conv_module( + inputs, n_out_channel=384, filter_size=(1, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0a_1x1' + ) + + with tf.variable_scope('Branch_1'): + branch_1, _ = conv_module( + inputs, n_out_channel=192, filter_size=(1, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0a_1x1' + ) + + branch_1, _ = conv_module( + branch_1, n_out_channel=224, filter_size=(1, 7), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0b_1x7' + ) + + branch_1, _ = conv_module( + branch_1, n_out_channel=256, filter_size=(7, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0c_7x1' + ) + + with tf.variable_scope('Branch_2'): + branch_2, _ = conv_module( + inputs, n_out_channel=192, filter_size=(1, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0a_1x1' + ) + + branch_2, _ = conv_module( + branch_2, n_out_channel=192, filter_size=(7, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0b_7x1' + ) + + branch_2, _ = conv_module( + branch_2, n_out_channel=224, filter_size=(1, 7), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0c_1x7' + ) + + branch_2, _ = conv_module( + branch_2, n_out_channel=224, filter_size=(7, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0d_7x1' + ) + + branch_2, _ = conv_module( + branch_2, n_out_channel=256, filter_size=(1, 7), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0e_1x7' + ) + + with tf.variable_scope('Branch_3'): + branch_3 = tl.layers.MeanPool2d( + inputs, filter_size=(3, 3), strides=(1, 1), padding='SAME', name='AvgPool_0a_3x3' + ) + + branch_3, _ = conv_module( + branch_3, n_out_channel=128, filter_size=(1, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0b_1x1' + ) + + return tl.layers.ConcatLayer([branch_0, branch_1, branch_2, branch_3], concat_dim=3, name='concat_layer') + + +def block_reduction_b(inputs, scope=None, is_train=False): + """Builds Reduction-B block for Inception v4 network.""" + # By default use stride=1 and SAME padding + + with tf.variable_scope(scope, 'BlockReductionB', [inputs]): + with tf.variable_scope('Branch_0'): + branch_0, _ = conv_module( + inputs, n_out_channel=192, filter_size=(1, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0a_1x1' + ) + + branch_0, _ = conv_module( + branch_0, n_out_channel=192, filter_size=(3, 3), strides=(2, 2), padding='VALID', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_1a_3x3' + ) + + with tf.variable_scope('Branch_1'): + branch_1, _ = conv_module( + inputs, n_out_channel=256, filter_size=(1, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0a_1x1' + ) + + branch_1, _ = conv_module( + branch_1, n_out_channel=256, filter_size=(1, 7), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0b_1x7' + ) + + branch_1, _ = conv_module( + branch_1, n_out_channel=320, filter_size=(7, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0c_7x1' + ) + + branch_1, _ = conv_module( + branch_1, n_out_channel=320, filter_size=(3, 3), strides=(2, 2), padding='VALID', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_1a_3x3' + ) + + with tf.variable_scope('Branch_2'): + branch_2 = tl.layers.MaxPool2d(inputs, (3, 3), strides=(2, 2), padding='VALID', name='MaxPool_1a_3x3') + + return tl.layers.ConcatLayer([branch_0, branch_1, branch_2], concat_dim=3, name='concat_layer') + + +def block_inception_c(inputs, scope=None, is_train=False): + """Builds Inception-C block for Inception v4 network.""" + # By default use stride=1 and SAME padding + + with tf.variable_scope(scope, 'BlockInceptionC', [inputs]): + with tf.variable_scope('Branch_0'): + branch_0, _ = conv_module( + inputs, n_out_channel=256, filter_size=(1, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0a_1x1' + ) + + with tf.variable_scope('Branch_1'): + branch_1, _ = conv_module( + inputs, n_out_channel=384, filter_size=(1, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0a_1x1' + ) + + branch_1a, _ = conv_module( + branch_1, n_out_channel=256, filter_size=(1, 3), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0b_1x3' + ) + + branch_1b, _ = conv_module( + branch_1, n_out_channel=256, filter_size=(3, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0c_3x1' + ) + + branch_1 = tl.layers.ConcatLayer([branch_1a, branch_1b], concat_dim=3, name='concat_layer') + + with tf.variable_scope('Branch_2'): + branch_2, _ = conv_module( + inputs, n_out_channel=384, filter_size=(1, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0a_1x1' + ) + + branch_2, _ = conv_module( + branch_2, n_out_channel=448, filter_size=(3, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0b_3x1' + ) + + branch_2, _ = conv_module( + branch_2, n_out_channel=512, filter_size=(1, 3), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0c_1x3' + ) + + branch_2a, _ = conv_module( + branch_2, n_out_channel=256, filter_size=(1, 3), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0d_1x3' + ) + + branch_2b, _ = conv_module( + branch_2, n_out_channel=256, filter_size=(3, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0e_3x1' + ) + + branch_2 = tl.layers.ConcatLayer([branch_2a, branch_2b], concat_dim=3, name='concat_layer') + + with tf.variable_scope('Branch_3'): + branch_3 = tl.layers.MeanPool2d( + inputs, filter_size=(3, 3), strides=(1, 1), padding='SAME', name='AvgPool_0a_3x3' + ) + + branch_3, _ = conv_module( + branch_3, n_out_channel=256, filter_size=(1, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0b_1x1' + ) + + return tl.layers.ConcatLayer([branch_0, branch_1, branch_2, branch_3], concat_dim=3, name='concat_layer') diff --git a/tests/utils/custom_networks/__init__.py b/tests/utils/custom_networks/__init__.py new file mode 100644 index 0000000..81dd159 --- /dev/null +++ b/tests/utils/custom_networks/__init__.py @@ -0,0 +1,4 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from tests.utils.custom_networks.inceptionv4 import * \ No newline at end of file diff --git a/tests/utils/custom_networks/inceptionv4.py b/tests/utils/custom_networks/inceptionv4.py new file mode 100644 index 0000000..bac2ae8 --- /dev/null +++ b/tests/utils/custom_networks/inceptionv4.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl + +from tests.utils.custom_layers.basic_layers import conv_module +from tests.utils.custom_layers.basic_layers import dense_module + +from tests.utils.custom_layers.inception_blocks import block_inception_a +from tests.utils.custom_layers.inception_blocks import block_inception_b +from tests.utils.custom_layers.inception_blocks import block_inception_c + +from tests.utils.custom_layers.inception_blocks import block_reduction_a +from tests.utils.custom_layers.inception_blocks import block_reduction_b + +__all__ = ['InceptionV4_Network'] + + +class InceptionV4_Network(object): + """InceptionV4 model.""" + + def __init__(self, include_FC_head=True, flatten_output=True): + + self.include_FC_head = include_FC_head + self.flatten_output = flatten_output + + def __call__(self, inputs, reuse=False, is_train=False): + + with tf.variable_scope("InceptionV4", reuse=reuse): + + preprocessed = inputs + + with tf.variable_scope("preprocessing"): + + max_val = tf.reduce_max(preprocessed) + min_val = tf.reduce_min(preprocessed) + + need_int_rescale = tf.logical_and(tf.greater(max_val, 1.0), tf.greater_equal(min_val, 0.0)) + + need_float_rescale = tf.logical_and(tf.less_equal(max_val, 1.0), tf.greater_equal(min_val, 0.0)) + + preprocessed = tf.cond( + pred=need_int_rescale, true_fn=lambda: tf.subtract(tf.divide(preprocessed, 127.5), 1.0), + false_fn=lambda: preprocessed + ) + + preprocessed = tf.cond( + pred=need_float_rescale, true_fn=lambda: tf.multiply(tf.subtract(preprocessed, 0.5), 2.0), + false_fn=lambda: preprocessed + ) + + # Input Layers + input_layer = tl.layers.InputLayer(preprocessed, name='input') + + # 299 x 299 x 3 + net, _ = conv_module( + input_layer, n_out_channel=32, filter_size=(3, 3), strides=(2, 2), padding='VALID', + batch_norm_init=None, is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_1a_3x3' + ) + + # 149 x 149 x 32 + net, _ = conv_module( + net, n_out_channel=32, filter_size=(3, 3), strides=(1, 1), padding='VALID', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_2a_3x3' + ) + + # 147 x 147 x 32 + net, _ = conv_module( + net, n_out_channel=64, filter_size=(3, 3), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_2b_3x3' + ) + + # 147 x 147 x 64 + with tf.variable_scope('Mixed_3a'): + with tf.variable_scope('Branch_0'): + branch_0 = tl.layers.MaxPool2d(net, (3, 3), strides=(2, 2), padding='VALID', name='MaxPool_0a_3x3') + + with tf.variable_scope('Branch_1'): + branch_1, _ = conv_module( + net, n_out_channel=96, filter_size=(3, 3), strides=(2, 2), padding='VALID', + batch_norm_init=None, is_train=is_train, use_batchnorm=True, activation_fn='ReLU', + name='Conv2d_0a_3x3' + ) + + net = tl.layers.ConcatLayer([branch_0, branch_1], concat_dim=3) + + # 73 x 73 x 160 + with tf.variable_scope('Mixed_4a'): + with tf.variable_scope('Branch_0'): + branch_0, _ = conv_module( + net, n_out_channel=64, filter_size=(1, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0a_1x1' + ) + + branch_0, _ = conv_module( + branch_0, n_out_channel=96, filter_size=(3, 3), strides=(1, 1), padding='VALID', + batch_norm_init=None, is_train=is_train, use_batchnorm=True, activation_fn='ReLU', + name='Conv2d_1a_3x3' + ) + + with tf.variable_scope('Branch_1'): + branch_1, _ = conv_module( + net, n_out_channel=64, filter_size=(1, 1), strides=(1, 1), padding='SAME', batch_norm_init=None, + is_train=is_train, use_batchnorm=True, activation_fn='ReLU', name='Conv2d_0a_1x1' + ) + + branch_1, _ = conv_module( + branch_1, n_out_channel=64, filter_size=(1, 7), strides=(1, 1), padding='SAME', + batch_norm_init=None, is_train=is_train, use_batchnorm=True, activation_fn='ReLU', + name='Conv2d_0b_1x7' + ) + + branch_1, _ = conv_module( + branch_1, n_out_channel=64, filter_size=(7, 1), strides=(1, 1), padding='SAME', + batch_norm_init=None, is_train=is_train, use_batchnorm=True, activation_fn='ReLU', + name='Conv2d_0c_7x1' + ) + + branch_1, _ = conv_module( + branch_1, n_out_channel=96, filter_size=(3, 3), strides=(1, 1), padding='VALID', + batch_norm_init=None, is_train=is_train, use_batchnorm=True, activation_fn='ReLU', + name='Conv2d_1a_3x3' + ) + + net = tl.layers.ConcatLayer([branch_0, branch_1], concat_dim=3) + + # 71 x 71 x 192 + with tf.variable_scope('Mixed_5a'): + with tf.variable_scope('Branch_0'): + # 299 x 299 x 3 + branch_0, _ = conv_module( + net, n_out_channel=192, filter_size=(3, 3), strides=(2, 2), padding='VALID', + batch_norm_init=None, is_train=is_train, use_batchnorm=True, activation_fn='ReLU', + name='Conv2d_1a_3x3' + ) + + with tf.variable_scope('Branch_1'): + branch_1 = tl.layers.MaxPool2d(net, (3, 3), strides=(2, 2), padding='VALID', name='MaxPool_1a_3x3') + + net = tl.layers.ConcatLayer([branch_0, branch_1], concat_dim=3) + + # 35 x 35 x 384 + # 4 x Inception-A blocks + for idx in range(4): + block_scope = 'Mixed_5' + chr(ord('b') + idx) + net = block_inception_a(net, scope=block_scope, is_train=is_train) + + # 35 x 35 x 384 + # Reduction-A block + net = block_reduction_a(net, scope='Mixed_6a', is_train=is_train) + + # 17 x 17 x 1024 + # 7 x Inception-B blocks + for idx in range(7): + block_scope = 'Mixed_6' + chr(ord('b') + idx) + net = block_inception_b(net, scope=block_scope, is_train=is_train) + + # 17 x 17 x 1024 + # Reduction-B block + net = block_reduction_b(net, scope='Mixed_7a', is_train=is_train) + + # 8 x 8 x 1536 + # 3 x Inception-C blocks + for idx in range(3): + block_scope = 'Mixed_7' + chr(ord('b') + idx) + net = block_inception_c(net, scope=block_scope, is_train=is_train) + + if self.flatten_output and not self.include_FC_head: + net = tl.layers.FlattenLayer(net, name='flatten') + + if self.include_FC_head: + with tf.variable_scope("Logits", reuse=reuse): + + # 8 x 8 x 1536 + net = tl.layers.MeanPool2d( + net, filter_size=net.outputs.get_shape()[1:3], strides=(1, 1), padding='VALID', + name='AvgPool_1a' + ) + + # 1 x 1 x 1536 + net = tl.layers.DropoutLayer(net, keep=0.8, is_fix=True, is_train=is_train, name='Dropout_1b') + net = tl.layers.FlattenLayer(net, name='PreLogitsFlatten') + + # 1536 + net, _ = dense_module( + net, n_units=1001, activation_fn="softmax", use_batchnorm=False, batch_norm_init=None, + is_train=is_train, name="Logits" + ) + + return net diff --git a/tests/utils/custom_testcase.py b/tests/utils/custom_testcase.py new file mode 100644 index 0000000..a67f413 --- /dev/null +++ b/tests/utils/custom_testcase.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import unittest +from contextlib import contextmanager + +__all__ = [ + 'CustomTestCase', +] + + +class CustomTestCase(unittest.TestCase): + + @contextmanager + def assertNotRaises(self, exc_type): + try: + yield None + except exc_type: + raise self.failureException('{} raised'.format(exc_type.__name__)) diff --git a/tests/utils/list_py_files.py b/tests/utils/list_py_files.py new file mode 100644 index 0000000..4cc08c1 --- /dev/null +++ b/tests/utils/list_py_files.py @@ -0,0 +1,26 @@ +import os + +__all__ = [ + 'list_all_py_files', +] + +_excludes = [ + 'tensorlayer/db.py', +] + + +def _list_py_files(root): + for root, _dirs, files in os.walk(root): + if root.find('third_party') != -1: + continue + for file in files: + if file.endswith('.py'): + yield os.path.join(root, file) + + +def list_all_py_files(): + dirs = ['tensorlayer', 'tests', 'example'] + for d in dirs: + for filename in _list_py_files(d): + if filename not in _excludes: + yield filename diff --git a/tests/utils/timeout_utils.py b/tests/utils/timeout_utils.py new file mode 100644 index 0000000..6a155a5 --- /dev/null +++ b/tests/utils/timeout_utils.py @@ -0,0 +1,36 @@ +import platform + +if platform.system() != "Windows": + import signal +else: + signal = None + +__all__ = ['TimeoutError', 'WindowsError', 'TimeoutContext'] + + +class TimeoutError(Exception): + pass + + +class WindowsError(Exception): + pass + + +class TimeoutContext(): + """Timeout class using ALARM signal.""" + + def __init__(self, sec): + self.sec = sec + + def __enter__(self): + if signal is None: + raise WindowsError("Windows is not supported for this test") + + signal.signal(signal.SIGALRM, self.raise_timeout) + signal.alarm(self.sec) + + def __exit__(self, *args): + signal.alarm(0) # disable alarm + + def raise_timeout(self, *args): + raise TimeoutError("A timeout error have been raised.") diff --git a/tl b/tl new file mode 100644 index 0000000..3c5a78a --- /dev/null +++ b/tl @@ -0,0 +1,13 @@ +#!/bin/bash + +SOURCE="${BASH_SOURCE[0]}" +while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink + DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located +done +DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" + +export PYTHONPATH="${DIR}/src:${PYTHONPATH}" + +python3 -m tensorlayer.cli "$@"