├── tensorpack ├── contrib │ └── __init__.py ├── utils │ ├── compatible_serialize.py │ ├── naming.py │ ├── __init__.py │ ├── debug.py │ ├── gpu.py │ ├── serialize.py │ ├── fs.py │ ├── timer.py │ └── palette.py ├── dataflow │ ├── dataset │ │ ├── .gitignore │ │ ├── __init__.py │ │ ├── places.py │ │ ├── caltech101.py │ │ ├── svhn.py │ │ ├── bsds500.py │ │ └── mnist.py │ ├── imgaug │ │ ├── __init__.py │ │ ├── convert.py │ │ ├── noise.py │ │ ├── external.py │ │ ├── paste.py │ │ ├── deform.py │ │ └── meta.py │ ├── __init__.py │ ├── serialize_test.py │ └── raw.py ├── graph_builder │ ├── model_desc.py │ └── __init__.py ├── callbacks │ ├── stats.py │ ├── __init__.py │ ├── prof.py │ ├── concurrency.py │ ├── hooks.py │ ├── group.py │ ├── misc.py │ └── param_test.py ├── train │ ├── utility.py │ ├── __init__.py │ ├── interface.py │ └── model_desc.py ├── models │ ├── common.py │ ├── shapes.py │ ├── utils.py │ ├── __init__.py │ ├── fc.py │ ├── shape_utils.py │ ├── nonlin.py │ ├── tflayer.py │ ├── linearwrap.py │ ├── models_test.py │ └── layer_norm.py ├── __init__.py ├── tfutils │ ├── unit_tests.py │ ├── __init__.py │ ├── distributed.py │ ├── symbolic_functions.py │ ├── dependency.py │ ├── model_utils.py │ ├── sesscreate.py │ ├── common.py │ ├── collect_env.py │ └── argscope.py ├── compat │ ├── __init__.py │ └── tensor_spec.py ├── input_source │ └── __init__.py ├── predict │ ├── __init__.py │ ├── feedfree.py │ └── multigpu.py └── libinfo.py ├── setup.cfg ├── NOTICE ├── MaskRCNN ├── utils │ ├── __init__.py │ ├── README.md │ ├── randomnness.py │ ├── box_ops.py │ ├── generate_anchors.py │ └── np_box_ops.py └── model │ ├── fpn.py │ └── mask_head.py ├── requirements.txt ├── .github └── PULL_REQUEST_TEMPLATE.md ├── CODE_OF_CONDUCT.md ├── .gitignore ├── Dockerfile ├── .vscode ├── launch.json └── tasks.json ├── setup.py ├── CODEBASE.md └── CONTRIBUTING.md /tensorpack/contrib/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | license_file = LICENSE 3 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Mask Rcnn Tensorflow 2 | Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | -------------------------------------------------------------------------------- /tensorpack/utils/compatible_serialize.py: -------------------------------------------------------------------------------- 1 | from .serialize import loads, dumps # noqa 2 | 3 | # keep this file for BC 4 | -------------------------------------------------------------------------------- /tensorpack/dataflow/dataset/.gitignore: -------------------------------------------------------------------------------- 1 | mnist_data 2 | cifar10_data 3 | cifar100_data 4 | svhn_data 5 | ilsvrc_metadata 6 | bsds500_data 7 | -------------------------------------------------------------------------------- /MaskRCNN/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | -------------------------------------------------------------------------------- /tensorpack/graph_builder/model_desc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: model_desc.py 3 | 4 | 5 | from ..train.model_desc import ModelDesc, ModelDescBase # kept for BC # noqa 6 | 7 | 8 | __all__ = [] 9 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ujson==5.7.0 2 | opencv-python==4.8.1.78 3 | Cython==0.29.33 4 | pycocotools==2.0.6 5 | matplotlib==3.7.1 6 | markdown==3.4.3 7 | pybind11==2.10.0 8 | scikit-image==0.19.3 9 | numba==0.56.4 10 | tk==0.1.0 -------------------------------------------------------------------------------- /tensorpack/callbacks/stats.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: stats.py 3 | 4 | from .graph import DumpParamAsImage # noqa 5 | # for compatibility only 6 | from .misc import InjectShell, SendStat # noqa 7 | 8 | __all__ = [] 9 | -------------------------------------------------------------------------------- /tensorpack/train/utility.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: utility.py 3 | 4 | # for backwards-compatibility 5 | from ..graph_builder.utils import LeastLoadedDeviceSetter, OverrideToLocalVariable, override_to_local_variable # noqa 6 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *Issue #, if available:* 2 | 3 | *Description of changes:* 4 | 5 | 6 | By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. 7 | -------------------------------------------------------------------------------- /tensorpack/utils/naming.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: naming.py 3 | 4 | 5 | GLOBAL_STEP_INCR_OP_NAME = 'global_step_incr' 6 | 7 | # extra variables to summarize during training in a moving-average way 8 | MOVING_SUMMARY_OPS_KEY = 'MOVING_SUMMARY_OPS' 9 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /tensorpack/models/common.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: common.py 3 | 4 | from .registry import layer_register, disable_layer_logging # noqa 5 | from .tflayer import rename_tflayer_get_variable 6 | from .utils import VariableHolder # noqa 7 | 8 | __all__ = ['layer_register', 'VariableHolder', 'rename_tflayer_get_variable', 9 | 'disable_layer_logging'] 10 | -------------------------------------------------------------------------------- /MaskRCNN/utils/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Some third-party helper functions 3 | 4 | + generate_anchors.py: copied from [py-faster-rcnn](https://github.com/rbgirshick/py-faster-rcnn/blob/master/lib/rpn/generate_anchors.py). 5 | + box_ops.py: modified from [TF object detection API](https://github.com/tensorflow/models/blob/master/research/object_detection/core/box_list_ops.py). 6 | + np_box_ops.py: copied from [TF object detection API](https://github.com/tensorflow/models/blob/master/research/object_detection/utils/np_box_ops.py). 7 | 8 | -------------------------------------------------------------------------------- /MaskRCNN/utils/randomnness.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | class SeedGenerator: 4 | def __init__(self, seed): 5 | self.seed = seed 6 | self.counters = dict() 7 | 8 | def next(self, key='default'): 9 | if self.seed == None: 10 | return None 11 | 12 | if key not in self.counters: 13 | self.counters[key] = self.seed 14 | return self.counters[key] 15 | else: 16 | self.counters[key] += 1 17 | return self.counters[key] -------------------------------------------------------------------------------- /tensorpack/models/shapes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: shapes.py 3 | 4 | 5 | import tensorflow as tf 6 | 7 | from .common import layer_register 8 | 9 | __all__ = ['ConcatWith'] 10 | 11 | 12 | @layer_register(use_scope=None) 13 | def ConcatWith(x, tensor, dim): 14 | """ 15 | A wrapper around ``tf.concat`` to cooperate with :class:`LinearWrap`. 16 | 17 | Args: 18 | x (tf.Tensor): input 19 | tensor (list[tf.Tensor]): a tensor or list of tensors to concatenate with x. 20 | x will be at the beginning 21 | dim (int): the dimension along which to concatenate 22 | 23 | Returns: 24 | tf.Tensor: ``tf.concat([x] + tensor, dim)`` 25 | """ 26 | if type(tensor) != list: 27 | tensor = [tensor] 28 | return tf.concat([x] + tensor, dim) 29 | -------------------------------------------------------------------------------- /tensorpack/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: __init__.py 3 | 4 | 5 | from tensorpack.libinfo import __version__, __git_version__, _HAS_TF 6 | 7 | from tensorpack.utils import * 8 | from tensorpack.dataflow import * 9 | 10 | # dataflow can be used alone without installing tensorflow 11 | 12 | # https://github.com/celery/kombu/blob/7d13f9b95d0b50c94393b962e6def928511bfda6/kombu/__init__.py#L34-L36 13 | STATICA_HACK = True 14 | globals()['kcah_acitats'[::-1].upper()] = _HAS_TF 15 | if STATICA_HACK: 16 | from tensorpack.models import * 17 | 18 | from tensorpack.callbacks import * 19 | from tensorpack.tfutils import * 20 | 21 | from tensorpack.train import * 22 | from tensorpack.input_source import * 23 | from tensorpack.predict import * 24 | 25 | from tensorpack.compat import tfv1 26 | -------------------------------------------------------------------------------- /tensorpack/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: __init__.py 3 | 4 | """ 5 | Common utils. 6 | These utils should be irrelevant to tensorflow. 7 | """ 8 | 9 | # https://github.com/celery/kombu/blob/7d13f9b95d0b50c94393b962e6def928511bfda6/kombu/__init__.py#L34-L36 10 | STATICA_HACK = True 11 | globals()['kcah_acitats'[::-1].upper()] = False 12 | if STATICA_HACK: 13 | from .utils import * 14 | 15 | 16 | __all__ = [] 17 | 18 | 19 | def _global_import(name): 20 | p = __import__(name, globals(), None, level=1) 21 | lst = p.__all__ if '__all__' in dir(p) else dir(p) 22 | for k in lst: 23 | if not k.startswith('__'): 24 | globals()[k] = p.__dict__[k] 25 | __all__.append(k) 26 | 27 | 28 | _global_import('utils') 29 | 30 | # Import no other submodules. they are supposed to be explicitly imported by users. 31 | __all__.extend(['logger']) 32 | -------------------------------------------------------------------------------- /tensorpack/tfutils/unit_tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import unittest 4 | import tensorflow as tf 5 | 6 | from ..utils import logger 7 | from .scope_utils import under_name_scope 8 | 9 | 10 | class ScopeUtilsTest(unittest.TestCase): 11 | 12 | @under_name_scope(name_scope='s') 13 | def _f(self, check=True): 14 | if check: 15 | assert tf.compat.v1.get_default_graph().get_name_scope().endswith('s') 16 | return True 17 | 18 | def test_under_name_scope(self): 19 | self.assertTrue(self._f()) 20 | with self.assertRaises(AssertionError): 21 | self._f() # name conflict 22 | 23 | def test_under_name_scope_warning(self): 24 | x = tfv1.placeholder(tf.float32, [3]) 25 | tf.nn.relu(x, name='s') 26 | with self.assertLogs(logger=logger._logger, level='WARNING'): 27 | self._f(check=False, name_scope='s') 28 | 29 | 30 | if __name__ == '__main__': 31 | unittest.main() 32 | -------------------------------------------------------------------------------- /tensorpack/compat/__init__.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | 3 | 4 | def _backport_tensor_spec(): 5 | if hasattr(tf, 'TensorSpec'): 6 | return tf.TensorSpec 7 | try: 8 | # available since 1.7 9 | from tensorflow.python.framework.tensor_spec import TensorSpec 10 | except ImportError: 11 | pass 12 | else: 13 | tf.TensorSpec = TensorSpec 14 | return TensorSpec 15 | 16 | from .tensor_spec import TensorSpec 17 | tf.TensorSpec = TensorSpec 18 | return TensorSpec 19 | 20 | 21 | def is_tfv2(): 22 | """ 23 | Returns whether tensorflow is operating in V2 mode. 24 | """ 25 | try: 26 | from tensorflow.python import tf2 27 | return tf2.enabled() 28 | except Exception: 29 | return False 30 | 31 | 32 | if is_tfv2(): 33 | tfv1 = tf.compat.v1 34 | if not hasattr(tf, 'layers'): 35 | # promised at https://github.com/tensorflow/community/pull/24#issuecomment-440453886 36 | tf.layers = tf.keras.layers 37 | else: 38 | try: 39 | tfv1 = tf.compat.v1 # this will silent some warnings 40 | except AttributeError: 41 | tfv1 = tf 42 | -------------------------------------------------------------------------------- /tensorpack/input_source/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: __init__.py 3 | 4 | # https://github.com/celery/kombu/blob/7d13f9b95d0b50c94393b962e6def928511bfda6/kombu/__init__.py#L34-L36 5 | STATICA_HACK = True 6 | globals()['kcah_acitats'[::-1].upper()] = False 7 | if STATICA_HACK: 8 | from .input_source_base import * 9 | from .input_source import * 10 | 11 | from pkgutil import iter_modules 12 | import os 13 | import os.path 14 | 15 | __all__ = [] 16 | 17 | 18 | def global_import(name): 19 | p = __import__(name, globals(), locals(), level=1) 20 | lst = p.__all__ if '__all__' in dir(p) else [] 21 | del globals()[name] 22 | for k in lst: 23 | if not k.startswith('__'): 24 | globals()[k] = p.__dict__[k] 25 | __all__.append(k) 26 | 27 | 28 | _CURR_DIR = os.path.dirname(__file__) 29 | _SKIP = [] 30 | for _, module_name, _ in iter_modules( 31 | [_CURR_DIR]): 32 | srcpath = os.path.join(_CURR_DIR, module_name + '.py') 33 | if not os.path.isfile(srcpath): 34 | continue 35 | if module_name.startswith('_'): 36 | continue 37 | if module_name not in _SKIP: 38 | global_import(module_name) 39 | -------------------------------------------------------------------------------- /tensorpack/predict/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: __init__.py 3 | 4 | # https://github.com/celery/kombu/blob/7d13f9b95d0b50c94393b962e6def928511bfda6/kombu/__init__.py#L34-L36 5 | STATICA_HACK = True 6 | globals()['kcah_acitats'[::-1].upper()] = False 7 | if STATICA_HACK: 8 | from .base import * 9 | from .concurrency import * 10 | from .config import * 11 | from .dataset import * 12 | from .multigpu import * 13 | 14 | 15 | from pkgutil import iter_modules 16 | import os 17 | import os.path 18 | 19 | __all__ = [] 20 | 21 | 22 | def global_import(name): 23 | p = __import__(name, globals(), locals(), level=1) 24 | lst = p.__all__ if '__all__' in dir(p) else dir(p) 25 | if lst: 26 | del globals()[name] 27 | for k in lst: 28 | globals()[k] = p.__dict__[k] 29 | __all__.append(k) 30 | 31 | 32 | _CURR_DIR = os.path.dirname(__file__) 33 | for _, module_name, _ in iter_modules( 34 | [_CURR_DIR]): 35 | srcpath = os.path.join(_CURR_DIR, module_name + '.py') 36 | if not os.path.isfile(srcpath): 37 | continue 38 | if module_name.startswith('_'): 39 | continue 40 | global_import(module_name) 41 | -------------------------------------------------------------------------------- /tensorpack/utils/debug.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: debug.py 3 | 4 | 5 | import sys 6 | 7 | 8 | def enable_call_trace(): 9 | """ Enable trace for calls to any function. """ 10 | def tracer(frame, event, arg): 11 | if event == 'call': 12 | co = frame.f_code 13 | func_name = co.co_name 14 | if func_name == 'write' or func_name == 'print': 15 | # ignore write() calls from print statements 16 | return 17 | func_line_no = frame.f_lineno 18 | func_filename = co.co_filename 19 | caller = frame.f_back 20 | if caller: 21 | caller_line_no = caller.f_lineno 22 | caller_filename = caller.f_code.co_filename 23 | print('Call to `%s` on line %s:%s from %s:%s' % 24 | (func_name, func_filename, func_line_no, 25 | caller_filename, caller_line_no)) 26 | return 27 | sys.settrace(tracer) 28 | 29 | 30 | if __name__ == '__main__': 31 | enable_call_trace() 32 | 33 | def b(a): 34 | print(2) 35 | 36 | def a(): 37 | print(1) 38 | b(1) 39 | 40 | a() 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | train_log 2 | train_log_* 3 | logs 4 | *.npy 5 | *.npz 6 | *.caffemodel 7 | *.tfmodel 8 | *.meta 9 | *.log* 10 | *.bin 11 | *.png 12 | *.jpg 13 | checkpoint 14 | *.prototxt 15 | *.tgz 16 | *.gz 17 | 18 | 19 | 20 | 21 | # Byte-compiled / optimized / DLL files 22 | **/__pycache__/ 23 | *.py[cod] 24 | 25 | # C extensions 26 | *.so 27 | 28 | # Distribution / packaging 29 | .Python 30 | env/ 31 | build/ 32 | develop-eggs/ 33 | dist/ 34 | downloads/ 35 | eggs/ 36 | .eggs/ 37 | lib/ 38 | lib64/ 39 | parts/ 40 | sdist/ 41 | var/ 42 | *.egg-info/ 43 | .installed.cfg 44 | *.egg 45 | 46 | # PyInstaller 47 | # Usually these files are written by a python script from a template 48 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 49 | *.manifest 50 | *.spec 51 | 52 | # Installer logs 53 | pip-log.txt 54 | pip-delete-this-directory.txt 55 | 56 | # Unit test / coverage reports 57 | htmlcov/ 58 | .tox/ 59 | .coverage 60 | .coverage.* 61 | .cache 62 | nosetests.xml 63 | coverage.xml 64 | *,cover 65 | 66 | # Translations 67 | *.mo 68 | *.pot 69 | 70 | # Django stuff: 71 | *.log 72 | 73 | # Sphinx documentation 74 | docs/_build/ 75 | 76 | # PyBuilder 77 | target/ 78 | *.dat 79 | 80 | .idea/ 81 | -------------------------------------------------------------------------------- /tensorpack/dataflow/dataset/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: __init__.py 3 | 4 | # https://github.com/celery/kombu/blob/7d13f9b95d0b50c94393b962e6def928511bfda6/kombu/__init__.py#L34-L36 5 | STATICA_HACK = True 6 | globals()['kcah_acitats'[::-1].upper()] = False 7 | if STATICA_HACK: 8 | from .bsds500 import * 9 | from .cifar import * 10 | from .ilsvrc import * 11 | from .mnist import * 12 | from .svhn import * 13 | from .caltech101 import * 14 | 15 | from pkgutil import iter_modules 16 | import os 17 | import os.path 18 | 19 | __all__ = [] 20 | 21 | 22 | def global_import(name): 23 | p = __import__(name, globals(), locals(), level=1) 24 | lst = p.__all__ if '__all__' in dir(p) else dir(p) 25 | if lst: 26 | del globals()[name] 27 | for k in lst: 28 | if not k.startswith('__'): 29 | globals()[k] = p.__dict__[k] 30 | __all__.append(k) 31 | 32 | 33 | _CURR_DIR = os.path.dirname(__file__) 34 | for _, module_name, _ in iter_modules( 35 | [_CURR_DIR]): 36 | srcpath = os.path.join(_CURR_DIR, module_name + '.py') 37 | if not os.path.isfile(srcpath): 38 | continue 39 | if not module_name.startswith('_'): 40 | global_import(module_name) 41 | -------------------------------------------------------------------------------- /tensorpack/train/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: __init__.py 3 | # flake8: noqa 4 | 5 | # https://github.com/celery/kombu/blob/7d13f9b95d0b50c94393b962e6def928511bfda6/kombu/__init__.py#L34-L36 6 | STATICA_HACK = True 7 | globals()['kcah_acitats'[::-1].upper()] = False 8 | if STATICA_HACK: 9 | from .base import * 10 | from .config import * 11 | from .interface import * 12 | from .tower import * 13 | from .trainers import * 14 | 15 | 16 | from pkgutil import iter_modules 17 | import os 18 | import os.path 19 | 20 | __all__ = [] 21 | 22 | 23 | def global_import(name): 24 | p = __import__(name, globals(), locals(), level=1) 25 | lst = p.__all__ if '__all__' in dir(p) else [] 26 | if lst: 27 | del globals()[name] 28 | for k in lst: 29 | globals()[k] = p.__dict__[k] 30 | __all__.append(k) 31 | 32 | 33 | _CURR_DIR = os.path.dirname(__file__) 34 | _SKIP = ['utility'] 35 | for _, module_name, _ in iter_modules( 36 | [_CURR_DIR]): 37 | srcpath = os.path.join(_CURR_DIR, module_name + '.py') 38 | if not os.path.isfile(srcpath): 39 | continue 40 | if module_name.startswith('_'): 41 | continue 42 | if module_name not in _SKIP: 43 | global_import(module_name) 44 | -------------------------------------------------------------------------------- /tensorpack/graph_builder/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: __init__.py 3 | 4 | # https://github.com/celery/kombu/blob/7d13f9b95d0b50c94393b962e6def928511bfda6/kombu/__init__.py#L34-L36 5 | STATICA_HACK = True 6 | globals()['kcah_acitats'[::-1].upper()] = False 7 | if STATICA_HACK: 8 | from .model_desc import * 9 | from .training import * 10 | from .distributed import * 11 | from .utils import * 12 | 13 | from .model_desc import ModelDesc, ModelDescBase 14 | 15 | from pkgutil import iter_modules 16 | import os 17 | import os.path 18 | 19 | __all__ = [] 20 | 21 | def global_import(name): 22 | p = __import__(name, globals(), locals(), level=1) 23 | lst = p.__all__ if '__all__' in dir(p) else [] 24 | del globals()[name] 25 | for k in lst: 26 | if not k.startswith('__'): 27 | globals()[k] = p.__dict__[k] 28 | __all__.append(k) 29 | 30 | 31 | _CURR_DIR = os.path.dirname(__file__) 32 | _SKIP = ['distributed'] 33 | for _, module_name, _ in iter_modules( 34 | [_CURR_DIR]): 35 | srcpath = os.path.join(_CURR_DIR, module_name + '.py') 36 | if not os.path.isfile(srcpath): 37 | continue 38 | if module_name.startswith('_'): 39 | continue 40 | if module_name not in _SKIP: 41 | global_import(module_name) 42 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM 763104351884.dkr.ecr.us-west-2.amazonaws.com/tensorflow-training:2.12.0-gpu-py310-cu118-ubuntu20.04-ec2 2 | 3 | RUN pip3 install --upgrade pip 4 | RUN pip3 uninstall -y tensorflow-io 5 | 6 | RUN pip3 install nvidia-cudnn-cu11==8.9.2.26 7 | RUN pip3 install tensorflow-io==0.32.0 8 | 9 | ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib/python3.8/dist-packages/nvidia/cudnn/lib 10 | 11 | 12 | # Keeps Python from generating .pyc files in the container 13 | ENV PYTHONDONTWRITEBYTECODE=1 14 | 15 | # Turns off buffering for easier container logging 16 | ENV PYTHONUNBUFFERED=1 17 | 18 | # Install pip requirements 19 | COPY requirements.txt . 20 | RUN pip3 install -r requirements.txt 21 | 22 | WORKDIR /app 23 | COPY . /app 24 | 25 | RUN pip3 install -e /app 26 | 27 | # Creates a non-root user with an explicit UID and adds permission to access the /app folder 28 | # For more info, please refer to https://aka.ms/vscode-docker-python-configure-containers 29 | #RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app 30 | #USER appuser 31 | 32 | RUN pip3 install jupyterlab 33 | RUN pip3 install notebook 34 | 35 | # During debugging, this entry point will be overridden. For more information, please refer to https://aka.ms/vscode-docker-python-debug 36 | CMD ["python", "MaskRCNN/train.py"] -------------------------------------------------------------------------------- /tensorpack/models/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: utils.py 3 | 4 | import six 5 | 6 | 7 | class VariableHolder(object): 8 | """ A proxy to access variables defined in a layer. """ 9 | def __init__(self, **kwargs): 10 | """ 11 | Args: 12 | kwargs: {name:variable} 13 | """ 14 | self._vars = {} 15 | for k, v in six.iteritems(kwargs): 16 | self._add_variable(k, v) 17 | 18 | def _add_variable(self, name, var): 19 | assert name not in self._vars 20 | self._vars[name] = var 21 | 22 | def __setattr__(self, name, var): 23 | if not name.startswith('_'): 24 | self._add_variable(name, var) 25 | else: 26 | # private attributes 27 | super(VariableHolder, self).__setattr__(name, var) 28 | 29 | def __getattr__(self, name): 30 | return self._vars[name] 31 | 32 | def all(self): 33 | """ 34 | Returns: 35 | list of all variables 36 | """ 37 | return list(six.itervalues(self._vars)) 38 | 39 | 40 | try: 41 | # When BN is used as an activation, keras layers try to autograph.convert it 42 | # This leads to massive warnings so we disable it. 43 | from tensorflow.python.autograph.impl.api import do_not_convert as disable_autograph 44 | except ImportError: 45 | def disable_autograph(): 46 | return lambda x: x 47 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Docker: Python - General", 5 | "type": "docker", 6 | "request": "launch", 7 | "preLaunchTask": "docker-run: debug", 8 | "python": { 9 | "pathMappings": [ 10 | { 11 | "localRoot": "${workspaceFolder}", 12 | "remoteRoot": "/app" 13 | } 14 | ], 15 | "env": {"TF_DEVICE_MIN_SYS_MEMORY_IN_MB": "1024"}, 16 | "projectType": "general", 17 | "args": [ 18 | "--logdir", 19 | "/logs/maskrcnn-optimized/mask-rcnn-tensorflow", 20 | "--images_per_epoch", 21 | "120000", 22 | "--config", 23 | "MODE_MASK=True", 24 | "MODE_FPN=True", 25 | "DATA.BASEDIR=/data", 26 | "TRAIN.EVAL_PERIOD=1", 27 | "TRAIN.BATCH_SIZE_PER_GPU=2", 28 | "BACKBONE.WEIGHTS=/data/pretrained-models/ImageNet-R50-AlignPadding.npz", 29 | "BACKBONE.NORM=FreezeBN", 30 | "TRAIN.GRADIENT_CLIP=0.36" 31 | ] 32 | } 33 | }, 34 | { 35 | "name": "Docker: Notebooks", 36 | "type": "docker", 37 | "request": "launch", 38 | "preLaunchTask": "docker-run: notebooks" 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /tensorpack/tfutils/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: __init__.py 3 | 4 | 5 | from .tower import get_current_tower_context, TowerContext 6 | 7 | # https://github.com/celery/kombu/blob/7d13f9b95d0b50c94393b962e6def928511bfda6/kombu/__init__.py#L34-L36 8 | STATICA_HACK = True 9 | globals()['kcah_acitats'[::-1].upper()] = False 10 | if STATICA_HACK: 11 | from .common import * 12 | from .sessinit import * 13 | from .argscope import * 14 | 15 | 16 | # don't want to include everything from .tower 17 | __all__ = ['get_current_tower_context', 'TowerContext'] 18 | 19 | 20 | def _global_import(name): 21 | p = __import__(name, globals(), None, level=1) 22 | lst = p.__all__ if '__all__' in dir(p) else dir(p) 23 | for k in lst: 24 | if not k.startswith('__'): 25 | globals()[k] = p.__dict__[k] 26 | __all__.append(k) 27 | 28 | 29 | _TO_IMPORT = frozenset([ 30 | 'common', 31 | 'sessinit', 32 | 'argscope', 33 | ]) 34 | 35 | for module_name in _TO_IMPORT: 36 | _global_import(module_name) 37 | 38 | """ 39 | TODO remove this line in the future. 40 | Better to keep submodule names (sesscreate, varmanip, etc) out of __all__, 41 | so that these names will be invisible under `tensorpack.` namespace. 42 | 43 | To use these utilities, users are expected to import them explicitly, e.g.: 44 | 45 | import tensorpack.tfutils.sessinit as sessinit 46 | """ 47 | __all__.extend(['sessinit', 'summary', 'optimizer', 48 | 'sesscreate', 'gradproc', 'varreplace', 49 | 'tower']) 50 | -------------------------------------------------------------------------------- /tensorpack/models/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: __init__.py 3 | 4 | # https://github.com/celery/kombu/blob/7d13f9b95d0b50c94393b962e6def928511bfda6/kombu/__init__.py#L34-L36 5 | STATICA_HACK = True 6 | globals()['kcah_acitats'[::-1].upper()] = False 7 | if STATICA_HACK: 8 | from .batch_norm import * 9 | from .common import * 10 | from .conv2d import * 11 | from .fc import * 12 | from .layer_norm import * 13 | from .linearwrap import * 14 | from .nonlin import * 15 | from .pool import * 16 | from .regularize import * 17 | 18 | 19 | from pkgutil import iter_modules 20 | import os 21 | import os.path 22 | # this line is necessary for _TFModuleFunc to work 23 | import tensorflow as tf # noqa: F401 24 | 25 | __all__ = [] 26 | 27 | 28 | def _global_import(name): 29 | p = __import__(name, globals(), locals(), level=1) 30 | lst = p.__all__ if '__all__' in dir(p) else dir(p) 31 | del globals()[name] 32 | for k in lst: 33 | if not k.startswith('__'): 34 | globals()[k] = p.__dict__[k] 35 | __all__.append(k) 36 | 37 | 38 | _CURR_DIR = os.path.dirname(__file__) 39 | _SKIP = ['utils', 'registry', 'tflayer'] 40 | for _, module_name, _ in iter_modules( 41 | [_CURR_DIR]): 42 | srcpath = os.path.join(_CURR_DIR, module_name + '.py') 43 | if not os.path.isfile(srcpath): 44 | continue 45 | if module_name.startswith('_'): 46 | continue 47 | if "_test" in module_name: 48 | continue 49 | if module_name not in _SKIP: 50 | _global_import(module_name) 51 | -------------------------------------------------------------------------------- /tensorpack/callbacks/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: __init__.py 3 | 4 | 5 | # https://github.com/celery/kombu/blob/7d13f9b95d0b50c94393b962e6def928511bfda6/kombu/__init__.py#L34-L36 6 | STATICA_HACK = True 7 | globals()['kcah_acitats'[::-1].upper()] = False 8 | if STATICA_HACK: 9 | from .base import * 10 | from .concurrency import * 11 | from .graph import * 12 | from .group import * 13 | from .hooks import * 14 | from .inference import * 15 | from .inference_runner import * 16 | from .monitor import * 17 | from .param import * 18 | from .prof import * 19 | from .saver import * 20 | from .misc import * 21 | from .steps import * 22 | from .summary import * 23 | from .trigger import * 24 | 25 | 26 | from pkgutil import iter_modules 27 | import os 28 | 29 | 30 | __all__ = [] 31 | 32 | 33 | def _global_import(name): 34 | p = __import__(name, globals(), locals(), level=1) 35 | lst = p.__all__ if '__all__' in dir(p) else dir(p) 36 | if lst: 37 | del globals()[name] 38 | for k in lst: 39 | if not k.startswith('__'): 40 | globals()[k] = p.__dict__[k] 41 | __all__.append(k) 42 | 43 | 44 | _CURR_DIR = os.path.dirname(__file__) 45 | for _, module_name, _ in iter_modules( 46 | [_CURR_DIR]): 47 | srcpath = os.path.join(_CURR_DIR, module_name + '.py') 48 | if not os.path.isfile(srcpath): 49 | continue 50 | if module_name.endswith('_test'): 51 | continue 52 | if not module_name.startswith('_'): 53 | _global_import(module_name) 54 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "docker-build", 6 | "label": "docker-build", 7 | "dockerBuild": { 8 | "tag": "mask-rcnn-tensorflow:latest", 9 | "dockerfile": "${workspaceFolder}/Dockerfile", 10 | "context": "${workspaceFolder}", 11 | "pull": true 12 | } 13 | }, 14 | { 15 | "type": "docker-run", 16 | "label": "docker-run: debug", 17 | "dependsOn": [ 18 | "docker-build" 19 | ], 20 | "python": { 21 | "file": "MaskRCNN/train.py" 22 | }, 23 | "dockerRun": { 24 | "image": "mask-rcnn-tensorflow:latest", 25 | "customOptions": "--gpus=all", 26 | "remove": true, 27 | "volumes": [ 28 | { 29 | "localPath": "/home/ubuntu/efs/mask-rcnn/data", 30 | "containerPath": "/data" 31 | }, 32 | { 33 | "localPath": "/home/ubuntu/efs/logs", 34 | "containerPath": "/logs" 35 | } 36 | ] 37 | } 38 | }, 39 | { 40 | "type": "docker-run", 41 | "label": "docker-run: notebooks", 42 | "dependsOn": ["docker-build"], 43 | "dockerRun": { 44 | "image": "mask-rcnn-tensorflow:latest", 45 | "customOptions": "--gpus=all", 46 | "remove": true, 47 | "command": "nohup jupyter-lab --no-browser --ip=0.0.0.0 --allow-root", 48 | "ports": [ {"hostPort": 8888, "containerPort": 8888, "protocol": "tcp"}], 49 | "volumes": [ 50 | { 51 | "localPath": "/home/ubuntu/efs/mask-rcnn/data", 52 | "containerPath": "/data" 53 | }, 54 | { 55 | "localPath": "/home/ubuntu/efs/logs", 56 | "containerPath": "/logs" 57 | } 58 | ] 59 | } 60 | } 61 | ] 62 | } -------------------------------------------------------------------------------- /tensorpack/dataflow/imgaug/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: __init__.py 3 | 4 | # https://github.com/celery/kombu/blob/7d13f9b95d0b50c94393b962e6def928511bfda6/kombu/__init__.py#L34-L36 5 | STATICA_HACK = True 6 | globals()['kcah_acitats'[::-1].upper()] = False 7 | if STATICA_HACK: 8 | from .base import * 9 | from .convert import * 10 | from .crop import * 11 | from .deform import * 12 | from .geometry import * 13 | from .imgproc import * 14 | from .meta import * 15 | from .misc import * 16 | from .noise import * 17 | from .paste import * 18 | from .transform import * 19 | from .external import * 20 | 21 | 22 | import os 23 | from pkgutil import iter_modules 24 | 25 | __all__ = [] 26 | 27 | 28 | def global_import(name): 29 | p = __import__(name, globals(), locals(), level=1) 30 | lst = p.__all__ if '__all__' in dir(p) else dir(p) 31 | if lst: 32 | del globals()[name] 33 | for k in lst: 34 | if not k.startswith('__'): 35 | globals()[k] = p.__dict__[k] 36 | __all__.append(k) 37 | 38 | 39 | try: 40 | import cv2 # noqa 41 | except ImportError: 42 | from ...utils import logger 43 | logger.warn("Cannot import 'cv2', therefore image augmentation is not available.") 44 | else: 45 | _CURR_DIR = os.path.dirname(__file__) 46 | for _, module_name, _ in iter_modules( 47 | [os.path.dirname(__file__)]): 48 | srcpath = os.path.join(_CURR_DIR, module_name + '.py') 49 | if not os.path.isfile(srcpath): 50 | continue 51 | if not module_name.startswith('_') and "_test" not in module_name: 52 | global_import(module_name) 53 | -------------------------------------------------------------------------------- /tensorpack/tfutils/distributed.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: distributed.py 3 | 4 | 5 | import tensorflow as tf 6 | 7 | 8 | def get_distributed_session_creator(server): 9 | """ 10 | Args: 11 | server (tf.train.Server): 12 | 13 | Returns: 14 | tf.train.SessionCreator 15 | """ 16 | 17 | server_def = server.server_def 18 | is_chief = (server_def.job_name == 'worker') and (server_def.task_index == 0) 19 | 20 | init_op = tf.global_variables_initializer() 21 | local_init_op = tf.local_variables_initializer() 22 | ready_op = tf.report_uninitialized_variables() 23 | ready_for_local_init_op = tf.report_uninitialized_variables(tf.global_variables()) 24 | sm = tf.train.SessionManager( 25 | local_init_op=local_init_op, 26 | ready_op=ready_op, 27 | ready_for_local_init_op=ready_for_local_init_op, 28 | graph=tf.compat.v1.get_default_graph()) 29 | 30 | # to debug wrong variable collection 31 | # from pprint import pprint 32 | # print("GLOBAL:") 33 | # pprint([(k.name, k.device) for k in tf.global_variables()]) 34 | # print("LOCAL:") 35 | # pprint([(k.name, k.device) for k in tf.local_variables()]) 36 | 37 | class _Creator(tf.train.SessionCreator): 38 | def create_session(self): 39 | if is_chief: 40 | return sm.prepare_session(master=server.target, init_op=init_op) 41 | else: 42 | tf.math.logging.set_verbosity(tf.math.logging.INFO) # print message about uninitialized vars 43 | ret = sm.wait_for_session(master=server.target) 44 | tf.math.logging.set_verbosity(tf.math.logging.WARN) 45 | return ret 46 | 47 | return _Creator() 48 | -------------------------------------------------------------------------------- /tensorpack/dataflow/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: __init__.py 3 | 4 | # https://github.com/celery/kombu/blob/7d13f9b95d0b50c94393b962e6def928511bfda6/kombu/__init__.py#L34-L36 5 | STATICA_HACK = True 6 | globals()['kcah_acitats'[::-1].upper()] = False 7 | if STATICA_HACK: 8 | from .base import * 9 | from .common import * 10 | from .format import * 11 | from .image import * 12 | from .parallel_map import * 13 | from .parallel import * 14 | from .raw import * 15 | from .remote import * 16 | from .serialize import * 17 | from . import imgaug 18 | from . import dataset 19 | 20 | 21 | from pkgutil import iter_modules 22 | import os 23 | import os.path 24 | from ..utils.develop import LazyLoader 25 | 26 | __all__ = [] 27 | 28 | 29 | def _global_import(name): 30 | p = __import__(name, globals(), locals(), level=1) 31 | lst = p.__all__ if '__all__' in dir(p) else dir(p) 32 | if lst: 33 | globals().pop(name, None) 34 | for k in lst: 35 | if not k.startswith('__'): 36 | globals()[k] = p.__dict__[k] 37 | __all__.append(k) 38 | 39 | 40 | __SKIP = set(['dataset', 'imgaug']) 41 | _CURR_DIR = os.path.dirname(__file__) 42 | for _, module_name, __ in iter_modules( 43 | [os.path.dirname(__file__)]): 44 | srcpath = os.path.join(_CURR_DIR, module_name + '.py') 45 | if not os.path.isfile(srcpath): 46 | continue 47 | if "_test" not in module_name and \ 48 | not module_name.startswith('_') and \ 49 | module_name not in __SKIP: 50 | _global_import(module_name) 51 | 52 | 53 | globals()['dataset'] = LazyLoader('dataset', globals(), __name__ + '.dataset') 54 | globals()['imgaug'] = LazyLoader('imgaug', globals(), __name__ + '.imgaug') 55 | 56 | del LazyLoader 57 | 58 | __all__.extend(['imgaug', 'dataset']) 59 | -------------------------------------------------------------------------------- /tensorpack/callbacks/prof.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: prof.py 3 | 4 | 5 | from ..utils.timer import Timer 6 | from .base import Callback 7 | 8 | __all__ = ['ThroughputTracker'] 9 | 10 | 11 | class ThroughputTracker(Callback): 12 | """ 13 | This callback writes the training throughput (in terms of either steps/sec, or samples/sec) 14 | to the monitors everytime it is triggered. 15 | The throughput is computed based on the duration between the consecutive triggers. 16 | 17 | The time spent on callbacks after each epoch is excluded. 18 | """ 19 | 20 | _chief_only = False 21 | 22 | def __init__(self, samples_per_step=None): 23 | """ 24 | Args: 25 | samples_per_step (int or None): total number of samples processed in each step 26 | (i.e., your total batch size in each step). 27 | If not provided, this callback will record "steps/sec" instead of "samples/sec". 28 | """ 29 | if samples_per_step is not None: 30 | samples_per_step = int(samples_per_step) 31 | self._samples_per_step = samples_per_step 32 | self._timer = Timer() 33 | self._timer.pause() 34 | 35 | # only include the time between before_epoch/after_epoch 36 | def _before_epoch(self): 37 | self._timer.resume() 38 | 39 | def _after_epoch(self): 40 | self._timer.pause() 41 | 42 | def _before_train(self): 43 | self._update_last() 44 | 45 | def _update_last(self): 46 | old_pause = self._timer.is_paused() 47 | self._timer.reset() 48 | if old_pause: 49 | self._timer.pause() 50 | self._last_step = self.global_step 51 | 52 | def _trigger(self): 53 | steps_per_sec = (self.global_step - self._last_step) / self._timer.seconds() 54 | self._update_last() 55 | 56 | if self._samples_per_step is None: 57 | self.trainer.monitors.put_scalar("Throughput (steps/sec)", steps_per_sec) 58 | else: 59 | self.trainer.monitors.put_scalar("Throughput (samples/sec)", steps_per_sec * self._samples_per_step) 60 | -------------------------------------------------------------------------------- /tensorpack/callbacks/concurrency.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: concurrency.py 3 | 4 | import multiprocessing as mp 5 | 6 | from ..utils import logger 7 | from ..utils.concurrency import StoppableThread, start_proc_mask_signal 8 | from .base import Callback 9 | 10 | __all__ = ['StartProcOrThread'] 11 | 12 | 13 | class StartProcOrThread(Callback): 14 | """ 15 | Start some threads or processes before training. 16 | """ 17 | 18 | _chief_only = False 19 | 20 | def __init__(self, startable, stop_at_last=True): 21 | """ 22 | Args: 23 | startable (list): list of processes or threads which have ``start()`` method. 24 | Can also be a single instance of process of thread. 25 | stop_at_last (bool): whether to stop the processes or threads 26 | after training. It will use :meth:`Process.terminate()` or 27 | :meth:`StoppableThread.stop()`, but will do nothing on normal 28 | ``threading.Thread`` or other startable objects. 29 | """ 30 | if not isinstance(startable, list): 31 | startable = [startable] 32 | self._procs_threads = startable 33 | self._stop_at_last = stop_at_last 34 | 35 | def _before_train(self): 36 | logger.info("Starting " + 37 | ', '.join([k.name for k in self._procs_threads]) + ' ...') 38 | # avoid sigint get handled by other processes 39 | start_proc_mask_signal(self._procs_threads) 40 | 41 | def _after_train(self): 42 | if not self._stop_at_last: 43 | return 44 | for k in self._procs_threads: 45 | if not k.is_alive(): 46 | continue 47 | if isinstance(k, mp.Process): 48 | logger.info("Stopping {} ...".format(k.name)) 49 | k.terminate() 50 | k.join(5.0) 51 | if k.is_alive(): 52 | logger.error("Cannot join process {}.".format(k.name)) 53 | elif isinstance(k, StoppableThread): 54 | logger.info("Stopping {} ...".format(k.name)) 55 | k.stop() 56 | k.join(5.0) 57 | if k.is_alive(): 58 | logger.error("Cannot join thread {}.".format(k.name)) 59 | -------------------------------------------------------------------------------- /tensorpack/tfutils/symbolic_functions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: symbolic_functions.py 3 | 4 | 5 | import tensorflow as tf 6 | 7 | from ..compat import tfv1 8 | 9 | __all__ = ['print_stat', 'rms'] 10 | 11 | 12 | def print_stat(x, message=None): 13 | """ A simple print Op that might be easier to use than :meth:`tf.Print`. 14 | Use it like: ``x = print_stat(x, message='This is x')``. 15 | """ 16 | if message is None: 17 | message = x.op.name 18 | lst = [tf.shape(x), tf.reduce_mean(x)] 19 | if x.dtype.is_floating: 20 | lst.append(rms(x)) 21 | return tf.Print(x, lst + [x], summarize=20, 22 | message=message, name='print_' + x.op.name) 23 | 24 | 25 | # for internal use only 26 | def rms(x, name=None): 27 | """ 28 | Returns: 29 | root mean square of tensor x. 30 | """ 31 | if name is None: 32 | name = x.op.name + '/rms' 33 | with tfv1.name_scope(None): # name already contains the scope 34 | return tf.sqrt(tf.reduce_mean(tf.square(x)), name=name) 35 | return tf.sqrt(tf.reduce_mean(tf.square(x)), name=name) 36 | 37 | 38 | # don't hurt to leave it here 39 | def psnr(prediction, ground_truth, maxp=None, name='psnr'): 40 | """`Peak Signal to Noise Ratio `_. 41 | 42 | .. math:: 43 | 44 | PSNR = 20 \cdot \log_{10}(MAX_p) - 10 \cdot \log_{10}(MSE) 45 | 46 | Args: 47 | prediction: a :class:`tf.Tensor` representing the prediction signal. 48 | ground_truth: another :class:`tf.Tensor` with the same shape. 49 | maxp: maximum possible pixel value of the image (255 in in 8bit images) 50 | 51 | Returns: 52 | A scalar tensor representing the PSNR 53 | """ 54 | 55 | maxp = float(maxp) 56 | 57 | def log10(x): 58 | with tf.name_scope("log10"): 59 | numerator = tf.math.log(x) 60 | denominator = tf.math.log(tf.constant(10, dtype=numerator.dtype)) 61 | return numerator / denominator 62 | 63 | mse = tf.reduce_mean(tf.square(prediction - ground_truth)) 64 | if maxp is None: 65 | psnr = tf.multiply(log10(mse), -10., name=name) 66 | else: 67 | psnr = tf.multiply(log10(mse), -10.) 68 | psnr = tf.add(tf.multiply(20., log10(maxp)), psnr, name=name) 69 | 70 | return psnr 71 | -------------------------------------------------------------------------------- /MaskRCNN/model/fpn.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | import numpy as np 5 | import tensorflow as tf 6 | 7 | from tensorpack.models import Conv2D, FixedUnPooling, MaxPooling, layer_register 8 | from tensorpack.tfutils.argscope import argscope 9 | 10 | from model.backbone import GroupNorm 11 | from config import config as cfg 12 | 13 | @layer_register(log_shape=True) 14 | def fpn_model(features, seed_gen): 15 | """ 16 | Args: 17 | features ([tf.Tensor]): ResNet features c2-c5 18 | 19 | Returns: 20 | [tf.Tensor]: FPN features p2-p6 21 | """ 22 | assert len(features) == 4, features 23 | num_channel = cfg.FPN.NUM_CHANNEL 24 | 25 | use_gn = cfg.FPN.NORM == 'GN' 26 | 27 | def upsample2x(name, x): 28 | dtype_str = 'float32' 29 | return FixedUnPooling( 30 | name, x, 2, unpool_mat=np.ones((2, 2), dtype=dtype_str), 31 | data_format='channels_first' if cfg.TRAIN.FPN_NCHW else 'channels_last') 32 | 33 | with argscope(Conv2D, data_format='channels_first' if cfg.TRAIN.FPN_NCHW else 'channels_last', 34 | activation=tf.identity, use_bias=True, 35 | kernel_initializer=tf.keras.initializers.VarianceScaling(scale=1., seed=seed_gen.next())): 36 | lat_2345 = [Conv2D('lateral_1x1_c{}'.format(i + 2), c, num_channel, 1, seed=seed_gen.next()) 37 | for i, c in enumerate(features)] 38 | if use_gn: 39 | lat_2345 = [GroupNorm('gn_c{}'.format(i + 2), c) for i, c in enumerate(lat_2345)] 40 | lat_sum_5432 = [] 41 | for idx, lat in enumerate(lat_2345[::-1]): 42 | if idx == 0: 43 | lat_sum_5432.append(lat) 44 | else: 45 | lat = lat + upsample2x('upsample_lat{}'.format(6 - idx), lat_sum_5432[-1]) 46 | lat_sum_5432.append(lat) 47 | p2345 = [Conv2D('posthoc_3x3_p{}'.format(i + 2), c, num_channel, 3, seed=seed_gen.next()) 48 | for i, c in enumerate(lat_sum_5432[::-1])] 49 | if use_gn: 50 | p2345 = [GroupNorm('gn_p{}'.format(i + 2), c) for i, c in enumerate(p2345)] 51 | p6 = MaxPooling('maxpool_p6', p2345[-1], pool_size=1, strides=2, data_format='channels_first' if cfg.TRAIN.FPN_NCHW else 'channels_last', padding='VALID') 52 | 53 | return p2345 + [p6] 54 | -------------------------------------------------------------------------------- /tensorpack/models/fc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: fc.py 3 | 4 | 5 | import numpy as np 6 | from ..compat import tfv1 as tf # this should be avoided first in model code 7 | 8 | from .common import VariableHolder, layer_register 9 | from .tflayer import convert_to_tflayer_args, rename_get_variable 10 | 11 | __all__ = ['FullyConnected'] 12 | 13 | 14 | def batch_flatten(x): 15 | """ 16 | Flatten the tensor except the first dimension. 17 | """ 18 | shape = x.get_shape().as_list()[1:] 19 | if None not in shape: 20 | return tf.reshape(x, [-1, int(np.prod(shape))]) 21 | return tf.reshape(x, tf.stack([tf.shape(x)[0], -1])) 22 | 23 | 24 | @layer_register(log_shape=True) 25 | @convert_to_tflayer_args( 26 | args_names=['units'], 27 | name_mapping={'out_dim': 'units'}) 28 | def FullyConnected( 29 | inputs, 30 | units, 31 | activation=None, 32 | use_bias=True, 33 | kernel_initializer=None, 34 | bias_initializer=tf.zeros_initializer(), 35 | kernel_regularizer=None, 36 | bias_regularizer=None, 37 | activity_regularizer=None, 38 | seed=None): 39 | """ 40 | A wrapper around `tf.layers.Dense`. 41 | One difference to maintain backward-compatibility: 42 | Default weight initializer is variance_scaling_initializer(2.0). 43 | 44 | Variable Names: 45 | 46 | * ``W``: weights of shape [in_dim, out_dim] 47 | * ``b``: bias 48 | """ 49 | if kernel_initializer is None: 50 | kernel_initializer = tf.keras.initializers.VarianceScaling(2.0, distribution='untruncated_normal', seed=seed) 51 | 52 | inputs = batch_flatten(inputs) 53 | with rename_get_variable({'kernel': 'W', 'bias': 'b'}): 54 | layer = tf.layers.Dense( 55 | units=units, 56 | activation=activation, 57 | use_bias=use_bias, 58 | kernel_initializer=kernel_initializer, 59 | bias_initializer=bias_initializer, 60 | kernel_regularizer=kernel_regularizer, 61 | bias_regularizer=bias_regularizer, 62 | activity_regularizer=activity_regularizer, 63 | _reuse=tf.get_variable_scope().reuse) 64 | ret = layer(inputs, scope=tf.get_variable_scope()) 65 | ret = tf.identity(ret, name='output') 66 | 67 | ret.variables = VariableHolder(W=layer.kernel) 68 | if use_bias: 69 | ret.variables.b = layer.bias 70 | return ret 71 | -------------------------------------------------------------------------------- /tensorpack/dataflow/imgaug/convert.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: convert.py 3 | 4 | import numpy as np 5 | import cv2 6 | 7 | from .base import PhotometricAugmentor 8 | 9 | __all__ = ['ColorSpace', 'Grayscale', 'ToUint8', 'ToFloat32'] 10 | 11 | 12 | class ColorSpace(PhotometricAugmentor): 13 | """ Convert into another color space. """ 14 | 15 | def __init__(self, mode, keepdims=True): 16 | """ 17 | Args: 18 | mode: OpenCV color space conversion code (e.g., ``cv2.COLOR_BGR2HSV``) 19 | keepdims (bool): keep the dimension of image unchanged if OpenCV 20 | changes it. 21 | """ 22 | super(ColorSpace, self).__init__() 23 | self._init(locals()) 24 | 25 | def _augment(self, img, _): 26 | transf = cv2.cvtColor(img, self.mode) 27 | if self.keepdims: 28 | if len(transf.shape) is not len(img.shape): 29 | transf = transf[..., None] 30 | return transf 31 | 32 | 33 | class Grayscale(ColorSpace): 34 | """ Convert RGB or BGR image to grayscale. """ 35 | 36 | def __init__(self, keepdims=True, rgb=False, keepshape=False): 37 | """ 38 | Args: 39 | keepdims (bool): return image of shape [H, W, 1] instead of [H, W] 40 | rgb (bool): interpret input as RGB instead of the default BGR 41 | keepshape (bool): whether to duplicate the gray image into 3 channels 42 | so the result has the same shape as input. 43 | """ 44 | mode = cv2.COLOR_RGB2GRAY if rgb else cv2.COLOR_BGR2GRAY 45 | if keepshape: 46 | assert keepdims, "keepdims must be True when keepshape==True" 47 | super(Grayscale, self).__init__(mode, keepdims) 48 | self.keepshape = keepshape 49 | self.rgb = rgb 50 | 51 | def _augment(self, img, _): 52 | ret = super()._augment(img, _) 53 | if self.keepshape: 54 | return np.concatenate([ret] * 3, axis=2) 55 | else: 56 | return ret 57 | 58 | 59 | class ToUint8(PhotometricAugmentor): 60 | """ Clip and convert image to uint8. Useful to reduce communication overhead. """ 61 | def _augment(self, img, _): 62 | return np.clip(img, 0, 255).astype(np.uint8) 63 | 64 | 65 | class ToFloat32(PhotometricAugmentor): 66 | """ Convert image to float32, may increase quality of the augmentor. """ 67 | def _augment(self, img, _): 68 | return img.astype(np.float32) 69 | -------------------------------------------------------------------------------- /tensorpack/models/shape_utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: shape_utils.py 3 | 4 | import tensorflow as tf 5 | 6 | __all__ = [] 7 | 8 | 9 | class StaticDynamicAxis(object): 10 | def __init__(self, static, dynamic): 11 | self.static = static 12 | self.dynamic = dynamic 13 | 14 | def apply(self, f): 15 | try: 16 | st = f(self.static) 17 | return StaticDynamicAxis(st, st) 18 | except TypeError: 19 | return StaticDynamicAxis(None, f(self.dynamic)) 20 | 21 | def __str__(self): 22 | return "S={}, D={}".format(str(self.static), str(self.dynamic)) 23 | 24 | 25 | def DynamicLazyAxis(shape, idx): 26 | return lambda: shape[idx] 27 | 28 | 29 | def StaticLazyAxis(dim): 30 | return lambda: dim 31 | 32 | 33 | class StaticDynamicShape(object): 34 | def __init__(self, tensor): 35 | assert isinstance(tensor, tf.Tensor), tensor 36 | ndims = tensor.shape.ndims 37 | self.static = tensor.shape.as_list() 38 | if tensor.shape.is_fully_defined(): 39 | self.dynamic = self.static[:] 40 | else: 41 | dynamic = tf.shape(tensor) 42 | self.dynamic = [DynamicLazyAxis(dynamic, k) for k in range(ndims)] 43 | 44 | for k in range(ndims): 45 | if self.static[k] is not None: 46 | self.dynamic[k] = StaticLazyAxis(self.static[k]) 47 | 48 | def apply(self, axis, f): 49 | if self.static[axis] is not None: 50 | try: 51 | st = f(self.static[axis]) 52 | self.static[axis] = st 53 | self.dynamic[axis] = StaticLazyAxis(st) 54 | return 55 | except TypeError: 56 | pass 57 | self.static[axis] = None 58 | dyn = self.dynamic[axis] 59 | self.dynamic[axis] = lambda: f(dyn()) 60 | 61 | def get_static(self): 62 | return self.static 63 | 64 | @property 65 | def ndims(self): 66 | return len(self.static) 67 | 68 | def get_dynamic(self, axis=None): 69 | if axis is None: 70 | return [self.dynamic[k]() for k in range(self.ndims)] 71 | return self.dynamic[axis]() 72 | 73 | 74 | if __name__ == '__main__': 75 | x = tfv1.placeholder(tf.float32, shape=[None, 3, None, 10]) 76 | shape = StaticDynamicShape(x) 77 | shape.apply(1, lambda x: x * 3) 78 | shape.apply(2, lambda x: x + 5) 79 | print(shape.get_static()) 80 | print(shape.get_dynamic()) 81 | -------------------------------------------------------------------------------- /MaskRCNN/utils/box_ops.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # -*- coding: utf-8 -*- 4 | # File: box_ops.py 5 | 6 | import tensorflow as tf 7 | 8 | from tensorpack.tfutils.scope_utils import under_name_scope 9 | 10 | 11 | """ 12 | This file is modified from 13 | https://github.com/tensorflow/models/blob/master/object_detection/core/box_list_ops.py 14 | """ 15 | 16 | 17 | @under_name_scope() 18 | def area(boxes): 19 | """ 20 | Args: 21 | boxes: BS X N X 4 (x1, y1, x2, y2) 22 | 23 | Returns: 24 | BS X N 25 | """ 26 | x_min, y_min, x_max, y_max = tf.split(boxes, 4, axis=-1) 27 | return (y_max - y_min) * (x_max - x_min) 28 | 29 | 30 | @under_name_scope() 31 | def pairwise_intersection(boxes1, boxes2): 32 | """Compute pairwise intersection areas between boxes. 33 | 34 | Args: 35 | boxes1: BS X N X 4 (x1, y1, x2, y2) 36 | boxes2: BS X M X 4 (x1, y1, x2, y2) 37 | 38 | Returns: 39 | a tensor with shape [BS X N X M] representing pairwise intersections 40 | """ 41 | x_min1, y_min1, x_max1, y_max1 = tf.split(boxes1, 4, axis=-1) 42 | x_min2, y_min2, x_max2, y_max2 = tf.split(boxes2, 4, axis=-1) 43 | all_pairs_min_ymax = tf.minimum(y_max1, tf.transpose(y_max2, perm=[0, 2, 1])) 44 | all_pairs_max_ymin = tf.maximum(y_min1, tf.transpose(y_min2, perm=[0, 2, 1])) 45 | intersect_heights = tf.maximum(0.0, all_pairs_min_ymax - all_pairs_max_ymin) 46 | all_pairs_min_xmax = tf.minimum(x_max1, tf.transpose(x_max2, perm=[0, 2, 1])) 47 | all_pairs_max_xmin = tf.maximum(x_min1, tf.transpose(x_min2, perm=[0, 2, 1])) 48 | intersect_widths = tf.maximum(0.0, all_pairs_min_xmax - all_pairs_max_xmin) 49 | return intersect_heights * intersect_widths 50 | 51 | 52 | @under_name_scope() 53 | def pairwise_iou(boxes1, boxes2): 54 | """Computes pairwise intersection-over-union between box collections. 55 | 56 | Args: 57 | boxes1: BS X N X 4 (x1, y1, x2, y2) 58 | boxes2: BS X M X 4 (x1, y1, x2, y2) 59 | 60 | Returns: 61 | a tensor with shape [BS, N, M] representing pairwise iou scores. 62 | """ 63 | intersections = pairwise_intersection(boxes1, boxes2) 64 | areas1 = area(boxes1) 65 | areas2 = area(boxes2) 66 | unions = (areas1 + tf.transpose(areas2, [0, 2, 1]) - intersections) 67 | return tf.where( 68 | tf.equal(intersections, 0.0), 69 | tf.zeros_like(intersections), tf.truediv(intersections, unions)) -------------------------------------------------------------------------------- /tensorpack/dataflow/imgaug/noise.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: noise.py 3 | 4 | 5 | import numpy as np 6 | import cv2 7 | 8 | from .base import PhotometricAugmentor 9 | 10 | __all__ = ['JpegNoise', 'GaussianNoise', 'SaltPepperNoise'] 11 | 12 | 13 | class JpegNoise(PhotometricAugmentor): 14 | """ Random JPEG noise. """ 15 | 16 | def __init__(self, quality_range=(40, 100)): 17 | """ 18 | Args: 19 | quality_range (tuple): range to sample JPEG quality 20 | """ 21 | super(JpegNoise, self).__init__() 22 | self._init(locals()) 23 | 24 | def _get_augment_params(self, img): 25 | return self.rng.randint(*self.quality_range) 26 | 27 | def _augment(self, img, q): 28 | enc = cv2.imencode('.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, q])[1] 29 | return cv2.imdecode(enc, 1).astype(img.dtype) 30 | 31 | 32 | class GaussianNoise(PhotometricAugmentor): 33 | """ 34 | Add random Gaussian noise N(0, sigma^2) of the same shape to img. 35 | """ 36 | def __init__(self, sigma=1, clip=True): 37 | """ 38 | Args: 39 | sigma (float): stddev of the Gaussian distribution. 40 | clip (bool): clip the result to [0,255] in the end. 41 | """ 42 | super(GaussianNoise, self).__init__() 43 | self._init(locals()) 44 | 45 | def _get_augment_params(self, img): 46 | return self.rng.randn(*img.shape) 47 | 48 | def _augment(self, img, noise): 49 | old_dtype = img.dtype 50 | ret = img + noise * self.sigma 51 | if self.clip or old_dtype == np.uint8: 52 | ret = np.clip(ret, 0, 255) 53 | return ret.astype(old_dtype) 54 | 55 | 56 | class SaltPepperNoise(PhotometricAugmentor): 57 | """ Salt and pepper noise. 58 | Randomly set some elements in image to 0 or 255, regardless of its channels. 59 | """ 60 | 61 | def __init__(self, white_prob=0.05, black_prob=0.05): 62 | """ 63 | Args: 64 | white_prob (float), black_prob (float): probabilities setting an element to 255 or 0. 65 | """ 66 | assert white_prob + black_prob <= 1, "Sum of probabilities cannot be greater than 1" 67 | super(SaltPepperNoise, self).__init__() 68 | self._init(locals()) 69 | 70 | def _get_augment_params(self, img): 71 | return self.rng.uniform(low=0, high=1, size=img.shape) 72 | 73 | def _augment(self, img, param): 74 | img[param > (1 - self.white_prob)] = 255 75 | img[param < self.black_prob] = 0 76 | return img 77 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from os import path 2 | import setuptools 3 | from setuptools import setup, find_packages 4 | 5 | version = int(setuptools.__version__.split('.')[0]) 6 | assert version > 30, "Tensorpack installation requires setuptools > 30" 7 | 8 | this_directory = path.abspath(path.dirname(__file__)) 9 | 10 | # setup metainfo 11 | libinfo_py = path.join(this_directory, 'tensorpack', 'libinfo.py') 12 | libinfo_content = open(libinfo_py, "r").readlines() 13 | version_line = [l.strip() for l in libinfo_content if l.startswith('__version__')][0] 14 | exec(version_line) # produce __version__ 15 | 16 | with open(path.join(this_directory, 'README.md'), 'rb') as f: 17 | long_description = f.read().decode('utf-8') 18 | 19 | 20 | def add_git_version(): 21 | 22 | def get_git_version(): 23 | from subprocess import check_output 24 | try: 25 | return check_output("git describe --tags --long --dirty".split()).decode('utf-8').strip() 26 | except Exception: 27 | return __version__ # noqa 28 | 29 | newlibinfo_content = [l for l in libinfo_content if not l.startswith('__git_version__')] 30 | newlibinfo_content.append('__git_version__ = "{}"'.format(get_git_version())) 31 | with open(libinfo_py, "w") as f: 32 | f.write("".join(newlibinfo_content)) 33 | 34 | 35 | add_git_version() 36 | 37 | 38 | setup( 39 | name='tensorpack', 40 | author="TensorPack contributors", 41 | author_email="ppwwyyxxc@gmail.com", 42 | url="https://github.com/tensorpack/tensorpack", 43 | keywords="tensorflow, deep learning, neural network", 44 | license="Apache", 45 | 46 | version=__version__, # noqa 47 | description='A Neural Network Training Interface on TensorFlow', 48 | long_description=long_description, 49 | long_description_content_type='text/markdown', 50 | 51 | packages=find_packages(exclude=["examples", "tests"]), 52 | zip_safe=False, # dataset and __init__ use file 53 | 54 | install_requires=[ 55 | "numpy>=1.14", 56 | "six", 57 | "termcolor>=1.1", 58 | "tabulate>=0.7.7", 59 | "tqdm>4.29.0", 60 | "msgpack>=0.5.2", 61 | "msgpack-numpy>=0.4.4.2", 62 | "pyzmq>=16", 63 | "psutil>=5", 64 | ], 65 | tests_require=['flake8', 'scikit-image'], 66 | extras_require={ 67 | 'all': ['scipy', 'h5py>=2.1', 'lmdb>=0.92', 'matplotlib', 'scikit-learn'], 68 | 'all: "linux" in sys_platform': ['python-prctl'], 69 | }, 70 | 71 | # https://packaging.python.org/guides/distributing-packages-using-setuptools/#universal-wheels 72 | options={'bdist_wheel': {'universal': '1'}}, 73 | ) 74 | -------------------------------------------------------------------------------- /tensorpack/models/nonlin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: nonlin.py 3 | 4 | 5 | import tensorflow as tf 6 | 7 | from ..utils.develop import log_deprecated 8 | from ..compat import tfv1 9 | from .batch_norm import BatchNorm 10 | from .common import VariableHolder, layer_register 11 | from .utils import disable_autograph 12 | 13 | __all__ = ['Maxout', 'PReLU', 'BNReLU'] 14 | 15 | 16 | @layer_register(use_scope=None) 17 | def Maxout(x, num_unit): 18 | """ 19 | Maxout as in the paper `Maxout Networks `_. 20 | 21 | Args: 22 | x (tf.Tensor): a NHWC or NC tensor. Channel has to be known. 23 | num_unit (int): a int. Must be divisible by C. 24 | 25 | Returns: 26 | tf.Tensor: of shape NHW(C/num_unit) named ``output``. 27 | """ 28 | input_shape = x.get_shape().as_list() 29 | ndim = len(input_shape) 30 | assert ndim == 4 or ndim == 2 31 | ch = input_shape[-1] 32 | assert ch is not None and ch % num_unit == 0 33 | if ndim == 4: 34 | x = tf.reshape(x, [-1, input_shape[1], input_shape[2], ch / num_unit, num_unit]) 35 | else: 36 | x = tf.reshape(x, [-1, ch / num_unit, num_unit]) 37 | return tf.reduce_max(x, ndim, name='output') 38 | 39 | 40 | @layer_register() 41 | @disable_autograph() 42 | def PReLU(x, init=0.001, name=None): 43 | """ 44 | Parameterized ReLU as in the paper `Delving Deep into Rectifiers: Surpassing 45 | Human-Level Performance on ImageNet Classification 46 | `_. 47 | 48 | Args: 49 | x (tf.Tensor): input 50 | init (float): initial value for the learnable slope. 51 | name (str): deprecated argument. Don't use 52 | 53 | Variable Names: 54 | 55 | * ``alpha``: learnable slope. 56 | """ 57 | if name is not None: 58 | log_deprecated("PReLU(name=...)", "The output tensor will be named `output`.") 59 | init = tfv1.constant_initializer(init) 60 | alpha = tfv1.get_variable('alpha', [], initializer=init) 61 | x = ((1 + alpha) * x + (1 - alpha) * tf.abs(x)) 62 | ret = tf.multiply(x, 0.5, name=name or None) 63 | 64 | ret.variables = VariableHolder(alpha=alpha) 65 | return ret 66 | 67 | 68 | @layer_register(use_scope=None) 69 | def BNReLU(x, name=None): 70 | """ 71 | A shorthand of BatchNormalization + ReLU. 72 | 73 | Args: 74 | x (tf.Tensor): the input 75 | name: deprecated, don't use. 76 | """ 77 | if name is not None: 78 | log_deprecated("BNReLU(name=...)", "The output tensor will be named `output`.") 79 | 80 | x = BatchNorm('bn', x) 81 | x = tf.nn.relu(x, name=name) 82 | return x 83 | -------------------------------------------------------------------------------- /CODEBASE.md: -------------------------------------------------------------------------------- 1 | # Codebase Details 2 | 3 | For training, the codebase is broken into a few major parts: 4 | 5 | * `train.py` handles parsing the command line arguments, loading the model, and configuring and launching the training. 6 | * `config.py` holds various configurations needed by the model. 7 | * `dataset.py` handles a lot of COCO specific logic such as parsing annotations and computing evaluation metrics from a set of predictions. 8 | * `common.py`, `model_box.py`, `performance.py`, `viz.py` and `utils/` hold various utilities, mostly for either debugging or manipulating bounding boxes, anchors and segmentation masks. 9 | 10 | The core model has two components - the Tensorflow graph and the non-Tensorflow data pipelines implemented using Tensorpack DataFlows and numpy. 11 | 12 | * Code for the Tensorflow graph is in `model/`. 13 | - `model/generalized_rcnn.py` has a `DetectionModel` class whose `build_graph()` method outlines a generic two stage detector model. 14 | - `ResNetFPNModel` subclasses that and provides implementations of the backbone, rpn and roi_heads. `ResNetFPNModel.inputs()` uses `tfv1.placeholders` to define the input it expects from the dataflow 15 | * `data.py` holds the data pipeline logic. The key function for training is `get_batch_train_dataflow()`, which contains logic for reading data from disk and batching datapoints together. 16 | 17 | ## General Optimizations 18 | 19 | ### Adding batch dimension 20 | 21 | We added the ability have a per-GPU batch size greater than 1. Below are some implementation details of what that means: 22 | 23 | * In the data pipeline, we took the existing outputs (image, gt_label, gt_boxes, gt_masks) and added a batch dimension, padding when different images have different shapes. To account for this padding in the model, we added two new inputs, `orig_image_dims` and `orig_gt_counts` which are used to slice away padding when necessary. 24 | * The model only supports the [Feature Pyramid Network (FPN)](https://arxiv.org/pdf/1612.03144.pdf) option for the ResNet backbone. We do not support Cascade RCNN. 25 | * Region Proposal Network (RPN) proposals for each FPN level are generated with batch dimension. 26 | * ROI Align features are computed with batch dimension. 27 | 28 | ## AWS specific optimizations 29 | 30 | ### Using EFA for faster network communication 31 | 32 | Elastic Fabric Adapter (EFA) is a network interface for Amazon EC2 instances that enables customers to run applications requiring high levels of inter-node communications at scale on AWS. EFA works together with MPI (Message Passing Interface) and NCCL (NVIDIA Collective Communications Library) make all cross node communication faster. 33 | 34 | Reference: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/efa.html 35 | 36 | -------------------------------------------------------------------------------- /tensorpack/tfutils/dependency.py: -------------------------------------------------------------------------------- 1 | 2 | import tensorflow as tf 3 | 4 | from ..utils.argtools import graph_memoized 5 | 6 | """ 7 | Utils about parsing dependencies in the graph. 8 | """ 9 | 10 | __all__ = [ 11 | 'dependency_of_targets', 'dependency_of_fetches' 12 | ] 13 | 14 | 15 | @graph_memoized 16 | def dependency_of_targets(targets, op): 17 | """ 18 | Check that op is in the subgraph induced by the dependencies of targets. 19 | The result is memoized. 20 | 21 | This is useful if some SessionRunHooks should be run only together with certain ops. 22 | 23 | Args: 24 | targets: a tuple of ops or tensors. The targets to find dependencies of. 25 | op (tf.Operation or tf.Tensor): 26 | 27 | Returns: 28 | bool: True if any one of `targets` depend on `op`. 29 | """ 30 | # TODO tensorarray? sparsetensor? 31 | if isinstance(op, tf.Tensor): 32 | op = op.op 33 | assert isinstance(op, tf.Operation), op 34 | 35 | try: 36 | from tensorflow.contrib.graph_editor import get_backward_walk_ops # deprecated 37 | except ImportError: 38 | from tensorflow.python.ops.op_selector import get_backward_walk_ops 39 | # alternative implementation can use graph_util.extract_sub_graph 40 | dependent_ops = get_backward_walk_ops(targets, control_inputs=True) 41 | return op in dependent_ops 42 | 43 | 44 | def dependency_of_fetches(fetches, op): 45 | """ 46 | Check that op is in the subgraph induced by the dependencies of fetches. 47 | fetches may have more general structure. 48 | 49 | Args: 50 | fetches: An argument to `sess.run`. Nested structure will affect performance. 51 | op (tf.Operation or tf.Tensor): 52 | 53 | Returns: 54 | bool: True if any of `fetches` depend on `op`. 55 | """ 56 | try: 57 | from tensorflow.python.client.session import _FetchHandler as FetchHandler 58 | # use the graph of the op, so that this function can be called without being under a default graph 59 | handler = FetchHandler(op.graph, fetches, {}) 60 | targets = tuple(handler.fetches() + handler.targets()) 61 | except ImportError: 62 | if isinstance(fetches, list): 63 | targets = tuple(fetches) 64 | elif isinstance(fetches, dict): 65 | raise ValueError("Don't know how to parse dictionary to fetch list! " 66 | "This is a bug of tensorpack.") 67 | else: 68 | targets = (fetches, ) 69 | return dependency_of_targets(targets, op) 70 | 71 | 72 | if __name__ == '__main__': 73 | a = tf.random_normal(shape=[3, 3]) 74 | b = tf.random_normal(shape=[3, 3]) 75 | print(dependency_of_fetches(a, a)) 76 | print(dependency_of_fetches([a, b], a)) 77 | -------------------------------------------------------------------------------- /tensorpack/utils/gpu.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: gpu.py 3 | 4 | 5 | import os 6 | 7 | from . import logger 8 | from .concurrency import subproc_call 9 | from .nvml import NVMLContext 10 | from .utils import change_env 11 | 12 | __all__ = ['change_gpu', 'get_nr_gpu', 'get_num_gpu'] 13 | 14 | 15 | def change_gpu(val): 16 | """ 17 | Args: 18 | val: an integer, the index of the GPU or -1 to disable GPU. 19 | 20 | Returns: 21 | a context where ``CUDA_VISIBLE_DEVICES=val``. 22 | """ 23 | val = str(val) 24 | if val == '-1': 25 | val = '' 26 | return change_env('CUDA_VISIBLE_DEVICES', val) 27 | 28 | 29 | def get_num_gpu(): 30 | """ 31 | Returns: 32 | int: #available GPUs in CUDA_VISIBLE_DEVICES, or in the system. 33 | """ 34 | 35 | def warn_return(ret, message): 36 | try: 37 | import tensorflow as tf 38 | except ImportError: 39 | return ret 40 | 41 | built_with_cuda = tf.test.is_built_with_cuda() 42 | if not built_with_cuda and ret > 0: 43 | logger.warn(message + "But TensorFlow was not built with CUDA support and could not use GPUs!") 44 | return ret 45 | 46 | try: 47 | # Use NVML to query device properties 48 | with NVMLContext() as ctx: 49 | nvml_num_dev = ctx.num_devices() 50 | except Exception: 51 | nvml_num_dev = None 52 | 53 | env = os.environ.get('CUDA_VISIBLE_DEVICES', None) 54 | if env: 55 | num_dev = len(env.split(',')) 56 | assert num_dev <= nvml_num_dev, \ 57 | "Only {} GPU(s) available, but CUDA_VISIBLE_DEVICES is set to {}".format(nvml_num_dev, env) 58 | return warn_return(num_dev, "Found non-empty CUDA_VISIBLE_DEVICES. ") 59 | 60 | output, code = subproc_call("nvidia-smi -L", timeout=5) 61 | if code == 0: 62 | output = output.decode('utf-8') 63 | return warn_return(len(output.strip().split('\n')), "Found nvidia-smi. ") 64 | 65 | if nvml_num_dev is not None: 66 | return warn_return(nvml_num_dev, "NVML found nvidia devices. ") 67 | 68 | # Fallback to TF 69 | logger.info("Loading local devices by TensorFlow ...") 70 | 71 | try: 72 | import tensorflow as tf 73 | # available since TF 1.14 74 | gpu_devices = tf.config.experimental.list_physical_devices('GPU') 75 | except AttributeError: 76 | from tensorflow.python.client import device_lib 77 | local_device_protos = device_lib.list_local_devices() 78 | # Note this will initialize all GPUs and therefore has side effect 79 | # https://github.com/tensorflow/tensorflow/issues/8136 80 | gpu_devices = [x.name for x in local_device_protos if x.device_type == 'GPU'] 81 | return len(gpu_devices) 82 | 83 | 84 | get_nr_gpu = get_num_gpu 85 | -------------------------------------------------------------------------------- /tensorpack/predict/feedfree.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from tensorflow.python.training.monitored_session import _HookedSession as HookedSession 4 | 5 | from ..callbacks import Callbacks 6 | from ..tfutils.tower import PredictTowerContext 7 | from .base import PredictorBase 8 | 9 | __all__ = ['FeedfreePredictor'] 10 | 11 | 12 | class FeedfreePredictor(PredictorBase): 13 | """ 14 | Create a predictor that takes inputs from an :class:`InputSource`, instead of from feeds. 15 | An instance `pred` of :class:`FeedfreePredictor` can be called only by `pred()`, which returns 16 | a list of output values as defined in config.output_names. 17 | """ 18 | 19 | def __init__(self, config, input_source): 20 | """ 21 | Args: 22 | config (PredictConfig): the config to use. 23 | input_source (InputSource): the feedfree InputSource to use. 24 | Must match the signature of the tower function in config. 25 | """ 26 | self._config = config 27 | self._input_source = input_source 28 | assert config.return_input is False, \ 29 | "return_input is not supported in FeedfreePredictor! " \ 30 | "If you need to fetch inputs, add the names to the output_names!" 31 | 32 | self._hooks = [] 33 | self.graph = config._maybe_create_graph() 34 | with self.graph.as_default(): 35 | self._input_callbacks = Callbacks( 36 | self._input_source.setup(config.input_signature)) 37 | with PredictTowerContext(''): 38 | self._input_tensors = self._input_source.get_input_tensors() 39 | config.tower_func(*self._input_tensors) 40 | self._tower_handle = config.tower_func.towers[-1] 41 | 42 | self._output_tensors = self._tower_handle.get_tensors(config.output_names) 43 | 44 | self._input_callbacks.setup_graph(None) 45 | 46 | for h in self._input_callbacks.get_hooks(): 47 | self._register_hook(h) 48 | self._initialize_session() 49 | 50 | def _register_hook(self, hook): 51 | """ 52 | Args: 53 | hook (tf.train.SessionRunHook): 54 | """ 55 | self._hooks.append(hook) 56 | 57 | def _initialize_session(self): 58 | # init the session 59 | self._config.session_init._setup_graph() 60 | self._sess = self._config.session_creator.create_session() 61 | self._config.session_init._run_init(self._sess) 62 | 63 | with self._sess.as_default(): 64 | self._input_callbacks.before_train() 65 | self._hooked_sess = HookedSession(self._sess, self._hooks) 66 | 67 | def __call__(self): 68 | return self._hooked_sess.run(self._output_tensors) 69 | 70 | def _do_call(self): 71 | raise NotImplementedError("You're calling the wrong function!") 72 | -------------------------------------------------------------------------------- /tensorpack/callbacks/hooks.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: hooks.py 3 | 4 | 5 | """ Compatible layers between tf.train.SessionRunHook and Callback""" 6 | 7 | import tensorflow as tf 8 | 9 | from ..compat import tfv1 10 | from ..utils.develop import HIDE_DOC 11 | 12 | from .base import Callback 13 | 14 | __all__ = ['CallbackToHook', 'HookToCallback', 'TFLocalCLIDebugHook'] 15 | 16 | 17 | class CallbackToHook(tfv1.train.SessionRunHook): 18 | """ 19 | Hooks are less powerful than callbacks so the conversion is incomplete. 20 | It only converts the ``before_run/after_run`` calls. 21 | 22 | This is only for internal implementation of 23 | ``before_run/after_run`` callbacks. 24 | You shouldn't need to use this. 25 | """ 26 | 27 | def __init__(self, cb): 28 | self._cb = cb 29 | 30 | @HIDE_DOC 31 | def before_run(self, ctx): 32 | return self._cb.before_run(ctx) 33 | 34 | @HIDE_DOC 35 | def after_run(self, ctx, vals): 36 | self._cb.after_run(ctx, vals) 37 | 38 | 39 | class HookToCallback(Callback): 40 | """ 41 | Make a ``tf.train.SessionRunHook`` into a callback. 42 | Note that when ``SessionRunHook.after_create_session`` is called, the ``coord`` argument will be None. 43 | """ 44 | 45 | _chief_only = False 46 | 47 | def __init__(self, hook): 48 | """ 49 | Args: 50 | hook (tf.train.SessionRunHook): 51 | """ 52 | self._hook = hook 53 | 54 | def _setup_graph(self): 55 | with tf.name_scope(None): # jump out of the name scope 56 | self._hook.begin() 57 | 58 | def _before_train(self): 59 | sess = tf.get_default_session() 60 | # coord is set to None when converting 61 | self._hook.after_create_session(sess, None) 62 | 63 | def _before_run(self, ctx): 64 | return self._hook.before_run(ctx) 65 | 66 | def _after_run(self, ctx, run_values): 67 | self._hook.after_run(ctx, run_values) 68 | 69 | def _after_train(self): 70 | self._hook.end(self.trainer.sess) 71 | 72 | 73 | class TFLocalCLIDebugHook(HookToCallback): 74 | """ 75 | Use the hook `tfdbg.LocalCLIDebugHook` in tensorpack. 76 | """ 77 | 78 | _chief_only = True 79 | 80 | def __init__(self, *args, **kwargs): 81 | """ 82 | Args: 83 | args, kwargs: arguments to create `tfdbg.LocalCLIDebugHook`. 84 | Refer to tensorflow documentation for details. 85 | """ 86 | from tensorflow.python import debug as tfdbg 87 | super(TFLocalCLIDebugHook, self).__init__(tfdbg.LocalCLIDebugHook(*args, **kwargs)) 88 | 89 | def add_tensor_filter(self, *args, **kwargs): 90 | """ 91 | Wrapper of `tfdbg.LocalCLIDebugHook.add_tensor_filter`. 92 | Refer to tensorflow documentation for details. 93 | """ 94 | self._hook.add_tensor_filter(*args, **kwargs) 95 | -------------------------------------------------------------------------------- /tensorpack/dataflow/dataset/places.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | 3 | import os 4 | import numpy as np 5 | 6 | from ...utils import logger 7 | from ..base import RNGDataFlow 8 | 9 | 10 | class Places365Standard(RNGDataFlow): 11 | """ 12 | The Places365-Standard Dataset, in low resolution format only. 13 | Produces BGR images of shape (256, 256, 3) in range [0, 255]. 14 | """ 15 | def __init__(self, dir, name, shuffle=None): 16 | """ 17 | Args: 18 | dir: path to the Places365-Standard dataset in its "easy directory 19 | structure". See http://places2.csail.mit.edu/download.html 20 | name: one of "train" or "val" 21 | shuffle (bool): shuffle the dataset. Defaults to True if name=='train'. 22 | """ 23 | assert name in ['train', 'val'], name 24 | dir = os.path.expanduser(dir) 25 | assert os.path.isdir(dir), dir 26 | self.name = name 27 | if shuffle is None: 28 | shuffle = name == 'train' 29 | self.shuffle = shuffle 30 | 31 | label_file = os.path.join(dir, name + ".txt") 32 | all_files = [] 33 | labels = set() 34 | with open(label_file) as f: 35 | for line in f: 36 | filepath = os.path.join(dir, line.strip()) 37 | line = line.strip().split("/") 38 | label = line[1] 39 | all_files.append((filepath, label)) 40 | labels.add(label) 41 | self._labels = sorted(labels) 42 | # class ids are sorted alphabetically: 43 | # https://github.com/CSAILVision/places365/blob/master/categories_places365.txt 44 | labelmap = {label: id for id, label in enumerate(self._labels)} 45 | self._files = [(path, labelmap[x]) for path, x in all_files] 46 | logger.info("Found {} images in {}.".format(len(self._files), label_file)) 47 | 48 | def get_label_names(self): 49 | """ 50 | Returns: 51 | [str]: name of each class. 52 | """ 53 | return self._labels 54 | 55 | def __len__(self): 56 | return len(self._files) 57 | 58 | def __iter__(self): 59 | idxs = np.arange(len(self._files)) 60 | if self.shuffle: 61 | self.rng.shuffle(idxs) 62 | for k in idxs: 63 | fname, label = self._files[k] 64 | im = cv2.imread(fname, cv2.IMREAD_COLOR) 65 | assert im is not None, fname 66 | yield [im, label] 67 | 68 | 69 | try: 70 | import cv2 71 | except ImportError: 72 | from ...utils.develop import create_dummy_class 73 | Places365Standard = create_dummy_class('Places365Standard', 'cv2') # noqa 74 | 75 | if __name__ == '__main__': 76 | from tensorpack.dataflow import PrintData 77 | ds = Places365Standard("~/data/places365_standard/", 'train') 78 | ds = PrintData(ds, num=100) 79 | ds.reset_state() 80 | for k in ds: 81 | pass 82 | -------------------------------------------------------------------------------- /tensorpack/dataflow/dataset/caltech101.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: caltech101.py 3 | 4 | 5 | import os 6 | 7 | from ...utils import logger 8 | from ...utils.fs import download, get_dataset_path 9 | from ..base import RNGDataFlow 10 | 11 | __all__ = ["Caltech101Silhouettes"] 12 | 13 | 14 | def maybe_download(url, work_directory): 15 | """Download the data from Marlin's website, unless it's already here.""" 16 | filename = url.split("/")[-1] 17 | filepath = os.path.join(work_directory, filename) 18 | if not os.path.exists(filepath): 19 | logger.info("Downloading to {}...".format(filepath)) 20 | download(url, work_directory) 21 | return filepath 22 | 23 | 24 | class Caltech101Silhouettes(RNGDataFlow): 25 | """ 26 | Produces [image, label] in Caltech101 Silhouettes dataset, 27 | image is 28x28 in the range [0,1], label is an int in the range [0,100]. 28 | """ 29 | 30 | _DIR_NAME = "caltech101_data" 31 | _SOURCE_URL = "https://people.cs.umass.edu/~marlin/data/" 32 | 33 | def __init__(self, name, shuffle=True, dir=None): 34 | """ 35 | Args: 36 | name (str): 'train', 'test', 'val' 37 | shuffle (bool): shuffle the dataset 38 | """ 39 | if dir is None: 40 | dir = get_dataset_path(self._DIR_NAME) 41 | assert name in ['train', 'test', 'val'] 42 | self.name = name 43 | self.shuffle = shuffle 44 | 45 | def get_images_and_labels(data_file): 46 | f = maybe_download(self._SOURCE_URL + data_file, dir) 47 | data = scipy.io.loadmat(f) 48 | return data 49 | 50 | self.data = get_images_and_labels("caltech101_silhouettes_28_split1.mat") 51 | 52 | if self.name == "train": 53 | self.images = self.data["train_data"].reshape((4100, 28, 28)) 54 | self.labels = self.data["train_labels"].ravel() - 1 55 | elif self.name == "test": 56 | self.images = self.data["test_data"].reshape((2307, 28, 28)) 57 | self.labels = self.data["test_labels"].ravel() - 1 58 | else: 59 | self.images = self.data["val_data"].reshape((2264, 28, 28)) 60 | self.labels = self.data["val_labels"].ravel() - 1 61 | 62 | def __len__(self): 63 | return self.images.shape[0] 64 | 65 | def __iter__(self): 66 | idxs = list(range(self.__len__())) 67 | if self.shuffle: 68 | self.rng.shuffle(idxs) 69 | for k in idxs: 70 | img = self.images[k] 71 | label = self.labels[k] 72 | yield [img, label] 73 | 74 | 75 | try: 76 | import scipy.io 77 | except ImportError: 78 | from ...utils.develop import create_dummy_class 79 | Caltech101Silhouettes = create_dummy_class('Caltech101Silhouettes', 'scipy.io') # noqa 80 | 81 | 82 | if __name__ == "__main__": 83 | ds = Caltech101Silhouettes("train") 84 | ds.reset_state() 85 | for _ in ds: 86 | from IPython import embed 87 | 88 | embed() 89 | break 90 | -------------------------------------------------------------------------------- /tensorpack/dataflow/dataset/svhn.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: svhn.py 3 | 4 | 5 | import numpy as np 6 | import os 7 | 8 | from ...utils import logger 9 | from ...utils.fs import download, get_dataset_path 10 | from ..base import RNGDataFlow 11 | 12 | __all__ = ['SVHNDigit'] 13 | 14 | SVHN_URL = "http://ufldl.stanford.edu/housenumbers/" 15 | 16 | 17 | class SVHNDigit(RNGDataFlow): 18 | """ 19 | `SVHN `_ Cropped Digit Dataset. 20 | Produces [img, label], img of 32x32x3 in range [0,255], label of 0-9 21 | """ 22 | _Cache = {} 23 | 24 | def __init__(self, name, data_dir=None, shuffle=True): 25 | """ 26 | Args: 27 | name (str): 'train', 'test', or 'extra'. 28 | data_dir (str): a directory containing the original {train,test,extra}_32x32.mat. 29 | shuffle (bool): shuffle the dataset. 30 | """ 31 | self.shuffle = shuffle 32 | 33 | if name in SVHNDigit._Cache: 34 | self.X, self.Y = SVHNDigit._Cache[name] 35 | return 36 | if data_dir is None: 37 | data_dir = get_dataset_path('svhn_data') 38 | assert name in ['train', 'test', 'extra'], name 39 | filename = os.path.join(data_dir, name + '_32x32.mat') 40 | if not os.path.isfile(filename): 41 | url = SVHN_URL + os.path.basename(filename) 42 | logger.info("File {} not found!".format(filename)) 43 | logger.info("Downloading from {} ...".format(url)) 44 | download(url, os.path.dirname(filename)) 45 | logger.info("Loading {} ...".format(filename)) 46 | data = scipy.io.loadmat(filename) 47 | self.X = data['X'].transpose(3, 0, 1, 2) 48 | self.Y = data['y'].reshape((-1)) 49 | self.Y[self.Y == 10] = 0 50 | SVHNDigit._Cache[name] = (self.X, self.Y) 51 | 52 | def __len__(self): 53 | return self.X.shape[0] 54 | 55 | def __iter__(self): 56 | n = self.X.shape[0] 57 | idxs = np.arange(n) 58 | if self.shuffle: 59 | self.rng.shuffle(idxs) 60 | for k in idxs: 61 | # since svhn is quite small, just do it for safety 62 | yield [self.X[k], self.Y[k]] 63 | 64 | @staticmethod 65 | def get_per_pixel_mean(names=('train', 'test', 'extra')): 66 | """ 67 | Args: 68 | names (tuple[str]): names of the dataset split 69 | 70 | Returns: 71 | a 32x32x3 image, the mean of all images in the given datasets 72 | """ 73 | for name in names: 74 | assert name in ['train', 'test', 'extra'], name 75 | images = [SVHNDigit(x).X for x in names] 76 | return np.concatenate(tuple(images)).mean(axis=0) 77 | 78 | 79 | try: 80 | import scipy.io 81 | except ImportError: 82 | from ...utils.develop import create_dummy_class 83 | SVHNDigit = create_dummy_class('SVHNDigit', 'scipy.io') # noqa 84 | 85 | if __name__ == '__main__': 86 | a = SVHNDigit('train') 87 | b = SVHNDigit.get_per_pixel_mean() 88 | -------------------------------------------------------------------------------- /tensorpack/callbacks/group.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: group.py 3 | 4 | 5 | import traceback 6 | from contextlib import contextmanager 7 | from time import perf_counter as timer # noqa 8 | from ..compat import tfv1 as tf 9 | 10 | from ..utils import logger 11 | from ..utils.utils import humanize_time_delta 12 | from .base import Callback 13 | from .hooks import CallbackToHook 14 | 15 | __all__ = ['Callbacks'] 16 | 17 | 18 | class CallbackTimeLogger(object): 19 | def __init__(self): 20 | self.times = [] 21 | self.tot = 0 22 | 23 | def add(self, name, time): 24 | self.tot += time 25 | self.times.append((name, time)) 26 | 27 | @contextmanager 28 | def timed_callback(self, name): 29 | s = timer() 30 | yield 31 | self.add(name, timer() - s) 32 | 33 | def log(self): 34 | 35 | """ log the time of some heavy callbacks """ 36 | if self.tot < 2: 37 | return 38 | msgs = [] 39 | for name, t in self.times: 40 | if t / self.tot > 0.3 and t > 1: 41 | msgs.append(name + ": " + humanize_time_delta(t)) 42 | logger.info( 43 | "Callbacks took {:.3f} sec in total. {}".format( 44 | self.tot, '; '.join(msgs))) 45 | 46 | 47 | class Callbacks(Callback): 48 | """ 49 | A container to hold all callbacks, and trigger them iteratively. 50 | 51 | This is only used by the base trainer to run all the callbacks. 52 | Users do not need to use this class. 53 | """ 54 | 55 | def __init__(self, cbs): 56 | """ 57 | Args: 58 | cbs(list): a list of :class:`Callback` instances. 59 | """ 60 | # check type 61 | for cb in cbs: 62 | assert isinstance(cb, Callback), cb.__class__ 63 | self.cbs = cbs 64 | 65 | def _setup_graph(self): 66 | with tf.name_scope(None): # clear the name scope 67 | for cb in self.cbs: 68 | cb.setup_graph(self.trainer) 69 | 70 | def _before_train(self): 71 | for cb in self.cbs: 72 | cb.before_train() 73 | 74 | def _after_train(self): 75 | for cb in self.cbs: 76 | # make sure callbacks are properly finalized 77 | try: 78 | cb.after_train() 79 | except Exception: 80 | traceback.print_exc() 81 | 82 | def get_hooks(self): 83 | return [CallbackToHook(cb) for cb in self.cbs] 84 | 85 | def trigger_step(self): 86 | for cb in self.cbs: 87 | cb.trigger_step() 88 | 89 | def _trigger_epoch(self): 90 | tm = CallbackTimeLogger() 91 | 92 | for cb in self.cbs: 93 | display_name = str(cb) 94 | with tm.timed_callback(display_name): 95 | cb.trigger_epoch() 96 | tm.log() 97 | 98 | def _before_epoch(self): 99 | for cb in self.cbs: 100 | cb.before_epoch() 101 | 102 | def _after_epoch(self): 103 | for cb in self.cbs: 104 | cb.after_epoch() 105 | -------------------------------------------------------------------------------- /tensorpack/tfutils/model_utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: model_utils.py 3 | # Author: tensorpack contributors 4 | 5 | from ..compat import tfv1 as tf 6 | from tabulate import tabulate 7 | from termcolor import colored 8 | 9 | from .common import get_op_tensor_name 10 | from ..utils import logger 11 | 12 | __all__ = [] 13 | 14 | 15 | def describe_trainable_vars(): 16 | """ 17 | Print a description of the current model parameters. 18 | Skip variables starting with "tower", as they are just duplicates built by data-parallel logic. 19 | """ 20 | train_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES) 21 | if len(train_vars) == 0: 22 | logger.warn("No trainable variables in the graph!") 23 | return 24 | total = 0 25 | total_bytes = 0 26 | data = [] 27 | for v in train_vars: 28 | if v.name.startswith('tower'): 29 | continue 30 | shape = v.get_shape() 31 | ele = shape.num_elements() 32 | if ele is None: 33 | logger.warn("Shape of variable {} is not fully defined but {}.".format(v.name, shape)) 34 | ele = 0 35 | try: 36 | shape = shape.as_list() 37 | except ValueError: 38 | shape = '' 39 | 40 | total += ele 41 | total_bytes += ele * v.dtype.size 42 | data.append([get_op_tensor_name(v.name)[0], shape, ele, v.device, v.dtype.base_dtype.name]) 43 | headers = ['name', 'shape', '#elements', 'device', 'dtype'] 44 | 45 | dtypes = list({x[4] for x in data}) 46 | if len(dtypes) == 1 and dtypes[0] == "float32": 47 | # don't log the dtype if all vars are float32 (default dtype) 48 | for x in data: 49 | del x[4] 50 | del headers[4] 51 | 52 | devices = {x[3] for x in data} 53 | if len(devices) == 1: 54 | # don't log the device if all vars on the same device 55 | for x in data: 56 | del x[3] 57 | del headers[3] 58 | 59 | table = tabulate(data, headers=headers) 60 | 61 | size_mb = total_bytes / 1024.0**2 62 | summary_msg = colored( 63 | "\nNumber of trainable variables: {}".format(len(data)) + 64 | "\nNumber of parameters (elements): {}".format(total) + 65 | "\nStorage space needed for all trainable variables: {:.02f}MB".format(size_mb), 66 | 'cyan') 67 | logger.info(colored("List of Trainable Variables: \n", 'cyan') + table + summary_msg) 68 | 69 | 70 | def get_shape_str(tensors): 71 | """ 72 | Internally used by layer registry, to print shapes of inputs/outputs of layers. 73 | 74 | Args: 75 | tensors (list or tf.Tensor): a tensor or a list of tensors 76 | Returns: 77 | str: a string to describe the shape 78 | """ 79 | if isinstance(tensors, (list, tuple)): 80 | for v in tensors: 81 | assert isinstance(v, (tf.Tensor, tf.Variable)), "Not a tensor: {}".format(type(v)) 82 | shape_str = ", ".join(map(get_shape_str, tensors)) 83 | else: 84 | assert isinstance(tensors, (tf.Tensor, tf.Variable)), "Not a tensor: {}".format(type(tensors)) 85 | shape_str = str(tensors.get_shape().as_list()).replace("None", "?") 86 | return shape_str 87 | -------------------------------------------------------------------------------- /tensorpack/dataflow/imgaug/external.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import numpy as np 4 | 5 | from .base import ImageAugmentor 6 | from .transform import Transform 7 | 8 | __all__ = ['IAAugmentor', 'Albumentations'] 9 | 10 | 11 | class IAATransform(Transform): 12 | def __init__(self, aug, img_shape): 13 | self._init(locals()) 14 | 15 | def apply_image(self, img): 16 | return self.aug.augment_image(img) 17 | 18 | def apply_coords(self, coords): 19 | import imgaug as IA 20 | points = [IA.Keypoint(x=x, y=y) for x, y in coords] 21 | points = IA.KeypointsOnImage(points, shape=self.img_shape) 22 | augmented = self.aug.augment_keypoints([points])[0].keypoints 23 | return np.asarray([[p.x, p.y] for p in augmented]) 24 | 25 | 26 | class IAAugmentor(ImageAugmentor): 27 | """ 28 | Wrap an augmentor form the IAA library: https://github.com/aleju/imgaug. 29 | Both images and coordinates are supported. 30 | 31 | Note: 32 | 1. It's NOT RECOMMENDED 33 | to use coordinates because the IAA library does not handle coordinates accurately. 34 | 35 | 2. Only uint8 images are supported by the IAA library. 36 | 37 | 3. The IAA library can only produces images of the same shape. 38 | 39 | Example: 40 | 41 | .. code-block:: python 42 | 43 | from imgaug import augmenters as iaa # this is the aleju/imgaug library 44 | from tensorpack import imgaug # this is not the aleju/imgaug library 45 | # or from dataflow import imgaug # if you're using the standalone version of dataflow 46 | myaug = imgaug.IAAugmentor( 47 | iaa.Sequential([ 48 | iaa.Sharpen(alpha=(0, 1), lightness=(0.75, 1.5)), 49 | iaa.Fliplr(0.5), 50 | iaa.Crop(px=(0, 100)), 51 | ]) 52 | """ 53 | 54 | def __init__(self, augmentor): 55 | """ 56 | Args: 57 | augmentor (iaa.Augmenter): 58 | """ 59 | super(IAAugmentor, self).__init__() 60 | self._aug = augmentor 61 | 62 | def get_transform(self, img): 63 | return IAATransform(self._aug.to_deterministic(), img.shape) 64 | 65 | 66 | class AlbumentationsTransform(Transform): 67 | def __init__(self, aug, param): 68 | self._init(locals()) 69 | 70 | def apply_image(self, img): 71 | return self.aug.apply(img, **self.param) 72 | 73 | 74 | class Albumentations(ImageAugmentor): 75 | """ 76 | Wrap an augmentor form the albumentations library: https://github.com/albu/albumentations. 77 | Coordinate augmentation is not supported by the library. 78 | 79 | Example: 80 | 81 | .. code-block:: python 82 | 83 | from tensorpack import imgaug 84 | # or from dataflow import imgaug # if you're using the standalone version of dataflow 85 | import albumentations as AB 86 | myaug = imgaug.Albumentations(AB.RandomRotate90(p=1)) 87 | """ 88 | def __init__(self, augmentor): 89 | """ 90 | Args: 91 | augmentor (albumentations.BasicTransform): 92 | """ 93 | super(Albumentations, self).__init__() 94 | self._aug = augmentor 95 | 96 | def get_transform(self, img): 97 | return AlbumentationsTransform(self._aug, self._aug.get_params()) 98 | -------------------------------------------------------------------------------- /tensorpack/dataflow/serialize_test.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import tempfile 5 | import numpy as np 6 | import os 7 | import unittest 8 | 9 | from tensorpack.dataflow import HDF5Serializer, LMDBSerializer, NumpySerializer, TFRecordSerializer 10 | from tensorpack.dataflow.base import DataFlow 11 | 12 | 13 | def delete_file_if_exists(fn): 14 | try: 15 | os.remove(fn) 16 | except OSError: 17 | pass 18 | 19 | 20 | class SeededFakeDataFlow(DataFlow): 21 | """docstring for SeededFakeDataFlow""" 22 | 23 | def __init__(self, seed=42, size=32): 24 | super(SeededFakeDataFlow, self).__init__() 25 | self.seed = seed 26 | self._size = size 27 | self.cache = [] 28 | 29 | def reset_state(self): 30 | np.random.seed(self.seed) 31 | for _ in range(self._size): 32 | label = np.random.randint(low=0, high=10) 33 | img = np.random.randn(28, 28, 3) 34 | self.cache.append([label, img]) 35 | 36 | def __len__(self): 37 | return self._size 38 | 39 | def __iter__(self): 40 | for dp in self.cache: 41 | yield dp 42 | 43 | 44 | class SerializerTest(unittest.TestCase): 45 | 46 | def run_write_read_test(self, file, serializer, w_args, w_kwargs, r_args, r_kwargs, error_msg): 47 | try: 48 | delete_file_if_exists(file) 49 | 50 | ds_expected = SeededFakeDataFlow() 51 | serializer.save(ds_expected, file, *w_args, **w_kwargs) 52 | ds_actual = serializer.load(file, *r_args, **r_kwargs) 53 | 54 | ds_actual.reset_state() 55 | ds_expected.reset_state() 56 | 57 | for dp_expected, dp_actual in zip(ds_expected.__iter__(), ds_actual.__iter__()): 58 | self.assertEqual(dp_expected[0], dp_actual[0]) 59 | self.assertTrue(np.allclose(dp_expected[1], dp_actual[1])) 60 | except ImportError: 61 | print(error_msg) 62 | 63 | def test_lmdb(self): 64 | with tempfile.TemporaryDirectory() as f: 65 | self.run_write_read_test( 66 | os.path.join(f, 'test.lmdb'), 67 | LMDBSerializer, 68 | {}, {}, 69 | {}, {'shuffle': False}, 70 | 'Skip test_lmdb, no lmdb available') 71 | 72 | def test_tfrecord(self): 73 | with tempfile.TemporaryDirectory() as f: 74 | self.run_write_read_test( 75 | os.path.join(f, 'test.tfrecord'), 76 | TFRecordSerializer, 77 | {}, {}, 78 | {}, {'size': 32}, 79 | 'Skip test_tfrecord, no tensorflow available') 80 | 81 | def test_numpy(self): 82 | with tempfile.TemporaryDirectory() as f: 83 | self.run_write_read_test( 84 | os.path.join(f, 'test.npz'), 85 | NumpySerializer, 86 | {}, {}, 87 | {}, {'shuffle': False}, 88 | 'Skip test_numpy, no numpy available') 89 | 90 | def test_hdf5(self): 91 | args = [['label', 'image']] 92 | with tempfile.TemporaryDirectory() as f: 93 | self.run_write_read_test( 94 | os.path.join(f, 'test.h5'), 95 | HDF5Serializer, 96 | args, {}, 97 | args, {'shuffle': False}, 98 | 'Skip test_hdf5, no h5py available') 99 | 100 | 101 | if __name__ == '__main__': 102 | unittest.main() 103 | -------------------------------------------------------------------------------- /tensorpack/utils/serialize.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: serialize.py 3 | 4 | import os 5 | 6 | import pickle 7 | from multiprocessing.reduction import ForkingPickler 8 | import msgpack 9 | import msgpack_numpy 10 | 11 | msgpack_numpy.patch() 12 | assert msgpack.version >= (0, 5, 2) 13 | 14 | __all__ = ['loads', 'dumps'] 15 | 16 | 17 | MAX_MSGPACK_LEN = 1000000000 18 | 19 | 20 | class MsgpackSerializer(object): 21 | 22 | @staticmethod 23 | def dumps(obj): 24 | """ 25 | Serialize an object. 26 | 27 | Returns: 28 | Implementation-dependent bytes-like object. 29 | """ 30 | return msgpack.dumps(obj, use_bin_type=True) 31 | 32 | @staticmethod 33 | def loads(buf): 34 | """ 35 | Args: 36 | buf: the output of `dumps`. 37 | """ 38 | # Since 0.6, the default max size was set to 1MB. 39 | # We change it to approximately 1G. 40 | return msgpack.loads(buf, raw=False, 41 | max_bin_len=MAX_MSGPACK_LEN, 42 | max_array_len=MAX_MSGPACK_LEN, 43 | max_map_len=MAX_MSGPACK_LEN, 44 | max_str_len=MAX_MSGPACK_LEN) 45 | 46 | 47 | class PyarrowSerializer(object): 48 | @staticmethod 49 | def dumps(obj): 50 | """ 51 | Serialize an object. 52 | 53 | Returns: 54 | Implementation-dependent bytes-like object. 55 | May not be compatible across different versions of pyarrow. 56 | """ 57 | import pyarrow as pa 58 | return pa.serialize(obj).to_buffer() 59 | 60 | @staticmethod 61 | def dumps_bytes(obj): 62 | """ 63 | Returns: 64 | bytes 65 | """ 66 | return PyarrowSerializer.dumps(obj).to_pybytes() 67 | 68 | @staticmethod 69 | def loads(buf): 70 | """ 71 | Args: 72 | buf: the output of `dumps` or `dumps_bytes`. 73 | """ 74 | import pyarrow as pa 75 | return pa.deserialize(buf) 76 | 77 | 78 | class PickleSerializer(object): 79 | @staticmethod 80 | def dumps(obj): 81 | """ 82 | Returns: 83 | bytes 84 | """ 85 | return pickle.dumps(obj, protocol=-1) 86 | 87 | @staticmethod 88 | def loads(buf): 89 | """ 90 | Args: 91 | bytes 92 | """ 93 | return pickle.loads(buf) 94 | 95 | 96 | # Define the default serializer to be used that dumps data to bytes 97 | _DEFAULT_S = os.environ.get('TENSORPACK_SERIALIZE', 'pickle') 98 | 99 | if _DEFAULT_S == "pyarrow": 100 | dumps = PyarrowSerializer.dumps_bytes 101 | loads = PyarrowSerializer.loads 102 | elif _DEFAULT_S == "pickle": 103 | dumps = PickleSerializer.dumps 104 | loads = PickleSerializer.loads 105 | else: 106 | dumps = MsgpackSerializer.dumps 107 | loads = MsgpackSerializer.loads 108 | 109 | # Define the default serializer to be used for passing data 110 | # among a pair of peers. In this case the deserialization is 111 | # known to happen only once 112 | _DEFAULT_S = os.environ.get('TENSORPACK_ONCE_SERIALIZE', 'pickle') 113 | 114 | if _DEFAULT_S == "pyarrow": 115 | dumps_once = PyarrowSerializer.dumps 116 | loads_once = PyarrowSerializer.loads 117 | elif _DEFAULT_S == "pickle": 118 | dumps_once = ForkingPickler.dumps 119 | loads_once = ForkingPickler.loads 120 | else: 121 | dumps_once = MsgpackSerializer.dumps 122 | loads_once = MsgpackSerializer.loads 123 | -------------------------------------------------------------------------------- /MaskRCNN/utils/generate_anchors.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # https://github.com/rbgirshick/py-faster-rcnn/blob/master/lib/rpn/generate_anchors.py 4 | 5 | # -------------------------------------------------------- 6 | # Faster R-CNN 7 | # Copyright (c) 2015 Microsoft 8 | # Licensed under The MIT License [see LICENSE for details] 9 | # Written by Ross Girshick and Sean Bell 10 | # -------------------------------------------------------- 11 | 12 | import numpy as np 13 | from six.moves import range 14 | 15 | # Verify that we compute the same anchors as Shaoqing's matlab implementation: 16 | # 17 | # >> load output/rpn_cachedir/faster_rcnn_VOC2007_ZF_stage1_rpn/anchors.mat 18 | # >> anchors 19 | # 20 | # anchors = 21 | # 22 | # -83 -39 100 56 23 | # -175 -87 192 104 24 | # -359 -183 376 200 25 | # -55 -55 72 72 26 | # -119 -119 136 136 27 | # -247 -247 264 264 28 | # -35 -79 52 96 29 | # -79 -167 96 184 30 | # -167 -343 184 360 31 | 32 | # array([[ -83., -39., 100., 56.], 33 | # [-175., -87., 192., 104.], 34 | # [-359., -183., 376., 200.], 35 | # [ -55., -55., 72., 72.], 36 | # [-119., -119., 136., 136.], 37 | # [-247., -247., 264., 264.], 38 | # [ -35., -79., 52., 96.], 39 | # [ -79., -167., 96., 184.], 40 | # [-167., -343., 184., 360.]]) 41 | 42 | 43 | def generate_anchors(base_size=16, ratios=[0.5, 1, 2], 44 | scales=2**np.arange(3, 6)): 45 | """ 46 | Generate anchor (reference) windows by enumerating aspect ratios X 47 | scales wrt a reference (0, 0, 15, 15) window. 48 | """ 49 | 50 | base_anchor = np.array([1, 1, base_size, base_size], dtype='float32') - 1 51 | ratio_anchors = _ratio_enum(base_anchor, ratios) 52 | anchors = np.vstack([_scale_enum(ratio_anchors[i, :], scales) 53 | for i in range(ratio_anchors.shape[0])]) 54 | return anchors 55 | 56 | 57 | def _whctrs(anchor): 58 | """ 59 | Return width, height, x center, and y center for an anchor (window). 60 | """ 61 | 62 | w = anchor[2] - anchor[0] + 1 63 | h = anchor[3] - anchor[1] + 1 64 | x_ctr = anchor[0] + 0.5 * (w - 1) 65 | y_ctr = anchor[1] + 0.5 * (h - 1) 66 | return w, h, x_ctr, y_ctr 67 | 68 | 69 | def _mkanchors(ws, hs, x_ctr, y_ctr): 70 | """ 71 | Given a vector of widths (ws) and heights (hs) around a center 72 | (x_ctr, y_ctr), output a set of anchors (windows). 73 | """ 74 | 75 | ws = ws[:, np.newaxis] 76 | hs = hs[:, np.newaxis] 77 | anchors = np.hstack((x_ctr - 0.5 * (ws - 1), 78 | y_ctr - 0.5 * (hs - 1), 79 | x_ctr + 0.5 * (ws - 1), 80 | y_ctr + 0.5 * (hs - 1))) 81 | return anchors 82 | 83 | 84 | def _ratio_enum(anchor, ratios): 85 | """ 86 | Enumerate a set of anchors for each aspect ratio wrt an anchor. 87 | """ 88 | 89 | w, h, x_ctr, y_ctr = _whctrs(anchor) 90 | size = w * h 91 | size_ratios = size / ratios 92 | ws = np.round(np.sqrt(size_ratios)) 93 | hs = np.round(ws * ratios) 94 | anchors = _mkanchors(ws, hs, x_ctr, y_ctr) 95 | return anchors 96 | 97 | 98 | def _scale_enum(anchor, scales): 99 | """ 100 | Enumerate a set of anchors for each scale wrt an anchor. 101 | """ 102 | 103 | w, h, x_ctr, y_ctr = _whctrs(anchor) 104 | ws = w * scales 105 | hs = h * scales 106 | anchors = _mkanchors(ws, hs, x_ctr, y_ctr) 107 | return anchors 108 | -------------------------------------------------------------------------------- /tensorpack/compat/tensor_spec.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | Copied from tensorflow/python/framework/tensor_spec.py 4 | """ 5 | 6 | from __future__ import absolute_import 7 | from __future__ import division 8 | from __future__ import print_function 9 | 10 | import numpy as np 11 | 12 | from tensorflow.python.framework import common_shapes 13 | from tensorflow.python.framework import dtypes 14 | from tensorflow.python.framework import ops 15 | from tensorflow.python.framework import tensor_shape 16 | 17 | 18 | class TensorSpec(object): 19 | """Describes a tf.Tensor. 20 | 21 | Metadata for describing the `tf.Tensor` objects accepted or returned 22 | by some TensorFlow APIs. 23 | """ 24 | 25 | __slots__ = ["_shape", "_shape_tuple", "_dtype", "_name"] 26 | 27 | def __init__(self, shape, dtype=dtypes.float32, name=None): 28 | """Creates a TensorSpec. 29 | 30 | Args: 31 | shape: Value convertible to `tf.TensorShape`. The shape of the tensor. 32 | dtype: Value convertible to `tf.DType`. The type of the tensor values. 33 | name: Optional name for the Tensor. 34 | 35 | Raises: 36 | TypeError: If shape is not convertible to a `tf.TensorShape`, or dtype is 37 | not convertible to a `tf.DType`. 38 | """ 39 | self._shape = tensor_shape.TensorShape(shape) 40 | try: 41 | self._shape_tuple = tuple(self.shape.as_list()) 42 | except ValueError: 43 | self._shape_tuple = None 44 | self._dtype = dtypes.as_dtype(dtype) 45 | self._name = name 46 | 47 | @classmethod 48 | def from_spec(cls, spec, name=None): 49 | return cls(spec.shape, spec.dtype, name or spec.name) 50 | 51 | @classmethod 52 | def from_tensor(cls, tensor, name=None): 53 | if isinstance(tensor, ops.EagerTensor): 54 | return TensorSpec(tensor.shape, tensor.dtype, name) 55 | elif isinstance(tensor, ops.Tensor): 56 | return TensorSpec(tensor.shape, tensor.dtype, name or tensor.op.name) 57 | else: 58 | raise ValueError("`tensor` should be a tf.Tensor") 59 | 60 | @property 61 | def shape(self): 62 | """Returns the `TensorShape` that represents the shape of the tensor.""" 63 | return self._shape 64 | 65 | @property 66 | def dtype(self): 67 | """Returns the `dtype` of elements in the tensor.""" 68 | return self._dtype 69 | 70 | @property 71 | def name(self): 72 | """Returns the (optionally provided) name of the described tensor.""" 73 | return self._name 74 | 75 | def is_compatible_with(self, spec_or_tensor): 76 | """Returns True if spec_or_tensor is compatible with this TensorSpec. 77 | 78 | Two tensors are considered compatible if they have the same dtype 79 | and their shapes are compatible (see `tf.TensorShape.is_compatible_with`). 80 | 81 | Args: 82 | spec_or_tensor: A tf.TensorSpec or a tf.Tensor 83 | 84 | Returns: 85 | True if spec_or_tensor is compatible with self. 86 | """ 87 | return (self._dtype.is_compatible_with(spec_or_tensor.dtype) and 88 | self._shape.is_compatible_with(spec_or_tensor.shape)) 89 | 90 | def __repr__(self): 91 | return "TensorSpec(shape={}, dtype={}, name={})".format( 92 | self.shape, repr(self.dtype), repr(self.name)) 93 | 94 | def __hash__(self): 95 | return hash((self._shape_tuple, self.dtype)) 96 | 97 | def __eq__(self, other): 98 | return (self._shape_tuple == other._shape_tuple # pylint: disable=protected-access 99 | and self.dtype == other.dtype 100 | and self._name == other._name) # pylint: disable=protected-access 101 | 102 | def __ne__(self, other): 103 | return not self == other 104 | 105 | def __reduce__(self): 106 | return TensorSpec, (self._shape, self._dtype, self._name) 107 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check [existing open](https://github.com/aws-samples/mask-rcnn-tensorflow/issues), or [recently closed](https://github.com/aws-samples/mask-rcnn-tensorflow/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *master* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/aws-samples/mask-rcnn-tensorflow/labels/help%20wanted) issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](https://github.com/aws-samples/mask-rcnn-tensorflow/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | 61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 62 | -------------------------------------------------------------------------------- /tensorpack/models/tflayer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: tflayer.py 3 | 4 | import functools 5 | import six 6 | import tensorflow as tf 7 | 8 | from ..tfutils.varreplace import custom_getter_scope 9 | from ..utils.argtools import get_data_format 10 | 11 | __all__ = [] 12 | 13 | 14 | def map_common_tfargs(kwargs): 15 | df = kwargs.pop('data_format', None) 16 | if df is not None: 17 | df = get_data_format(df, keras_mode=True) 18 | kwargs['data_format'] = df 19 | 20 | old_nl = kwargs.pop('nl', None) 21 | if old_nl is not None: 22 | kwargs['activation'] = lambda x, name=None: old_nl(x, name=name) 23 | 24 | if 'W_init' in kwargs: 25 | kwargs['kernel_initializer'] = kwargs.pop('W_init') 26 | 27 | if 'b_init' in kwargs: 28 | kwargs['bias_initializer'] = kwargs.pop('b_init') 29 | return kwargs 30 | 31 | 32 | def convert_to_tflayer_args(args_names, name_mapping): 33 | """ 34 | After applying this decorator: 35 | 1. data_format becomes tf.layers style 36 | 2. nl becomes activation 37 | 3. initializers are renamed 38 | 4. positional args are transformed to corresponding kwargs, according to args_names 39 | 5. kwargs are mapped to tf.layers names if needed, by name_mapping 40 | """ 41 | 42 | def decorator(func): 43 | @functools.wraps(func) 44 | def decorated_func(inputs, *args, **kwargs): 45 | kwargs = map_common_tfargs(kwargs) 46 | 47 | posarg_dic = {} 48 | assert len(args) <= len(args_names), \ 49 | "Please use kwargs instead of positional args to call this model, " \ 50 | "except for the following arguments: {}".format(', '.join(args_names)) 51 | for pos_arg, name in zip(args, args_names): 52 | posarg_dic[name] = pos_arg 53 | 54 | ret = {} 55 | for name, arg in six.iteritems(kwargs): 56 | newname = name_mapping.get(name, None) 57 | if newname is not None: 58 | assert newname not in kwargs, \ 59 | "Argument {} and {} conflicts!".format(name, newname) 60 | else: 61 | newname = name 62 | ret[newname] = arg 63 | ret.update(posarg_dic) # Let pos arg overwrite kw arg, for argscope to work 64 | 65 | return func(inputs, **ret) 66 | 67 | return decorated_func 68 | 69 | return decorator 70 | 71 | 72 | def rename_get_variable(mapping): 73 | """ 74 | Args: 75 | mapping(dict): an old -> new mapping for variable basename. e.g. {'kernel': 'W'} 76 | 77 | Returns: 78 | A context where the variables are renamed. 79 | """ 80 | def custom_getter(getter, name, *args, **kwargs): 81 | splits = name.split('/') 82 | basename = splits[-1] 83 | if basename in mapping: 84 | basename = mapping[basename] 85 | splits[-1] = basename 86 | name = '/'.join(splits) 87 | return getter(name, *args, **kwargs) 88 | return custom_getter_scope(custom_getter) 89 | 90 | 91 | def rename_tflayer_get_variable(): 92 | """ 93 | Rename all :func:`tf.get_variable` with rules that transforms tflayer style to tensorpack style. 94 | 95 | Returns: 96 | A context where the variables are renamed. 97 | 98 | Example: 99 | 100 | .. code-block:: python 101 | 102 | with rename_tflayer_get_variable(): 103 | x = tf.layer.conv2d(input, 3, 3, name='conv0') 104 | # variables will be named 'conv0/W', 'conv0/b' 105 | """ 106 | mapping = { 107 | 'kernel': 'W', 108 | 'bias': 'b', 109 | 'moving_mean': 'mean/EMA', 110 | 'moving_variance': 'variance/EMA', 111 | } 112 | return rename_get_variable(mapping) 113 | -------------------------------------------------------------------------------- /tensorpack/dataflow/imgaug/paste.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: paste.py 3 | 4 | 5 | import numpy as np 6 | from abc import abstractmethod 7 | 8 | from .base import ImageAugmentor 9 | from .transform import TransformFactory 10 | 11 | __all__ = ['CenterPaste', 'BackgroundFiller', 'ConstantBackgroundFiller', 12 | 'RandomPaste'] 13 | 14 | 15 | class BackgroundFiller(object): 16 | """ Base class for all BackgroundFiller""" 17 | 18 | def fill(self, background_shape, img): 19 | """ 20 | Return a proper background image of background_shape, given img. 21 | 22 | Args: 23 | background_shape (tuple): a shape (h, w) 24 | img: an image 25 | Returns: 26 | a background image 27 | """ 28 | background_shape = tuple(background_shape) 29 | return self._fill(background_shape, img) 30 | 31 | @abstractmethod 32 | def _fill(self, background_shape, img): 33 | pass 34 | 35 | 36 | class ConstantBackgroundFiller(BackgroundFiller): 37 | """ Fill the background by a constant """ 38 | 39 | def __init__(self, value): 40 | """ 41 | Args: 42 | value (float): the value to fill the background. 43 | """ 44 | self.value = value 45 | 46 | def _fill(self, background_shape, img): 47 | assert img.ndim in [3, 2] 48 | if img.ndim == 3: 49 | return_shape = background_shape + (img.shape[2],) 50 | else: 51 | return_shape = background_shape 52 | return np.zeros(return_shape, dtype=img.dtype) + self.value 53 | 54 | 55 | # NOTE: 56 | # apply_coords should be implemeted in paste transform, but not yet done 57 | 58 | 59 | class CenterPaste(ImageAugmentor): 60 | """ 61 | Paste the image onto the center of a background canvas. 62 | """ 63 | 64 | def __init__(self, background_shape, background_filler=None): 65 | """ 66 | Args: 67 | background_shape (tuple): shape of the background canvas. 68 | background_filler (BackgroundFiller): How to fill the background. Defaults to zero-filler. 69 | """ 70 | if background_filler is None: 71 | background_filler = ConstantBackgroundFiller(0) 72 | 73 | self._init(locals()) 74 | 75 | def get_transform(self, _): 76 | return TransformFactory(name=str(self), apply_image=lambda img: self._impl(img)) 77 | 78 | def _impl(self, img): 79 | img_shape = img.shape[:2] 80 | assert self.background_shape[0] >= img_shape[0] and self.background_shape[1] >= img_shape[1] 81 | 82 | background = self.background_filler.fill( 83 | self.background_shape, img) 84 | y0 = int((self.background_shape[0] - img_shape[0]) * 0.5) 85 | x0 = int((self.background_shape[1] - img_shape[1]) * 0.5) 86 | background[y0:y0 + img_shape[0], x0:x0 + img_shape[1]] = img 87 | return background 88 | 89 | 90 | class RandomPaste(CenterPaste): 91 | """ 92 | Randomly paste the image onto a background canvas. 93 | """ 94 | 95 | def get_transform(self, img): 96 | img_shape = img.shape[:2] 97 | assert self.background_shape[0] > img_shape[0] and self.background_shape[1] > img_shape[1] 98 | 99 | y0 = self._rand_range(self.background_shape[0] - img_shape[0]) 100 | x0 = self._rand_range(self.background_shape[1] - img_shape[1]) 101 | l = int(x0), int(y0) 102 | return TransformFactory(name=str(self), apply_image=lambda img: self._impl(img, l)) 103 | 104 | def _impl(self, img, loc): 105 | x0, y0 = loc 106 | img_shape = img.shape[:2] 107 | background = self.background_filler.fill( 108 | self.background_shape, img) 109 | background[y0:y0 + img_shape[0], x0:x0 + img_shape[1]] = img 110 | return background 111 | -------------------------------------------------------------------------------- /tensorpack/callbacks/misc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: misc.py 3 | 4 | 5 | import numpy as np 6 | import os 7 | import time 8 | from collections import deque 9 | 10 | from ..utils import logger 11 | from ..utils.utils import humanize_time_delta 12 | from .base import Callback 13 | 14 | __all__ = ['SendStat', 'InjectShell', 'EstimatedTimeLeft'] 15 | 16 | 17 | class SendStat(Callback): 18 | """ An equivalent of :class:`SendMonitorData`, but as a normal callback. """ 19 | def __init__(self, command, names): 20 | self.command = command 21 | if not isinstance(names, list): 22 | names = [names] 23 | self.names = names 24 | 25 | def _trigger(self): 26 | M = self.trainer.monitors 27 | v = {k: M.get_latest(k) for k in self.names} 28 | cmd = self.command.format(**v) 29 | ret = os.system(cmd) 30 | if ret != 0: 31 | logger.error("Command {} failed with ret={}!".format(cmd, ret)) 32 | 33 | 34 | class InjectShell(Callback): 35 | """ 36 | Allow users to create a specific file as a signal to pause 37 | and iteratively debug the training. 38 | Once the :meth:`trigger` method is called, it detects whether the file exists, and opens an 39 | IPython/pdb shell if yes. 40 | In the shell, ``self`` is this callback, ``self.trainer`` is the trainer, and 41 | from that you can access everything else. 42 | 43 | Example: 44 | 45 | .. code-block:: none 46 | 47 | callbacks=[InjectShell('/path/to/pause-training.tmp'), ...] 48 | 49 | # the following command will pause the training and start a shell when the epoch finishes: 50 | $ touch /path/to/pause-training.tmp 51 | 52 | """ 53 | 54 | def __init__(self, file='INJECT_SHELL.tmp', shell='ipython'): 55 | """ 56 | Args: 57 | file (str): if this file exists, will open a shell. 58 | shell (str): one of 'ipython', 'pdb' 59 | """ 60 | self._file = file 61 | assert shell in ['ipython', 'pdb'] 62 | self._shell = shell 63 | logger.info("Create a file '{}' to open {} shell.".format(file, shell)) 64 | 65 | def _trigger(self): 66 | if os.path.isfile(self._file): 67 | logger.info("File {} exists, entering shell.".format(self._file)) 68 | self._inject() 69 | 70 | def _inject(self): 71 | trainer = self.trainer # noqa 72 | if self._shell == 'ipython': 73 | import IPython as IP # noqa 74 | IP.embed() 75 | elif self._shell == 'pdb': 76 | import pdb # noqa 77 | pdb.set_trace() 78 | 79 | def _after_train(self): 80 | if os.path.isfile(self._file): 81 | os.unlink(self._file) 82 | 83 | 84 | class EstimatedTimeLeft(Callback): 85 | """ 86 | Estimate the time left until completion of training. 87 | """ 88 | def __init__(self, last_k_epochs=5, median=True): 89 | """ 90 | Args: 91 | last_k_epochs (int): Use the time spent on last k epochs to estimate total time left. 92 | median (bool): Use the mean or median time spent on last k epochs. 93 | """ 94 | self._times = deque(maxlen=last_k_epochs) 95 | self._median = median 96 | 97 | def _before_train(self): 98 | self._max_epoch = self.trainer.max_epoch 99 | self._last_time = time.time() 100 | 101 | def _trigger_epoch(self): 102 | duration = time.time() - self._last_time 103 | self._last_time = time.time() 104 | self._times.append(duration) 105 | 106 | epoch_time = np.median(self._times) if self._median else np.mean(self._times) 107 | time_left = (self._max_epoch - self.epoch_num) * epoch_time 108 | if time_left > 0: 109 | logger.info("Estimated Time Left: " + humanize_time_delta(time_left)) 110 | -------------------------------------------------------------------------------- /MaskRCNN/utils/np_box_ops.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # Copyright 2017 The TensorFlow Authors. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # ============================================================================== 17 | 18 | """Operations for [N, 4] numpy arrays representing bounding boxes. 19 | 20 | Example box operations that are supported: 21 | * Areas: compute bounding box areas 22 | * IOU: pairwise intersection-over-union scores 23 | """ 24 | import numpy as np 25 | 26 | 27 | def area(boxes): 28 | """Computes area of boxes. 29 | 30 | Args: 31 | boxes: Numpy array with shape [N, 4] holding N boxes 32 | 33 | Returns: 34 | a numpy array with shape [N*1] representing box areas 35 | """ 36 | return (boxes[:, 2] - boxes[:, 0]) * (boxes[:, 3] - boxes[:, 1]) 37 | 38 | 39 | def intersection(boxes1, boxes2): 40 | """Compute pairwise intersection areas between boxes. 41 | 42 | Args: 43 | boxes1: a numpy array with shape [N, 4] holding N boxes 44 | boxes2: a numpy array with shape [M, 4] holding M boxes 45 | 46 | Returns: 47 | a numpy array with shape [N*M] representing pairwise intersection area 48 | """ 49 | [y_min1, x_min1, y_max1, x_max1] = np.split(boxes1, 4, axis=1) 50 | [y_min2, x_min2, y_max2, x_max2] = np.split(boxes2, 4, axis=1) 51 | 52 | all_pairs_min_ymax = np.minimum(y_max1, np.transpose(y_max2)) 53 | all_pairs_max_ymin = np.maximum(y_min1, np.transpose(y_min2)) 54 | intersect_heights = np.maximum( 55 | np.zeros(all_pairs_max_ymin.shape, dtype='f4'), 56 | all_pairs_min_ymax - all_pairs_max_ymin) 57 | all_pairs_min_xmax = np.minimum(x_max1, np.transpose(x_max2)) 58 | all_pairs_max_xmin = np.maximum(x_min1, np.transpose(x_min2)) 59 | intersect_widths = np.maximum( 60 | np.zeros(all_pairs_max_xmin.shape, dtype='f4'), 61 | all_pairs_min_xmax - all_pairs_max_xmin) 62 | return intersect_heights * intersect_widths 63 | 64 | 65 | def iou(boxes1, boxes2): 66 | """Computes pairwise intersection-over-union between box collections. 67 | 68 | Args: 69 | boxes1: a numpy array with shape [N, 4] holding N boxes. 70 | boxes2: a numpy array with shape [M, 4] holding M boxes. 71 | 72 | Returns: 73 | a numpy array with shape [N, M] representing pairwise iou scores. 74 | """ 75 | intersect = intersection(boxes1, boxes2) 76 | area1 = area(boxes1) 77 | area2 = area(boxes2) 78 | union = np.expand_dims(area1, axis=1) + np.expand_dims( 79 | area2, axis=0) - intersect 80 | return intersect / union 81 | 82 | 83 | def ioa(boxes1, boxes2): 84 | """Computes pairwise intersection-over-area between box collections. 85 | 86 | Intersection-over-area (ioa) between two boxes box1 and box2 is defined as 87 | their intersection area over box2's area. Note that ioa is not symmetric, 88 | that is, IOA(box1, box2) != IOA(box2, box1). 89 | 90 | Args: 91 | boxes1: a numpy array with shape [N, 4] holding N boxes. 92 | boxes2: a numpy array with shape [M, 4] holding N boxes. 93 | 94 | Returns: 95 | a numpy array with shape [N, M] representing pairwise ioa scores. 96 | """ 97 | intersect = intersection(boxes1, boxes2) 98 | inv_areas = np.expand_dims(1.0 / area(boxes2), axis=0) 99 | return intersect * inv_areas 100 | -------------------------------------------------------------------------------- /tensorpack/dataflow/dataset/bsds500.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: bsds500.py 3 | 4 | 5 | import glob 6 | import numpy as np 7 | import os 8 | 9 | from ...utils.fs import download, get_dataset_path 10 | from ..base import RNGDataFlow 11 | 12 | __all__ = ['BSDS500'] 13 | 14 | 15 | DATA_URL = "http://www.eecs.berkeley.edu/Research/Projects/CS/vision/grouping/BSR/BSR_bsds500.tgz" 16 | DATA_SIZE = 70763455 17 | IMG_W, IMG_H = 481, 321 18 | 19 | 20 | class BSDS500(RNGDataFlow): 21 | """ 22 | `Berkeley Segmentation Data Set and Benchmarks 500 dataset 23 | `_. 24 | 25 | Produce ``(image, label)`` pair, where ``image`` has shape (321, 481, 3(BGR)) and 26 | ranges in [0,255]. 27 | ``Label`` is a floating point image of shape (321, 481) in range [0, 1]. 28 | The value of each pixel is ``number of times it is annotated as edge / total number of annotators for this image``. 29 | """ 30 | 31 | def __init__(self, name, data_dir=None, shuffle=True): 32 | """ 33 | Args: 34 | name (str): 'train', 'test', 'val' 35 | data_dir (str): a directory containing the original 'BSR' directory. 36 | """ 37 | # check and download data 38 | if data_dir is None: 39 | data_dir = get_dataset_path('bsds500_data') 40 | if not os.path.isdir(os.path.join(data_dir, 'BSR')): 41 | download(DATA_URL, data_dir, expect_size=DATA_SIZE) 42 | filename = DATA_URL.split('/')[-1] 43 | filepath = os.path.join(data_dir, filename) 44 | import tarfile 45 | tarfile.open(filepath, 'r:gz').extractall(data_dir) 46 | self.data_root = os.path.join(data_dir, 'BSR', 'BSDS500', 'data') 47 | assert os.path.isdir(self.data_root) 48 | 49 | self.shuffle = shuffle 50 | assert name in ['train', 'test', 'val'] 51 | self._load(name) 52 | 53 | def _load(self, name): 54 | image_glob = os.path.join(self.data_root, 'images', name, '*.jpg') 55 | image_files = glob.glob(image_glob) 56 | gt_dir = os.path.join(self.data_root, 'groundTruth', name) 57 | self.data = np.zeros((len(image_files), IMG_H, IMG_W, 3), dtype='uint8') 58 | self.label = np.zeros((len(image_files), IMG_H, IMG_W), dtype='float32') 59 | 60 | for idx, f in enumerate(image_files): 61 | im = cv2.imread(f, cv2.IMREAD_COLOR) 62 | assert im is not None 63 | if im.shape[0] > im.shape[1]: 64 | im = np.transpose(im, (1, 0, 2)) 65 | assert im.shape[:2] == (IMG_H, IMG_W), "{} != {}".format(im.shape[:2], (IMG_H, IMG_W)) 66 | 67 | imgid = os.path.basename(f).split('.')[0] 68 | gt_file = os.path.join(gt_dir, imgid) 69 | gt = loadmat(gt_file)['groundTruth'][0] 70 | n_annot = gt.shape[0] 71 | gt = sum(gt[k]['Boundaries'][0][0] for k in range(n_annot)) 72 | gt = gt.astype('float32') 73 | gt *= 1.0 / n_annot 74 | if gt.shape[0] > gt.shape[1]: 75 | gt = gt.transpose() 76 | assert gt.shape == (IMG_H, IMG_W) 77 | 78 | self.data[idx] = im 79 | self.label[idx] = gt 80 | 81 | def __len__(self): 82 | return self.data.shape[0] 83 | 84 | def __iter__(self): 85 | idxs = np.arange(self.data.shape[0]) 86 | if self.shuffle: 87 | self.rng.shuffle(idxs) 88 | for k in idxs: 89 | yield [self.data[k], self.label[k]] 90 | 91 | 92 | try: 93 | from scipy.io import loadmat 94 | import cv2 95 | except ImportError: 96 | from ...utils.develop import create_dummy_class 97 | BSDS500 = create_dummy_class('BSDS500', ['scipy.io', 'cv2']) # noqa 98 | 99 | if __name__ == '__main__': 100 | a = BSDS500('val') 101 | a.reset_state() 102 | for k in a: 103 | cv2.imshow("haha", k[1].astype('uint8') * 255) 104 | cv2.waitKey(1000) 105 | -------------------------------------------------------------------------------- /tensorpack/libinfo.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | 4 | # issue#7378 may happen with custom opencv. It doesn't hurt to disable opencl 5 | os.environ['OPENCV_OPENCL_RUNTIME'] = 'disabled' # https://github.com/opencv/opencv/pull/10155 6 | try: 7 | # issue#1924 may happen on old systems 8 | import cv2 # noqa 9 | # cv2.setNumThreads(0) 10 | if int(cv2.__version__.split('.')[0]) >= 3: 11 | cv2.ocl.setUseOpenCL(False) 12 | # check if cv is built with cuda or openmp 13 | info = cv2.getBuildInformation().split('\n') 14 | for line in info: 15 | splits = line.split() 16 | if not len(splits): 17 | continue 18 | answer = splits[-1].lower() 19 | if answer in ['yes', 'no']: 20 | if 'cuda' in line.lower() and answer == 'yes': 21 | # issue#1197 22 | print("OpenCV is built with CUDA support. " 23 | "This may cause slow initialization or sometimes segfault with TensorFlow.") 24 | if answer == 'openmp': 25 | print("OpenCV is built with OpenMP support. This usually results in poor performance. For details, see " 26 | "https://github.com/tensorpack/benchmarks/blob/master/ImageNet/benchmark-opencv-resize.py") 27 | except (ImportError, TypeError): 28 | pass 29 | 30 | os.environ['TF_ENABLE_WINOGRAD_NONFUSED'] = '1' # issue#9339 31 | os.environ['TF_AUTOTUNE_THRESHOLD'] = '2' # use more warm-up 32 | 33 | # Since 1.3, this is not needed 34 | os.environ['TF_AVGPOOL_USE_CUDNN'] = '1' # issue#8566 35 | 36 | # TF1.5 features 37 | os.environ['TF_SYNC_ON_FINISH'] = '0' # will become default 38 | os.environ['TF_GPU_THREAD_MODE'] = 'gpu_private' 39 | os.environ['TF_GPU_THREAD_COUNT'] = '2' 40 | 41 | # Available in TF1.6+ & cudnn7. Haven't seen different performance on R50. 42 | # NOTE we disable it because: 43 | # this mode may use scaled atomic integer reduction that may cause a numerical 44 | # overflow for certain input data range. 45 | os.environ['TF_USE_CUDNN_BATCHNORM_SPATIAL_PERSISTENT'] = '0' 46 | 47 | # Available since 1.12. issue#15874 48 | # But they're sometimes buggy. We leave this decision to users. 49 | # os.environ['TF_ENABLE_WHILE_V2'] = '1' 50 | # os.environ['TF_ENABLE_COND_V2'] = '1' 51 | 52 | try: 53 | import tensorflow as tf # noqa 54 | _version = tf.__version__.split('.') 55 | assert (int(_version[0]), int(_version[1])) >= (1, 3), "TF>=1.3 is required!" 56 | _HAS_TF = True 57 | except ImportError: 58 | print("Failed to import tensorflow.") 59 | _HAS_TF = False 60 | else: 61 | # Install stacktrace handler 62 | try: 63 | from tensorflow.python.framework import test_util 64 | test_util.InstallStackTraceHandler() 65 | except Exception: 66 | pass 67 | 68 | # silence the massive deprecation warnings in TF 1.13+ 69 | if (int(_version[0]), int(_version[1])) >= (1, 13): 70 | try: 71 | from tensorflow.python.util.deprecation import silence 72 | except Exception: 73 | pass 74 | else: 75 | silence().__enter__() 76 | try: 77 | from tensorflow.python.util import deprecation_wrapper 78 | deprecation_wrapper._PER_MODULE_WARNING_LIMIT = 0 79 | except Exception: 80 | pass 81 | 82 | # Monkey-patch tf.test.is_gpu_available to avoid side effects: 83 | # https://github.com/tensorflow/tensorflow/issues/26460 84 | try: 85 | list_dev = tf.config.experimental.list_physical_devices 86 | except AttributeError: 87 | pass 88 | else: 89 | old_is_gpu_available = tf.test.is_gpu_available 90 | 91 | def is_gpu_available(*args, **kwargs): 92 | if len(args) == 0 and len(kwargs) == 0: 93 | return len(list_dev('GPU')) > 0 94 | return old_is_gpu_available(*args, **kwargs) 95 | 96 | tf.test.is_gpu_available = is_gpu_available 97 | 98 | 99 | # These lines will be programatically read/write by setup.py 100 | # Don't touch them. 101 | __version__ = '0.11' 102 | __git_version__ = "v0.0.0-175-g59255cc-dirty" -------------------------------------------------------------------------------- /tensorpack/models/linearwrap.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: linearwrap.py 3 | 4 | 5 | from types import ModuleType 6 | import six 7 | 8 | from .registry import get_registered_layer 9 | 10 | __all__ = ['LinearWrap'] 11 | 12 | 13 | class LinearWrap(object): 14 | """ A simple wrapper to easily create "linear" graph, 15 | consisting of layers / symbolic functions with only one input & output. 16 | """ 17 | 18 | class _TFModuleFunc(object): 19 | def __init__(self, mod, tensor): 20 | self._mod = mod 21 | self._t = tensor 22 | 23 | def __getattr__(self, name): 24 | ret = getattr(self._mod, name) 25 | if isinstance(ret, ModuleType): 26 | return LinearWrap._TFModuleFunc(ret, self._t) 27 | else: 28 | # assume to be a tf function 29 | def f(*args, **kwargs): 30 | o = ret(self._t, *args, **kwargs) 31 | return LinearWrap(o) 32 | return f 33 | 34 | def __init__(self, tensor): 35 | """ 36 | Args: 37 | tensor (tf.Tensor): the tensor to wrap 38 | """ 39 | self._t = tensor 40 | 41 | def __getattr__(self, layer_name): 42 | layer = get_registered_layer(layer_name) 43 | if layer is not None: 44 | # this is a registered tensorpack layer 45 | # parse arguments by tensorpack model convention 46 | if layer.use_scope: 47 | def layer_func(name, *args, **kwargs): 48 | ret = layer(name, self._t, *args, **kwargs) 49 | return LinearWrap(ret) 50 | else: 51 | def layer_func(*args, **kwargs): 52 | if len(args) and isinstance(args[0], six.string_types): 53 | name, args = args[0], args[1:] 54 | ret = layer(name, self._t, *args, **kwargs) 55 | else: 56 | ret = layer(self._t, *args, **kwargs) 57 | return LinearWrap(ret) 58 | return layer_func 59 | else: 60 | assert layer_name == 'tf', \ 61 | "Calling LinearWrap.{}:" \ 62 | " neither a layer nor 'tf'! " \ 63 | "Did you forget to extract tensor from LinearWrap?".format(layer_name) 64 | import tensorflow as layer # noqa 65 | assert isinstance(layer, ModuleType), layer 66 | return LinearWrap._TFModuleFunc(layer, self._t) 67 | 68 | def apply(self, func, *args, **kwargs): 69 | """ 70 | Apply a function on the wrapped tensor. 71 | 72 | Returns: 73 | LinearWrap: ``LinearWrap(func(self.tensor(), *args, **kwargs))``. 74 | """ 75 | ret = func(self._t, *args, **kwargs) 76 | return LinearWrap(ret) 77 | 78 | def apply2(self, func, *args, **kwargs): 79 | """ 80 | Apply a function on the wrapped tensor. The tensor 81 | will be the second argument of func. 82 | 83 | This is because many symbolic functions 84 | (such as tensorpack's layers) takes 'scope' as the first argument. 85 | 86 | Returns: 87 | LinearWrap: ``LinearWrap(func(args[0], self.tensor(), *args[1:], **kwargs))``. 88 | """ 89 | ret = func(args[0], self._t, *(args[1:]), **kwargs) 90 | return LinearWrap(ret) 91 | 92 | def __call__(self): 93 | """ 94 | Returns: 95 | tf.Tensor: the underlying wrapped tensor. 96 | """ 97 | return self._t 98 | 99 | def tensor(self): 100 | """ 101 | Equivalent to ``self.__call__()``. 102 | 103 | Returns: 104 | tf.Tensor: the underlying wrapped tensor. 105 | """ 106 | return self._t 107 | 108 | def print_tensor(self): 109 | """ 110 | Print the underlying tensor and return self. Can be useful to get the 111 | name of tensors inside :class:`LinearWrap`. 112 | 113 | :return: self 114 | """ 115 | print(self._t) 116 | return self 117 | -------------------------------------------------------------------------------- /tensorpack/utils/fs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: fs.py 3 | 4 | 5 | import errno 6 | import os 7 | import tqdm 8 | from six.moves import urllib 9 | 10 | from . import logger 11 | from .utils import execute_only_once 12 | 13 | __all__ = ['mkdir_p', 'download', 'recursive_walk', 'get_dataset_path', 'normpath'] 14 | 15 | 16 | def mkdir_p(dirname): 17 | """ Like "mkdir -p", make a dir recursively, but do nothing if the dir exists 18 | 19 | Args: 20 | dirname(str): 21 | """ 22 | assert dirname is not None 23 | if dirname == '' or os.path.isdir(dirname): 24 | return 25 | try: 26 | os.makedirs(dirname) 27 | except OSError as e: 28 | if e.errno != errno.EEXIST: 29 | raise e 30 | 31 | 32 | def download(url, dir, filename=None, expect_size=None): 33 | """ 34 | Download URL to a directory. 35 | Will figure out the filename automatically from URL, if not given. 36 | """ 37 | mkdir_p(dir) 38 | if filename is None: 39 | filename = url.split('/')[-1] 40 | fpath = os.path.join(dir, filename) 41 | 42 | if os.path.isfile(fpath): 43 | if expect_size is not None and os.stat(fpath).st_size == expect_size: 44 | logger.info("File {} exists! Skip download.".format(filename)) 45 | return fpath 46 | else: 47 | logger.warn("File {} exists. Will overwrite with a new download!".format(filename)) 48 | 49 | def hook(t): 50 | last_b = [0] 51 | 52 | def inner(b, bsize, tsize=None): 53 | if tsize is not None: 54 | t.total = tsize 55 | t.update((b - last_b[0]) * bsize) 56 | last_b[0] = b 57 | return inner 58 | try: 59 | with tqdm.tqdm(unit='B', unit_scale=True, miniters=1, desc=filename) as t: 60 | fpath, _ = urllib.request.urlretrieve(url, fpath, reporthook=hook(t)) 61 | statinfo = os.stat(fpath) 62 | size = statinfo.st_size 63 | except IOError: 64 | logger.error("Failed to download {}".format(url)) 65 | raise 66 | assert size > 0, "Downloaded an empty file from {}!".format(url) 67 | 68 | if expect_size is not None and size != expect_size: 69 | logger.error("File downloaded from {} does not match the expected size!".format(url)) 70 | logger.error("You may have downloaded a broken file, or the upstream may have modified the file.") 71 | 72 | # TODO human-readable size 73 | logger.info('Succesfully downloaded ' + filename + ". " + str(size) + ' bytes.') 74 | return fpath 75 | 76 | 77 | def recursive_walk(rootdir): 78 | """ 79 | Yields: 80 | str: All files in rootdir, recursively. 81 | """ 82 | for r, dirs, files in os.walk(rootdir): 83 | for f in files: 84 | yield os.path.join(r, f) 85 | 86 | 87 | def get_dataset_path(*args): 88 | """ 89 | Get the path to some dataset under ``$TENSORPACK_DATASET``. 90 | 91 | Args: 92 | args: strings to be joined to form path. 93 | 94 | Returns: 95 | str: path to the dataset. 96 | """ 97 | d = os.environ.get('TENSORPACK_DATASET', None) 98 | if d is None: 99 | d = os.path.join(os.path.expanduser('~'), 'tensorpack_data') 100 | if execute_only_once(): 101 | logger.warn("Env var $TENSORPACK_DATASET not set, using {} for datasets.".format(d)) 102 | if not os.path.isdir(d): 103 | mkdir_p(d) 104 | logger.info("Created the directory {}.".format(d)) 105 | assert os.path.isdir(d), d 106 | return os.path.join(d, *args) 107 | 108 | 109 | def normpath(path): 110 | """ 111 | Normalizes a path to a folder by taking into consideration remote storages like Cloud storaged 112 | referenced by '://' at the beginning of the path. 113 | 114 | Args: 115 | args: path to be normalized. 116 | 117 | Returns: 118 | str: normalized path. 119 | """ 120 | return path if '://' in path else os.path.normpath(path) 121 | 122 | 123 | if __name__ == '__main__': 124 | download('http://dl.caffe.berkeleyvision.org/caffe_ilsvrc12.tar.gz', '.') 125 | -------------------------------------------------------------------------------- /tensorpack/dataflow/imgaug/deform.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: deform.py 3 | 4 | 5 | import numpy as np 6 | 7 | from ...utils import logger 8 | from .base import ImageAugmentor 9 | from .transform import TransformFactory 10 | 11 | __all__ = [] 12 | 13 | # Code was temporarily kept here for a future reference in case someone needs it 14 | # But it was already deprecated, 15 | # because this augmentation is not a general one that people will often find helpful. 16 | 17 | 18 | class GaussianMap(object): 19 | """ Generate Gaussian weighted deformation map""" 20 | # TODO really needs speedup 21 | 22 | def __init__(self, image_shape, sigma=0.5): 23 | assert len(image_shape) == 2 24 | self.shape = image_shape 25 | self.sigma = sigma 26 | 27 | def get_gaussian_weight(self, anchor): 28 | """ 29 | Args: 30 | anchor: coordinate of the center 31 | """ 32 | ret = np.zeros(self.shape, dtype='float32') 33 | 34 | y, x = np.mgrid[:self.shape[0], :self.shape[1]] 35 | y = y.astype('float32') / ret.shape[0] - anchor[0] 36 | x = x.astype('float32') / ret.shape[1] - anchor[1] 37 | g = np.exp(-(x**2 + y ** 2) / self.sigma) 38 | # cv2.imshow(" ", g) 39 | # cv2.waitKey() 40 | return g 41 | 42 | 43 | def np_sample(img, coords): 44 | # a numpy implementation of ImageSample layer 45 | coords = np.maximum(coords, 0) 46 | coords = np.minimum(coords, np.array([img.shape[0] - 1, img.shape[1] - 1])) 47 | 48 | lcoor = np.floor(coords).astype('int32') 49 | ucoor = lcoor + 1 50 | ucoor = np.minimum(ucoor, np.array([img.shape[0] - 1, img.shape[1] - 1])) 51 | diff = coords - lcoor 52 | neg_diff = 1.0 - diff 53 | 54 | lcoory, lcoorx = np.split(lcoor, 2, axis=2) 55 | ucoory, ucoorx = np.split(ucoor, 2, axis=2) 56 | diff = np.repeat(diff, 3, 2).reshape((diff.shape[0], diff.shape[1], 2, 3)) 57 | neg_diff = np.repeat(neg_diff, 3, 2).reshape((diff.shape[0], diff.shape[1], 2, 3)) 58 | diffy, diffx = np.split(diff, 2, axis=2) 59 | ndiffy, ndiffx = np.split(neg_diff, 2, axis=2) 60 | 61 | ret = img[lcoory, lcoorx, :] * ndiffx * ndiffy + \ 62 | img[ucoory, ucoorx, :] * diffx * diffy + \ 63 | img[lcoory, ucoorx, :] * ndiffy * diffx + \ 64 | img[ucoory, lcoorx, :] * diffy * ndiffx 65 | return ret[:, :, 0, :] 66 | 67 | 68 | class GaussianDeform(ImageAugmentor): 69 | """ 70 | Some kind of slow deformation I made up. Don't count on it. 71 | """ 72 | 73 | # TODO input/output with different shape 74 | 75 | def __init__(self, anchors, shape, sigma=0.5, randrange=None): 76 | """ 77 | Args: 78 | anchors (list): list of center coordinates in range [0,1]. 79 | shape(list or tuple): image shape in [h, w]. 80 | sigma (float): sigma for Gaussian weight 81 | randrange (int): offset range. Defaults to shape[0] / 8 82 | """ 83 | logger.warn("GaussianDeform is slow. Consider using it with 4 or more prefetching processes.") 84 | super(GaussianDeform, self).__init__() 85 | self.anchors = anchors 86 | self.K = len(self.anchors) 87 | self.shape = shape 88 | self.grid = np.mgrid[0:self.shape[0], 0:self.shape[1]].transpose(1, 2, 0) 89 | self.grid = self.grid.astype('float32') # HxWx2 90 | 91 | gm = GaussianMap(self.shape, sigma=sigma) 92 | self.gws = np.array([gm.get_gaussian_weight(ank) 93 | for ank in self.anchors], dtype='float32') # KxHxW 94 | self.gws = self.gws.transpose(1, 2, 0) # HxWxK 95 | if randrange is None: 96 | self.randrange = self.shape[0] / 8 97 | else: 98 | self.randrange = randrange 99 | self.sigma = sigma 100 | 101 | def get_transform(self, img): 102 | v = self.rng.rand(self.K, 2).astype('float32') - 0.5 103 | v = v * 2 * self.randrange 104 | return TransformFactory(name=str(self), apply_image=lambda img: self._augment(img, v)) 105 | 106 | def _augment(self, img, v): 107 | grid = self.grid + np.dot(self.gws, v) 108 | return np_sample(img, grid) 109 | -------------------------------------------------------------------------------- /MaskRCNN/model/mask_head.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | import tensorflow as tf 5 | from MaskRCNN.performance import print_runtime_tensor 6 | 7 | from tensorpack.models import Conv2D, Conv2DTranspose, layer_register 8 | from tensorpack.tfutils.argscope import argscope 9 | from tensorpack.tfutils.scope_utils import auto_reuse_variable_scope, under_name_scope 10 | from tensorpack.tfutils.summary import add_moving_summary, add_tensor_summary 11 | 12 | from model.backbone import GroupNorm 13 | from config import config as cfg 14 | #from utils.mixed_precision import mixed_precision_scope 15 | 16 | @under_name_scope() 17 | def maskrcnn_loss(mask_logits, fg_labels, fg_target_masks): 18 | """ 19 | Args: 20 | mask_logits: Num_fg_boxes x num_category x H_roi x W_roi 21 | fg_labels: 1-D Num_fg_boxes, in 1~#class, int64 22 | fg_target_masks: Num_fg_boxes x H_roi x W_roi, float32 23 | Returns: mask loss 24 | """ 25 | num_fg = tf.size(fg_labels, out_type=tf.int64) # scalar Num_fg_boxes 26 | indices = tf.stack([tf.range(num_fg), fg_labels - 1], axis=1) # Num_fg_boxes x 2 27 | mask_logits = tf.gather_nd(mask_logits, indices) # Num_fg_boxes x H_roi x W_roi 28 | mask_probs = tf.sigmoid(mask_logits) 29 | 30 | # add some training visualizations to tensorboard 31 | with tf.name_scope('mask_viz'): 32 | viz = tf.concat([fg_target_masks, mask_probs], axis=1) 33 | viz = tf.expand_dims(viz, 3) 34 | viz = tf.cast(viz * 255, tf.uint8, name='viz') 35 | tf.summary.image('mask_truth|pred', viz, max_outputs=10) 36 | 37 | loss = tf.nn.sigmoid_cross_entropy_with_logits(labels=fg_target_masks, logits=mask_logits) 38 | loss = tf.math.reduce_mean(loss, name='maskrcnn_loss') 39 | 40 | # Calculate the accuracy 41 | pred_label = mask_probs > 0.5 42 | truth_label = fg_target_masks > 0.5 43 | accuracy = tf.math.reduce_mean(tf.cast(tf.equal(pred_label, truth_label), tf.float32), name='accuracy') 44 | pos_accuracy = tf.math.logical_and(tf.equal(pred_label, truth_label), tf.equal(truth_label, True)) 45 | pos_accuracy = tf.math.reduce_mean(tf.cast(pos_accuracy, tf.float32), name='pos_accuracy') 46 | fg_pixel_ratio = tf.math.reduce_mean(tf.cast(truth_label, tf.float32), name='fg_pixel_ratio') 47 | 48 | add_moving_summary(loss, accuracy, fg_pixel_ratio, pos_accuracy) 49 | return loss 50 | 51 | 52 | @layer_register(log_shape=True) 53 | @auto_reuse_variable_scope 54 | def maskrcnn_upXconv_head(feature, num_category, seed_gen, num_convs, norm=None): 55 | """ 56 | Args: 57 | feature: roi feature maps, Num_boxes x H_roi x W_roi x NumChannel 58 | num_category(int): Number of total classes 59 | num_convs (int): number of convolution layers 60 | norm (str or None): either None or 'GN' 61 | 62 | Returns: 63 | mask_logits: Num_boxes x num_category X (2 * H_roi) x (2 * W_roi) 64 | """ 65 | assert norm in [None, 'GN'], norm 66 | l = feature 67 | with argscope([Conv2D, Conv2DTranspose], data_format='channels_first' if cfg.TRAIN.MASK_NCHW else 'channels_last', 68 | kernel_initializer=tf.keras.initializers.VarianceScaling( 69 | scale=2.0, mode='fan_out', 70 | distribution='untruncated_normal', 71 | seed=seed_gen.next())): 72 | # c2's MSRAFill is fan_out 73 | for k in range(num_convs): 74 | l = Conv2D('fcn{}'.format(k), l, cfg.MRCNN.HEAD_DIM, 3, activation=tf.nn.relu, seed=seed_gen.next()) 75 | if norm is not None: 76 | l = GroupNorm('gn{}'.format(k), l) 77 | l = Conv2DTranspose('deconv', l, cfg.MRCNN.HEAD_DIM, 2, strides=2, activation=tf.nn.relu, seed=seed_gen.next()) # 2x upsampling 78 | l = Conv2D('conv', l, num_category, 1, seed=seed_gen.next()) 79 | if not cfg.TRAIN.MASK_NCHW: 80 | l = tf.transpose(l, [0, 3, 1, 2]) 81 | return l 82 | 83 | # Without Group Norm 84 | def maskrcnn_up4conv_head(*args, **kwargs): 85 | return maskrcnn_upXconv_head(*args, num_convs=4, **kwargs) 86 | 87 | # With Group Norm 88 | def maskrcnn_up4conv_gn_head(*args, **kwargs): 89 | return maskrcnn_upXconv_head(*args, num_convs=4, norm='GN', **kwargs) 90 | -------------------------------------------------------------------------------- /tensorpack/dataflow/imgaug/meta.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: meta.py 3 | 4 | 5 | from .base import ImageAugmentor 6 | from .transform import NoOpTransform, TransformList, TransformFactory 7 | 8 | __all__ = ['RandomChooseAug', 'MapImage', 'Identity', 'RandomApplyAug', 9 | 'RandomOrderAug'] 10 | 11 | 12 | class Identity(ImageAugmentor): 13 | """ A no-op augmentor """ 14 | def get_transform(self, img): 15 | return NoOpTransform() 16 | 17 | 18 | class RandomApplyAug(ImageAugmentor): 19 | """ Randomly apply the augmentor with a probability. 20 | Otherwise do nothing 21 | """ 22 | 23 | def __init__(self, aug, prob): 24 | """ 25 | Args: 26 | aug (ImageAugmentor): an augmentor. 27 | prob (float): the probability to apply the augmentor. 28 | """ 29 | self._init(locals()) 30 | super(RandomApplyAug, self).__init__() 31 | 32 | def get_transform(self, img): 33 | p = self.rng.rand() 34 | if p < self.prob: 35 | return self.aug.get_transform(img) 36 | else: 37 | return NoOpTransform() 38 | 39 | def reset_state(self): 40 | super(RandomApplyAug, self).reset_state() 41 | self.aug.reset_state() 42 | 43 | 44 | class RandomChooseAug(ImageAugmentor): 45 | """ Randomly choose one from a list of augmentors """ 46 | def __init__(self, aug_lists): 47 | """ 48 | Args: 49 | aug_lists (list): list of augmentors, or list of (augmentor, probability) tuples 50 | """ 51 | if isinstance(aug_lists[0], (tuple, list)): 52 | prob = [k[1] for k in aug_lists] 53 | aug_lists = [k[0] for k in aug_lists] 54 | self._init(locals()) 55 | else: 56 | prob = [1.0 / len(aug_lists)] * len(aug_lists) 57 | self._init(locals()) 58 | super(RandomChooseAug, self).__init__() 59 | 60 | def reset_state(self): 61 | super(RandomChooseAug, self).reset_state() 62 | for a in self.aug_lists: 63 | a.reset_state() 64 | 65 | def get_transform(self, img): 66 | aug_idx = self.rng.choice(len(self.aug_lists), p=self.prob) 67 | return self.aug_lists[aug_idx].get_transform(img) 68 | 69 | 70 | class RandomOrderAug(ImageAugmentor): 71 | """ 72 | Apply the augmentors with randomized order. 73 | """ 74 | 75 | def __init__(self, aug_lists): 76 | """ 77 | Args: 78 | aug_lists (list): list of augmentors. 79 | The augmentors are assumed to not change the shape of images. 80 | """ 81 | self._init(locals()) 82 | super(RandomOrderAug, self).__init__() 83 | 84 | def reset_state(self): 85 | super(RandomOrderAug, self).reset_state() 86 | for a in self.aug_lists: 87 | a.reset_state() 88 | 89 | def get_transform(self, img): 90 | # Note: this makes assumption that the augmentors do not make changes 91 | # to the image that will affect how the transforms will be instantiated 92 | # in the subsequent augmentors. 93 | idxs = self.rng.permutation(len(self.aug_lists)) 94 | tfms = [self.aug_lists[k].get_transform(img) 95 | for k in range(len(self.aug_lists))] 96 | return TransformList([tfms[k] for k in idxs]) 97 | 98 | 99 | class MapImage(ImageAugmentor): 100 | """ 101 | Map the image array by simple functions. 102 | """ 103 | 104 | def __init__(self, func, coord_func=None): 105 | """ 106 | Args: 107 | func: a function which takes an image array and return an augmented one 108 | coord_func: optional. A function which takes coordinates and return augmented ones. 109 | Coordinates should be Nx2 array of (x, y)s. 110 | """ 111 | super(MapImage, self).__init__() 112 | self.func = func 113 | self.coord_func = coord_func 114 | 115 | def get_transform(self, img): 116 | if self.coord_func: 117 | return TransformFactory(name="MapImage", apply_image=self.func, apply_coords=self.coord_func) 118 | else: 119 | return TransformFactory(name="MapImage", apply_image=self.func) 120 | -------------------------------------------------------------------------------- /tensorpack/models/models_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: _test.py 3 | 4 | 5 | import logging 6 | import unittest 7 | import tensorflow as tf 8 | import numpy as np 9 | 10 | from .conv2d import Conv2DTranspose 11 | from .pool import FixedUnPooling 12 | 13 | 14 | class TestModel(unittest.TestCase): 15 | 16 | def eval(self, x, feed_dict=None): 17 | sess = tf.Session() 18 | sess.run(tf.global_variables_initializer()) 19 | if isinstance(x, list): 20 | return sess.run(x, feed_dict=feed_dict) 21 | else: 22 | return sess.run([x], feed_dict=feed_dict)[0] 23 | 24 | def make_variable(self, *args): 25 | if len(args) > 1: 26 | return [tf.Variable(k) for k in args] 27 | else: 28 | return tf.Variable(args[0]) 29 | 30 | 31 | class TestPool(TestModel): 32 | def test_FixedUnPooling(self): 33 | h, w = 3, 4 34 | scale = 2 35 | mat = np.random.rand(h, w, 3).astype('float32') 36 | input = self.make_variable(mat) 37 | input = tf.reshape(input, [1, h, w, 3]) 38 | output = FixedUnPooling('unpool', input, scale) 39 | res = self.eval(output) 40 | self.assertEqual(res.shape, (1, scale * h, scale * w, 3)) 41 | 42 | # mat is on corner 43 | ele = res[0, ::scale, ::scale, 0] 44 | self.assertTrue((ele == mat[:, :, 0]).all()) 45 | # the rest are zeros 46 | res[0, ::scale, ::scale, :] = 0 47 | self.assertTrue((res == 0).all()) 48 | 49 | # Below was originally for the BilinearUpsample layer used in the HED example 50 | # def test_BilinearUpSample(self): 51 | # h, w = 12, 12 52 | # scale = 2 53 | # 54 | # mat = np.random.rand(h, w).astype('float32') 55 | # inp = self.make_variable(mat) 56 | # inp = tf.reshape(inp, [1, h, w, 1]) 57 | # 58 | # output = BilinearUpSample(inp, scale) 59 | # res = self.eval(output)[0, :, :, 0] 60 | # 61 | # from skimage.transform import rescale 62 | # res2 = rescale(mat, scale, mode='edge') 63 | # 64 | # diff = np.abs(res2 - res) 65 | # 66 | # # if not diff.max() < 1e-4: 67 | # # import IPython 68 | # # IPython.embed(config=IPython.terminal.ipapp.load_default_config()) 69 | # self.assertTrue(diff.max() < 1e-4, diff.max()) 70 | 71 | 72 | class TestConv2DTranspose(TestModel): 73 | def setUp(self): 74 | tf.reset_default_graph() 75 | 76 | def test_shape_match(self): 77 | h, w = 12, 18 78 | input = self.make_variable(np.random.rand(1, h, w, 3).astype("float32")) 79 | for padding in ["same", "valid"]: 80 | for stride in [1, 2]: 81 | output = Conv2DTranspose( 82 | 'deconv_s{}_pad{}'.format(stride, padding), 83 | input, 20, 3, strides=stride, padding=padding) 84 | 85 | static_shape = output.shape 86 | dynamic_shape = self.eval(output).shape 87 | self.assertTrue(static_shape == dynamic_shape) 88 | 89 | def test_unspecified_shape_match(self): 90 | h, w = 12, 18 91 | input = tfv1.placeholder(shape=(1, h, None, 3), dtype=tf.float32) 92 | for padding in ["same", "valid"]: 93 | for stride in [1, 2]: 94 | output = Conv2DTranspose( 95 | 'deconv_s{}_pad{}'.format(stride, padding), 96 | input, 20, 3, strides=stride, padding=padding) 97 | 98 | static_shape = tuple(output.shape.as_list()) 99 | dynamic_shape = self.eval( 100 | output, 101 | feed_dict={input: np.random.rand(1, h, w, 3)}).shape 102 | self.assertTrue(static_shape[2] is None) 103 | self.assertTrue(static_shape[:2] == dynamic_shape[:2]) 104 | self.assertTrue(static_shape[3] == dynamic_shape[3]) 105 | 106 | 107 | def run_test_case(case): 108 | suite = unittest.TestLoader().loadTestsFromTestCase(case) 109 | unittest.TextTestRunner(verbosity=2).run(suite) 110 | 111 | 112 | if __name__ == '__main__': 113 | from tensorpack.utils import logger 114 | logger.setLevel(logging.CRITICAL) 115 | unittest.main() 116 | -------------------------------------------------------------------------------- /tensorpack/callbacks/param_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import unittest 3 | import tensorflow as tf 4 | 5 | from ..utils import logger 6 | from ..train.trainers import NoOpTrainer 7 | from .param import ScheduledHyperParamSetter, ObjAttrParam 8 | 9 | 10 | class ParamObject(object): 11 | """ 12 | An object that holds the param to be set, for testing purposes. 13 | """ 14 | PARAM_NAME = 'param' 15 | 16 | def __init__(self): 17 | self.param_history = {} 18 | self.__dict__[self.PARAM_NAME] = 1.0 19 | 20 | def __setattr__(self, name, value): 21 | if name == self.PARAM_NAME: 22 | self._set_param(value) 23 | super(ParamObject, self).__setattr__(name, value) 24 | 25 | def _set_param(self, value): 26 | self.param_history[self.trainer.global_step] = value 27 | 28 | 29 | class ScheduledHyperParamSetterTest(unittest.TestCase): 30 | def setUp(self): 31 | self._param_obj = ParamObject() 32 | 33 | def tearDown(self): 34 | tf.reset_default_graph() 35 | 36 | def _create_trainer_with_scheduler(self, scheduler, 37 | steps_per_epoch, max_epoch, starting_epoch=1): 38 | trainer = NoOpTrainer() 39 | tf.get_variable(name='test_var', shape=[]) 40 | self._param_obj.trainer = trainer 41 | trainer.train_with_defaults( 42 | callbacks=[scheduler], 43 | extra_callbacks=[], 44 | monitors=[], 45 | steps_per_epoch=steps_per_epoch, 46 | max_epoch=max_epoch, 47 | starting_epoch=starting_epoch 48 | ) 49 | return self._param_obj.param_history 50 | 51 | def testInterpolation(self): 52 | scheduler = ScheduledHyperParamSetter( 53 | ObjAttrParam(self._param_obj, ParamObject.PARAM_NAME), 54 | [(30, 0.3), (40, 0.4), (50, 0.5)], interp='linear', step_based=True) 55 | history = self._create_trainer_with_scheduler(scheduler, 10, 50, starting_epoch=20) 56 | self.assertEqual(min(history.keys()), 30) 57 | self.assertEqual(history[30], 0.3) 58 | self.assertEqual(history[40], 0.4) 59 | self.assertEqual(history[45], 0.45) 60 | 61 | def testSchedule(self): 62 | scheduler = ScheduledHyperParamSetter( 63 | ObjAttrParam(self._param_obj, ParamObject.PARAM_NAME), 64 | [(10, 0.3), (20, 0.4), (30, 0.5)]) 65 | history = self._create_trainer_with_scheduler(scheduler, 1, 50) 66 | self.assertEqual(min(history.keys()), 10) 67 | self.assertEqual(len(history), 3) 68 | 69 | def testStartAfterSchedule(self): 70 | scheduler = ScheduledHyperParamSetter( 71 | ObjAttrParam(self._param_obj, ParamObject.PARAM_NAME), 72 | [(10, 0.3), (20, 0.4), (30, 0.5)], set_at_beginning=False) 73 | history = self._create_trainer_with_scheduler(scheduler, 1, 92, starting_epoch=90) 74 | self.assertEqual(len(history), 0) 75 | 76 | def testStartAfterSchedule_SetAtBeginning(self): 77 | scheduler = ScheduledHyperParamSetter( 78 | ObjAttrParam(self._param_obj, ParamObject.PARAM_NAME), 79 | [(10, 0.3), (20, 0.4), (30, 0.5)], set_at_beginning=True) 80 | history = self._create_trainer_with_scheduler(scheduler, 1, 92, starting_epoch=90) 81 | self.assertEqual(history, {0: 0.5}) 82 | 83 | def testWarningStartInTheMiddle(self): 84 | scheduler = ScheduledHyperParamSetter( 85 | ObjAttrParam(self._param_obj, ParamObject.PARAM_NAME), 86 | [(10, 0.3), (20, 0.4), (30, 0.5)]) 87 | with self.assertLogs(logger=logger._logger, level='WARNING'): 88 | self._create_trainer_with_scheduler(scheduler, 1, 21, starting_epoch=20) 89 | 90 | def testNoWarningStartInTheMiddle(self): 91 | scheduler = ScheduledHyperParamSetter( 92 | ObjAttrParam(self._param_obj, ParamObject.PARAM_NAME), 93 | [(10, 0.3), (20, 1.0), (30, 1.5)]) 94 | with unittest.mock.patch('tensorpack.utils.logger.warning') as warning: 95 | self._create_trainer_with_scheduler(scheduler, 1, 22, starting_epoch=21) 96 | self.assertFalse(warning.called) 97 | 98 | 99 | if __name__ == '__main__': 100 | unittest.main() 101 | -------------------------------------------------------------------------------- /tensorpack/utils/timer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: timer.py 3 | 4 | 5 | import atexit 6 | from collections import defaultdict 7 | from contextlib import contextmanager 8 | from time import perf_counter as timer # noqa 9 | 10 | from . import logger 11 | from .stats import StatCounter 12 | 13 | 14 | __all__ = ['timed_operation', 'IterSpeedCounter', 'Timer'] 15 | 16 | 17 | @contextmanager 18 | def timed_operation(msg, log_start=False): 19 | """ 20 | Surround a context with a timer. 21 | 22 | Args: 23 | msg(str): the log to print. 24 | log_start(bool): whether to print also at the beginning. 25 | 26 | Example: 27 | .. code-block:: python 28 | 29 | with timed_operation('Good Stuff'): 30 | time.sleep(1) 31 | 32 | Will print: 33 | 34 | .. code-block:: python 35 | 36 | Good stuff finished, time:1sec. 37 | """ 38 | assert len(msg) 39 | if log_start: 40 | logger.info('Start {} ...'.format(msg)) 41 | start = timer() 42 | yield 43 | msg = msg[0].upper() + msg[1:] 44 | logger.info('{} finished, time:{:.4f} sec.'.format( 45 | msg, timer() - start)) 46 | 47 | 48 | _TOTAL_TIMER_DATA = defaultdict(StatCounter) 49 | 50 | 51 | @contextmanager 52 | def total_timer(msg): 53 | """ A context which add the time spent inside to the global TotalTimer. """ 54 | start = timer() 55 | yield 56 | t = timer() - start 57 | _TOTAL_TIMER_DATA[msg].feed(t) 58 | 59 | 60 | def print_total_timer(): 61 | """ 62 | Print the content of the global TotalTimer, if it's not empty. This function will automatically get 63 | called when program exits. 64 | """ 65 | if len(_TOTAL_TIMER_DATA) == 0: 66 | return 67 | for k, v in _TOTAL_TIMER_DATA.items(): 68 | logger.info("Total Time: {} -> {:.2f} sec, {} times, {:.3g} sec/time".format( 69 | k, v.sum, v.count, v.average)) 70 | 71 | 72 | atexit.register(print_total_timer) 73 | 74 | 75 | class IterSpeedCounter(object): 76 | """ Test how often some code gets reached. 77 | 78 | Example: 79 | Print the speed of the iteration every 100 times. 80 | 81 | .. code-block:: python 82 | 83 | speed = IterSpeedCounter(100) 84 | for k in range(1000): 85 | # do something 86 | speed() 87 | """ 88 | 89 | def __init__(self, print_every, name=None): 90 | """ 91 | Args: 92 | print_every(int): interval to print. 93 | name(str): name to used when print. 94 | """ 95 | self.cnt = 0 96 | self.print_every = int(print_every) 97 | self.name = name if name else 'IterSpeed' 98 | 99 | def reset(self): 100 | self.start = timer() 101 | 102 | def __call__(self): 103 | if self.cnt == 0: 104 | self.reset() 105 | self.cnt += 1 106 | if self.cnt % self.print_every != 0: 107 | return 108 | t = timer() - self.start 109 | logger.info("{}: {:.2f} sec, {} times, {:.3g} sec/time".format( 110 | self.name, t, self.cnt, t / self.cnt)) 111 | 112 | 113 | class Timer(): 114 | """ 115 | A timer class which computes the time elapsed since the start/reset of the timer. 116 | """ 117 | def __init__(self): 118 | self.reset() 119 | 120 | def reset(self): 121 | """ 122 | Reset the timer. 123 | """ 124 | self._start = timer() 125 | self._paused = False 126 | self._total_paused = 0 127 | 128 | def pause(self): 129 | """ 130 | Pause the timer. 131 | """ 132 | assert self._paused is False 133 | self._paused = timer() 134 | 135 | def is_paused(self): 136 | return self._paused is not False 137 | 138 | def resume(self): 139 | """ 140 | Resume the timer. 141 | """ 142 | assert self._paused is not False 143 | self._total_paused += timer() - self._paused 144 | self._paused = False 145 | 146 | def seconds(self): 147 | """ 148 | Returns: 149 | float: the total number of seconds since the start/reset of the timer, excluding the 150 | time in between when the timer is paused. 151 | """ 152 | if self._paused: 153 | self.resume() 154 | self.pause() 155 | return timer() - self._start - self._total_paused 156 | -------------------------------------------------------------------------------- /tensorpack/train/interface.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: interface.py 3 | 4 | from ..compat import tfv1 5 | from ..input_source import FeedInput, InputSource, QueueInput, StagingInput 6 | from ..utils import logger 7 | from ..compat import is_tfv2 8 | from .config import TrainConfig 9 | from .tower import SingleCostTrainer 10 | from .trainers import SimpleTrainer 11 | 12 | __all__ = ['launch_train_with_config'] 13 | 14 | 15 | def apply_default_prefetch(input_source_or_dataflow, trainer): 16 | """ 17 | Apply a set of default rules to make a fast :class:`InputSource`. 18 | 19 | Args: 20 | input_source_or_dataflow(InputSource | DataFlow): 21 | trainer (Trainer): 22 | 23 | Returns: 24 | InputSource 25 | """ 26 | if not isinstance(input_source_or_dataflow, InputSource): 27 | # to mimic same behavior of the old trainer interface 28 | if type(trainer) == SimpleTrainer: 29 | input = FeedInput(input_source_or_dataflow) 30 | else: 31 | logger.info("Automatically applying QueueInput on the DataFlow.") 32 | input = QueueInput(input_source_or_dataflow) 33 | else: 34 | input = input_source_or_dataflow 35 | if hasattr(trainer, 'devices'): 36 | towers = trainer.devices 37 | if len(towers) > 1: # seem to only help on >1 GPUs 38 | assert not isinstance(trainer, SimpleTrainer) 39 | 40 | if isinstance(input, QueueInput): 41 | logger.info("Automatically applying StagingInput on the DataFlow.") 42 | input = StagingInput(input) 43 | return input 44 | 45 | 46 | def launch_train_with_config(config, trainer): 47 | """ 48 | Train with a :class:`TrainConfig` and a :class:`Trainer`, to 49 | present the simple and old training interface. It basically does the following 50 | 3 things (and you can easily do them by yourself if you need more control): 51 | 52 | 1. Setup the input with automatic prefetching heuristics, 53 | from `config.data` or `config.dataflow`. 54 | 2. Call `trainer.setup_graph` with the input as well as `config.model`. 55 | 3. Call `trainer.train` with rest of the attributes of config. 56 | 57 | See the `related tutorial 58 | `_ 59 | to learn more. 60 | 61 | Args: 62 | config (TrainConfig): 63 | trainer (Trainer): an instance of :class:`SingleCostTrainer`. 64 | 65 | Example: 66 | 67 | .. code-block:: python 68 | 69 | launch_train_with_config( 70 | config, SyncMultiGPUTrainerParameterServer(8, ps_device='gpu')) 71 | """ 72 | if is_tfv2(): 73 | tfv1.disable_eager_execution() 74 | 75 | assert isinstance(trainer, SingleCostTrainer), trainer 76 | assert isinstance(config, TrainConfig), config 77 | assert config.model is not None 78 | assert config.dataflow is not None or config.data is not None 79 | 80 | model = config.model 81 | input = config.data or config.dataflow 82 | input = apply_default_prefetch(input, trainer) 83 | 84 | # This is the only place where the `ModelDesc` abstraction is useful. 85 | # We should gradually stay away from this unuseful abstraction. 86 | # TowerFunc is a better abstraction (similar to tf.function in the future) 87 | trainer.setup_graph( 88 | model.get_input_signature(), input, 89 | model.build_graph, model.get_optimizer) 90 | _check_unused_regularization() 91 | trainer.train_with_defaults( 92 | callbacks=config.callbacks, 93 | monitors=config.monitors, 94 | session_creator=config.session_creator, 95 | session_init=config.session_init, 96 | steps_per_epoch=config.steps_per_epoch, 97 | starting_epoch=config.starting_epoch, 98 | max_epoch=config.max_epoch, 99 | extra_callbacks=config.extra_callbacks) 100 | 101 | 102 | def _check_unused_regularization(): 103 | coll = tfv1.get_collection(tfv1.GraphKeys.REGULARIZATION_LOSSES) 104 | unconsumed_reg = [] 105 | for c in coll: 106 | if len(c.consumers()) == 0: 107 | unconsumed_reg.append(c) 108 | if unconsumed_reg: 109 | logger.warn("The following tensors appear in REGULARIZATION_LOSSES collection but have no " 110 | "consumers! You may have forgotten to add regularization to total cost.") 111 | logger.warn("Unconsumed regularization: {}".format(', '.join([x.name for x in unconsumed_reg]))) 112 | -------------------------------------------------------------------------------- /tensorpack/dataflow/raw.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: raw.py 3 | 4 | 5 | import copy 6 | import numpy as np 7 | import six 8 | 9 | from .base import DataFlow, RNGDataFlow 10 | 11 | __all__ = ['FakeData', 'DataFromQueue', 'DataFromList', 'DataFromGenerator', 'DataFromIterable'] 12 | 13 | 14 | class FakeData(RNGDataFlow): 15 | """ Generate fake data of given shapes""" 16 | 17 | def __init__(self, shapes, size=1000, random=True, dtype='float32', domain=(0, 1)): 18 | """ 19 | Args: 20 | shapes (list): a list of lists/tuples. Shapes of each component. 21 | size (int): size of this DataFlow. 22 | random (bool): whether to randomly generate data every iteration. 23 | Note that merely generating the data could sometimes be time-consuming! 24 | dtype (str or list): data type as string, or a list of data types. 25 | domain (tuple or list): (min, max) tuple, or a list of such tuples 26 | """ 27 | super(FakeData, self).__init__() 28 | self.shapes = shapes 29 | self._size = int(size) 30 | self.random = random 31 | self.dtype = [dtype] * len(shapes) if isinstance(dtype, six.string_types) else dtype 32 | self.domain = [domain] * len(shapes) if isinstance(domain, tuple) else domain 33 | assert len(self.dtype) == len(self.shapes) 34 | assert len(self.domain) == len(self.domain) 35 | 36 | def __len__(self): 37 | return self._size 38 | 39 | def __iter__(self): 40 | if self.random: 41 | for _ in range(self._size): 42 | val = [] 43 | for k in range(len(self.shapes)): 44 | v = self.rng.rand(*self.shapes[k]) * (self.domain[k][1] - self.domain[k][0]) + self.domain[k][0] 45 | val.append(v.astype(self.dtype[k])) 46 | yield val 47 | else: 48 | val = [] 49 | for k in range(len(self.shapes)): 50 | v = self.rng.rand(*self.shapes[k]) * (self.domain[k][1] - self.domain[k][0]) + self.domain[k][0] 51 | val.append(v.astype(self.dtype[k])) 52 | for _ in range(self._size): 53 | yield copy.copy(val) 54 | 55 | 56 | class DataFromQueue(DataFlow): 57 | """ Produce data from a queue """ 58 | def __init__(self, queue): 59 | """ 60 | Args: 61 | queue (queue): a queue with ``get()`` method. 62 | """ 63 | self.queue = queue 64 | 65 | def __iter__(self): 66 | while True: 67 | yield self.queue.get() 68 | 69 | 70 | class DataFromList(RNGDataFlow): 71 | """ Wrap a list of datapoints to a DataFlow""" 72 | 73 | def __init__(self, lst, shuffle=True): 74 | """ 75 | Args: 76 | lst (list): input list. Each element is a datapoint. 77 | shuffle (bool): shuffle data. 78 | """ 79 | super(DataFromList, self).__init__() 80 | self.lst = lst 81 | self.shuffle = shuffle 82 | 83 | def __len__(self): 84 | return len(self.lst) 85 | 86 | def __iter__(self): 87 | if not self.shuffle: 88 | yield from self.lst 89 | else: 90 | idxs = np.arange(len(self.lst)) 91 | self.rng.shuffle(idxs) 92 | for k in idxs: 93 | yield self.lst[k] 94 | 95 | 96 | class DataFromGenerator(DataFlow): 97 | """ 98 | Wrap a generator to a DataFlow. 99 | The dataflow will not have length. 100 | """ 101 | def __init__(self, gen): 102 | """ 103 | Args: 104 | gen: iterable, or a callable that returns an iterable 105 | """ 106 | self._gen = gen 107 | 108 | def __iter__(self): 109 | if not callable(self._gen): 110 | yield from self._gen 111 | else: 112 | yield from self._gen() 113 | 114 | def __len__(self): 115 | return len(self._gen) 116 | 117 | 118 | class DataFromIterable(DataFlow): 119 | """ Wrap an iterable of datapoints to a DataFlow""" 120 | def __init__(self, iterable): 121 | """ 122 | Args: 123 | iterable: an iterable object 124 | """ 125 | self._itr = iterable 126 | try: 127 | self._len = len(iterable) 128 | except Exception: 129 | self._len = None 130 | 131 | def __len__(self): 132 | if self._len is None: 133 | raise NotImplementedError 134 | return self._len 135 | 136 | def __iter__(self): 137 | yield from self._itr 138 | -------------------------------------------------------------------------------- /tensorpack/models/layer_norm.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: layer_norm.py 3 | 4 | 5 | from ..compat import tfv1 as tf # this should be avoided first in model code 6 | 7 | from ..utils.argtools import get_data_format 8 | from ..utils.develop import log_deprecated 9 | from .common import VariableHolder, layer_register 10 | from .tflayer import convert_to_tflayer_args 11 | 12 | __all__ = ['LayerNorm', 'InstanceNorm'] 13 | 14 | 15 | @layer_register() 16 | @convert_to_tflayer_args( 17 | args_names=[], 18 | name_mapping={ 19 | 'use_bias': 'center', 20 | 'use_scale': 'scale', 21 | 'gamma_init': 'gamma_initializer', 22 | }) 23 | def LayerNorm( 24 | x, epsilon=1e-5, *, 25 | center=True, scale=True, 26 | gamma_initializer=tf.ones_initializer(), 27 | data_format='channels_last'): 28 | """ 29 | Layer Normalization layer, as described in the paper: 30 | `Layer Normalization `_. 31 | 32 | Args: 33 | x (tf.Tensor): a 4D or 2D tensor. When 4D, the layout should match data_format. 34 | epsilon (float): epsilon to avoid divide-by-zero. 35 | center, scale (bool): whether to use the extra affine transformation or not. 36 | """ 37 | data_format = get_data_format(data_format, keras_mode=False) 38 | shape = x.get_shape().as_list() 39 | ndims = len(shape) 40 | assert ndims in [2, 4] 41 | 42 | mean, var = tf.nn.moments(x, list(range(1, len(shape))), keep_dims=True) 43 | 44 | if data_format == 'NCHW': 45 | chan = shape[1] 46 | new_shape = [1, chan, 1, 1] 47 | else: 48 | chan = shape[-1] 49 | new_shape = [1, 1, 1, chan] 50 | if ndims == 2: 51 | new_shape = [1, chan] 52 | 53 | if center: 54 | beta = tf.get_variable('beta', [chan], initializer=tf.constant_initializer()) 55 | beta = tf.reshape(beta, new_shape) 56 | else: 57 | beta = tf.zeros([1] * ndims, name='beta') 58 | if scale: 59 | gamma = tf.get_variable('gamma', [chan], initializer=gamma_initializer) 60 | gamma = tf.reshape(gamma, new_shape) 61 | else: 62 | gamma = tf.ones([1] * ndims, name='gamma') 63 | 64 | ret = tf.nn.batch_normalization(x, mean, var, beta, gamma, epsilon, name='output') 65 | 66 | vh = ret.variables = VariableHolder() 67 | if scale: 68 | vh.gamma = gamma 69 | if center: 70 | vh.beta = beta 71 | return ret 72 | 73 | 74 | @layer_register() 75 | @convert_to_tflayer_args( 76 | args_names=[], 77 | name_mapping={ 78 | 'gamma_init': 'gamma_initializer', 79 | }) 80 | def InstanceNorm(x, epsilon=1e-5, *, center=True, scale=True, 81 | gamma_initializer=tf.ones_initializer(), 82 | data_format='channels_last', use_affine=None): 83 | """ 84 | Instance Normalization, as in the paper: 85 | `Instance Normalization: The Missing Ingredient for Fast Stylization 86 | `_. 87 | 88 | Args: 89 | x (tf.Tensor): a 4D tensor. 90 | epsilon (float): avoid divide-by-zero 91 | center, scale (bool): whether to use the extra affine transformation or not. 92 | use_affine: deprecated. Don't use. 93 | """ 94 | data_format = get_data_format(data_format, keras_mode=False) 95 | shape = x.get_shape().as_list() 96 | assert len(shape) == 4, "Input of InstanceNorm has to be 4D!" 97 | 98 | if use_affine is not None: 99 | log_deprecated("InstanceNorm(use_affine=)", "Use center= or scale= instead!", "2020-06-01") 100 | center = scale = use_affine 101 | 102 | if data_format == 'NHWC': 103 | axis = [1, 2] 104 | ch = shape[3] 105 | new_shape = [1, 1, 1, ch] 106 | else: 107 | axis = [2, 3] 108 | ch = shape[1] 109 | new_shape = [1, ch, 1, 1] 110 | assert ch is not None, "Input of InstanceNorm require known channel!" 111 | 112 | mean, var = tf.nn.moments(x, axis, keep_dims=True) 113 | 114 | if center: 115 | beta = tf.get_variable('beta', [ch], initializer=tf.constant_initializer()) 116 | beta = tf.reshape(beta, new_shape) 117 | else: 118 | beta = tf.zeros([1, 1, 1, 1], name='beta', dtype=x.dtype) 119 | if scale: 120 | gamma = tf.get_variable('gamma', [ch], initializer=gamma_initializer) 121 | gamma = tf.reshape(gamma, new_shape) 122 | else: 123 | gamma = tf.ones([1, 1, 1, 1], name='gamma', dtype=x.dtype) 124 | ret = tf.nn.batch_normalization(x, mean, var, beta, gamma, epsilon, name='output') 125 | 126 | vh = ret.variables = VariableHolder() 127 | if scale: 128 | vh.gamma = gamma 129 | if center: 130 | vh.beta = beta 131 | return ret 132 | -------------------------------------------------------------------------------- /tensorpack/dataflow/dataset/mnist.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: mnist.py 3 | 4 | 5 | import gzip 6 | import numpy 7 | import os 8 | 9 | from ...utils import logger 10 | from ...utils.fs import download, get_dataset_path 11 | from ..base import RNGDataFlow 12 | 13 | __all__ = ['Mnist', 'FashionMnist'] 14 | 15 | 16 | def maybe_download(url, work_directory): 17 | """Download the data from Yann's website, unless it's already here.""" 18 | filename = url.split('/')[-1] 19 | filepath = os.path.join(work_directory, filename) 20 | if not os.path.exists(filepath): 21 | logger.info("Downloading to {}...".format(filepath)) 22 | download(url, work_directory) 23 | return filepath 24 | 25 | 26 | def _read32(bytestream): 27 | dt = numpy.dtype(numpy.uint32).newbyteorder('>') 28 | return numpy.frombuffer(bytestream.read(4), dtype=dt)[0] 29 | 30 | 31 | def extract_images(filename): 32 | """Extract the images into a 4D uint8 numpy array [index, y, x, depth].""" 33 | with gzip.open(filename) as bytestream: 34 | magic = _read32(bytestream) 35 | if magic != 2051: 36 | raise ValueError( 37 | 'Invalid magic number %d in MNIST image file: %s' % 38 | (magic, filename)) 39 | num_images = _read32(bytestream) 40 | rows = _read32(bytestream) 41 | cols = _read32(bytestream) 42 | buf = bytestream.read(rows * cols * num_images) 43 | data = numpy.frombuffer(buf, dtype=numpy.uint8) 44 | data = data.reshape(num_images, rows, cols, 1) 45 | data = data.astype('float32') / 255.0 46 | return data 47 | 48 | 49 | def extract_labels(filename): 50 | """Extract the labels into a 1D uint8 numpy array [index].""" 51 | with gzip.open(filename) as bytestream: 52 | magic = _read32(bytestream) 53 | if magic != 2049: 54 | raise ValueError( 55 | 'Invalid magic number %d in MNIST label file: %s' % 56 | (magic, filename)) 57 | num_items = _read32(bytestream) 58 | buf = bytestream.read(num_items) 59 | labels = numpy.frombuffer(buf, dtype=numpy.uint8) 60 | return labels 61 | 62 | 63 | class Mnist(RNGDataFlow): 64 | """ 65 | Produces [image, label] in MNIST dataset, 66 | image is 28x28 in the range [0,1], label is an int. 67 | """ 68 | 69 | _DIR_NAME = 'mnist_data' 70 | _SOURCE_URL = 'http://yann.lecun.com/exdb/mnist/' 71 | 72 | def __init__(self, train_or_test, shuffle=True, dir=None): 73 | """ 74 | Args: 75 | train_or_test (str): either 'train' or 'test' 76 | shuffle (bool): shuffle the dataset 77 | """ 78 | if dir is None: 79 | dir = get_dataset_path(self._DIR_NAME) 80 | assert train_or_test in ['train', 'test'] 81 | self.train_or_test = train_or_test 82 | self.shuffle = shuffle 83 | 84 | def get_images_and_labels(image_file, label_file): 85 | f = maybe_download(self._SOURCE_URL + image_file, dir) 86 | images = extract_images(f) 87 | f = maybe_download(self._SOURCE_URL + label_file, dir) 88 | labels = extract_labels(f) 89 | assert images.shape[0] == labels.shape[0] 90 | return images, labels 91 | 92 | if self.train_or_test == 'train': 93 | self.images, self.labels = get_images_and_labels( 94 | 'train-images-idx3-ubyte.gz', 95 | 'train-labels-idx1-ubyte.gz') 96 | else: 97 | self.images, self.labels = get_images_and_labels( 98 | 't10k-images-idx3-ubyte.gz', 99 | 't10k-labels-idx1-ubyte.gz') 100 | 101 | def __len__(self): 102 | return self.images.shape[0] 103 | 104 | def __iter__(self): 105 | idxs = list(range(self.__len__())) 106 | if self.shuffle: 107 | self.rng.shuffle(idxs) 108 | for k in idxs: 109 | img = self.images[k].reshape((28, 28)) 110 | label = self.labels[k] 111 | yield [img, label] 112 | 113 | 114 | class FashionMnist(Mnist): 115 | """ 116 | Same API as :class:`Mnist`, but more fashion. 117 | """ 118 | 119 | _DIR_NAME = 'fashion_mnist_data' 120 | _SOURCE_URL = 'http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/' 121 | 122 | def get_label_names(self): 123 | """ 124 | Returns: 125 | [str]: the name of each class 126 | """ 127 | # copied from https://github.com/zalandoresearch/fashion-mnist 128 | return ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 129 | 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot'] 130 | 131 | 132 | if __name__ == '__main__': 133 | ds = Mnist('train') 134 | ds.reset_state() 135 | for _ in ds: 136 | from IPython import embed 137 | embed() 138 | break 139 | -------------------------------------------------------------------------------- /tensorpack/tfutils/sesscreate.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: sesscreate.py 3 | 4 | 5 | from ..compat import tfv1 as tf 6 | from ..utils import logger 7 | from .common import get_default_sess_config 8 | 9 | __all__ = ['NewSessionCreator', 'ReuseSessionCreator', 'SessionCreatorAdapter'] 10 | 11 | """ 12 | A SessionCreator should: 13 | create the session 14 | initialize all variables 15 | return a session that is ready to use 16 | not finalize the graph 17 | """ 18 | 19 | 20 | _WRN1 = """User-provided custom session config may not work due to TF bugs. If you saw logs like 21 | ``` 22 | tensorflow/core/common_runtime/gpu/gpu_device.cc:1433] Found device 0 with properties: 23 | ``` 24 | before this line, then your GPU has been initialized and custom GPU options may not take effect. """ 25 | 26 | _WRN2 = """To workaround this issue, you can do one of the following: 27 | 1. Avoid initializing the GPU too early. Find code that initializes the GPU and skip it. 28 | Typically examples are: creating a session; check GPU availability; check GPU number. 29 | 2. Manually set your GPU options earlier. You can create a session with custom 30 | GPU options at the beginning of your program, as described in 31 | https://github.com/tensorpack/tensorpack/issues/497 32 | """ 33 | 34 | 35 | class NewSessionCreator(tf.train.SessionCreator): 36 | def __init__(self, target='', config=None): 37 | """ 38 | Args: 39 | target, config: same as :meth:`Session.__init__()`. 40 | config: a :class:`tf.ConfigProto` instance, defaults to :func:`tfutils.get_default_sess_config()` 41 | """ 42 | self.target = target 43 | 44 | if config is None: 45 | # distributed trainer doesn't support user-provided config 46 | # we set this attribute so that they can check 47 | self.user_provided_config = False 48 | config = get_default_sess_config() 49 | else: 50 | self.user_provided_config = True 51 | logger.warn(_WRN1) 52 | logger.warn(_WRN2) 53 | self.config = config 54 | 55 | def create_session(self): 56 | sess = tf.Session(target=self.target, config=self.config) 57 | 58 | def blocking_op(x): 59 | """ 60 | Whether an op is possibly blocking. 61 | """ 62 | if x.op_def is not None and not x.op_def.is_stateful: 63 | return False 64 | if "Dequeue" in x.type or "Enqueue" in x.type: 65 | return True 66 | if "Unstage" in x.type: 67 | return True 68 | if x.type in ["ZMQPull"]: 69 | return True 70 | return False 71 | 72 | def run(op): 73 | try: 74 | from tensorflow.contrib.graph_editor import get_backward_walk_ops # deprecated 75 | except ImportError: 76 | from tensorflow.python.ops.op_selector import get_backward_walk_ops 77 | 78 | deps = get_backward_walk_ops(op, control_inputs=True) 79 | for dep_op in deps: 80 | if blocking_op(dep_op): 81 | logger.warn( 82 | "Initializer '{}' depends on a blocking op '{}'. " 83 | "This initializer is likely to hang!".format( 84 | op.name, dep_op.name)) 85 | 86 | sess.run(op) 87 | 88 | run(tf.global_variables_initializer()) 89 | run(tf.local_variables_initializer()) 90 | run(tf.tables_initializer()) 91 | return sess 92 | 93 | 94 | class ReuseSessionCreator(tf.train.SessionCreator): 95 | """ 96 | Returns an existing session. 97 | """ 98 | def __init__(self, sess): 99 | """ 100 | Args: 101 | sess (tf.Session): the session to reuse 102 | """ 103 | self.sess = sess 104 | 105 | def create_session(self): 106 | return self.sess 107 | 108 | 109 | class SessionCreatorAdapter(tf.train.SessionCreator): 110 | """ 111 | Apply a function on the output of a SessionCreator. Can be used to create a debug session. 112 | 113 | Note: 114 | Since TF 1.6, debug session may not work properly with Monitored session. 115 | This is a tensorflow bug. To use tfdbg, use the :class:`TFLocalCLIDebugHook` callback instead. 116 | """ 117 | def __init__(self, session_creator, func): 118 | """ 119 | Args: 120 | session_creator (tf.train.SessionCreator): a session creator 121 | func (tf.Session -> tf.Session): takes a session created by 122 | ``session_creator``, and return a new session to be returned by ``self.create_session`` 123 | """ 124 | self._creator = session_creator 125 | self._func = func 126 | 127 | def create_session(self): 128 | sess = self._creator.create_session() 129 | return self._func(sess) 130 | -------------------------------------------------------------------------------- /tensorpack/tfutils/common.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: common.py 3 | 4 | import tensorflow as tf 5 | 6 | from ..compat import tfv1 7 | from ..utils.argtools import graph_memoized 8 | from .collect_env import collect_env_info 9 | 10 | 11 | __all__ = ['get_default_sess_config', 12 | 'get_global_step_value', 13 | 'get_global_step_var', 14 | 'get_tf_version_tuple', 15 | 'collect_env_info' 16 | # 'get_op_tensor_name', 17 | # 'get_tensors_by_names', 18 | # 'get_op_or_tensor_by_name', 19 | ] 20 | 21 | 22 | def get_default_sess_config(mem_fraction=0.99): 23 | """ 24 | Return a tf.ConfigProto to use as default session config. 25 | You can modify the returned config to fit your needs. 26 | 27 | Args: 28 | mem_fraction(float): see the `per_process_gpu_memory_fraction` option 29 | in TensorFlow's GPUOptions protobuf: 30 | https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/protobuf/config.proto 31 | 32 | Returns: 33 | tf.ConfigProto: the config to use. 34 | """ 35 | conf = tfv1.ConfigProto() 36 | 37 | conf.allow_soft_placement = True 38 | # conf.log_device_placement = True 39 | 40 | conf.intra_op_parallelism_threads = 1 41 | conf.inter_op_parallelism_threads = 0 42 | # TF benchmark use cpu_count() - gpu_thread_count(), e.g. 80 - 8 * 2 43 | # Didn't see much difference. 44 | 45 | conf.gpu_options.per_process_gpu_memory_fraction = mem_fraction 46 | 47 | # This hurt performance of large data pipeline: 48 | # https://github.com/tensorflow/benchmarks/commit/1528c46499cdcff669b5d7c006b7b971884ad0e6 49 | # conf.gpu_options.force_gpu_compatible = True 50 | 51 | conf.gpu_options.allow_growth = True 52 | 53 | # from tensorflow.core.protobuf import rewriter_config_pb2 as rwc 54 | # conf.graph_options.rewrite_options.memory_optimization = \ 55 | # rwc.RewriterConfig.HEURISTICS 56 | 57 | # May hurt performance? 58 | # conf.graph_options.optimizer_options.global_jit_level = tf.OptimizerOptions.ON_1 59 | # conf.graph_options.place_pruned_graph = True 60 | return conf 61 | 62 | 63 | @graph_memoized 64 | def get_global_step_var(): 65 | """ 66 | Returns: 67 | tf.Tensor: the global_step variable in the current graph. Create if doesn't exist. 68 | """ 69 | scope = tfv1.VariableScope(reuse=False, name='') # the root vs 70 | with tfv1.variable_scope(scope): 71 | var = tfv1.train.get_or_create_global_step() 72 | return var 73 | 74 | 75 | def get_global_step_value(): 76 | """ 77 | Returns: 78 | int: global_step value in current graph and session 79 | 80 | Has to be called under a default session. 81 | """ 82 | 83 | return tfv1.train.global_step( 84 | tfv1.get_default_session(), 85 | get_global_step_var()) 86 | 87 | 88 | def get_op_tensor_name(name): 89 | """ 90 | Will automatically determine if ``name`` is a tensor name (ends with ':x') 91 | or a op name. 92 | If it is an op name, the corresponding tensor name is assumed to be ``op_name + ':0'``. 93 | 94 | Args: 95 | name(str): name of an op or a tensor 96 | Returns: 97 | tuple: (op_name, tensor_name) 98 | """ 99 | if len(name) >= 3 and name[-2] == ':': 100 | return name[:-2], name 101 | else: 102 | return name, name + ':0' 103 | 104 | 105 | def get_tensors_by_names(names): 106 | """ 107 | Get a list of tensors in the default graph by a list of names. 108 | 109 | Args: 110 | names (list): 111 | """ 112 | ret = [] 113 | G = tfv1.get_default_graph() 114 | for n in names: 115 | opn, varn = get_op_tensor_name(n) 116 | ret.append(G.get_tensor_by_name(varn)) 117 | return ret 118 | 119 | 120 | def get_op_or_tensor_by_name(name): 121 | """ 122 | Get either tf.Operation of tf.Tensor from names. 123 | 124 | Args: 125 | name (list[str] or str): names of operations or tensors. 126 | 127 | Raises: 128 | KeyError, if the name doesn't exist 129 | """ 130 | G = tfv1.get_default_graph() 131 | 132 | def f(n): 133 | if len(n) >= 3 and n[-2] == ':': 134 | return G.get_tensor_by_name(n) 135 | else: 136 | return G.get_operation_by_name(n) 137 | 138 | if not isinstance(name, list): 139 | return f(name) 140 | else: 141 | return list(map(f, name)) 142 | 143 | 144 | def gpu_available_in_session(): 145 | sess = tfv1.get_default_session() 146 | for dev in sess.list_devices(): 147 | if dev.device_type.lower() == 'gpu': 148 | return True 149 | return False 150 | 151 | 152 | def get_tf_version_tuple(): 153 | """ 154 | Return TensorFlow version as a 2-element tuple (for comparison). 155 | """ 156 | return tuple(map(int, tf.__version__.split('.')[:2])) 157 | -------------------------------------------------------------------------------- /tensorpack/tfutils/collect_env.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import sys 4 | import psutil 5 | import tensorflow as tf 6 | import numpy as np 7 | from collections import defaultdict, OrderedDict 8 | from tabulate import tabulate 9 | 10 | import tensorpack 11 | from ..compat import tfv1 12 | from ..utils.utils import find_library_full_path as find_library 13 | from ..utils.nvml import NVMLContext 14 | from ..libinfo import __git_version__ 15 | 16 | 17 | def parse_TF_build_info(): 18 | ret = OrderedDict() 19 | from tensorflow.python.platform import build_info 20 | try: 21 | for k, v in list(build_info.build_info.items()): 22 | if k == "cuda_version": 23 | ret["TF built with CUDA"] = v 24 | elif k == "cudnn_version": 25 | ret["TF built with CUDNN"] = v 26 | elif k == "cuda_compute_capabilities": 27 | ret["TF compute capabilities"] = ",".join([k.replace("compute_", "") for k in v]) 28 | return ret 29 | except AttributeError: 30 | pass 31 | try: 32 | ret["TF built with CUDA"] = build_info.cuda_version_number 33 | ret["TF built with CUDNN"] = build_info.cudnn_version_number 34 | except AttributeError: 35 | pass 36 | return ret 37 | 38 | 39 | def collect_env_info(): 40 | """ 41 | Returns: 42 | str - a table contains important information about the environment 43 | """ 44 | data = [] 45 | data.append(("sys.platform", sys.platform)) 46 | data.append(("Python", sys.version.replace("\n", ""))) 47 | data.append(("Tensorpack", __git_version__ + " @" + os.path.dirname(tensorpack.__file__))) 48 | data.append(("Numpy", np.__version__)) 49 | 50 | data.append(("TensorFlow", tfv1.VERSION + "/" + tfv1.GIT_VERSION + " @" + os.path.dirname(tf.__file__))) 51 | data.append(("TF Compiler Version", tfv1.COMPILER_VERSION)) 52 | has_cuda = tf.test.is_built_with_cuda() 53 | data.append(("TF CUDA support", has_cuda)) 54 | 55 | try: 56 | from tensorflow.python.framework import test_util 57 | data.append(("TF MKL support", test_util.IsMklEnabled())) 58 | except Exception: 59 | pass 60 | 61 | try: 62 | from tensorflow.python.framework import test_util 63 | data.append(("TF XLA support", test_util.is_xla_enabled())) 64 | except Exception: 65 | pass 66 | 67 | if has_cuda: 68 | data.append(("Nvidia Driver", find_library("nvidia-ml"))) 69 | data.append(("CUDA libs", find_library("cudart"))) 70 | data.append(("CUDNN libs", find_library("cudnn"))) 71 | for k, v in parse_TF_build_info().items(): 72 | data.append((k, v)) 73 | data.append(("NCCL libs", find_library("nccl"))) 74 | 75 | # List devices with NVML 76 | data.append( 77 | ("CUDA_VISIBLE_DEVICES", 78 | os.environ.get("CUDA_VISIBLE_DEVICES", "Unspecified"))) 79 | try: 80 | devs = defaultdict(list) 81 | with NVMLContext() as ctx: 82 | for idx, dev in enumerate(ctx.devices()): 83 | devs[dev.name()].append(str(idx)) 84 | 85 | for devname, devids in devs.items(): 86 | data.append( 87 | ("GPU " + ",".join(devids), devname)) 88 | except Exception: 89 | data.append(("GPU", "Not found with NVML")) 90 | 91 | vram = psutil.virtual_memory() 92 | data.append(("Free RAM", "{:.2f}/{:.2f} GB".format(vram.available / 1024**3, vram.total / 1024**3))) 93 | data.append(("CPU Count", psutil.cpu_count())) 94 | 95 | # Other important dependencies: 96 | try: 97 | import horovod 98 | data.append(("Horovod", horovod.__version__ + " @" + os.path.dirname(horovod.__file__))) 99 | except ImportError: 100 | pass 101 | 102 | try: 103 | import cv2 104 | data.append(("cv2", cv2.__version__)) 105 | except ImportError: 106 | pass 107 | 108 | import msgpack 109 | data.append(("msgpack", ".".join([str(x) for x in msgpack.version]))) 110 | 111 | has_prctl = True 112 | try: 113 | import prctl 114 | _ = prctl.set_pdeathsig # noqa 115 | except Exception: 116 | has_prctl = False 117 | data.append(("python-prctl", has_prctl)) 118 | 119 | return tabulate(data) 120 | 121 | 122 | if __name__ == '__main__': 123 | print(collect_env_info()) 124 | print("Detecting GPUs using TensorFlow:") 125 | try: 126 | # available since TF 1.14 127 | gpu_devices = tf.config.experimental.list_physical_devices('GPU') 128 | gpu_devices = [x.name for x in gpu_devices] 129 | except AttributeError: 130 | from tensorflow.python.client import device_lib 131 | local_device_protos = device_lib.list_local_devices() 132 | gpu_devices = [x.name for x in local_device_protos if x.device_type == 'GPU'] 133 | print("GPUs:", ", ".join(gpu_devices)) 134 | -------------------------------------------------------------------------------- /tensorpack/tfutils/argscope.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: argscope.py 3 | 4 | import copy 5 | from collections import defaultdict 6 | from contextlib import contextmanager 7 | from functools import wraps 8 | from inspect import getmembers, isfunction 9 | import tensorflow as tf 10 | 11 | from ..compat import is_tfv2 12 | from ..utils import logger 13 | from .model_utils import get_shape_str 14 | from .tower import get_current_tower_context 15 | 16 | __all__ = ['argscope', 'get_arg_scope', 'enable_argscope_for_module', 17 | 'enable_argscope_for_function'] 18 | 19 | _ArgScopeStack = [] 20 | 21 | 22 | @contextmanager 23 | def argscope(layers, **kwargs): 24 | """ 25 | Args: 26 | layers (list or layer): layer or list of layers to apply the arguments. 27 | 28 | Returns: 29 | a context where all appearance of these layer will by default have the 30 | arguments specified by kwargs. 31 | 32 | Example: 33 | .. code-block:: python 34 | 35 | with argscope(Conv2D, kernel_shape=3, nl=tf.nn.relu, out_channel=32): 36 | x = Conv2D('conv0', x) 37 | x = Conv2D('conv1', x) 38 | x = Conv2D('conv2', x, out_channel=64) # override argscope 39 | 40 | """ 41 | if not isinstance(layers, list): 42 | layers = [layers] 43 | 44 | for l in layers: 45 | assert hasattr(l, '__argscope_enabled__'), "Argscope not supported for {}".format(l) 46 | 47 | # need to deepcopy so that changes to new_scope does not affect outer scope 48 | new_scope = copy.deepcopy(get_arg_scope()) 49 | for l in layers: 50 | new_scope[l.__name__].update(kwargs) 51 | _ArgScopeStack.append(new_scope) 52 | yield 53 | del _ArgScopeStack[-1] 54 | 55 | 56 | def get_arg_scope(): 57 | """ 58 | Returns: 59 | dict: the current argscope. 60 | 61 | An argscope is a dict of dict: ``dict[layername] = {arg: val}`` 62 | """ 63 | if len(_ArgScopeStack) > 0: 64 | return _ArgScopeStack[-1] 65 | else: 66 | return defaultdict(dict) 67 | 68 | 69 | def enable_argscope_for_function(func, log_shape=True): 70 | """Decorator for function to support argscope 71 | 72 | Example: 73 | 74 | .. code-block:: python 75 | 76 | from mylib import myfunc 77 | myfunc = enable_argscope_for_function(myfunc) 78 | 79 | Args: 80 | func: A function mapping one or multiple tensors to one or multiple 81 | tensors. 82 | log_shape (bool): Specify whether the first input resp. output tensor 83 | shape should be printed once. 84 | 85 | Remarks: 86 | If the function ``func`` returns multiple input or output tensors, 87 | only the first input/output tensor shape is displayed during logging. 88 | 89 | Returns: 90 | The decorated function. 91 | 92 | """ 93 | 94 | assert callable(func), "func should be a callable" 95 | 96 | @wraps(func) 97 | def wrapped_func(*args, **kwargs): 98 | actual_args = copy.copy(get_arg_scope()[func.__name__]) 99 | actual_args.update(kwargs) 100 | out_tensor = func(*args, **actual_args) 101 | in_tensor = args[0] 102 | 103 | ctx = get_current_tower_context() 104 | name = func.__name__ if 'name' not in kwargs else kwargs['name'] 105 | if log_shape: 106 | if ('tower' not in ctx.ns_name.lower()) or ctx.is_main_training_tower: 107 | # we assume the first parameter is the most interesting 108 | if isinstance(out_tensor, tuple): 109 | out_tensor_descr = out_tensor[0] 110 | else: 111 | out_tensor_descr = out_tensor 112 | logger.info("{:<12}: {} --> {}".format( 113 | "'" + name + "'", 114 | get_shape_str(in_tensor), 115 | get_shape_str(out_tensor_descr))) 116 | 117 | return out_tensor 118 | wrapped_func.__argscope_enabled__ = True 119 | return wrapped_func 120 | 121 | 122 | def enable_argscope_for_module(module, log_shape=True): 123 | """ 124 | Overwrite all functions of a given module to support argscope. 125 | Note that this function monkey-patches the module and therefore could 126 | have unexpected consequences. 127 | It has been only tested to work well with ``tf.layers`` module. 128 | 129 | Example: 130 | 131 | .. code-block:: python 132 | 133 | import tensorflow as tf 134 | enable_argscope_for_module(tf.layers) 135 | 136 | Args: 137 | log_shape (bool): print input/output shapes of each function. 138 | """ 139 | if is_tfv2() and module == tf.layers: 140 | module = tfv1.layers 141 | for name, obj in getmembers(module): 142 | if isfunction(obj): 143 | setattr(module, name, enable_argscope_for_function(obj, 144 | log_shape=log_shape)) 145 | -------------------------------------------------------------------------------- /tensorpack/predict/multigpu.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: multigpu.py 3 | 4 | 5 | import tensorflow as tf 6 | 7 | from ..input_source import PlaceholderInput 8 | from ..tfutils.tower import PredictTowerContext 9 | from ..utils import logger 10 | from .base import OnlinePredictor 11 | 12 | __all__ = ['MultiTowerOfflinePredictor', 13 | 'DataParallelOfflinePredictor'] 14 | 15 | 16 | class MultiTowerOfflinePredictor(OnlinePredictor): 17 | """ A multi-tower multi-GPU predictor. 18 | It builds one predictor for each tower. 19 | """ 20 | 21 | def __init__(self, config, towers): 22 | """ 23 | Args: 24 | config (PredictConfig): the config to use. 25 | towers: a list of relative GPU id. 26 | """ 27 | assert len(towers) > 0 28 | self.graph = config._maybe_create_graph() 29 | self.predictors = [] 30 | self.return_input = config.return_input 31 | with self.graph.as_default(): 32 | handles = [] 33 | 34 | input = PlaceholderInput() 35 | input.setup(config.input_signature) 36 | 37 | for idx, t in enumerate(towers): 38 | tower_name = 'tower' + str(t) 39 | 40 | device = '/gpu:{}'.format(t) 41 | with tf.compat.v1.variable_scope (tf.get_variable_scope(), reuse=idx > 0), \ 42 | tf.device(device), \ 43 | PredictTowerContext(tower_name): 44 | logger.info("Building graph for predict tower '{}' on device {} ...".format(tower_name, device)) 45 | config.tower_func(*input.get_input_tensors()) 46 | handles.append(config.tower_func.towers[-1]) 47 | 48 | config.session_init._setup_graph() 49 | self.sess = config.session_creator.create_session() 50 | config.session_init._run_init(self.sess) 51 | 52 | for h in handles: 53 | input_tensors = h.get_tensors(config.input_names) 54 | output_tensors = h.get_tensors(config.output_names) 55 | self.predictors.append(OnlinePredictor( 56 | input_tensors, output_tensors, config.return_input, self.sess)) 57 | 58 | def _do_call(self, dp): 59 | # use the first tower for compatible PredictorBase interface 60 | return self.predictors[0]._do_call(dp) 61 | 62 | def get_predictor(self, n): 63 | """ 64 | Returns: 65 | OnlinePredictor: the nth predictor on the nth tower. 66 | """ 67 | l = len(self.predictors) 68 | if n >= l: 69 | logger.warn("n > #towers, will assign predictor to GPU by round-robin") 70 | return [self.predictors[k % l] for k in range(n)] 71 | 72 | def get_predictors(self): 73 | """ 74 | Returns: 75 | list[OnlinePredictor]: a list of predictor 76 | """ 77 | return self.predictors 78 | 79 | 80 | class DataParallelOfflinePredictor(OnlinePredictor): 81 | """ 82 | A data-parallel predictor. It builds one predictor that utilizes all GPUs. 83 | 84 | Note that it doesn't split/concat inputs/outputs automatically. 85 | Instead, its inputs are: 86 | ``[input[0] in tower[0], input[1] in tower[0], ..., input[0] in tower[1], input[1] in tower[1], ...]`` 87 | Similar for the outputs. 88 | """ 89 | 90 | def __init__(self, config, towers): 91 | """ 92 | Args: 93 | config (PredictConfig): the config to use. 94 | towers: a list of relative GPU id. 95 | """ 96 | self.graph = config._maybe_create_graph() 97 | with self.graph.as_default(): 98 | input_tensors = [] 99 | output_tensors = [] 100 | 101 | for idx, t in enumerate(towers): 102 | tower_name = 'tower' + str(t) 103 | 104 | new_sig = [tf.TensorSpec(dtype=p.dtype, shape=p.shape, name=tower_name + '_' + p.name) 105 | for p in config.input_signature] 106 | input = PlaceholderInput() 107 | input.setup(new_sig) 108 | 109 | with tf.compat.v1.variable_scope (tf.get_variable_scope(), reuse=idx > 0), \ 110 | tf.device('/gpu:{}'.format(t)), \ 111 | PredictTowerContext(tower_name): 112 | config.tower_func(*input.get_input_tensors()) 113 | h = config.tower_func.towers[-1] 114 | input_tensors.extend(h.get_tensors(config.input_names)) 115 | output_tensors.extend(h.get_tensors(config.output_names)) 116 | 117 | config.session_init._setup_graph() 118 | sess = config.session_creator.create_session() 119 | config.session_init._run_init(sess) 120 | super(DataParallelOfflinePredictor, self).__init__( 121 | input_tensors, output_tensors, config.return_input, sess) 122 | -------------------------------------------------------------------------------- /tensorpack/utils/palette.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: palette.py 3 | 4 | import numpy as np 5 | 6 | __all__ = ['PALETTE_RGB'] 7 | 8 | # Copied from https://stackoverflow.com/questions/2328339/how-to-generate-n-different-colors-for-any-natural-number-n 9 | PALETTE_HEX = [ 10 | "#000000", "#FFFF00", "#1CE6FF", "#FF34FF", "#FF4A46", "#008941", "#006FA6", "#A30059", 11 | "#FFDBE5", "#7A4900", "#0000A6", "#63FFAC", "#B79762", "#004D43", "#8FB0FF", "#997D87", 12 | "#5A0007", "#809693", "#FEFFE6", "#1B4400", "#4FC601", "#3B5DFF", "#4A3B53", "#FF2F80", 13 | "#61615A", "#BA0900", "#6B7900", "#00C2A0", "#FFAA92", "#FF90C9", "#B903AA", "#D16100", 14 | "#DDEFFF", "#000035", "#7B4F4B", "#A1C299", "#300018", "#0AA6D8", "#013349", "#00846F", 15 | "#372101", "#FFB500", "#C2FFED", "#A079BF", "#CC0744", "#C0B9B2", "#C2FF99", "#001E09", 16 | "#00489C", "#6F0062", "#0CBD66", "#EEC3FF", "#456D75", "#B77B68", "#7A87A1", "#788D66", 17 | "#885578", "#FAD09F", "#FF8A9A", "#D157A0", "#BEC459", "#456648", "#0086ED", "#886F4C", 18 | "#34362D", "#B4A8BD", "#00A6AA", "#452C2C", "#636375", "#A3C8C9", "#FF913F", "#938A81", 19 | "#575329", "#00FECF", "#B05B6F", "#8CD0FF", "#3B9700", "#04F757", "#C8A1A1", "#1E6E00", 20 | "#7900D7", "#A77500", "#6367A9", "#A05837", "#6B002C", "#772600", "#D790FF", "#9B9700", 21 | "#549E79", "#FFF69F", "#201625", "#72418F", "#BC23FF", "#99ADC0", "#3A2465", "#922329", 22 | "#5B4534", "#FDE8DC", "#404E55", "#0089A3", "#CB7E98", "#A4E804", "#324E72", "#6A3A4C", 23 | "#83AB58", "#001C1E", "#D1F7CE", "#004B28", "#C8D0F6", "#A3A489", "#806C66", "#222800", 24 | "#BF5650", "#E83000", "#66796D", "#DA007C", "#FF1A59", "#8ADBB4", "#1E0200", "#5B4E51", 25 | "#C895C5", "#320033", "#FF6832", "#66E1D3", "#CFCDAC", "#D0AC94", 26 | "#7ED379", "#012C58"] 27 | 28 | 29 | # Copied from https://github.com/facebookresearch/Detectron/blob/master/detectron/utils/colormap.py 30 | DETECTRON_PALETTE = np.array( 31 | [ 32 | 0.000, 0.447, 0.741, 33 | 0.850, 0.325, 0.098, 34 | 0.929, 0.694, 0.125, 35 | 0.494, 0.184, 0.556, 36 | 0.466, 0.674, 0.188, 37 | 0.301, 0.745, 0.933, 38 | 0.635, 0.078, 0.184, 39 | 0.300, 0.300, 0.300, 40 | 0.600, 0.600, 0.600, 41 | 1.000, 0.000, 0.000, 42 | 1.000, 0.500, 0.000, 43 | 0.749, 0.749, 0.000, 44 | 0.000, 1.000, 0.000, 45 | 0.000, 0.000, 1.000, 46 | 0.667, 0.000, 1.000, 47 | 0.333, 0.333, 0.000, 48 | 0.333, 0.667, 0.000, 49 | 0.333, 1.000, 0.000, 50 | 0.667, 0.333, 0.000, 51 | 0.667, 0.667, 0.000, 52 | 0.667, 1.000, 0.000, 53 | 1.000, 0.333, 0.000, 54 | 1.000, 0.667, 0.000, 55 | 1.000, 1.000, 0.000, 56 | 0.000, 0.333, 0.500, 57 | 0.000, 0.667, 0.500, 58 | 0.000, 1.000, 0.500, 59 | 0.333, 0.000, 0.500, 60 | 0.333, 0.333, 0.500, 61 | 0.333, 0.667, 0.500, 62 | 0.333, 1.000, 0.500, 63 | 0.667, 0.000, 0.500, 64 | 0.667, 0.333, 0.500, 65 | 0.667, 0.667, 0.500, 66 | 0.667, 1.000, 0.500, 67 | 1.000, 0.000, 0.500, 68 | 1.000, 0.333, 0.500, 69 | 1.000, 0.667, 0.500, 70 | 1.000, 1.000, 0.500, 71 | 0.000, 0.333, 1.000, 72 | 0.000, 0.667, 1.000, 73 | 0.000, 1.000, 1.000, 74 | 0.333, 0.000, 1.000, 75 | 0.333, 0.333, 1.000, 76 | 0.333, 0.667, 1.000, 77 | 0.333, 1.000, 1.000, 78 | 0.667, 0.000, 1.000, 79 | 0.667, 0.333, 1.000, 80 | 0.667, 0.667, 1.000, 81 | 0.667, 1.000, 1.000, 82 | 1.000, 0.000, 1.000, 83 | 1.000, 0.333, 1.000, 84 | 1.000, 0.667, 1.000, 85 | 0.167, 0.000, 0.000, 86 | 0.333, 0.000, 0.000, 87 | 0.500, 0.000, 0.000, 88 | 0.667, 0.000, 0.000, 89 | 0.833, 0.000, 0.000, 90 | 1.000, 0.000, 0.000, 91 | 0.000, 0.167, 0.000, 92 | 0.000, 0.333, 0.000, 93 | 0.000, 0.500, 0.000, 94 | 0.000, 0.667, 0.000, 95 | 0.000, 0.833, 0.000, 96 | 0.000, 1.000, 0.000, 97 | 0.000, 0.000, 0.167, 98 | 0.000, 0.000, 0.333, 99 | 0.000, 0.000, 0.500, 100 | 0.000, 0.000, 0.667, 101 | 0.000, 0.000, 0.833, 102 | 0.000, 0.000, 1.000, 103 | 0.000, 0.000, 0.000, 104 | 0.143, 0.143, 0.143, 105 | 0.286, 0.286, 0.286, 106 | 0.429, 0.429, 0.429, 107 | 0.571, 0.571, 0.571, 108 | 0.714, 0.714, 0.714, 109 | 0.857, 0.857, 0.857, 110 | 1.000, 1.000, 1.000 111 | ] 112 | ).astype(np.float32).reshape(-1, 3) * 255 113 | 114 | 115 | def _parse_hex_color(s): 116 | r = int(s[1:3], 16) 117 | g = int(s[3:5], 16) 118 | b = int(s[5:7], 16) 119 | return (r, g, b) 120 | 121 | 122 | # PALETTE_RGB = np.asarray( 123 | # list(map(_parse_hex_color, PALETTE_HEX)), 124 | # dtype='int32') 125 | 126 | # This seems more beautiful 127 | PALETTE_RGB = DETECTRON_PALETTE 128 | -------------------------------------------------------------------------------- /tensorpack/train/model_desc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File: model_desc.py 3 | 4 | 5 | import tensorflow as tf 6 | 7 | from ..utils.argtools import memoized_method 8 | from ..tfutils.common import get_op_tensor_name 9 | from ..tfutils.tower import get_current_tower_context 10 | from ..compat import _backport_tensor_spec, tfv1 11 | 12 | TensorSpec = _backport_tensor_spec() 13 | 14 | 15 | __all__ = ['ModelDesc', 'ModelDescBase'] 16 | 17 | 18 | class ModelDescBase(object): 19 | """ 20 | Base class for a model description. 21 | 22 | It is used for the simple training interface described in 23 | `Training Interface Tutorial `_. 24 | 25 | Subclass is expected to implement :meth:`inputs` and :meth:`build_graph`, as they 26 | together define a tower function. 27 | """ 28 | 29 | @memoized_method 30 | def get_input_signature(self): 31 | """ 32 | Returns: 33 | A list of :class:`tf.TensorSpec`, which describes the inputs of this model. 34 | The result is cached for each instance of :class:`ModelDescBase`. 35 | """ 36 | with tf.Graph().as_default() as G: # create these placeholder in a temporary graph 37 | inputs = self.inputs() 38 | assert isinstance(inputs, (list, tuple)), \ 39 | "ModelDesc.inputs() should return a list of tf.TensorSpec objects! Got {} instead.".format(str(inputs)) 40 | if isinstance(inputs[0], tf.Tensor): 41 | for p in inputs: 42 | assert "Placeholder" in p.op.type, \ 43 | "inputs() have to return TensorSpec or placeholders! Found {} instead.".format(p) 44 | assert p.graph == G, "Placeholders returned by inputs() should be created inside inputs()!" 45 | return [TensorSpec(shape=p.shape, dtype=p.dtype, name=get_op_tensor_name(p.name)[0]) for p in inputs] 46 | 47 | @property 48 | def input_names(self): 49 | """ 50 | list[str]: the names of all the inputs. 51 | """ 52 | return [k.name for k in self.get_input_signature()] 53 | 54 | def inputs(self): 55 | """ 56 | A subclass is expected to implement this method. 57 | 58 | If returning placeholders, 59 | the placeholders **have to** be created inside this method. 60 | Don't return placeholders created in other places. 61 | 62 | Also, users should never call this method by yourself. 63 | 64 | Returns: 65 | list[tf.TensorSpec or tfv1.placeholder]. 66 | """ 67 | raise NotImplementedError() 68 | 69 | def build_graph(self, *args): 70 | """ 71 | A subclass is expected to implement this method. 72 | 73 | Build the whole symbolic graph. 74 | This is supposed to be part of the "tower function" when used with :class:`TowerTrainer`. 75 | 76 | Args: 77 | args ([tf.Tensor]): tensors that matches the list of inputs defined by ``inputs()``. 78 | 79 | Returns: 80 | In general it returns nothing, but a subclass 81 | may require it to return necessary information to build the trainer. 82 | For example, `SingleCostTrainer` expect this method to return the cost tensor. 83 | """ 84 | raise NotImplementedError() 85 | 86 | @property 87 | def training(self): 88 | """ 89 | bool: whether the caller is under a training context or not. 90 | """ 91 | return get_current_tower_context().is_training 92 | 93 | 94 | class ModelDesc(ModelDescBase): 95 | """ 96 | One subclass of :class:`ModelDescBase` with the assupmtion of 97 | **single cost** and **single optimizer** training. 98 | It has the following constraints in addition to :class:`ModelDescBase`: 99 | 100 | 1. `build_graph(...)` method should return a cost tensor when called under a training context. 101 | The cost will be the final cost to be optimized by the optimizer. 102 | Therefore it should include necessary regularization. 103 | 104 | 2. Subclass is expected to implement :meth:`optimizer()` method. 105 | """ 106 | 107 | @memoized_method 108 | def get_optimizer(self): 109 | """ 110 | Return the memoized optimizer returned by `optimizer()`. 111 | 112 | Users of :class:`ModelDesc` will need to implement `optimizer()`, 113 | which will only be called once per each model. 114 | 115 | Returns: 116 | a :class:`tf.train.Optimizer` instance. 117 | """ 118 | ret = self.optimizer() 119 | assert isinstance(ret, tfv1.train.Optimizer), \ 120 | "ModelDesc.optimizer() must return an instance of tf.train.Optimizer! Got {} instead.".format(str(ret)) 121 | return ret 122 | 123 | def optimizer(self): 124 | """ 125 | A subclass is expected to implement this method. 126 | 127 | Returns: 128 | a `tf.train.Optimizer` instance. 129 | """ 130 | raise NotImplementedError() 131 | --------------------------------------------------------------------------------