From a629e64aa3db6930e84f3b01d92ee43368ac6faf Mon Sep 17 00:00:00 2001 From: rizar Date: Wed, 27 Jan 2016 10:32:05 -0500 Subject: [PATCH] Squashed 'libs/blocks/' changes from 72bb20b..38535d8 38535d8 Merge remote-tracking branch 'blocks-origin/master' into new_blocks ed481ef Merge pull request #953 from dwf/shut_up_sqlite 417dbdf Skip test_save_the_best with sqlite. f5ee622 Merge pull request #946 from vdumoulin/abstract_conv 648646d Add test for ConvolutionalTransposeActivation 87d143f Reorder constructor args to help remove duplicate code 843d3ff Replace deprecated `image_shape` kwarg with `input_shape` 65e9435 Clarify parameter description 6712e44 Make ConvolutionalTranspose inherit from Convolutional ff08eb8 Reduce repeated code in constructors. abf41f8 Make imshp not depend on input_ 7c895aa Construct grad op using constant values rather than depend on a shared variable 2a68d89 Fix superclass call 836267d Revert to not forcing compilation mode c237b51 Comment on the use of FAST_RUN mode in blocks.bricks.conv tests fa4f314 Fix conv tests failing to compile Theano functions c985b30 Fix import error ee5efc6 Fix oversights 071ad2b Add ConvolutionTranspose and ConvolutionTransposeActivation 9255647 Use Theano's new abstract_conv interface 348de31 Adapt Pooling to Theano's downsample -> pool renaming 98797a1 Merge pull request #941 from dwf/batch_norm_bricks 90cc8bb Fix StepRules to honour broadcastable. d9171ff Move __init__ logic to _allocate in SBN. a88b193 batch_normalization_updates -> get_batch_normalization_updates 725ec01 Idiomatize test. 4b39c93 Remove from 'with' for clarity. e714903 Make duplicate updates docs more explicit. 333b49c Rename W, b -> scale, shift. 09da496 Add batch_normalization_updates. d81c03c Annotate population parameters with brick. 474d3f1 Fix subtle bug with mutable metadata. b03fffc Fix typo in epsilon assignment. 6895b87 Remove needless properties. f41c286 Improve _add_batch_axis. cf4f4db Ensure correct nesting of context managers. 9805b73 Improve docs. Add example for apply_... 7efa956 Correct the assertion. 5ba9b38 Add comment and assertion following review. c70a26b Fix Scrutinizer error from 6ba3364. 2ea41a8 Improve robustness of context manager logic. e22b151 Add an assertion following review. a26e2c6 Simplify generator expression after review. d35c22b Amend error message following review. 2e7cd0e Rename save_memory -> conserve_memory. 3dc9135 More tests, move graph tests to tests/graph. ad3a84d Fix doctest. 73a6902 Comments explaining tests. 93e55f6 Big refactor to enable context manager operation. 59fd7cd Add brick filter and smoke test for bug fixed. b358849 Remove leading batch axis from BN shared variables. 02982e9 Correctly crawl the graph backward. f26c1e5 Expose save_memory in BatchNormalizedMLP. 7ede89b Make batch_normalize return the pop->minibatch map. 9ff63b0 Add tests. 6b52b35 Refactor batchnorm + graph module. 9b94116 Reorganize bricks subpackage. 0f6623f Initial batch-normalization implementation. 2046525 Merge pull request #944 from mila-udem/bound_application_error_msg 07d43ae Correct error message in filter.py. 07cec2c Merge pull request #774 from lamblin/update_doc 25c5141 Merge pull request #940 from rizar/without_namespace_packages c0640b0 Disable namespace packages suppport 83c5d2a Merge pull request #838 from matthiasreisser/docstring_additions 260296d Merge pull request #929 from dwf/conv_doc_fixes acf9c80 Merge pull request #930 from r2007/patch-1 353d525 Update bricks_overview.rst 506dfe1 Merge pull request #928 from dwf/shared_floatx_kwargs b7f5df3 Pass along kwargs in shared_like. fce7f6b Merge pull request #925 from dwf/get_output_shape_update ff70906 More robust get_output_shape call. 928dcbb Better document get_output_shape. c6cd413 Update ConvolutionalSequence docs. 5928f34 Pass shared_floatx kwargs to theano.shared. 3eddcf8 Merge pull request #921 from dwf/die_conv_layer_die 65e9952 Replace deprecated getOutputShape interface. 589543e Remove mention of `ConvolutionalLayer` from a docstring. 3cd7bfd Get rid of ConvolutionalLayer. Fixes #912. df5c55c Merge pull request #924 from dwf/fix_doctest_debugprint eedab38 Fix debugprint output for Theano/Theano#1953. 6827a9e Merge pull request #891 from dmitriy-serdyuk/release-doc 1d63fa8 Merge pull request #899 from dwf/conv_improvements 769eb70 Test for overzealous support code. 6177388 Making unpickling robust to MaxPooling change. 24a489a Merge pull request #909 from akhti/doc_fix 7734be3 Increase font size in the generator scheme 04783b7 Merge pull request #886 from sotelo/return_states_seq_gen f7866d8 Merge pull request #892 from akhti/fix_bidir 05fea66 Merge pull request #907 from dmitriy-serdyuk/fix-log-doc 7576bcc Add API sections for two log backends b87ad2e Make ignore_border=True the default. c374557 Test Pooling Bricks work in ConvolutionalSequence. 27d71a5 Tests for AveragePooling, new MaxPooling flags. f1e5908 AveragePooling Brick. ac9f095 ConvolutionalSequence: use num_output_channels. dbb407c Refactor MaxPooling. 414519d Merge pull request #903 from dwf/fix_travis_again e185f3d Hardcode a prefix for Travis Miniconda install. 7e10ed5 Merge pull request #897 from dwf/custom_scaling_dropout c4426c3 Notes on dropout usage. 07c98e2 Add dropout tests, including custom_divisor. d7d8b62 apply_dropout: support a custom divisor. 9ffc3e1 Improve apply_dropout documentation. 23d94b2 Fix label in sequence generator scheme 6750051 Merge pull request #896 from rizar/add_original_svg d7eb341 Update after review. 8950f73 Backport fixes done to Fuel's install doc 68842e7 Update developer doc to mention Blocks and Fuel ea34217 Add the original for SequenceGenerator picture 455d65f Improve docs for making new release bc4f62c Fix too long line error 46652f7 Add test for a stack of bidirectional layers 98aed8e Add instructions to make a new release 3ec837a Merge pull request #887 from rizar/fix_confpy_and_bump_version 4f74b4c Merge pull request #888 from akhti/fix_bidir 69bc613 Add get_dim to Bidirectional 0a4e3b3 Fixes conf.py and bumps version to 0.1.1 afaa45f Added the final values of states as auxiliary variables in a sequence generator so they can be reused. 12e50d9 Merge pull request #772 from adbrebs/doc_brick 92654e1 Merge pull request #881 from mila-udem/correct_main_loop_error_message 5dc2bdd brick tutorial improvements 168c7a8 Merge pull request #878 from rizar/release-0.1 f275332 Merge pull request #879 from sotelo/generator_cost_documentation e43cfbe Correct main loop message for the interrupt 6c99076 Added the missing links. c11698c Merge pull request #880 from sotelo/missing_apply_decorator_documentation 679b01c Improved the error message when an application decorator is missing. efe3585 Solved the flake8 mistake. 6ebcb37 Added documentation about the emitter cost in sequence generators. a06878f Bump version number 0f889aa Merge pull request #875 from dwf/use_bias_convolutional_activation_layer 0ca4e86 Merge pull request #873 from dwf/border_mode_conv_sequence d1af6c9 ConvolutionalSequence: propagate use_bias. 7bc11d4 Convolutional{Activation,Layer}: respect use_bias. 457049d Convolutional{Activation,Layer}: Refactor alloc. 6dec566 ConvolutionalSequence: Don't auto push border_mode. b3754dc Merge pull request #849 from mila-udem/redefine_modelr 837da08 Merge pull request #868 from lukemetz/lm/set_parameter_values 8a06204 warn if setting wrong shape parameters c843fa8 Merge pull request #867 from dwf/conv2d_impl 620bc7b Additional developer documentation. 8a8e8c1 Fix Scrutinizer complaint about whitespace. c2ebc25 Make Convolutional's conv implementation hackable. c427fa7 Merge pull request #864 from mila-udem/rtd2 377688c Add mocking back in 2fc06f3 Merge pull request #861 from mila-udem/rtd c5b9f1c Changes 743cbf3 Add a test and fix imports fe9daeb Back to items() bb81302 Call __init__ and add iteritems a9751cb Merge pull request #852 from rizar/testing_utils 5772cc3 Fix formatting 8fd7da4 Improve documentation 0cc3031 Fix imports in doctests 3100e11 Move testing utilities to make them importable from outside 5899425 Merge pull request #844 from mila-udem/mention_extras_and_examples b46dbe0 Refactor model and write a bit of documentation f6a99bc Mention examples in the documentation as well. f38881a Add forgotten period 3c1fdb5 Also refer to Fuel 96d63bf Better looking reference names bb3f6c8 Mention blocks-extras and blocks-examples in README.md 8bf07e7 typos 4b83783 Merge pull request #839 from mila-udem/deps 1436c25 Add nose 59a9553 Update deps f295d76 Added line break cba09d2 Changed docstring to plural bba2e2d added docstring to apply method of LSTM d576831 Small fixes in create your own brick tutorial bdff06b Merge pull request #1 from dmitriy-serdyuk/doc_brick 5da4696 Separate paragraph 536bf18 Make small fixes in create your own brick e377e43 Merge pull request #834 from yingzha/dev bd12f44 Rephrase docs f6e9896 Merge pull request #777 from galv/master 03a491b Add new line to comment block. ecd121d Pass input from ComputationGraph.get_theano_function() to theano.function() 53979de Example of lazy brick with get_dim 5628491 More explanations in the tutorial to create a custom brick 627da45 Refactoring. Brick example with children. ca1c7cf Very first draft of the tutorial to create a brick. 7e2535d Merge pull request #835 from johnarevalo/patch-2 e844b92 Allow uint type for lookup indexing cebec4e Replace uses of named_copy with var.copy(name=...) 7ff0f6b Merge pull request #827 from vzhong/conv-1d bc140ba fix for case in which no image size is specified for Convolution #825 9e4d0e4 Merge pull request #826 from lukemetz/lm/algorithm_roles a976b9f add roles and names to algorithm shared variables 98ed3d1 Merge pull request #823 from rizar/sgd_profile d335fd0 use parens 1f16ba5 add test a4b50e0 add theano_func_kwargs to algorithm init and use respectively 3403846 add kwargs to base class initialize func 53f292e remove unnecessary vars 3b0bbd0 add profile via kwargs to gradient descent initialize 0f6b4e0 Merge pull request #819 from rizar/fix_iterate_false 67f7388 Remove old code 2a8dff3 Arguments for SimpleRecurrent should be required 69b8ce2 Merge pull request #818 from ASalvail/master 436134a Reformated modified doc for docstrings requirements. 0e15726 Reformated modified doc for docstrings requirements. e5e14a4 Elaborate LSTM inputs argument to apply method da5d320 Merge pull request #815 from yingzha/dev f88a638 Merge pull request #2 from dwf/yingzha_ccw 79243e5 Simplify documentation. dce9c48 Merge pull request #813 from dwf/parallel_routing c7d3540 Use OrderedDict per Dima's review. b17fc13 Refactor into utils func, per Dima's request. 23fd90a Merge pull request #1 from dwf/yingzha_ccw d3c5caa Add test for argument Parallel.apply argument validation. 16c50ab Fix Parallel.apply argument routing logic. e26d04d Fix for str config with default of None. 3144fc9 remove dir argument in test_config 0fe9a92 fixed flake8/pep errors 29099d3 Override base path for temporary file creation f2c9f3e Merge pull request #798 from ASalvail/master 760b144 Merge pull request #808 from mila-udem/selector_docs d7bf7f7 Remove unnecessary import in doctest. 45c400a Eschew needless initialization. 4f3a075 Reword per Dima's review. f95f919 Improve Selector.get_parameter documentation. 325a431 Merge pull request #805 from mila-udem/cost_cost_matrix_args_kwargs 83151ca Make Cost and CostMatrix more general. 9c2a1a1 Merge pull request #803 from Tejas-Khot/Tejas-Khot-patch-1 5c1062b made line length less than 75 characters d299457 corrected trailing whitespace problem 968edbc added docstring for tied_biases 0a8cc3d Corrected docstring of Softplus. e48bbb9 Add activation tests (Softplus, Softmax, Logistic) 9ad61bc Softplus docs and reference. fab1fbe Added a Softplus brick. c72a833 Merge pull request #759 from rizar/good_softmax_for_all 4da97cd Merge pull request #790 from mila-udem/linear_docs c689353 Make Linear docstring clearer. git-subtree-dir: libs/blocks git-subtree-split: 38535d89ec166d8b4bef912d4745a51231cdc6da --- .travis.yml | 4 +- README.rst | 8 + blocks/__init__.py | 5 +- blocks/algorithms/__init__.py | 76 ++- blocks/bricks/__init__.py | 747 +-------------------- blocks/bricks/base.py | 5 +- blocks/bricks/bn.py | 348 ++++++++++ blocks/bricks/conv.py | 401 ++++++++--- blocks/bricks/cost.py | 8 +- blocks/bricks/interfaces.py | 214 ++++++ blocks/bricks/lookup.py | 2 +- blocks/bricks/parallel.py | 9 +- blocks/bricks/recurrent.py | 26 +- blocks/bricks/sequence_generators.py | 13 + blocks/bricks/sequences.py | 153 +++++ blocks/bricks/simple.py | 395 +++++++++++ blocks/config.py | 13 + blocks/filter.py | 5 +- blocks/{graph.py => graph/__init__.py} | 173 ++--- blocks/graph/annotations.py | 116 ++++ blocks/graph/bn.py | 269 ++++++++ blocks/main_loop.py | 25 +- blocks/model.py | 221 +++--- blocks/roles.py | 101 ++- blocks/select.py | 34 +- blocks/serialization.py | 5 +- blocks/utils/__init__.py | 146 +++- blocks/utils/testing.py | 128 ++++ docs/_static/sequence_generator_scheme.png | Bin 187434 -> 189314 bytes docs/_static/sequence_generator_scheme.svg | 698 +++++++++++++++++++ docs/api/log.rst | 19 + docs/bricks_overview.rst | 2 +- docs/conf.py | 7 +- docs/create_your_own_brick.rst | 433 ++++++++++++ docs/development/docs.rst | 28 +- docs/development/index.rst | 120 +++- docs/index.rst | 8 +- docs/setup.rst | 9 +- docs/tutorial.rst | 2 +- doctests/__init__.py | 2 +- req-rtd.txt | 14 +- req-travis-conda.txt | 18 +- req-travis-pip.txt | 11 +- requirements.txt | 8 +- setup.py | 4 +- tests/__init__.py | 107 --- tests/algorithms/test_algorithms.py | 74 +- tests/bricks/test_bn.py | 306 +++++++++ tests/bricks/test_bricks.py | 10 +- tests/bricks/test_conv.py | 257 ++++++- tests/bricks/test_recurrent.py | 47 +- tests/extensions/test_monitoring.py | 4 +- tests/extensions/test_timing.py | 2 +- tests/extensions/test_training.py | 9 +- tests/graph/test_bn.py | 133 ++++ tests/test_graph.py | 33 +- tests/test_main_loop.py | 2 +- tests/test_model.py | 22 +- tests/test_serialization.py | 11 +- tests/utils/test_utils.py | 60 +- 60 files changed, 4703 insertions(+), 1407 deletions(-) create mode 100644 blocks/bricks/bn.py create mode 100644 blocks/bricks/interfaces.py create mode 100644 blocks/bricks/sequences.py create mode 100644 blocks/bricks/simple.py rename blocks/{graph.py => graph/__init__.py} (78%) create mode 100644 blocks/graph/annotations.py create mode 100644 blocks/graph/bn.py create mode 100644 blocks/utils/testing.py create mode 100644 docs/_static/sequence_generator_scheme.svg create mode 100644 docs/create_your_own_brick.rst create mode 100644 tests/bricks/test_bn.py create mode 100644 tests/graph/test_bn.py diff --git a/.travis.yml b/.travis.yml index e0b42b1..8b2e0ad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,13 +25,13 @@ before_install: - # Setup Python environment with BLAS libraries - wget -q http://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh - chmod +x miniconda.sh - - ./miniconda.sh -b + - ./miniconda.sh -b -p $HOME/miniconda - export PATH=$HOME/miniconda/bin:$PATH - conda update -q --yes conda - export FUEL_DATA_PATH=$TRAVIS_BUILD_DIR/data install: # Install all Python dependencies - - conda install -q --yes python=$TRAVIS_PYTHON_VERSION mkl --file req-travis-conda.txt + - conda install -q --yes python=$TRAVIS_PYTHON_VERSION --file req-travis-conda.txt - pip install -q -r req-travis-pip.txt script: - pip install -e . -r requirements.txt # Tests setup.py diff --git a/README.rst b/README.rst index e24f0e2..2ec5faf 100644 --- a/README.rst +++ b/README.rst @@ -33,6 +33,11 @@ In the future we also hope to support: * Dimension, type and axes-checking +See Also: + * `Fuel`_, the data processing engine developed primarily for Blocks. + * `Blocks-examples`_ for maintained examples of scripts using Blocks. + * `Blocks-extras`_ for semi-maintained additional Blocks components. + Citing Blocks If you use Blocks or Fuel in your work, we'd really appreciate it if you could cite the following paper: @@ -47,3 +52,6 @@ Contributing .. _documentation: http://blocks.readthedocs.org .. _developer guidelines: http://blocks.readthedocs.org/en/latest/development/index.html .. _Blocks and Fuel\: Frameworks for deep learning: http://arxiv.org/abs/1506.00619 +.. _Blocks-examples: https://github.com/mila-udem/blocks-examples +.. _Blocks-extras: https://github.com/mila-udem/blocks-extras +.. _Fuel: https://github.com/mila-udem/fuel diff --git a/blocks/__init__.py b/blocks/__init__.py index 37850e3..d648e01 100644 --- a/blocks/__init__.py +++ b/blocks/__init__.py @@ -1,5 +1,2 @@ """The blocks library for parametrized Theano ops.""" -# Scary warning: Adding code to this file can break namespace packages -# See https://pythonhosted.org/setuptools/setuptools.html#namespace-packages -__import__("pkg_resources").declare_namespace(__name__) -__version__ = '0.0.1' +__version__ = '0.1.1' diff --git a/blocks/algorithms/__init__.py b/blocks/algorithms/__init__.py index 6449c13..96a36fd 100644 --- a/blocks/algorithms/__init__.py +++ b/blocks/algorithms/__init__.py @@ -12,8 +12,10 @@ from theano import tensor from blocks.graph import ComputationGraph -from blocks.utils import dict_subset, named_copy, pack, shared_floatx +from blocks.roles import add_role, ALGORITHM_HYPERPARAMETER, ALGORITHM_BUFFER from blocks.theano_expressions import l2_norm +from blocks.utils import (dict_subset, pack, shared_floatx, + shared_floatx_zeros_matching) logger = logging.getLogger(__name__) @@ -30,7 +32,7 @@ class TrainingAlgorithm(object): """ @abstractmethod - def initialize(self): + def initialize(self, **kwargs): """Initialize the training algorithm.""" pass @@ -191,6 +193,10 @@ class GradientDescent(DifferentiableCostMinimizer): be backpropagated. Only makes sense when `gradients` is `None`. on_unused_sources : str, one of 'raise' (default), 'ignore', 'warn' Controls behavior when not all sources are used. + theano_func_kwargs : dict, optional + A passthrough to `theano.function` for additional arguments. + Useful for passing `profile` or `mode` arguments to the theano + function that will be compiled for the algorithm. Attributes ---------- @@ -201,9 +207,8 @@ class GradientDescent(DifferentiableCostMinimizer): """ def __init__(self, step_rule=None, gradients=None, known_grads=None, - consider_constant=None, - on_unused_sources='raise', - **kwargs): + consider_constant=None, on_unused_sources='raise', + theano_func_kwargs=None, **kwargs): if gradients: kwargs.setdefault("parameters", gradients.keys()) super(GradientDescent, self).__init__(**kwargs) @@ -226,13 +231,15 @@ def __init__(self, step_rule=None, gradients=None, known_grads=None, "gradients are passed in") self.step_rule = step_rule if step_rule else Scale() - self.total_gradient_norm = named_copy(l2_norm(self.gradients.values()), - "total_gradient_norm") + self.total_gradient_norm = l2_norm( + self.gradients.values()).copy(name="total_gradient_norm") self.steps, self.step_rule_updates = ( self.step_rule.compute_steps(self.gradients)) - self.total_step_norm = named_copy(l2_norm(self.steps.values()), - "total_step_norm") + self.total_step_norm = l2_norm( + self.steps.values()).copy(name="total_step_norm") self.on_unused_sources = on_unused_sources + self.theano_func_kwargs = (theano_func_kwargs if theano_func_kwargs + is not None else dict()) def initialize(self): logger.info("Initializing the training algorithm") @@ -243,7 +250,8 @@ def initialize(self): for parameter in self.parameters: all_updates.append((parameter, parameter - self.steps[parameter])) all_updates += self.step_rule_updates - self._function = theano.function(self.inputs, [], updates=all_updates) + self._function = theano.function( + self.inputs, [], updates=all_updates, **self.theano_func_kwargs) logger.info("The training algorithm is initialized") def _validate_source_names(self, batch): @@ -386,7 +394,8 @@ class Scale(StepRule): """ def __init__(self, learning_rate=1.0): - self.learning_rate = shared_floatx(learning_rate) + self.learning_rate = shared_floatx(learning_rate, "learning_rate") + add_role(self.learning_rate, ALGORITHM_HYPERPARAMETER) def compute_step(self, parameter, previous_step): return self.learning_rate * previous_step, [] @@ -408,10 +417,12 @@ class BasicMomentum(StepRule): """ def __init__(self, momentum=0.): - self.momentum = shared_floatx(momentum) + self.momentum = shared_floatx(momentum, "momentum") + add_role(self.momentum, ALGORITHM_HYPERPARAMETER) def compute_step(self, parameter, previous_step): - velocity = shared_floatx(parameter.get_value() * 0.) + velocity = shared_floatx_zeros_matching(parameter, "velocity") + add_role(velocity, ALGORITHM_BUFFER) step = self.momentum * velocity + previous_step updates = [(velocity, step)] return step, updates @@ -471,12 +482,18 @@ class AdaDelta(StepRule): def __init__(self, decay_rate=0.95, epsilon=1e-6): if not 0.0 <= decay_rate <= 1.0: raise ValueError("decay rate needs to be in [0, 1]") - self.decay_rate = shared_floatx(decay_rate) - self.epsilon = shared_floatx(epsilon) + self.decay_rate = shared_floatx(decay_rate, "decay_rate") + add_role(self.decay_rate, ALGORITHM_HYPERPARAMETER) + self.epsilon = shared_floatx(epsilon, "epsilon") + add_role(self.epsilon, ALGORITHM_HYPERPARAMETER) def compute_step(self, parameter, previous_step): - mean_square_step_tm1 = shared_floatx(parameter.get_value() * 0.) - mean_square_delta_x_tm1 = shared_floatx(parameter.get_value() * 0.) + mean_square_step_tm1 = shared_floatx_zeros_matching( + parameter, "mean_square_step_tm1") + add_role(mean_square_step_tm1, ALGORITHM_BUFFER) + mean_square_delta_x_tm1 = shared_floatx_zeros_matching( + parameter, "mean_square_delta_x_tm1") + add_role(mean_square_delta_x_tm1, ALGORITHM_BUFFER) mean_square_step_t = ( self.decay_rate * mean_square_step_tm1 + @@ -529,14 +546,18 @@ def __init__(self, decay_rate=0.9, max_scaling=1e5): raise ValueError("decay rate needs to be in [0, 1]") if max_scaling <= 0: raise ValueError("max. scaling needs to be greater than 0") - self.decay_rate = shared_floatx(decay_rate) + self.decay_rate = shared_floatx(decay_rate, "decay_rate") + add_role(self.decay_rate, ALGORITHM_HYPERPARAMETER) self.epsilon = 1. / max_scaling def compute_step(self, parameter, previous_step): - mean_square_step_tm1 = shared_floatx(parameter.get_value() * 0.) + mean_square_step_tm1 = shared_floatx_zeros_matching( + parameter, "mean_square_step_tm1") + add_role(mean_square_step_tm1, ALGORITHM_BUFFER) mean_square_step_t = ( self.decay_rate * mean_square_step_tm1 + (1 - self.decay_rate) * tensor.sqr(previous_step)) + add_role(mean_square_step_t, ALGORITHM_BUFFER) rms_step_t = tensor.maximum( tensor.sqrt(mean_square_step_t), self.epsilon) step = previous_step / rms_step_t @@ -607,7 +628,8 @@ class StepClipping(StepRule): """ def __init__(self, threshold=None): if threshold: - self.threshold = shared_floatx(threshold) + self.threshold = shared_floatx(threshold, "threshold") + add_role(self.threshold, ALGORITHM_HYPERPARAMETER) def compute_steps(self, previous_steps): if not hasattr(self, 'threshold'): @@ -671,7 +693,8 @@ class VariableClipping(StepRule): def __init__(self, threshold, axis=None): axis = pack(axis) if axis is not None else () self.axis = set(axis) - self.threshold = shared_floatx(threshold) + self.threshold = shared_floatx(threshold, "threshold") + add_role(self.threshold, ALGORITHM_HYPERPARAMETER) if len(axis) != len(self.axis): raise ValueError("axis must be unique") @@ -727,8 +750,8 @@ def compute_step(self, parameter, previous_step): name = 'adagrad_sqs' if parameter.name: name += '_' + parameter.name - ssq = shared_floatx(parameter.get_value() * 0., - name=name) + ssq = shared_floatx_zeros_matching(parameter, name=name) + add_role(ssq, ALGORITHM_BUFFER) ssq_t = (tensor.sqr(previous_step) + ssq) step = (self.learning_rate * previous_step / @@ -773,9 +796,12 @@ def __init__(self, learning_rate=0.002, self.decay_factor = decay_factor def compute_step(self, parameter, previous_step): - mean = shared_floatx(parameter.get_value() * 0., 'mean') - variance = shared_floatx(parameter.get_value() * 0., 'variance') + mean = shared_floatx_zeros_matching(parameter, 'mean') + add_role(mean, ALGORITHM_BUFFER) + variance = shared_floatx_zeros_matching(parameter, 'variance') + add_role(variance, ALGORITHM_BUFFER) time = shared_floatx(0., 'time') + add_role(time, ALGORITHM_BUFFER) t1 = time + 1 learning_rate = (self.learning_rate * diff --git a/blocks/bricks/__init__.py b/blocks/bricks/__init__.py index a9e953c..4482f03 100644 --- a/blocks/bricks/__init__.py +++ b/blocks/bricks/__init__.py @@ -1,729 +1,18 @@ -"""The interface of bricks and some simple implementations.""" -import logging - -import numpy -from six import add_metaclass -from theano import tensor -from theano.sandbox.rng_mrg import MRG_RandomStreams -from toolz import interleave -from picklable_itertools.extras import equizip - -from blocks.config import config -from blocks.bricks.base import application, _Brick, Brick, lazy -from blocks.bricks.wrappers import WithExtraDims -from blocks.roles import add_role, WEIGHT, BIAS -from blocks.utils import pack, shared_floatx_nans, named_copy - -logger = logging.getLogger(__name__) - - -class Random(Brick): - """A mixin class for Bricks which need Theano RNGs. - - Parameters - ---------- - theano_seed : int or list, optional - Seed to use for a - :class:`~theano.sandbox.rng_mrg.MRG_RandomStreams` object. - - """ - seed_rng = numpy.random.RandomState(config.default_seed) - - def __init__(self, theano_seed=None, **kwargs): - super(Random, self).__init__(**kwargs) - self.theano_seed = theano_seed - - @property - def theano_seed(self): - if getattr(self, '_theano_seed', None) is not None: - return self._theano_seed - else: - self._theano_seed = self.seed_rng.randint( - numpy.iinfo(numpy.int32).max) - return self._theano_seed - - @theano_seed.setter - def theano_seed(self, value): - if hasattr(self, '_theano_seed'): - raise AttributeError("seed already set") - self._theano_seed = value - - @property - def theano_rng(self): - """Returns Brick's Theano RNG, or a default one. - - The default seed can be set through ``blocks.config``. - - """ - if not hasattr(self, '_theano_rng'): - self._theano_rng = MRG_RandomStreams(self.theano_seed) - return self._theano_rng - - @theano_rng.setter - def theano_rng(self, theano_rng): - self._theano_rng = theano_rng - - -class Initializable(Brick): - """Base class for bricks which push parameter initialization. - - Many bricks will initialize children which perform a linear - transformation, often with biases. This brick allows the weights - and biases initialization to be configured in the parent brick and - pushed down the hierarchy. - - Parameters - ---------- - weights_init : object - A `NdarrayInitialization` instance which will be used by to - initialize the weight matrix. Required by - :meth:`~.Brick.initialize`. - biases_init : :obj:`object`, optional - A `NdarrayInitialization` instance that will be used to initialize - the biases. Required by :meth:`~.Brick.initialize` when `use_bias` - is `True`. Only supported by bricks for which :attr:`has_biases` is - ``True``. - use_bias : :obj:`bool`, optional - Whether to use a bias. Defaults to `True`. Required by - :meth:`~.Brick.initialize`. Only supported by bricks for which - :attr:`has_biases` is ``True``. - rng : :class:`numpy.random.RandomState` - - Attributes - ---------- - has_biases : bool - ``False`` if the brick does not support biases, and only has - :attr:`weights_init`. For an example of this, see - :class:`.Bidirectional`. If this is ``False``, the brick does not - support the arguments ``biases_init`` or ``use_bias``. - - """ - has_biases = True - seed_rng = numpy.random.RandomState(config.default_seed) - - @lazy() - def __init__(self, weights_init=None, biases_init=None, use_bias=True, - seed=None, **kwargs): - super(Initializable, self).__init__(**kwargs) - self.weights_init = weights_init - if self.has_biases: - self.biases_init = biases_init - elif biases_init is not None or not use_bias: - raise ValueError("This brick does not support biases config") - self.use_bias = use_bias - self.seed = seed - - @property - def seed(self): - if getattr(self, '_seed', None) is not None: - return self._seed - else: - self._seed = self.seed_rng.randint( - numpy.iinfo(numpy.int32).max) - return self._seed - - @seed.setter - def seed(self, value): - if hasattr(self, '_seed'): - raise AttributeError("seed already set") - self._seed = value - - @property - def rng(self): - if getattr(self, '_rng', None) is not None: - return self._rng - else: - self._rng = numpy.random.RandomState(self.seed) - return self._rng - - @rng.setter - def rng(self, rng): - self._rng = rng - - def _push_initialization_config(self): - for child in self.children: - if isinstance(child, Initializable): - child.rng = self.rng - if self.weights_init: - child.weights_init = self.weights_init - if hasattr(self, 'biases_init') and self.biases_init: - for child in self.children: - if (isinstance(child, Initializable) and - hasattr(child, 'biases_init')): - child.biases_init = self.biases_init - - -class Feedforward(Brick): - """Declares an interface for bricks with one input and one output. - - Many bricks have just one input and just one output (activations, - :class:`Linear`, :class:`MLP`). To make such bricks interchangable - in most contexts they should share an interface for configuring - their input and output dimensions. This brick declares such an - interface. - - Attributes - ---------- - input_dim : int - The input dimension of the brick. - output_dim : int - The output dimension of the brick. - - """ - def __getattr__(self, name): - message = ("'{}' object does not have an attribute '{}'" - .format(self.__class__.__name__, name)) - if name in ('input_dim', 'output_dim'): - message += (" (which is a part of 'Feedforward' interface it" - " claims to support)") - raise AttributeError(message) - - -class Linear(Initializable, Feedforward): - r"""A linear transformation with optional bias. - - Linear brick which applies a linear (affine) transformation by - multiplying the input with a weight matrix. Optionally a bias is added. - - Parameters - ---------- - input_dim : int - The dimension of the input. Required by :meth:`~.Brick.allocate`. - output_dim : int - The dimension of the output. Required by :meth:`~.Brick.allocate`. - - Notes - ----- - See :class:`Initializable` for initialization parameters. - - A linear transformation with bias is a matrix multiplication followed - by a vector summation. - - .. math:: f(\mathbf{x}) = \mathbf{W}\mathbf{x} + \mathbf{b} - - """ - @lazy(allocation=['input_dim', 'output_dim']) - def __init__(self, input_dim, output_dim, **kwargs): - super(Linear, self).__init__(**kwargs) - self.input_dim = input_dim - self.output_dim = output_dim - - @property - def W(self): - return self.parameters[0] - - @property - def b(self): - return self.parameters[1] - - def _allocate(self): - W = shared_floatx_nans((self.input_dim, self.output_dim), name='W') - add_role(W, WEIGHT) - self.parameters.append(W) - self.add_auxiliary_variable(W.norm(2), name='W_norm') - if self.use_bias: - b = shared_floatx_nans((self.output_dim,), name='b') - add_role(b, BIAS) - self.parameters.append(b) - self.add_auxiliary_variable(b.norm(2), name='b_norm') - - def _initialize(self): - if self.use_bias: - W, b = self.parameters - self.biases_init.initialize(b, self.rng) - else: - W, = self.parameters - self.weights_init.initialize(W, self.rng) - - @application(inputs=['input_'], outputs=['output']) - def apply(self, input_): - """Apply the linear transformation. - - Parameters - ---------- - input_ : :class:`~tensor.TensorVariable` - The input on which to apply the transformation - - Returns - ------- - output : :class:`~tensor.TensorVariable` - The transformed input plus optional bias - - """ - if self.use_bias: - W, b = self.parameters - else: - W, = self.parameters - output = tensor.dot(input_, W) - if self.use_bias: - output += b - return output - - def get_dim(self, name): - if name == 'input_': - return self.input_dim - if name == 'output': - return self.output_dim - super(Linear, self).get_dim(name) - - -class Bias(Feedforward, Initializable): - """Add a bias (i.e. sum with a vector).""" - @lazy(allocation=['dim']) - def __init__(self, dim, **kwargs): - super(Bias, self).__init__(**kwargs) - self.dim = dim - - def _allocate(self): - b = shared_floatx_nans((self.output_dim,), name='b') - add_role(b, BIAS) - self.parameters.append(b) - - def _initialize(self): - b, = self.parameters - self.biases_init.initialize(b, self.rng) - - @application(inputs=['input_'], outputs=['output']) - def apply(self, input_): - """Apply the linear transformation. - - Parameters - ---------- - input_ : :class:`~tensor.TensorVariable` - The input on which to apply the transformation - - Returns - ------- - output : :class:`~tensor.TensorVariable` - The transformed input plus optional bias - - """ - b, = self.parameters - return input_ + b - - def get_dim(self, name): - if name in ['input_', 'output']: - return self.dim - super(Bias, self).get_dim(name) - - def _get_dim(self): - return self.dim - - def _set_dim(self, value): - self.dim = value - - input_dim = output_dim = property(_get_dim, _set_dim) - - -class Maxout(Brick): - """Maxout pooling transformation. - - A brick that does max pooling over groups of input units. If you use - this code in a research project, please cite [GWFM13]_. - - .. [GWFM13] Ian J. Goodfellow, David Warde-Farley, Mehdi Mirza, Aaron - Courville, and Yoshua Bengio, *Maxout networks*, ICML (2013), pp. - 1319-1327. - - Parameters - ---------- - num_pieces : int - The size of the groups the maximum is taken over. - - Notes - ----- - Maxout applies a set of linear transformations to a vector and selects - for each output dimension the result with the highest value. - - """ - @lazy(allocation=['num_pieces']) - def __init__(self, num_pieces, **kwargs): - super(Maxout, self).__init__(**kwargs) - self.num_pieces = num_pieces - - @application(inputs=['input_'], outputs=['output']) - def apply(self, input_): - """Apply the maxout transformation. - - Parameters - ---------- - input_ : :class:`~tensor.TensorVariable` - The input on which to apply the transformation - - Returns - ------- - output : :class:`~tensor.TensorVariable` - The transformed input - - """ - last_dim = input_.shape[-1] - output_dim = last_dim // self.num_pieces - new_shape = ([input_.shape[i] for i in range(input_.ndim - 1)] + - [output_dim, self.num_pieces]) - output = tensor.max(input_.reshape(new_shape, ndim=input_.ndim + 1), - axis=input_.ndim) - return output - - -class LinearMaxout(Initializable, Feedforward): - """Maxout pooling following a linear transformation. - - This code combines the :class:`Linear` brick with a :class:`Maxout` - brick. - - Parameters - ---------- - input_dim : int - The dimension of the input. Required by :meth:`~.Brick.allocate`. - output_dim : int - The dimension of the output. Required by :meth:`~.Brick.allocate`. - num_pieces : int - The number of linear functions. Required by - :meth:`~.Brick.allocate`. - - Notes - ----- - See :class:`Initializable` for initialization parameters. - - """ - @lazy(allocation=['input_dim', 'output_dim', 'num_pieces']) - def __init__(self, input_dim, output_dim, num_pieces, **kwargs): - super(LinearMaxout, self).__init__(**kwargs) - self.linear = Linear() - self.maxout = Maxout() - self.children = [self.linear, - self.maxout] - - self.input_dim = input_dim - self.output_dim = output_dim - self.num_pieces = num_pieces - - @property - def input_dim(self): - return self.linear.input_dim - - @input_dim.setter - def input_dim(self, value): - self.linear.input_dim = value - - def _push_allocation_config(self): - self.linear.output_dim = self.output_dim * self.num_pieces - self.maxout.num_pieces = self.num_pieces - - @application(inputs=['input_'], outputs=['output']) - def apply(self, input_): - """Apply the linear transformation followed by maxout. - - Parameters - ---------- - input_ : :class:`~tensor.TensorVariable` - The input on which to apply the transformations - - Returns - ------- - output : :class:`~tensor.TensorVariable` - The transformed input - - """ - pre_activation = self.linear.apply(input_) - output = self.maxout.apply(pre_activation) - return output - - -class ActivationDocumentation(_Brick): - """Dynamically adds documentation to activations. - - Notes - ----- - See http://bugs.python.org/issue12773. - - """ - def __new__(cls, name, bases, classdict): - classdict['__doc__'] = \ - """Elementwise application of {0} function.""".format(name.lower()) - if 'apply' in classdict: - classdict['apply'].__doc__ = \ - """Apply the {0} function element-wise. - - Parameters - ---------- - input_ : :class:`~tensor.TensorVariable` - Theano variable to apply {0} to, element-wise. - - Returns - ------- - output : :class:`~tensor.TensorVariable` - The input with the activation function applied. - - """.format(name.lower()) - return super(ActivationDocumentation, cls).__new__(cls, name, bases, - classdict) - - -@add_metaclass(ActivationDocumentation) -class Activation(Brick): - """A base class for simple, element-wise activation functions. - - This base class ensures that activation functions are automatically - documented using the :class:`ActivationDocumentation` metaclass. - - """ - pass - - -class Identity(Activation): - @application(inputs=['input_'], outputs=['output']) - def apply(self, input_): - return input_ - - -class Tanh(Activation): - @application(inputs=['input_'], outputs=['output']) - def apply(self, input_): - return tensor.tanh(input_) - - -class Logistic(Activation): - @application(inputs=['input_'], outputs=['output']) - def apply(self, input_): - return tensor.nnet.sigmoid(input_) - - -class Rectifier(Activation): - @application(inputs=['input_'], outputs=['output']) - def apply(self, input_): - return tensor.switch(input_ > 0, input_, 0) - - -class Softmax(Brick): - """A softmax brick. - - Works with 2-dimensional inputs only. If you need more, - see :class:`NDimensionalSoftmax`. - - """ - @application(inputs=['input_'], outputs=['output']) - def apply(self, input_): - """Standard softmax. - - Parameters - ---------- - input_ : :class:`~theano.Variable` - A matrix, each row contains unnormalized log-probabilities of a - distribution. - - Returns - ------- - output_ : :class:`~theano.Variable` - A matrix with probabilities in each row for each distribution - from `input_`. - - """ - return tensor.nnet.softmax(input_) - - @application(inputs=['input_'], outputs=['output']) - def log_probabilities(self, input_): - """Normalize log-probabilities. - - Converts unnormalized log-probabilities (exponents of which do not - sum to one) into actual log-probabilities (exponents of which sum - to one). - - Parameters - ---------- - input_ : :class:`~theano.Variable` - A matrix, each row contains unnormalized log-probabilities of a - distribution. - - Returns - ------- - output : :class:`~theano.Variable` - A matrix with normalized log-probabilities in each row for each - distribution from `input_`. - - """ - shifted = input_ - input_.max(axis=1, keepdims=True) - return shifted - tensor.log( - tensor.exp(shifted).sum(axis=1, keepdims=True)) - - @application(inputs=['y', 'x'], outputs=['output']) - def categorical_cross_entropy(self, application_call, y, x): - """Computationally stable cross-entropy for pre-softmax values. - - Parameters - ---------- - y : :class:`~tensor.TensorVariable` - In the case of a matrix argument, each row represents a - probabilility distribution. In the vector case, each element - represents a distribution by specifying the position of 1 in a - 1-hot vector. - x : :class:`~tensor.TensorVariable` - A matrix, each row contains unnormalized probabilities of a - distribution. - - Returns - ------- - cost : :class:`~tensor.TensorVariable` - A vector of cross-entropies between respective distributions - from y and x. - - """ - x = self.log_probabilities(x) - application_call.add_auxiliary_variable( - named_copy(x, 'log_probabilities')) - if y.ndim == x.ndim - 1: - indices = tensor.arange(y.shape[0]) * x.shape[1] + y - cost = -x.flatten()[indices] - elif y.ndim == x.ndim: - cost = -(x * y).sum(axis=1) - else: - raise TypeError('rank mismatch between x and y') - return cost - - -class NDimensionalSoftmax(Softmax): - decorators = [WithExtraDims()] - - -class Sequence(Brick): - """A sequence of bricks. - - This brick applies a sequence of bricks, assuming that their in- and - outputs are compatible. - - Parameters - ---------- - application_methods : list - List of :class:`.BoundApplication` to apply - - """ - def __init__(self, application_methods, **kwargs): - super(Sequence, self).__init__(**kwargs) - self.application_methods = application_methods - - seen = set() - self.children = [app.brick for app in application_methods - if not (app.brick in seen or seen.add(app.brick))] - - @application - def apply(self, *args): - child_input = args - for application_method in self.application_methods: - output = application_method(*pack(child_input)) - child_input = output - return output - - @apply.property('inputs') - def apply_inputs(self): - return self.application_methods[0].inputs - - @apply.property('outputs') - def apply_outputs(self): - return self.application_methods[-1].outputs - - -class FeedforwardSequence(Sequence, Feedforward): - """A sequence where the first and last bricks are feedforward. - - Parameters - ---------- - application_methods : list - List of :class:`.BoundApplication` to apply. The first and last - application method should belong to a :class:`Feedforward` brick. - - """ - @property - def input_dim(self): - return self.children[0].input_dim - - @input_dim.setter - def input_dim(self, value): - self.children[0].input_dim = value - - @property - def output_dim(self): - return self.children[-1].output_dim - - @output_dim.setter - def output_dim(self, value): - self.children[-1].output_dim = value - - -class MLP(Sequence, Initializable, Feedforward): - """A simple multi-layer perceptron. - - Parameters - ---------- - activations : list of :class:`.Brick`, :class:`.BoundApplication`, - or ``None`` - A list of activations to apply after each linear transformation. - Give ``None`` to not apply any activation. It is assumed that the - application method to use is ``apply``. Required for - :meth:`__init__`. - dims : list of ints - A list of input dimensions, as well as the output dimension of the - last layer. Required for :meth:`~.Brick.allocate`. - - Notes - ----- - See :class:`Initializable` for initialization parameters. - - Note that the ``weights_init``, ``biases_init`` and ``use_bias`` - configurations will overwrite those of the layers each time the - :class:`MLP` is re-initialized. For more fine-grained control, push the - configuration to the child layers manually before initialization. - - >>> from blocks.initialization import IsotropicGaussian, Constant - >>> mlp = MLP(activations=[Tanh(), None], dims=[30, 20, 10], - ... weights_init=IsotropicGaussian(), - ... biases_init=Constant(1)) - >>> mlp.push_initialization_config() # Configure children - >>> mlp.children[0].weights_init = IsotropicGaussian(0.1) - >>> mlp.initialize() - - """ - @lazy(allocation=['dims']) - def __init__(self, activations, dims, **kwargs): - self.activations = activations - - self.linear_transformations = [Linear(name='linear_{}'.format(i)) - for i in range(len(activations))] - # Interleave the transformations and activations - application_methods = [] - for entity in interleave([self.linear_transformations, activations]): - if entity is None: - continue - if isinstance(entity, Brick): - application_methods.append(entity.apply) - else: - application_methods.append(entity) - if not dims: - dims = [None] * (len(activations) + 1) - self.dims = dims - super(MLP, self).__init__(application_methods, **kwargs) - - @property - def input_dim(self): - return self.dims[0] - - @input_dim.setter - def input_dim(self, value): - self.dims[0] = value - - @property - def output_dim(self): - return self.dims[-1] - - @output_dim.setter - def output_dim(self, value): - self.dims[-1] = value - - def _push_allocation_config(self): - if not len(self.dims) - 1 == len(self.linear_transformations): - raise ValueError - for input_dim, output_dim, layer in \ - equizip(self.dims[:-1], self.dims[1:], - self.linear_transformations): - layer.input_dim = input_dim - layer.output_dim = output_dim - layer.use_bias = self.use_bias +"""Bricks are parameterized Theano operations.""" +from .base import application, Brick, lazy +from .bn import (BatchNormalization, SpatialBatchNormalization, + BatchNormalizedMLP) +from .interfaces import Activation, Feedforward, Initializable, Random +from .simple import (Linear, Bias, Maxout, LinearMaxout, Identity, Tanh, + Logistic, Softplus, Rectifier, Softmax, + NDimensionalSoftmax) +from .sequences import Sequence, FeedforwardSequence, MLP +from .wrappers import WithExtraDims + +__all__ = ('application', 'Brick', 'lazy', 'BatchNormalization', + 'SpatialBatchNormalization', 'BatchNormalizedMLP', + 'Activation', 'Feedforward', 'Initializable', 'Random', + 'Linear', 'Bias', 'Maxout', 'LinearMaxout', 'Identity', + 'Tanh', 'Logistic', 'Softplus', 'Rectifier', 'Softmax', + 'NDimensionalSoftmax', 'Sequence', 'FeedforwardSequence', + 'MLP', 'WithExtraDims') diff --git a/blocks/bricks/base.py b/blocks/bricks/base.py index 07a4ffb..5dbb737 100644 --- a/blocks/bricks/base.py +++ b/blocks/bricks/base.py @@ -304,7 +304,9 @@ def copy_and_tag(variable, role, name): brick not in last_brick.children): raise ValueError('Brick ' + str(self.call_stack[-1]) + ' tries ' 'to call brick ' + str(self.brick) + ' which ' - 'is not in the list of its children.') + 'is not in the list of its children. This could ' + 'be caused because an @application decorator is ' + 'missing.') self.call_stack.append(brick) try: outputs = self.application_function(brick, *args, **kwargs) @@ -879,6 +881,7 @@ class ApplicationCall(Annotation): """ def __init__(self, application): self.application = application + self.metadata = {} super(ApplicationCall, self).__init__() def add_auxiliary_variable(self, variable, roles=None, name=None): diff --git a/blocks/bricks/bn.py b/blocks/bricks/bn.py new file mode 100644 index 0000000..4aefff3 --- /dev/null +++ b/blocks/bricks/bn.py @@ -0,0 +1,348 @@ +import collections + +import numpy +from picklable_itertools.extras import equizip +import theano +from theano import tensor +from theano.tensor.nnet import bn + +from ..graph import add_annotation +from ..initialization import Constant +from ..roles import (BATCH_NORM_POPULATION_MEAN, + BATCH_NORM_POPULATION_STDEV, BATCH_NORM_OFFSET, + BATCH_NORM_DIVISOR, BATCH_NORM_MINIBATCH_ESTIMATE, + BATCH_NORM_SHIFT_PARAMETER, BATCH_NORM_SCALE_PARAMETER, + add_role) +from ..utils import (shared_floatx_zeros, shared_floatx, + shared_floatx_nans) +from .base import lazy, application +from .sequences import Sequence, Feedforward, MLP +from .interfaces import RNGMixin + + +def _add_batch_axis(var): + """Prepend a singleton axis to a TensorVariable and name it.""" + new_var = new_var = tensor.shape_padleft(var) + new_var.name = 'shape_padleft({})'.format(var.name) + return new_var + + +def _add_role_and_annotate(var, role, annotations=()): + """Add a role and zero or more annotations to a variable.""" + add_role(var, role) + for annotation in annotations: + add_annotation(var, annotation) + + +class BatchNormalization(RNGMixin, Feedforward): + r"""Normalizes activations, parameterizes a scale and shift. + + Parameters + ---------- + input_dim : int or tuple + Shape of a single input example. It is assumed that a batch axis + will be prepended to this. + broadcastable : tuple, optional + Tuple the same length as `input_dim` which specifies which of the + per-example axes should be averaged over to compute means and + standard deviations. For example, in order to normalize over all + spatial locations in a `(batch_index, channels, height, width)` + image, pass `(False, True, True)`. + conserve_memory : bool, optional + Use an implementation that stores less intermediate state and + therefore uses less memory, at the expense of 5-10% speed. Default + is `True`. + epsilon : float, optional + The stabilizing constant for the minibatch standard deviation + computation (when the brick is run in training mode). + Added to the variance inside the square root, as in the + batch normalization paper. + scale_init : object, optional + Initialization object to use for the learned scaling parameter + ($\\gamma$ in [BN]_). By default, uses constant initialization + of 1. + shift_init : object, optional + Initialization object to use for the learned shift parameter + ($\\beta$ in [BN]_). By default, uses constant initialization of 0. + + Notes + ----- + In order for trained models to behave sensibly immediately upon + upon deserialization, by default, this brick runs in *inference* mode, + using a population mean and population standard deviation (initialized + to zeros and ones respectively) to normalize activations. It is + expected that the user will adapt these during training in some + fashion, independently of the training objective, e.g. by taking a + moving average of minibatch-wise statistics. + + In order to *train* with batch normalization, one must obtain a + training graph by transforming the original inference graph. See + :func:`~blocks.graph.apply_batch_normalization` for a routine to + transform graphs, and :func:`~blocks.graph.batch_normalization` + for a context manager that may enable shorter compile times + (every instance of :class:`BatchNormalization` is itself a context + manager, entry into which causes applications to be in minibatch + "training" mode, however it is usually more convenient to use + :func:`~blocks.graph.batch_normalization` to enable this behaviour + for all of your graph's :class:`BatchNormalization` bricks at once). + + Note that training in inference mode should be avoided, as this + brick introduces scales and shift parameters (tagged with the + `PARAMETER` role) that, in the absence of batch normalization, + usually makes things unstable. If you must do this, filter for and + remove `BATCH_NORM_SHIFT_PARAMETER` and `BATCH_NORM_SCALE_PARAMETER` + from the list of parameters you are training, and this brick should + behave as a (somewhat expensive) no-op. + + This Brick accepts `scale_init` and `shift_init` arguments but is + *not* an instance of :class:`~blocks.bricks.Initializable`, and will + therefore not receive pushed initialization config from any parent + brick. In almost all cases, you will probably want to stick with the + defaults (unit scale and zero offset), but you can explicitly pass one + or both initializers to override this. + + This has the necessary properties to be inserted into a + :class:`blocks.bricks.conv.ConvolutionalSequence` as-is, in which case + the `input_dim` should be omitted at construction, to be inferred from + the layer below. + + """ + @lazy(allocation=['input_dim']) + def __init__(self, input_dim, broadcastable=None, + conserve_memory=True, epsilon=1e-4, scale_init=None, + shift_init=None, **kwargs): + self.input_dim = input_dim + self.broadcastable = broadcastable + self.conserve_memory = conserve_memory + self.epsilon = epsilon + self.scale_init = (Constant(1) if scale_init is None + else scale_init) + self.shift_init = (Constant(0) if shift_init is None + else shift_init) + self._training_mode = [] + super(BatchNormalization, self).__init__(**kwargs) + + @application(inputs=['input_'], outputs=['output']) + def apply(self, input_, application_call): + if self._training_mode: + mean, stdev = self._compute_training_statistics(input_) + else: + mean, stdev = self._prepare_population_statistics() + # Useful for filtration of calls that were already made in + # training mode when doing graph transformations. + # Very important to cast to bool, as self._training_mode is + # normally a list (to support nested context managers), which would + # otherwise get passed by reference and be remotely mutated. + application_call.metadata['training_mode'] = bool(self._training_mode) + # Useful for retrieving a list of updates for population + # statistics. Ditch the broadcastable first axis, though, to + # make it the same dimensions as the population mean and stdev + # shared variables. + application_call.metadata['offset'] = mean[0] + application_call.metadata['divisor'] = stdev[0] + # Give these quantities roles in the graph. + _add_role_and_annotate(mean, BATCH_NORM_OFFSET, + [self, application_call]) + _add_role_and_annotate(stdev, BATCH_NORM_DIVISOR, + [self, application_call]) + scale = _add_batch_axis(self.scale) + shift = _add_batch_axis(self.shift) + # Heavy lifting is done by the Theano utility function. + normalized = bn.batch_normalization(input_, scale, shift, mean, stdev, + mode=('low_mem' + if self.conserve_memory + else 'high_mem')) + return normalized + + def __enter__(self): + self._training_mode.append(True) + + def __exit__(self, *exc_info): + self._training_mode.pop() + + def _compute_training_statistics(self, input_): + axes = (0,) + tuple((i + 1) for i, b in + enumerate(self.population_mean.broadcastable) + if b) + mean = input_.mean(axis=axes, keepdims=True) + assert mean.broadcastable[1:] == self.population_mean.broadcastable + stdev = tensor.sqrt(tensor.var(input_, axis=axes, keepdims=True) + + numpy.cast[theano.config.floatX](self.epsilon)) + assert stdev.broadcastable[1:] == self.population_stdev.broadcastable + add_role(mean, BATCH_NORM_MINIBATCH_ESTIMATE) + add_role(stdev, BATCH_NORM_MINIBATCH_ESTIMATE) + return mean, stdev + + def _prepare_population_statistics(self): + mean = _add_batch_axis(self.population_mean) + stdev = _add_batch_axis(self.population_stdev) + return mean, stdev + + def _allocate(self): + input_dim = ((self.input_dim,) + if not isinstance(self.input_dim, collections.Sequence) + else self.input_dim) + broadcastable = (tuple(False for _ in input_dim) + if self.broadcastable is None else self.broadcastable) + if len(input_dim) != len(broadcastable): + raise ValueError("input_dim and broadcastable must be same length") + var_dim = tuple(1 if broadcast else dim for dim, broadcast in + equizip(input_dim, broadcastable)) + broadcastable = broadcastable + + # "gamma", from the Ioffe & Szegedy manuscript. + self.scale = shared_floatx_nans(var_dim, name='batch_norm_scale', + broadcastable=broadcastable) + + # "beta", from the Ioffe & Szegedy manuscript. + self.shift = shared_floatx_nans(var_dim, name='batch_norm_shift', + broadcastable=broadcastable) + add_role(self.scale, BATCH_NORM_SCALE_PARAMETER) + add_role(self.shift, BATCH_NORM_SHIFT_PARAMETER) + self.parameters.append(self.scale) + self.parameters.append(self.shift) + + # These aren't technically parameters, in that they should not be + # learned using the same cost function as other model parameters. + self.population_mean = shared_floatx_zeros(var_dim, + name='population_mean', + broadcastable=broadcastable) + self.population_stdev = shared_floatx(numpy.ones(var_dim), + name='population_stdev', + broadcastable=broadcastable) + add_role(self.population_mean, BATCH_NORM_POPULATION_MEAN) + add_role(self.population_stdev, BATCH_NORM_POPULATION_STDEV) + + # Normally these would get annotated by an AnnotatingList, but they + # aren't in self.parameters. + add_annotation(self.population_mean, self) + add_annotation(self.population_stdev, self) + + def _initialize(self): + self.shift_init.initialize(self.shift, self.rng) + self.scale_init.initialize(self.scale, self.rng) + + # Needed for the Feedforward interface. + @property + def output_dim(self): + return self.input_dim + + # The following properties allow for BatchNormalization bricks + # to be used directly inside of a ConvolutionalSequence. + @property + def image_size(self): + return self.input_dim[-2:] + + @image_size.setter + def image_size(self, value): + if not isinstance(self.input_dim, collections.Sequence): + self.input_dim = (None,) + tuple(value) + else: + self.input_dim = (self.input_dim[0],) + tuple(value) + + @property + def num_channels(self): + return self.input_dim[0] + + @num_channels.setter + def num_channels(self, value): + if not isinstance(self.input_dim, collections.Sequence): + self.input_dim = (value,) + (None, None) + else: + self.input_dim = (value,) + self.input_dim[-2:] + + def get_dim(self, name): + if name in ('input', 'output'): + return self.input_dim + else: + raise KeyError + + @property + def num_output_channels(self): + return self.num_channels + + +class SpatialBatchNormalization(BatchNormalization): + """Convenient subclass for batch normalization across spatial inputs. + + Parameters + ---------- + input_dim : int or tuple + The input size of a single example. Must be length at least 2. + It's assumed that the first axis of this tuple is a "channels" + axis, which should not be summed over, and all remaining + dimensions are spatial dimensions. + + Notes + ----- + See :class:`BatchNormalization` for more details (and additional + keyword arguments). + + """ + def _allocate(self): + if not isinstance(self.input_dim, + collections.Sequence) or len(self.input_dim) < 2: + raise ValueError('expected input_dim to be length >= 2 ' + 'e.g. (channels, height, width)') + self.broadcastable = (False,) + ((True,) * (len(self.input_dim) - 1)) + super(SpatialBatchNormalization, self)._allocate() + + +class BatchNormalizedMLP(MLP): + """Convenient subclass for building an MLP with batch normalization. + + Parameters + ---------- + conserve_memory : bool, optional + See :class:`BatchNormalization`. + + Notes + ----- + All other parameters are the same as :class:`~blocks.bricks.MLP`. Each + activation brick is wrapped in a :class:`~blocks.bricks.Sequence` + containing an appropriate :class:`BatchNormalization` brick and + the activation that follows it. + + By default, the contained :class:`~blocks.bricks.Linear` bricks will + not contain any biases, as they could be canceled out by the biases + in the :class:`BatchNormalization` bricks being added. Pass + `use_bias` with a value of `True` if you really want this for some + reason. + + """ + @lazy(allocation=['dims']) + def __init__(self, activations, dims, *args, **kwargs): + conserve_memory = kwargs.pop('conserve_memory', True) + activations = [ + Sequence([ + BatchNormalization(conserve_memory=conserve_memory).apply, + act.apply + ], name='batch_norm_activation_{}'.format(i)) + for i, act in enumerate(activations) + ] + # Batch normalization bricks incorporate a bias, so there's no + # need for our Linear bricks to have them. + kwargs.setdefault('use_bias', False) + super(BatchNormalizedMLP, self).__init__(activations, dims, *args, + **kwargs) + + @property + def conserve_memory(self): + return self._conserve_memory + + @conserve_memory.setter + def conserve_memory(self, value): + self._conserve_memory = value + for act in self.activations: + assert isinstance(act.children[0], BatchNormalization) + act.children[0].conserve_memory = value + + def _push_allocation_config(self): + super(BatchNormalizedMLP, self)._push_allocation_config() + # Do the extra allocation pushing for the BatchNormalization + # bricks. They need as their input dimension the output dimension + # of each linear transformation. Exclude the first dimension, + # which is the input dimension. + for act, dim in equizip(self.activations, self.dims[1:]): + assert isinstance(act.children[0], BatchNormalization) + act.children[0].input_dim = dim diff --git a/blocks/bricks/conv.py b/blocks/bricks/conv.py index c36278e..842d749 100644 --- a/blocks/bricks/conv.py +++ b/blocks/bricks/conv.py @@ -1,5 +1,8 @@ -from theano.tensor.nnet.conv import conv2d, ConvOp -from theano.tensor.signal.downsample import max_pool_2d, DownsampleFactorMax +from theano import tensor +from theano.tensor.nnet import conv2d +from theano.tensor.nnet.abstract_conv import (AbstractConv2d_gradInputs, + get_conv_output_shape) +from theano.tensor.signal.pool import pool_2d, Pool from blocks.bricks import Initializable, Feedforward, Sequence from blocks.bricks.base import application, Brick, lazy @@ -36,11 +39,38 @@ class Convolutional(Initializable): border_mode : {'valid', 'full'}, optional The border mode to use, see :func:`scipy.signal.convolve2d` for details. Defaults to 'valid'. + tied_biases : bool + If ``True``, it indicates that the biases of every filter in this + layer should be shared amongst all applications of that filter. + Setting this to ``False`` will untie the biases, yielding a + separate bias for every location at which the filter is applied. + Defaults to ``False``. """ + # Make it possible to override the implementation of conv2d that gets + # used, i.e. to use theano.sandbox.cuda.dnn.dnn_conv directly in order + # to leverage features not yet available in Theano's standard conv2d. + # The function you override with here should accept at least the + # input and the kernels as positionals, and the keyword arguments + # input_shape, subsample, border_mode, and filter_shape. If some of + # these are unsupported they should still be accepted and ignored, + # e.g. with a wrapper function that swallows **kwargs. + conv2d_impl = staticmethod(conv2d) + + # Used to override the output shape computation for a given value of + # conv2d_impl. Should accept 4 positional arguments: the shape of an + # image minibatch (with 4 elements: batch size, number of channels, + # height, and width), the shape of the filter bank (number of filters, + # number of output channels, filter height, filter width), the border + # mode, and the step (vertical and horizontal strides). It is expected + # to return a 4-tuple of (batch size, number of channels, output + # height, output width). The first element of this tuple is not used + # for anything by this brick. + get_output_shape = staticmethod(get_conv_output_shape) + @lazy(allocation=['filter_size', 'num_filters', 'num_channels']) def __init__(self, filter_size, num_filters, num_channels, batch_size=None, - image_size=None, step=(1, 1), border_mode='valid', + image_size=(None, None), step=(1, 1), border_mode='valid', tied_biases=False, **kwargs): super(Convolutional, self).__init__(**kwargs) @@ -63,6 +93,14 @@ def _allocate(self): if self.tied_biases: b = shared_floatx_nans((self.num_filters,), name='b') else: + # this error is raised here instead of during initializiation + # because ConvolutionalSequence may specify the image size + if self.image_size == (None, None) and not self.tied_biases: + raise ValueError('Cannot infer bias size without ' + 'image_size specified. If you use ' + 'variable image_size, you should use ' + 'tied_biases=True.') + b = shared_floatx_nans(self.get_dim('output'), name='b') add_role(b, BIAS) @@ -104,10 +142,15 @@ def apply(self, input_): else: W, = self.parameters - output = conv2d( + if self.image_size == (None, None): + input_shape = None + else: + input_shape = (self.batch_size, self.num_channels) + input_shape += self.image_size + + output = self.conv2d_impl( input_, W, - image_shape=(self.batch_size, self.num_channels) + - (self.image_size if self.image_size else (None, None)), + input_shape=input_shape, subsample=self.step, border_mode=self.border_mode, filter_shape=((self.num_filters, self.num_channels) + @@ -123,36 +166,108 @@ def get_dim(self, name): if name == 'input_': return (self.num_channels,) + self.image_size if name == 'output': - return ((self.num_filters,) + - ConvOp.getOutputShape(self.image_size, self.filter_size, - self.step, self.border_mode)) + input_shape = (None, self.num_channels) + self.image_size + kernel_shape = ((self.num_filters, self.num_channels) + + self.filter_size) + out_shape = self.get_output_shape(input_shape, kernel_shape, + self.border_mode, self.step) + assert len(out_shape) == 4 + return out_shape[1:] return super(Convolutional, self).get_dim(name) + @property + def num_output_channels(self): + return self.num_filters -class MaxPooling(Initializable, Feedforward): - """Max pooling layer. + +class ConvolutionalTranspose(Convolutional): + """Performs the transpose of a 2D convolution. Parameters ---------- - pooling_size : tuple - The height and width of the pooling region i.e. this is the factor - by which your input's last two dimensions will be downscaled. + original_image_size : tuple + The height and width of the image that forms the output of + the transpose operation, which is the input of the original + (non-transposed) convolution. + num_filters : int + Number of filters at the *output* of the transposed convolution, + i.e. the number of channels in the corresponding convolution. + num_channels : int + Number of channels at the *input* of the transposed convolution, + i.e. the number of output filters in the corresponding + convolution. step : tuple, optional - The vertical and horizontal shift (stride) between pooling regions. - By default this is equal to `pooling_size`. Setting this to a lower - number results in overlapping pooling regions. - input_dim : tuple, optional - A tuple of integers representing the shape of the input. The last - two dimensions will be used to calculate the output dimension. + The step (or stride) of the corresponding *convolution*. + Defaults to (1, 1). + image_size : tuple, optional + Image size of the input to the *transposed* convolution, i.e. + the output of the corresponding convolution. Required for tied + biases. Defaults to ``None``. + + See Also + -------- + :class:`Convolutional` : For the documentation of other parameters. """ - @lazy(allocation=['pooling_size']) - def __init__(self, pooling_size, step=None, input_dim=None, **kwargs): - super(MaxPooling, self).__init__(**kwargs) + @lazy(allocation=['original_image_size', 'filter_size', 'num_filters', + 'num_channels']) + def __init__(self, original_image_size, filter_size, num_filters, + num_channels, **kwargs): + super(ConvolutionalTranspose, self).__init__( + filter_size, num_filters, num_channels, **kwargs) + self.original_image_size = original_image_size + + def conv2d_impl(self, input_, W, input_shape, subsample, border_mode, + filter_shape): + # The AbstractConv2d_gradInputs op takes a kernel that was used for the + # **convolution**. We therefore have to invert num_channels and + # num_filters for W. + W = W.transpose(1, 0, 2, 3) + imshp = (None,) + self.get_dim('output') + kshp = (filter_shape[1], filter_shape[0]) + filter_shape[2:] + return AbstractConv2d_gradInputs( + imshp=imshp, kshp=kshp, border_mode=border_mode, + subsample=subsample)(W, input_, self.get_dim('output')[1:]) + + def get_dim(self, name): + if name == 'output': + return (self.num_filters,) + self.original_image_size + return super(ConvolutionalTranspose, self).get_dim(name) - self.input_dim = input_dim + +class Pooling(Initializable, Feedforward): + """Base Brick for pooling operations. + + This should generally not be instantiated directly; see + :class:`MaxPooling`. + + """ + @lazy(allocation=['mode', 'pooling_size']) + def __init__(self, mode, pooling_size, step, input_dim, ignore_border, + padding, **kwargs): + super(Pooling, self).__init__(**kwargs) self.pooling_size = pooling_size + self.mode = mode self.step = step + self.input_dim = input_dim if input_dim is not None else (None,) * 3 + self.ignore_border = ignore_border + self.padding = padding + + @property + def image_size(self): + return self.input_dim[-2:] + + @image_size.setter + def image_size(self, value): + self.input_dim = self.input_dim[:-2] + value + + @property + def num_channels(self): + return self.input_dim[0] + + @num_channels.setter + def num_channels(self, value): + self.input_dim = (value,) + self.input_dim[1:] @application(inputs=['input_'], outputs=['output']) def apply(self, input_): @@ -173,19 +288,131 @@ def apply(self, input_): with the last two dimensions downsampled. """ - output = max_pool_2d(input_, self.pooling_size, st=self.step) + output = pool_2d(input_, self.pooling_size, st=self.step, + mode=self.mode, padding=self.padding, + ignore_border=self.ignore_border) return output def get_dim(self, name): if name == 'input_': return self.input_dim if name == 'output': - return tuple(DownsampleFactorMax.out_shape(self.input_dim, - self.pooling_size, - st=self.step)) + return tuple(Pool.out_shape( + self.input_dim, self.pooling_size, st=self.step, + ignore_border=self.ignore_border, padding=self.padding)) + + @property + def num_output_channels(self): + return self.input_dim[0] + + +class MaxPooling(Pooling): + """Max pooling layer. + + Parameters + ---------- + pooling_size : tuple + The height and width of the pooling region i.e. this is the factor + by which your input's last two dimensions will be downscaled. + step : tuple, optional + The vertical and horizontal shift (stride) between pooling regions. + By default this is equal to `pooling_size`. Setting this to a lower + number results in overlapping pooling regions. + input_dim : tuple, optional + A tuple of integers representing the shape of the input. The last + two dimensions will be used to calculate the output dimension. + padding : tuple, optional + A tuple of integers representing the vertical and horizontal + zero-padding to be applied to each of the top and bottom + (vertical) and left and right (horizontal) edges. For example, + an argument of (4, 3) will apply 4 pixels of padding to the + top edge, 4 pixels of padding to the bottom edge, and 3 pixels + each for the left and right edge. By default, no padding is + performed. + ignore_border : bool, optional + Whether or not to do partial downsampling based on borders where + the extent of the pooling region reaches beyond the edge of the + image. If `True`, a (5, 5) image with (2, 2) pooling regions + and (2, 2) step will be downsampled to shape (2, 2), otherwise + it will be downsampled to (3, 3). `True` by default. + + Notes + ----- + .. warning:: + As of this writing, setting `ignore_border` to `False` with a step + not equal to the pooling size will force Theano to perform pooling + computations on CPU rather than GPU, even if you have specified + a GPU as your computation device. Additionally, Theano will only + use [cuDNN]_ (if available) for pooling computations with + `ignure_border` set to `True`. You can ensure that the entire + input is captured by at least one pool by using the `padding` + argument to add zero padding prior to pooling being performed. + + .. [cuDNN]: `NVIDIA cuDNN `_. + + """ + @lazy(allocation=['pooling_size']) + def __init__(self, pooling_size, step=None, input_dim=None, + ignore_border=True, padding=(0, 0), + **kwargs): + super(MaxPooling, self).__init__('max', pooling_size, + step=step, input_dim=input_dim, + ignore_border=ignore_border, + padding=padding, **kwargs) + + def __setstate__(self, state): + self.__dict__.update(state) + # Fix objects created before pull request #899. + self.mode = getattr(self, 'mode', 'max') + self.padding = getattr(self, 'padding', (0, 0)) + self.ignore_border = getattr(self, 'ignore_border', False) + +class AveragePooling(Pooling): + """Average pooling layer. -class ConvolutionalActivation(Sequence, Initializable): + Parameters + ---------- + include_padding : bool, optional + When calculating an average, include zeros that are the + result of zero padding added by the `padding` argument. + A value of `True` is only accepted if `ignore_border` + is also `True`. `False` by default. + + Notes + ----- + For documentation on the remainder of the arguments to this + class, see :class:`MaxPooling`. + + """ + @lazy(allocation=['pooling_size']) + def __init__(self, pooling_size, step=None, input_dim=None, + ignore_border=True, padding=(0, 0), + include_padding=False, **kwargs): + mode = 'average_inc_pad' if include_padding else 'average_exc_pad' + super(AveragePooling, self).__init__(mode, pooling_size, + step=step, input_dim=input_dim, + ignore_border=ignore_border, + padding=padding, **kwargs) + + +class _AllocationMixin(object): + def _push_allocation_config(self): + for attr in ['filter_size', 'num_filters', 'border_mode', + 'batch_size', 'num_channels', 'image_size', + 'tied_biases', 'use_bias']: + setattr(self.convolution, attr, getattr(self, attr)) + + @property + def num_output_channels(self): + # Assumes an elementwise activation function. Would need to + # change to support e.g. maxout, but that would also require + # a way of querying the activation function for this kind of + # information. + return self.num_filters + + +class ConvolutionalActivation(_AllocationMixin, Sequence, Initializable): """A convolution followed by an activation function. Parameters @@ -203,7 +430,7 @@ class ConvolutionalActivation(Sequence, Initializable): def __init__(self, activation, filter_size, num_filters, num_channels, batch_size=None, image_size=None, step=(1, 1), border_mode='valid', tied_biases=False, **kwargs): - self.convolution = Convolutional() + self._build_convolution() self.filter_size = filter_size self.num_filters = num_filters @@ -218,97 +445,57 @@ def __init__(self, activation, filter_size, num_filters, num_channels, application_methods=[self.convolution.apply, activation], **kwargs) - def _push_allocation_config(self): - for attr in ['filter_size', 'num_filters', 'step', 'border_mode', - 'batch_size', 'num_channels', 'image_size', - 'tied_biases']: - setattr(self.convolution, attr, getattr(self, attr)) + def _build_convolution(self): + self.convolution = Convolutional() def get_dim(self, name): # TODO The name of the activation output doesn't need to be `output` return self.convolution.get_dim(name) + def _push_allocation_config(self): + super(ConvolutionalActivation, self)._push_allocation_config() + self.convolution.step = self.step -class ConvolutionalLayer(Sequence, Initializable): - """A complete convolutional layer: Convolution, nonlinearity, pooling. - - .. todo:: - Mean pooling. +class ConvolutionalTransposeActivation(ConvolutionalActivation): + """A transposed convolution followed by an activation function. Parameters ---------- activation : :class:`.BoundApplication` - The application method to apply in the detector stage (i.e. the - nonlinearity before pooling. Needed for ``__init__``. + The application method to apply after convolution (i.e. + the nonlinear activation function) See Also -------- - :class:`Convolutional` : Documentation of convolution arguments. - :class:`MaxPooling` : Documentation of pooling arguments. - - Notes - ----- - Uses max pooling. + :class:`ConvolutionalTranspose` : For the documentation of other + parameters. """ - @lazy(allocation=['filter_size', 'num_filters', 'pooling_size', + @lazy(allocation=['original_image_size', 'filter_size', 'num_filters', 'num_channels']) - def __init__(self, activation, filter_size, num_filters, pooling_size, - num_channels, conv_step=(1, 1), pooling_step=None, - batch_size=None, image_size=None, border_mode='valid', - tied_biases=False, **kwargs): - self.convolution = ConvolutionalActivation(activation) - self.pooling = MaxPooling() - super(ConvolutionalLayer, self).__init__( - application_methods=[self.convolution.apply, - self.pooling.apply], **kwargs) - self.convolution.name = self.name + '_convolution' - self.pooling.name = self.name + '_pooling' + def __init__(self, activation, original_image_size, filter_size, + num_filters, num_channels, **kwargs): + super(ConvolutionalTransposeActivation, self).__init__( + activation, filter_size, num_filters, num_channels, **kwargs) + self.original_image_size = original_image_size - self.filter_size = filter_size - self.num_filters = num_filters - self.num_channels = num_channels - self.pooling_size = pooling_size - self.conv_step = conv_step - self.pooling_step = pooling_step - self.batch_size = batch_size - self.border_mode = border_mode - self.image_size = image_size - self.tied_biases = tied_biases + def _build_convolution(self): + self.convolution = ConvolutionalTranspose() def _push_allocation_config(self): - for attr in ['filter_size', 'num_filters', 'num_channels', - 'batch_size', 'border_mode', 'image_size', - 'tied_biases']: - setattr(self.convolution, attr, getattr(self, attr)) - self.convolution.step = self.conv_step - self.convolution._push_allocation_config() - if self.image_size is not None: - pooling_input_dim = self.convolution.get_dim('output') - else: - pooling_input_dim = None - self.pooling.input_dim = pooling_input_dim - self.pooling.pooling_size = self.pooling_size - self.pooling.step = self.pooling_step - self.pooling.batch_size = self.batch_size - - def get_dim(self, name): - if name == 'input_': - return self.convolution.get_dim('input_') - if name == 'output': - return self.pooling.get_dim('output') - return super(ConvolutionalLayer, self).get_dim(name) + super(ConvolutionalTransposeActivation, self)._push_allocation_config() + self.convolution.original_image_size = self.original_image_size class ConvolutionalSequence(Sequence, Initializable, Feedforward): - """A sequence of convolutional operations. + """A sequence of convolutional (or pooling) operations. Parameters ---------- layers : list - List of convolutional bricks (i.e. :class:`ConvolutionalActivation` - or :class:`ConvolutionalLayer`) + List of convolutional bricks (i.e. :class:`Convolutional`, + :class:`ConvolutionalActivation`, or :class:`Pooling` bricks). num_channels : int Number of input channels in the image. For the first layer this is normally 1 for grayscale images and 3 for color (RGB) images. For @@ -322,6 +509,14 @@ class ConvolutionalSequence(Sequence, Initializable, Feedforward): Width and height of the input (image/featuremap). If given, will be passed to theano's convolution operator resulting in possibly faster execution. + border_mode : 'valid', 'full' or None, optional + The border mode to use, see :func:`scipy.signal.convolve2d` for + details. Unlike with :class:`Convolutional`, this defaults to + None, in which case no default value is pushed down to child + bricks at allocation time. Child bricks will in this case + need to rely on either a default border mode (usually valid) + or one provided at construction and/or after construction + (but before allocation). Notes ----- @@ -331,10 +526,18 @@ class ConvolutionalSequence(Sequence, Initializable, Feedforward): input dimensions of a layer to the output dimensions of the previous layer by the :meth:`~.Brick.push_allocation_config` method. + The reason the `border_mode` parameter behaves the way it does is that + pushing a single default `border_mode` makes it very difficult to + have child bricks with different border modes. Normally, such things + would be overridden after `push_allocation_config()`, but this is + a particular hassle as the border mode affects the allocation + parameters of every subsequent child brick in the sequence. Thus, only + an explicitly specified border mode will be pushed down the hierarchy. + """ @lazy(allocation=['num_channels']) def __init__(self, layers, num_channels, batch_size=None, image_size=None, - border_mode='valid', tied_biases=False, **kwargs): + border_mode=None, tied_biases=False, **kwargs): self.layers = layers self.image_size = image_size self.num_channels = num_channels @@ -357,11 +560,13 @@ def _push_allocation_config(self): num_channels = self.num_channels image_size = self.image_size for layer in self.layers: - for attr in ['border_mode', 'tied_biases']: - setattr(layer, attr, getattr(self, attr)) + if self.border_mode is not None: + layer.border_mode = self.border_mode + layer.tied_biases = self.tied_biases layer.image_size = image_size layer.num_channels = num_channels layer.batch_size = self.batch_size + layer.use_bias = self.use_bias # Push input dimensions to children layer._push_allocation_config() @@ -371,7 +576,7 @@ def _push_allocation_config(self): if layer.image_size is not None: output_shape = layer.get_dim('output') image_size = output_shape[1:] - num_channels = layer.num_filters + num_channels = layer.num_output_channels class Flattener(Brick): diff --git a/blocks/bricks/cost.py b/blocks/bricks/cost.py index 72ffbd4..69f4ccd 100644 --- a/blocks/bricks/cost.py +++ b/blocks/bricks/cost.py @@ -11,7 +11,7 @@ class Cost(Brick): @abstractmethod @application - def apply(self, y, y_hat): + def apply(self, *args, **kwargs): pass @@ -23,12 +23,12 @@ class CostMatrix(Cost): """ @application(outputs=["cost"]) - def apply(self, y, y_hat): - return self.cost_matrix(y, y_hat).sum(axis=1).mean() + def apply(self, *args, **kwargs): + return self.cost_matrix(*args, **kwargs).sum(axis=1).mean() @abstractmethod @application - def cost_matrix(self, y, y_hat): + def cost_matrix(self, *args, **kwargs): pass diff --git a/blocks/bricks/interfaces.py b/blocks/bricks/interfaces.py new file mode 100644 index 0000000..aef73a4 --- /dev/null +++ b/blocks/bricks/interfaces.py @@ -0,0 +1,214 @@ +"""Bricks that are interfaces and/or mixins.""" +import numpy +from six import add_metaclass +from theano.sandbox.rng_mrg import MRG_RandomStreams + +from ..config import config +from .base import _Brick, Brick, lazy + + +class ActivationDocumentation(_Brick): + """Dynamically adds documentation to activations. + + Notes + ----- + See http://bugs.python.org/issue12773. + + """ + def __new__(cls, name, bases, classdict): + classdict['__doc__'] = \ + """Elementwise application of {0} function.""".format(name.lower()) + if 'apply' in classdict: + classdict['apply'].__doc__ = \ + """Apply the {0} function element-wise. + + Parameters + ---------- + input_ : :class:`~tensor.TensorVariable` + Theano variable to apply {0} to, element-wise. + + Returns + ------- + output : :class:`~tensor.TensorVariable` + The input with the activation function applied. + + """.format(name.lower()) + return super(ActivationDocumentation, cls).__new__(cls, name, bases, + classdict) + + +@add_metaclass(ActivationDocumentation) +class Activation(Brick): + """A base class for simple, element-wise activation functions. + + This base class ensures that activation functions are automatically + documented using the :class:`ActivationDocumentation` metaclass. + + """ + pass + + +class Feedforward(Brick): + """Declares an interface for bricks with one input and one output. + + Many bricks have just one input and just one output (activations, + :class:`Linear`, :class:`MLP`). To make such bricks interchangable + in most contexts they should share an interface for configuring + their input and output dimensions. This brick declares such an + interface. + + Attributes + ---------- + input_dim : int + The input dimension of the brick. + output_dim : int + The output dimension of the brick. + + """ + def __getattr__(self, name): + message = ("'{}' object does not have an attribute '{}'" + .format(self.__class__.__name__, name)) + if name in ('input_dim', 'output_dim'): + message += (" (which is a part of 'Feedforward' interface it" + " claims to support)") + raise AttributeError(message) + + +class RNGMixin(object): + """Mixin for initialization random number generators.""" + seed_rng = numpy.random.RandomState(config.default_seed) + + @property + def seed(self): + if getattr(self, '_seed', None) is not None: + return self._seed + else: + self._seed = self.seed_rng.randint( + numpy.iinfo(numpy.int32).max) + return self._seed + + @seed.setter + def seed(self, value): + if hasattr(self, '_seed'): + raise AttributeError("seed already set") + self._seed = value + + @property + def rng(self): + if getattr(self, '_rng', None) is not None: + return self._rng + else: + self._rng = numpy.random.RandomState(self.seed) + return self._rng + + @rng.setter + def rng(self, rng): + self._rng = rng + + +class Initializable(RNGMixin, Brick): + """Base class for bricks which push parameter initialization. + + Many bricks will initialize children which perform a linear + transformation, often with biases. This brick allows the weights + and biases initialization to be configured in the parent brick and + pushed down the hierarchy. + + Parameters + ---------- + weights_init : object + A `NdarrayInitialization` instance which will be used by to + initialize the weight matrix. Required by + :meth:`~.Brick.initialize`. + biases_init : :obj:`object`, optional + A `NdarrayInitialization` instance that will be used to initialize + the biases. Required by :meth:`~.Brick.initialize` when `use_bias` + is `True`. Only supported by bricks for which :attr:`has_biases` is + ``True``. + use_bias : :obj:`bool`, optional + Whether to use a bias. Defaults to `True`. Required by + :meth:`~.Brick.initialize`. Only supported by bricks for which + :attr:`has_biases` is ``True``. + rng : :class:`numpy.random.RandomState` + + Attributes + ---------- + has_biases : bool + ``False`` if the brick does not support biases, and only has + :attr:`weights_init`. For an example of this, see + :class:`.Bidirectional`. If this is ``False``, the brick does not + support the arguments ``biases_init`` or ``use_bias``. + + """ + has_biases = True + + @lazy() + def __init__(self, weights_init=None, biases_init=None, use_bias=True, + seed=None, **kwargs): + super(Initializable, self).__init__(**kwargs) + self.weights_init = weights_init + if self.has_biases: + self.biases_init = biases_init + elif biases_init is not None or not use_bias: + raise ValueError("This brick does not support biases config") + self.use_bias = use_bias + self.seed = seed + + def _push_initialization_config(self): + for child in self.children: + if isinstance(child, Initializable): + child.rng = self.rng + if self.weights_init: + child.weights_init = self.weights_init + if hasattr(self, 'biases_init') and self.biases_init: + for child in self.children: + if (isinstance(child, Initializable) and + hasattr(child, 'biases_init')): + child.biases_init = self.biases_init + + +class Random(Brick): + """A mixin class for Bricks which need Theano RNGs. + + Parameters + ---------- + theano_seed : int or list, optional + Seed to use for a + :class:`~theano.sandbox.rng_mrg.MRG_RandomStreams` object. + + """ + seed_rng = numpy.random.RandomState(config.default_seed) + + def __init__(self, theano_seed=None, **kwargs): + super(Random, self).__init__(**kwargs) + self.theano_seed = theano_seed + + @property + def theano_seed(self): + if getattr(self, '_theano_seed', None) is not None: + return self._theano_seed + else: + self._theano_seed = self.seed_rng.randint( + numpy.iinfo(numpy.int32).max) + return self._theano_seed + + @theano_seed.setter + def theano_seed(self, value): + if hasattr(self, '_theano_seed'): + raise AttributeError("seed already set") + self._theano_seed = value + + @property + def theano_rng(self): + """Returns Brick's Theano RNG, or a default one. + + The default seed can be set through ``blocks.config``. + + """ + if not hasattr(self, '_theano_rng'): + self._theano_rng = MRG_RandomStreams(self.theano_seed) + return self._theano_rng + + @theano_rng.setter + def theano_rng(self, theano_rng): + self._theano_rng = theano_rng diff --git a/blocks/bricks/lookup.py b/blocks/bricks/lookup.py index 7017be1..410a466 100644 --- a/blocks/bricks/lookup.py +++ b/blocks/bricks/lookup.py @@ -59,7 +59,7 @@ def apply(self, indices): representation element. """ - check_theano_variable(indices, None, "int") + check_theano_variable(indices, None, ("int", "uint")) output_shape = [indices.shape[i] for i in range(indices.ndim)] + [self.dim] return self.W[indices.flatten()].reshape(output_shape) diff --git a/blocks/bricks/parallel.py b/blocks/bricks/parallel.py index 1490175..8524b70 100644 --- a/blocks/bricks/parallel.py +++ b/blocks/bricks/parallel.py @@ -5,7 +5,7 @@ from blocks.bricks import Initializable, Linear from blocks.bricks.base import lazy, application -from blocks.utils import pack +from blocks.utils import pack, extract_args class Parallel(Initializable): @@ -82,10 +82,9 @@ def _push_allocation_config(self): @application def apply(self, *args, **kwargs): - args = args + tuple(kwargs[name] for name in - self.input_names[len(args):]) - return [child.apply(arg) - for arg, child in equizip(args, self.children)] + routed_args = extract_args(self.input_names, *args, **kwargs) + return [child.apply(routed_args[name]) + for name, child in equizip(self.input_names, self.children)] @apply.property('inputs') def apply_inputs(self): diff --git a/blocks/bricks/recurrent.py b/blocks/bricks/recurrent.py index fef538f..e0ceb01 100644 --- a/blocks/bricks/recurrent.py +++ b/blocks/bricks/recurrent.py @@ -158,11 +158,8 @@ def recurrent_apply(brick, application, application_call, if len(sequences_given): # TODO Assumes 1 time dim! shape = list(sequences_given.values())[0].shape - if not iterate: - batch_size = shape[0] - else: - n_steps = shape[0] - batch_size = shape[1] + n_steps = shape[0] + batch_size = shape[1] else: # TODO Raise error if n_steps and batch_size not found? n_steps = kwargs.pop('n_steps') @@ -308,7 +305,7 @@ def _initialize(self): @recurrent(sequences=['inputs', 'mask'], states=['states'], outputs=['states'], contexts=[]) - def apply(self, inputs=None, states=None, mask=None): + def apply(self, inputs, states, mask=None): """Apply the simple transition. Parameters @@ -435,11 +432,19 @@ def apply(self, inputs, states, cells, mask=None): (batch_size, features). Required for `one_step` usage. inputs : :class:`~tensor.TensorVariable` The 2 dimensional matrix of inputs in the shape (batch_size, - features * 4). + features * 4). The `inputs` needs to be four times the + dimension of the LSTM brick to insure each four gates receive + different transformations of the input. See [Grav13]_ + equations 7 to 10 for more details. The `inputs` are then split + in this order: Input gates, forget gates, cells and output + gates. mask : :class:`~tensor.TensorVariable` A 1D binary array in the shape (batch,) which is 1 if there is data available, 0 if not. Assumed to be 1-s only if not given. + .. [Grav13] Graves, Alex, *Generating sequences with recurrent + neural networks*, arXiv preprint arXiv:1308.0850 (2013). + Returns ------- states : :class:`~tensor.TensorVariable` @@ -661,7 +666,12 @@ def apply(self, *args, **kwargs): def apply_delegate(self): return self.children[0].apply -RECURRENTSTACK_SEPARATOR = '_' + def get_dim(self, name): + if name in self.apply.outputs: + return self.prototype.get_dim(name) * 2 + return self.prototype.get_dim(name) + +RECURRENTSTACK_SEPARATOR = '#' class RecurrentStack(BaseRecurrent, Initializable): diff --git a/blocks/bricks/sequence_generators.py b/blocks/bricks/sequence_generators.py index c67cccb..510d513 100644 --- a/blocks/bricks/sequence_generators.py +++ b/blocks/bricks/sequence_generators.py @@ -658,6 +658,19 @@ class AbstractEmitter(Brick): :class:`SoftmaxEmitter` : for integer outputs + Notes + ----- + An important detail about the emitter cost is that it will be + evaluated with inputs of different dimensions so it has to be + flexible enough to handle this. The two ways in which it can be + applied are: + + 1. In :meth:BaseSequenceGenerator.cost_matrix where it will + be applied to the whole sequence at once. + + 2. In :meth:BaseSequenceGenerator.generate where it will be + applied to only one step of the sequence. + """ @abstractmethod def emit(self, readouts): diff --git a/blocks/bricks/sequences.py b/blocks/bricks/sequences.py new file mode 100644 index 0000000..9218c54 --- /dev/null +++ b/blocks/bricks/sequences.py @@ -0,0 +1,153 @@ +"""Bricks that compose together other bricks in linear sequences.""" +from toolz import interleave +from picklable_itertools.extras import equizip + +from ..utils import pack +from .base import Brick, application, lazy +from .interfaces import Feedforward, Initializable +from .simple import Linear + + +class Sequence(Brick): + """A sequence of bricks. + + This brick applies a sequence of bricks, assuming that their in- and + outputs are compatible. + + Parameters + ---------- + application_methods : list + List of :class:`.BoundApplication` to apply + + """ + def __init__(self, application_methods, **kwargs): + super(Sequence, self).__init__(**kwargs) + self.application_methods = application_methods + + seen = set() + self.children = [app.brick for app in application_methods + if not (app.brick in seen or seen.add(app.brick))] + + @application + def apply(self, *args): + child_input = args + for application_method in self.application_methods: + output = application_method(*pack(child_input)) + child_input = output + return output + + @apply.property('inputs') + def apply_inputs(self): + return self.application_methods[0].inputs + + @apply.property('outputs') + def apply_outputs(self): + return self.application_methods[-1].outputs + + +class FeedforwardSequence(Sequence, Feedforward): + """A sequence where the first and last bricks are feedforward. + + Parameters + ---------- + application_methods : list + List of :class:`.BoundApplication` to apply. The first and last + application method should belong to a :class:`Feedforward` brick. + + """ + @property + def input_dim(self): + return self.children[0].input_dim + + @input_dim.setter + def input_dim(self, value): + self.children[0].input_dim = value + + @property + def output_dim(self): + return self.children[-1].output_dim + + @output_dim.setter + def output_dim(self, value): + self.children[-1].output_dim = value + + +class MLP(Sequence, Initializable, Feedforward): + """A simple multi-layer perceptron. + + Parameters + ---------- + activations : list of :class:`.Brick`, :class:`.BoundApplication`, + or ``None`` + A list of activations to apply after each linear transformation. + Give ``None`` to not apply any activation. It is assumed that the + application method to use is ``apply``. Required for + :meth:`__init__`. + dims : list of ints + A list of input dimensions, as well as the output dimension of the + last layer. Required for :meth:`~.Brick.allocate`. + + Notes + ----- + See :class:`Initializable` for initialization parameters. + + Note that the ``weights_init``, ``biases_init`` and ``use_bias`` + configurations will overwrite those of the layers each time the + :class:`MLP` is re-initialized. For more fine-grained control, push the + configuration to the child layers manually before initialization. + + >>> from blocks.bricks import Tanh + >>> from blocks.initialization import IsotropicGaussian, Constant + >>> mlp = MLP(activations=[Tanh(), None], dims=[30, 20, 10], + ... weights_init=IsotropicGaussian(), + ... biases_init=Constant(1)) + >>> mlp.push_initialization_config() # Configure children + >>> mlp.children[0].weights_init = IsotropicGaussian(0.1) + >>> mlp.initialize() + + """ + @lazy(allocation=['dims']) + def __init__(self, activations, dims, **kwargs): + self.activations = activations + + self.linear_transformations = [Linear(name='linear_{}'.format(i)) + for i in range(len(activations))] + # Interleave the transformations and activations + application_methods = [] + for entity in interleave([self.linear_transformations, activations]): + if entity is None: + continue + if isinstance(entity, Brick): + application_methods.append(entity.apply) + else: + application_methods.append(entity) + if not dims: + dims = [None] * (len(activations) + 1) + self.dims = dims + super(MLP, self).__init__(application_methods, **kwargs) + + @property + def input_dim(self): + return self.dims[0] + + @input_dim.setter + def input_dim(self, value): + self.dims[0] = value + + @property + def output_dim(self): + return self.dims[-1] + + @output_dim.setter + def output_dim(self, value): + self.dims[-1] = value + + def _push_allocation_config(self): + if not len(self.dims) - 1 == len(self.linear_transformations): + raise ValueError + for input_dim, output_dim, layer in \ + equizip(self.dims[:-1], self.dims[1:], + self.linear_transformations): + layer.input_dim = input_dim + layer.output_dim = output_dim + layer.use_bias = self.use_bias diff --git a/blocks/bricks/simple.py b/blocks/bricks/simple.py new file mode 100644 index 0000000..ed9215d --- /dev/null +++ b/blocks/bricks/simple.py @@ -0,0 +1,395 @@ +"""Some of the simplest individual bricks.""" +import logging + +from theano import tensor + +from blocks.bricks.base import application, Brick, lazy +from blocks.bricks.interfaces import Activation, Feedforward, Initializable +from blocks.bricks.interfaces import Random # noqa + +from blocks.bricks.wrappers import WithExtraDims +from blocks.roles import add_role, WEIGHT, BIAS +from blocks.utils import shared_floatx_nans + +logger = logging.getLogger(__name__) + + +class Linear(Initializable, Feedforward): + r"""A linear transformation with optional bias. + + Brick which applies a linear (affine) transformation by multiplying + the input with a weight matrix. By default, a bias term is added + (see :class:`Initializable` for information on disabling this). + + Parameters + ---------- + input_dim : int + The dimension of the input. Required by :meth:`~.Brick.allocate`. + output_dim : int + The dimension of the output. Required by :meth:`~.Brick.allocate`. + + Notes + ----- + See :class:`Initializable` for initialization parameters. + + A linear transformation with bias is a matrix multiplication followed + by a vector summation. + + .. math:: f(\mathbf{x}) = \mathbf{W}\mathbf{x} + \mathbf{b} + + """ + @lazy(allocation=['input_dim', 'output_dim']) + def __init__(self, input_dim, output_dim, **kwargs): + super(Linear, self).__init__(**kwargs) + self.input_dim = input_dim + self.output_dim = output_dim + + @property + def W(self): + return self.parameters[0] + + @property + def b(self): + return self.parameters[1] + + def _allocate(self): + W = shared_floatx_nans((self.input_dim, self.output_dim), name='W') + add_role(W, WEIGHT) + self.parameters.append(W) + self.add_auxiliary_variable(W.norm(2), name='W_norm') + if self.use_bias: + b = shared_floatx_nans((self.output_dim,), name='b') + add_role(b, BIAS) + self.parameters.append(b) + self.add_auxiliary_variable(b.norm(2), name='b_norm') + + def _initialize(self): + if self.use_bias: + W, b = self.parameters + self.biases_init.initialize(b, self.rng) + else: + W, = self.parameters + self.weights_init.initialize(W, self.rng) + + @application(inputs=['input_'], outputs=['output']) + def apply(self, input_): + """Apply the linear transformation. + + Parameters + ---------- + input_ : :class:`~tensor.TensorVariable` + The input on which to apply the transformation + + Returns + ------- + output : :class:`~tensor.TensorVariable` + The transformed input plus optional bias + + """ + if self.use_bias: + W, b = self.parameters + else: + W, = self.parameters + output = tensor.dot(input_, W) + if self.use_bias: + output += b + return output + + def get_dim(self, name): + if name == 'input_': + return self.input_dim + if name == 'output': + return self.output_dim + super(Linear, self).get_dim(name) + + +class Bias(Feedforward, Initializable): + """Add a bias (i.e. sum with a vector).""" + @lazy(allocation=['dim']) + def __init__(self, dim, **kwargs): + super(Bias, self).__init__(**kwargs) + self.dim = dim + + def _allocate(self): + b = shared_floatx_nans((self.output_dim,), name='b') + add_role(b, BIAS) + self.parameters.append(b) + + def _initialize(self): + b, = self.parameters + self.biases_init.initialize(b, self.rng) + + @application(inputs=['input_'], outputs=['output']) + def apply(self, input_): + """Apply the linear transformation. + + Parameters + ---------- + input_ : :class:`~tensor.TensorVariable` + The input on which to apply the transformation + + Returns + ------- + output : :class:`~tensor.TensorVariable` + The transformed input plus optional bias + + """ + b, = self.parameters + return input_ + b + + def get_dim(self, name): + if name in ['input_', 'output']: + return self.dim + super(Bias, self).get_dim(name) + + def _get_dim(self): + return self.dim + + def _set_dim(self, value): + self.dim = value + + input_dim = output_dim = property(_get_dim, _set_dim) + + +class Maxout(Brick): + """Maxout pooling transformation. + + A brick that does max pooling over groups of input units. If you use + this code in a research project, please cite [GWFM13]_. + + .. [GWFM13] Ian J. Goodfellow, David Warde-Farley, Mehdi Mirza, Aaron + Courville, and Yoshua Bengio, *Maxout networks*, ICML (2013), pp. + 1319-1327. + + Parameters + ---------- + num_pieces : int + The size of the groups the maximum is taken over. + + Notes + ----- + Maxout applies a set of linear transformations to a vector and selects + for each output dimension the result with the highest value. + + """ + @lazy(allocation=['num_pieces']) + def __init__(self, num_pieces, **kwargs): + super(Maxout, self).__init__(**kwargs) + self.num_pieces = num_pieces + + @application(inputs=['input_'], outputs=['output']) + def apply(self, input_): + """Apply the maxout transformation. + + Parameters + ---------- + input_ : :class:`~tensor.TensorVariable` + The input on which to apply the transformation + + Returns + ------- + output : :class:`~tensor.TensorVariable` + The transformed input + + """ + last_dim = input_.shape[-1] + output_dim = last_dim // self.num_pieces + new_shape = ([input_.shape[i] for i in range(input_.ndim - 1)] + + [output_dim, self.num_pieces]) + output = tensor.max(input_.reshape(new_shape, ndim=input_.ndim + 1), + axis=input_.ndim) + return output + + +class LinearMaxout(Initializable, Feedforward): + """Maxout pooling following a linear transformation. + + This code combines the :class:`Linear` brick with a :class:`Maxout` + brick. + + Parameters + ---------- + input_dim : int + The dimension of the input. Required by :meth:`~.Brick.allocate`. + output_dim : int + The dimension of the output. Required by :meth:`~.Brick.allocate`. + num_pieces : int + The number of linear functions. Required by + :meth:`~.Brick.allocate`. + + Notes + ----- + See :class:`Initializable` for initialization parameters. + + """ + @lazy(allocation=['input_dim', 'output_dim', 'num_pieces']) + def __init__(self, input_dim, output_dim, num_pieces, **kwargs): + super(LinearMaxout, self).__init__(**kwargs) + self.linear = Linear() + self.maxout = Maxout() + self.children = [self.linear, + self.maxout] + + self.input_dim = input_dim + self.output_dim = output_dim + self.num_pieces = num_pieces + + @property + def input_dim(self): + return self.linear.input_dim + + @input_dim.setter + def input_dim(self, value): + self.linear.input_dim = value + + def _push_allocation_config(self): + self.linear.output_dim = self.output_dim * self.num_pieces + self.maxout.num_pieces = self.num_pieces + + @application(inputs=['input_'], outputs=['output']) + def apply(self, input_): + """Apply the linear transformation followed by maxout. + + Parameters + ---------- + input_ : :class:`~tensor.TensorVariable` + The input on which to apply the transformations + + Returns + ------- + output : :class:`~tensor.TensorVariable` + The transformed input + + """ + pre_activation = self.linear.apply(input_) + output = self.maxout.apply(pre_activation) + return output + + +class Identity(Activation): + @application(inputs=['input_'], outputs=['output']) + def apply(self, input_): + return input_ + + +class Tanh(Activation): + @application(inputs=['input_'], outputs=['output']) + def apply(self, input_): + return tensor.tanh(input_) + + +class Logistic(Activation): + @application(inputs=['input_'], outputs=['output']) + def apply(self, input_): + return tensor.nnet.sigmoid(input_) + + +class Softplus(Activation): + r""" Softplus brick. + + The softplus is defined as :math:`\zeta(x) = \log(1+e^x)`. + + .. Dugas, C., Bengio, Y., Belisle, F., Nadeau, C., and Garcia, + R. (2001). Incorporating second-order functional knowledge + for better option pricing. In NIPS 13 . MIT Press. + + """ + @application(inputs=['input_'], outputs=['output']) + def apply(self, input_): + return tensor.nnet.softplus(input_) + + +class Rectifier(Activation): + @application(inputs=['input_'], outputs=['output']) + def apply(self, input_): + return tensor.switch(input_ > 0, input_, 0) + + +class Softmax(Brick): + """A softmax brick. + + Works with 2-dimensional inputs only. If you need more, + see :class:`NDimensionalSoftmax`. + + """ + @application(inputs=['input_'], outputs=['output']) + def apply(self, input_): + """Standard softmax. + + Parameters + ---------- + input_ : :class:`~theano.Variable` + A matrix, each row contains unnormalized log-probabilities of a + distribution. + + Returns + ------- + output_ : :class:`~theano.Variable` + A matrix with probabilities in each row for each distribution + from `input_`. + + """ + return tensor.nnet.softmax(input_) + + @application(inputs=['input_'], outputs=['output']) + def log_probabilities(self, input_): + """Normalize log-probabilities. + + Converts unnormalized log-probabilities (exponents of which do not + sum to one) into actual log-probabilities (exponents of which sum + to one). + + Parameters + ---------- + input_ : :class:`~theano.Variable` + A matrix, each row contains unnormalized log-probabilities of a + distribution. + + Returns + ------- + output : :class:`~theano.Variable` + A matrix with normalized log-probabilities in each row for each + distribution from `input_`. + + """ + shifted = input_ - input_.max(axis=1, keepdims=True) + return shifted - tensor.log( + tensor.exp(shifted).sum(axis=1, keepdims=True)) + + @application(inputs=['y', 'x'], outputs=['output']) + def categorical_cross_entropy(self, application_call, y, x): + """Computationally stable cross-entropy for pre-softmax values. + + Parameters + ---------- + y : :class:`~tensor.TensorVariable` + In the case of a matrix argument, each row represents a + probabilility distribution. In the vector case, each element + represents a distribution by specifying the position of 1 in a + 1-hot vector. + x : :class:`~tensor.TensorVariable` + A matrix, each row contains unnormalized probabilities of a + distribution. + + Returns + ------- + cost : :class:`~tensor.TensorVariable` + A vector of cross-entropies between respective distributions + from y and x. + + """ + x = self.log_probabilities(x) + application_call.add_auxiliary_variable( + x.copy(name='log_probabilities')) + if y.ndim == x.ndim - 1: + indices = tensor.arange(y.shape[0]) * x.shape[1] + y + cost = -x.flatten()[indices] + elif y.ndim == x.ndim: + cost = -(x * y).sum(axis=1) + else: + raise TypeError('rank mismatch between x and y') + return cost + + +class NDimensionalSoftmax(Softmax): + decorators = [WithExtraDims()] diff --git a/blocks/config.py b/blocks/config.py index dceee53..c6862e4 100644 --- a/blocks/config.py +++ b/blocks/config.py @@ -60,6 +60,12 @@ The maximum size of an object to store in an SQLite database in bytes. Objects beyond this size will trigger a warning. Defaults to 4 kilobyte. +.. option:: temp_dir + + The directory in which Blocks will create temporary files. If + unspecified, the platform-dependent default chosen by the Python + ``tempfile`` module is used. + .. _YAML: http://yaml.org/ .. _environment variables: https://en.wikipedia.org/wiki/Environment_variable @@ -159,6 +165,12 @@ def bool_(val): return False return bool(val) + +def str_or_none(val): + """Like str, but allows a value of None to pass through as-is.""" + return str(val) if val is not None else None + + # Define configuration options config = Configuration() config.add_config('default_seed', type_=int, default=1) @@ -170,4 +182,5 @@ def bool_(val): default=os.path.expanduser('~/blocks_log.sqlite'), env_var='BLOCKS_SQLITEDB') config.add_config('max_blob_size', type_=int, default=4096) +config.add_config('temp_dir', type_=str_or_none, default=None) config.load_yaml() diff --git a/blocks/filter.py b/blocks/filter.py index b5c41a6..8248ff3 100644 --- a/blocks/filter.py +++ b/blocks/filter.py @@ -84,7 +84,8 @@ class VariableFilter(object): Examples -------- - >>> from blocks.bricks import MLP, Linear, Logistic, Identity, BIAS + >>> from blocks.bricks import MLP, Linear, Logistic, Identity + >>> from blocks.roles import BIAS >>> mlp = MLP(activations=[Identity(), Logistic()], dims=[20, 10, 20]) >>> from theano import tensor >>> x = tensor.matrix() @@ -109,7 +110,7 @@ def __init__(self, roles=None, bricks=None, each_role=False, name=None, isinstance(application, BoundApplication) for application in applications): raise ValueError('`applications` should be a list of ' - 'Applications') + 'BoundApplications') self.roles = roles self.bricks = bricks self.each_role = each_role diff --git a/blocks/graph.py b/blocks/graph/__init__.py similarity index 78% rename from blocks/graph.py rename to blocks/graph/__init__.py index 0e00ff1..27172be 100644 --- a/blocks/graph.py +++ b/blocks/graph/__init__.py @@ -2,6 +2,7 @@ import logging from collections import OrderedDict from itertools import chain +import warnings import numpy import theano @@ -12,12 +13,14 @@ from theano.scan_module.scan_op import Scan from toolz import unique -from blocks.config import config -from blocks.roles import (add_role, has_roles, AUXILIARY, PARAMETER, DROPOUT, - COLLECTED, COLLECTOR) -from blocks.utils import (is_graph_input, is_shared_variable, dict_union, - shared_floatx_zeros, shared_like) -import warnings +from ..config import config +from ..roles import (add_role, has_roles, AUXILIARY, PARAMETER, DROPOUT, + COLLECTED, COLLECTOR) +from ..utils import (is_graph_input, is_shared_variable, dict_union, + shared_floatx_zeros, shared_like) +from .annotations import add_annotation, Annotation # noqa +from .bn import batch_normalization, apply_batch_normalization # noqa +from .bn import get_batch_normalization_updates # noqa logger = logging.getLogger(__name__) @@ -246,20 +249,21 @@ def replace(self, replacements): return ComputationGraph(outputs_cur) - def merge(self): - from theano.gof import FunctionGraph - from theano.gof.opt import merge_optimizer - fg = FunctionGraph(self.inputs + self.shared_variables, self.outputs, - clone=True) - merge_optimizer.optimize(fg) - return ComputationGraph(fg.outputs) + def get_theano_function(self, additional_updates=None, **kwargs): + r"""Create Theano function from the graph contained. + + Parameters + ---------- + \*\*kwargs : dict + Keyword arguments to theano.function. + Useful for specifying compilation modes or profiling. - def get_theano_function(self, additional_updates=None): - """Create Theano function from the graph contained.""" + """ updates = self.updates if additional_updates: updates = dict_union(updates, OrderedDict(additional_updates)) - return theano.function(self.inputs, self.outputs, updates=updates) + return theano.function(self.inputs, self.outputs, updates=updates, + **kwargs) def get_snapshot(self, data): """Evaluate all role-carrying Theano variables on given data. @@ -307,119 +311,6 @@ def has_inputs(self, variable): return self._has_inputs[variable] -def add_annotation(var, annotation): - annotations = getattr(var.tag, 'annotations', []) - if any(old_annotation.__class__ == annotation.__class__ - for old_annotation in annotations): - raise ValueError - else: - var.tag.annotations = annotations + [annotation] - - -class Annotation(object): - """Annotations on Theano variables in a graph. - - In Blocks annotations are automatically attached to variables created - using bricks. One form of annotation is that many variables are - assigned a role (see :class:`.VariableRole`). A second form of - annotation comes in the form of attaching a :class:`Annotation` - instance to the variable's ``tag`` attribute, with auxiliary variables - and/or updates. - - For example, we might be interested in the mean activation of certain - application of a :class:`.Linear` brick. The variable representing the - mean activation is attached as an auxiliary variable to the annotations - of the input and output variables of this brick. Using the - :class:`ComputationGraph` class (the - :attr:`~ComputationGraph.variables`, - :attr:`~ComputationGraph.auxiliary_variables`, etc. attributes in - particular) we can retrieve these Theano variables to pass on to the - monitor, use as a regularizer, etc. - - In most cases, annotations are added on a brick level (e.g. each brick - will assign the weight norm of its weights as an auxiliary value) or on - an application level (e.g. each time a brick is applied, its mean - activation will become an auxiliary variable). However, you can also - add annotations manually, by setting the ``annotation`` value of a - variable's ``tag`` field. - - Examples - -------- - >>> from theano import tensor - >>> x = tensor.vector() - >>> annotation = Annotation() - >>> annotation.add_auxiliary_variable(x + 1, name='x_plus_1') - >>> add_annotation(x, annotation) - >>> y = x ** 2 - >>> from blocks.graph import ComputationGraph - >>> cg = ComputationGraph([y]) - >>> cg.auxiliary_variables - [x_plus_1] - - """ - def __init__(self): - self.auxiliary_variables = [] - self.updates = OrderedDict() - - def add_auxiliary_variable(self, variable, roles=None, name=None): - """Attach an auxiliary variable to the graph. - - Auxiliary variables are Theano variables that are not part of a - brick's output, but can be useful nonetheless e.g. as a regularizer - or to monitor during training progress. - - Parameters - ---------- - variable : :class:`~tensor.TensorVariable` - The variable you want to add. - roles : list of :class:`.VariableRole` instances, optional - The roles of this variable. The :const:`.AUXILIARY` - role will automatically be added. Other options are - :const:`.COST`, :const:`.WEIGHT`, etc. - name : str, optional - Name to give to the variable. If the variable already has a - name it will be overwritten. - - Examples - -------- - >>> from blocks.bricks.base import application, Brick - >>> from blocks.roles import COST - >>> from blocks.utils import shared_floatx_nans - >>> class Foo(Brick): - ... def _allocate(self): - ... W = shared_floatx_nans((10, 10)) - ... self.add_auxiliary_variable(W.mean(), name='mean_W') - ... @application - ... def apply(self, x, application_call): - ... application_call.add_auxiliary_variable( - ... x - 1, name='x_minus_1') - ... application_call.add_auxiliary_variable( - ... x.mean(), roles=[COST], name='mean_x') - ... return x + 1 - >>> from theano import tensor - >>> x = tensor.vector() - >>> y = Foo().apply(x) - >>> from blocks.filter import VariableFilter - >>> cg = ComputationGraph([y]) - >>> var_filter = VariableFilter(roles=[AUXILIARY]) - >>> var_filter(cg.variables) # doctest: +SKIP - {x_minus_1, mean_W, mean_x} - >>> var_filter = VariableFilter(roles=[COST]) - >>> var_filter(cg.variables) # doctest: +SKIP - {mean_x} - - """ - add_annotation(variable, self) - if name is not None: - variable.name = name - variable.tag.name = name - add_role(variable, AUXILIARY) - if roles is not None: - for role in roles: - add_role(variable, role) - self.auxiliary_variables.append(variable) - - def apply_noise(computation_graph, variables, level, seed=None): """Add Gaussian noise to certain variable of a computation graph. @@ -534,8 +425,8 @@ def collect_parameters(computation_graph, parameters): def apply_dropout(computation_graph, variables, drop_prob, rng=None, - seed=None): - """Returns a graph to variables in a computational graph. + seed=None, custom_divisor=None): + """Apply dropout to specified variables in a graph. Parameters ---------- @@ -551,6 +442,19 @@ def apply_dropout(computation_graph, variables, drop_prob, rng=None, Random number generator. seed : int Random seed to be used if `rng` was not specified. + custom_divisor : float or None, optional + Divide dropped variables by a given scalar value. If `None`, + (default) dropped variables will be divided by `(1 - drop_prob)` + which is equivalent to scaling by `(1 - drop_prob)` at test + time as recommended in [DROPOUT]_. + + Returns + ------- + dropped_computation_graph : instance of :class:`ComputationGraph` + A new computation graph with dropout applied to the specified + variables. In order to train with, or monitor, the outputs + of the original computation graph with dropout applies, use + the variables contained in `dropped_computation_graph.outputs`. Notes ----- @@ -623,11 +527,14 @@ def apply_dropout(computation_graph, variables, drop_prob, rng=None, seed = config.default_seed if not rng: rng = MRG_RandomStreams(seed) - + if custom_divisor is None: + divisor = (1 - drop_prob) + else: + divisor = custom_divisor replacements = [(var, var * rng.binomial(var.shape, p=1 - drop_prob, dtype=theano.config.floatX) / - (1 - drop_prob)) + divisor) for var in variables] for variable, replacement in replacements: add_role(replacement, DROPOUT) diff --git a/blocks/graph/annotations.py b/blocks/graph/annotations.py new file mode 100644 index 0000000..b684c01 --- /dev/null +++ b/blocks/graph/annotations.py @@ -0,0 +1,116 @@ +from collections import OrderedDict +from ..roles import add_role, AUXILIARY + + +def add_annotation(var, annotation): + annotations = getattr(var.tag, 'annotations', []) + if any(old_annotation.__class__ == annotation.__class__ + for old_annotation in annotations): + raise ValueError + else: + var.tag.annotations = annotations + [annotation] + + +class Annotation(object): + """Annotations on Theano variables in a graph. + + In Blocks annotations are automatically attached to variables created + using bricks. One form of annotation is that many variables are + assigned a role (see :class:`.VariableRole`). A second form of + annotation comes in the form of attaching a :class:`Annotation` + instance to the variable's ``tag`` attribute, with auxiliary variables + and/or updates. + + For example, we might be interested in the mean activation of certain + application of a :class:`.Linear` brick. The variable representing the + mean activation is attached as an auxiliary variable to the annotations + of the input and output variables of this brick. Using the + :class:`ComputationGraph` class (the + :attr:`~ComputationGraph.variables`, + :attr:`~ComputationGraph.auxiliary_variables`, etc. attributes in + particular) we can retrieve these Theano variables to pass on to the + monitor, use as a regularizer, etc. + + In most cases, annotations are added on a brick level (e.g. each brick + will assign the weight norm of its weights as an auxiliary value) or on + an application level (e.g. each time a brick is applied, its mean + activation will become an auxiliary variable). However, you can also + add annotations manually, by setting the ``annotation`` value of a + variable's ``tag`` field. + + Examples + -------- + >>> from theano import tensor + >>> x = tensor.vector() + >>> annotation = Annotation() + >>> annotation.add_auxiliary_variable(x + 1, name='x_plus_1') + >>> add_annotation(x, annotation) + >>> y = x ** 2 + >>> from blocks.graph import ComputationGraph + >>> cg = ComputationGraph([y]) + >>> cg.auxiliary_variables + [x_plus_1] + + """ + def __init__(self): + self.auxiliary_variables = [] + self.updates = OrderedDict() + + def add_auxiliary_variable(self, variable, roles=None, name=None): + """Attach an auxiliary variable to the graph. + + Auxiliary variables are Theano variables that are not part of a + brick's output, but can be useful nonetheless e.g. as a regularizer + or to monitor during training progress. + + Parameters + ---------- + variable : :class:`~tensor.TensorVariable` + The variable you want to add. + roles : list of :class:`.VariableRole` instances, optional + The roles of this variable. The :const:`.AUXILIARY` + role will automatically be added. Other options are + :const:`.COST`, :const:`.WEIGHT`, etc. + name : str, optional + Name to give to the variable. If the variable already has a + name it will be overwritten. + + Examples + -------- + >>> from blocks.bricks.base import application, Brick + >>> from blocks.roles import COST + >>> from blocks.utils import shared_floatx_nans + >>> class Foo(Brick): + ... def _allocate(self): + ... W = shared_floatx_nans((10, 10)) + ... self.add_auxiliary_variable(W.mean(), name='mean_W') + ... @application + ... def apply(self, x, application_call): + ... application_call.add_auxiliary_variable( + ... x - 1, name='x_minus_1') + ... application_call.add_auxiliary_variable( + ... x.mean(), roles=[COST], name='mean_x') + ... return x + 1 + >>> from theano import tensor + >>> x = tensor.vector() + >>> y = Foo().apply(x) + >>> from blocks.graph import ComputationGraph + >>> cg = ComputationGraph([y]) + >>> from blocks.filter import VariableFilter + >>> var_filter = VariableFilter(roles=[AUXILIARY]) + >>> var_filter(cg.variables) # doctest: +SKIP + {x_minus_1, mean_W, mean_x} + >>> var_filter = VariableFilter(roles=[COST]) + >>> var_filter(cg.variables) # doctest: +SKIP + {mean_x} + + """ + add_annotation(variable, self) + if name is not None: + variable.name = name + variable.tag.name = name + add_role(variable, AUXILIARY) + if roles is not None: + for role in roles: + add_role(variable, role) + self.auxiliary_variables.append(variable) diff --git a/blocks/graph/bn.py b/blocks/graph/bn.py new file mode 100644 index 0000000..212f143 --- /dev/null +++ b/blocks/graph/bn.py @@ -0,0 +1,269 @@ +"""Implements the batch normalization training graph transform. + +Specifically, this module contains the implementation for the +transformation of a batch-normalized inference graph into training graph, +which uses minibatch statistics in place of population statistics. + +""" +import collections +import contextlib +from functools import partial + +import theano +from toolz import isdistinct + +from ..roles import BATCH_NORM_OFFSET, BATCH_NORM_DIVISOR, INPUT, OUTPUT +from ..utils import find_bricks + + +def _training_mode_application_calls(application_calls): + """Filter for application calls made in 'training mode'.""" + from ..bricks import BatchNormalization + out = [] + for app_call in application_calls: + assert isinstance(app_call.application.brick, BatchNormalization) + assert app_call.application.application == BatchNormalization.apply + if app_call.metadata.get('training_mode', False): + out.append(app_call) + return out + + +@contextlib.contextmanager +def batch_normalization(*bricks): + r"""Context manager to run batch normalization in "training mode". + + Parameters + ---------- + \*bricks + One or more bricks which will be inspected for descendant + instances of :class:`~blocks.bricks.BatchNormalization`. + + Notes + ----- + Graph replacement using :func:`apply_batch_normalization`, while + elegant, can lead to Theano graphs that are quite large and result + in very slow compiles. This provides an alternative mechanism for + building the batch normalized training graph. It can be somewhat + less convenient as it requires building the graph twice if one + wishes to monitor the output of the inference graph during training. + + Examples + -------- + First, we'll create a :class:`~blocks.bricks.BatchNormalizedMLP`. + + >>> import theano + >>> from blocks.bricks import BatchNormalizedMLP, Tanh + >>> from blocks.initialization import Constant, IsotropicGaussian + >>> mlp = BatchNormalizedMLP([Tanh(), Tanh()], [4, 5, 6], + ... weights_init=IsotropicGaussian(0.1), + ... biases_init=Constant(0)) + >>> mlp.initialize() + + Now, we'll construct an output variable as we would normally. This + is getting normalized by the *population* statistics, which by + default are initialized to 0 (mean) and 1 (standard deviation), + respectively. + + >>> x = theano.tensor.matrix() + >>> y = mlp.apply(x) + + And now, to construct an output with batch normalization enabled, + i.e. normalizing pre-activations using per-minibatch statistics, we + simply make a similar call inside of a `with` statement: + + >>> with batch_normalization(mlp): + ... y_bn = mlp.apply(x) + + Let's verify that these two graphs behave differently on the + same data: + + >>> import numpy + >>> data = numpy.arange(12, dtype=theano.config.floatX).reshape(3, 4) + >>> inf_y = y.eval({x: data}) + >>> trn_y = y_bn.eval({x: data}) + >>> numpy.allclose(inf_y, trn_y) + False + + """ + # Avoid circular imports. + from blocks.bricks import BatchNormalization + + bn = find_bricks(bricks, lambda b: isinstance(b, BatchNormalization)) + # Can't use either nested() (deprecated) nor ExitStack (not available + # on Python 2.7). Well, that sucks. + try: + for brick in bn: + brick.__enter__() + yield + finally: + for brick in bn[::-1]: + brick.__exit__() + + +def apply_batch_normalization(computation_graph): + """Transform a graph into a batch-normalized training graph. + + Parameters + ---------- + computation_graph : :class:`~blocks.graph.ComputationGraph` + The computation graph containing :class:`BatchNormalization` + brick applications. + + Returns + ------- + batch_normed_graph : :class:`~blocks.graph.ComputationGraph` + The computation graph, with :class:`BatchNormalization` + applications transformed to use minibatch statistics instead + of accumulated population statistics. + + See Also + -------- + :func:`batch_normalization`, for an alternative method to produce + batch normalized graphs. + + Examples + -------- + First, we'll create a :class:`~blocks.bricks.BatchNormalizedMLP`. + + >>> import theano + >>> from blocks.bricks import BatchNormalizedMLP, Tanh + >>> from blocks.initialization import Constant, IsotropicGaussian + >>> mlp = BatchNormalizedMLP([Tanh(), Tanh()], [4, 5, 6], + ... weights_init=IsotropicGaussian(0.1), + ... biases_init=Constant(0)) + >>> mlp.initialize() + + Now, we'll construct an output variable as we would normally. This + is getting normalized by the *population* statistics, which by + default are initialized to 0 (mean) and 1 (standard deviation), + respectively. + + >>> x = theano.tensor.matrix() + >>> y = mlp.apply(x) + + Finally, we'll create a :class:`~blocks.graph.ComputationGraph` + and transform it to switch to minibatch standardization: + + >>> from blocks.graph import ComputationGraph + >>> cg = apply_batch_normalization(ComputationGraph([y])) + >>> y_bn = cg.outputs[0] + + Let's verify that these two graphs behave differently on the + same data: + + >>> import numpy + >>> data = numpy.arange(12, dtype=theano.config.floatX).reshape(3, 4) + >>> inf_y = y.eval({x: data}) + >>> trn_y = y_bn.eval({x: data}) + >>> numpy.allclose(inf_y, trn_y) + False + + """ + # Avoid circular imports. + from blocks.bricks import BatchNormalization + from ..filter import VariableFilter, get_application_call + + # Create filters for variables involved in a batch normalization brick + # application. + def make_variable_filter(role): + return VariableFilter(bricks=[BatchNormalization], roles=[role]) + + # Group inputs and outputs into dicts indexed by application call. + def get_app_call_dict(variable_filter): + return collections.OrderedDict((get_application_call(v), v) for v in + variable_filter(computation_graph)) + + # Compose these two so that we get 4 dicts, grouped by application + # call, of different variable roles involved in BatchNormalization. + inputs, outputs, means, stdevs = map(get_app_call_dict, + map(make_variable_filter, + [INPUT, OUTPUT, BATCH_NORM_OFFSET, + BATCH_NORM_DIVISOR])) + + assert len(set([len(inputs), len(outputs), len(means), len(stdevs)])) == 1 + + # Remove any ApplicationCalls that were not generated by apply(), or + # were generated by an apply() while already in training mode. + app_calls = inputs.keys() + remove = _training_mode_application_calls(app_calls) + for app_call in app_calls: + if app_call in remove: + for mapping in (inputs, outputs, means, stdevs): + del mapping[app_call] + + replacements = [] + for app_call in inputs: + old_output = outputs[app_call] + # Get rid of the copy made on the way into the original apply. + op = inputs[app_call].owner.op + assert (isinstance(op, theano.tensor.Elemwise) and + isinstance(op.scalar_op, theano.scalar.basic.Identity)) + unpacked = inputs[app_call].owner.inputs[0] + with app_call.application.brick: + new_output = app_call.application.brick.apply(unpacked) + new_app_call = get_application_call(new_output) + assert new_app_call.metadata['training_mode'] + replacements.append((old_output, new_output)) + return computation_graph.replace(replacements) + + +def get_batch_normalization_updates(training_graph, allow_duplicates=False): + """Extract correspondences for learning BN population statistics. + + Parameters + ---------- + training_graph : :class:`~blocks.graph.ComputationGraph` + A graph of expressions wherein "training mode" batch normalization + is taking place. + allow_duplicates : bool, optional + If `True`, allow multiple training-mode application calls from the + same :class:`~blocks.bricks.BatchNormalization` instance, and + return pairs corresponding to all of them. It's then the user's + responsibility to do something sensible to resolve the duplicates. + + Returns + ------- + update_pairs : list of tuples + A list of 2-tuples where the first element of each tuple is the + shared variable containing a "population" mean or standard + deviation, and the second is a Theano variable for the + corresponding statistics on a minibatch. Note that multiple + applications of a single :class:`blocks.bricks.BatchNormalization` + may appear in the graph, and therefore (if `allow_duplicates` is + True) a single population variable may map to several different + minibatch variables, and appear multiple times in this mapping. + This can happen in recurrent models, siamese networks or other + models that reuse pathways. + + Notes + ----- + Used in their raw form, these updates will simply overwrite the + population statistics with the minibatch statistics at every gradient + step. You will probably want to transform these pairs into something + more sensible, such as keeping a moving average of minibatch values, + or accumulating an average over the entire training set once every few + epochs. + + """ + from ..bricks import BatchNormalization + from ..filter import VariableFilter, get_application_call + var_filter = VariableFilter(bricks=[BatchNormalization], roles=[OUTPUT]) + all_app_calls = map(get_application_call, var_filter(training_graph)) + train_app_calls = _training_mode_application_calls(all_app_calls) + if len(train_app_calls) == 0: + raise ValueError("no training mode BatchNormalization " + "applications found in graph") + bricks = [c.application.brick for c in train_app_calls] + + if not allow_duplicates and not isdistinct(bricks): + raise ValueError('multiple applications of the same ' + 'BatchNormalization brick; pass allow_duplicates ' + '= True to override this check') + + def extract_pair(brick_attribute, metadata_key, app_call): + return (getattr(app_call.application.brick, brick_attribute), + app_call.metadata[metadata_key]) + + mean_pair = partial(extract_pair, 'population_mean', 'offset') + stdev_pair = partial(extract_pair, 'population_stdev', 'divisor') + return sum([[mean_pair(a), stdev_pair(a)] for a in train_app_calls], []) diff --git a/blocks/main_loop.py b/blocks/main_loop.py index 6f132d0..b519a25 100644 --- a/blocks/main_loop.py +++ b/blocks/main_loop.py @@ -9,6 +9,7 @@ from blocks.utils.profile import Profile, Timer from blocks.algorithms import DifferentiableCostMinimizer from blocks.extensions import CallbackName +from blocks.model import Model logger = logging.getLogger(__name__) @@ -26,7 +27,7 @@ epoch_interrupt_message = """ -Blocks will complete this epoch iteration of training and run extensions \ +Blocks will complete this epoch of training and run extensions \ before exiting. If you do not want to complete this epoch, press CTRL + C \ again to stop training after the current batch.""" @@ -69,13 +70,16 @@ class MainLoop(object): Parameters ---------- - algorithm : object + algorithm : instance of :class:`~blocks.algorithms.TrainingAlgorithm` The training algorithm. data_stream : instance of :class:`.DataStream`. - The data stream. - model : :class:`.AbstractModel` instance, optional - The model object. It is entirely transparent for the main loop - but may be used by extensions. + The data stream. Should support :class:`AbstractDataStream` + interface from Fuel. + model : instance of :class:`.ComputationGraph`, optional + An annotated computation graph, typically represented + by :class:`ComputationGraph` or :class:`Model` object. The main + loop object uses the model only for optional sanity checks, it is + here mainly for the main loop extensions. log : instance of :class:`.TrainingLog`, optional The log. When not given, a :class:`.TrainingLog` is created. log_backend : str @@ -146,12 +150,9 @@ def run(self): # reset `profile.current`. Otherwise, it simply does not hurt. self.profile.current = [] - if self._model and isinstance(self.algorithm, - DifferentiableCostMinimizer): - # Sanity check: model and algorithm should be configured - # similarly. - if not self._model.get_objective() == self.algorithm.cost: - logger.warning("different costs for model and algorithm") + # Sanity check for the most common case + if (self._model and isinstance(self._model, Model) and + isinstance(self.algorithm, DifferentiableCostMinimizer)): if not (set(self._model.get_parameter_dict().values()) == set(self.algorithm.parameters)): logger.warning("different parameters for model and algorithm") diff --git a/blocks/model.py b/blocks/model.py index 685ca08..2febc37 100644 --- a/blocks/model.py +++ b/blocks/model.py @@ -1,75 +1,118 @@ -"""Defines models. - -A model is a thin layer of abstraction between the user-defined computation -graph, bricks, parameters and main loop extensions. This module provides -the basic :class:`AbstractModel` interface as well as its implementations -(currently only :class:`Model`). +"""Model - heavily annotated computation graph. + +A model in Blocks is simply an annotated computation graph. The class +:class:`Model` extends :class:`blocks.graph.ComputationGraph` :class:, +which is able to handle annotations and roles in general, but is +deliberately made unaware of specific annotations that a Theano graph +created by Blocks typically has, such as bricks and application calls. The +:class:`Model` adds this functionality. Using :class:`Model` you can do +things like query all the bricks used to build the computation graph, +request "hierarhical names" of the parameters (a hierarchical name is a +path-like string which in addition to the parameter's name contains names +of the bricks on the path from a root brick to the brick that owns the +parameters, e.g. ``/mlp/linear/W``). + +For more information, see :class:`Model` docstring. """ import logging -from abc import ABCMeta, abstractmethod from collections import OrderedDict, Counter from itertools import chain -from six import add_metaclass - from blocks.graph import ComputationGraph from blocks.select import Selector from blocks.filter import get_brick logger = logging.getLogger(__name__) -multiple_message = """ - -Model with multiple outputs are currently only partially supported \ -in Blocks. For instance a call of 'get_objective' will crash. \ -Contact Blocks developers for more details. -""" - -@add_metaclass(ABCMeta) -class AbstractModel(object): - """A parameterized entity trained in the main loop. +class Model(ComputationGraph): + """Handles annotations in Blocks-built computation graphs. - A model is a parameterized entity the user trains a in a main loop. - The following are traits of every model: + Use this class to handle your Blocks-created computation graph. - * It has parameters and supports a way to access them. In addition - to returning handles to parameter objects it can return their values - as numpy arrays and set their values to given numpy arrays. + Examples + -------- + >>> from theano import tensor + >>> from blocks.bricks import MLP, Tanh + >>> x = tensor.matrix('x') + >>> mlp = MLP([Tanh(), Tanh()], [10, 10, 10]) + >>> y = mlp.apply(x) + >>> model = Model(y) - * It has an optimality objective. + With :class:`Model` you can get access to the brick hierarchy. The + brick hierarchy is defined by ``children`` attributes that every brick + has. The bricks that are not children of other bricks are called top + bricks. It is often useful to have access to top bricks of a brick + hierarchy used to build a computation graph, and here is how you can do + it: - * It can be serialized and deserialized by mean of pickling. + >>> model.get_top_bricks() #doctest: +ELLIPSIS + [>> model.get_parameter_dict() #doctest: +NORMALIZE_WHITESPACE + OrderedDict([('/mlp/linear_1.b', b), ('/mlp/linear_0.b', b), + ('/mlp/linear_0.W', W), ('/mlp/linear_1.W', W)]) """ - @abstractmethod + def __init__(self, *args, **kwargs): + super(Model, self).__init__(*args, **kwargs) + bricks = [get_brick(var) for var + in self.variables + self.scan_variables if get_brick(var)] + children = set(chain(*(brick.children for brick in bricks))) + # Quadratic complexity: we should not have thousands of + # top-level bricks. + self.top_bricks = [] + for brick in bricks: + if brick not in children and brick not in self.top_bricks: + self.top_bricks.append(brick) + names = Counter([brick.name for brick in self.top_bricks]) + repeated_names = [name for name, count in names.items() if count > 1] + if repeated_names: + raise ValueError("top bricks with the same name:" + " {}".format(', '.join(repeated_names))) + brick_parameter_names = { + v: k for k, v in Selector( + self.top_bricks).get_parameters().items()} + parameter_list = [] + for parameter in self.parameters: + if parameter in brick_parameter_names: + parameter_list.append((brick_parameter_names[parameter], + parameter)) + else: + parameter_list.append((parameter.name, parameter)) + self._parameter_dict = OrderedDict(parameter_list) + def get_parameter_dict(self): - """Return the model parameters. + """Returns parameters with their hierarchical names. + + The parameter names are formed from positions of their owner bricks + in the bricks hierarchy. The variable names are used for the + parameters that do not belong to any brick. Returns ------- - parameters : OrderedDict - Dictionary of (name, parameter) pairs. + parameter_dict : dict + A dictionary of (hierarchical name, shared variable) pairs. """ - pass + return self._parameter_dict def get_parameter_values(self): """Return the values of model parameters. - The default implementation assumes that parameters are Theano - shared variables. + The same hierarhical names as in :meth:`get_parameter_dict` are + used to uniquely identify parameters. Returns ------- parameter_values : OrderedDict - Dictionary of (parameter name, :class:`~numpy.ndarray`) pairs. + Dictionary of (hierarchical name, :class:`~numpy.ndarray`) + pairs. """ return OrderedDict( @@ -79,13 +122,14 @@ def get_parameter_values(self): def set_parameter_values(self, parameter_values): """Set the values of model parameters. - The default implementation assumes that parameters are Theano - shared variables. + The same hierarhical names as in :meth:`get_parameter_dict` are + used to uniquely identify parameters. Parameters ---------- parameter_values : OrderedDict - Dictionary of (parameter name, :class:`~numpy.ndarray`) pairs. + Dictionary of (hierarchical name, :class:`~numpy.ndarray`) + pairs. """ parameters = self.get_parameter_dict() @@ -99,102 +143,19 @@ def set_parameter_values(self, parameter_values): for name, value in parameter_values.items(): if name in parameters: - old_shape = parameters[name].get_value().shape - if old_shape != value.shape: - raise ValueError(name + ' old: {} new: {}'.format(old_shape, value.shape)) + model_shape = parameters[name].container.data.shape + if model_shape != value.shape: + raise ValueError("Shape mismatch for parameter: {}. " + "Expected {}, got {}." + .format(name, model_shape, value.shape)) parameters[name].set_value(value) - @abstractmethod - def get_objective(self): - """Return the optimization objective.""" - pass - def get_top_bricks(self): - """Return the top-level bricks that are used in the model. + """Get the bricks that do not have parents. Returns ------- - bricks : list - List of bricks. + bricks : list of :class:`~blocks.bricks.base.Brick` """ - raise NotImplementedError() - - -class Model(AbstractModel, ComputationGraph): - """Wraps a computation graph to support model interface. - - This model covers the most common case when all information - about the model is contained in an annotated computation graph: - parameters are identified by the roles, bricks found by annotations. - Due to frequency of this case this class is called simply 'Model' - and not 'ComputationGraphModel'. - - .. todo:: - - Overriding the automatically found parameters and bricks might - be needed. - - If there are top bricks in scan inner graphs, those will not be - found. - - Parameters - ---------- - outputs : (list of) :class:`~theano.Variable` - The output variables of the computation graph. - - """ - def __init__(self, outputs): - super(Model, self).__init__(outputs) - if len(self.outputs) > 1: - logger.warning("model with multiple output " + multiple_message) - - bricks = [get_brick(var) for var - in self.variables + self.scan_variables if get_brick(var)] - children = set(chain(*(brick.children for brick in bricks))) - # Quadratic complexity: we should not have thousands of - # top-level bricks. - self.top_bricks = [] - for brick in bricks: - if brick not in children and brick not in self.top_bricks: - self.top_bricks.append(brick) - names = Counter([brick.name for brick in self.top_bricks]) - repeated_names = [name for name, count in names.items() if count > 1] - if repeated_names: - raise ValueError("top bricks with the same name:" - " {}".format(', '.join(repeated_names))) - brick_parameter_names = { - v: k for k, v in Selector( - self.top_bricks).get_parameters().items()} - parameter_list = [] - for parameter in self.parameters: - if parameter in brick_parameter_names: - parameter_list.append((brick_parameter_names[parameter], - parameter)) - else: - parameter_list.append((parameter.name, parameter)) - self._parameter_dict = OrderedDict(parameter_list) - - def get_objective(self): - """Return the output variable, if there is a single one. - - If there is only one output variable, it is a reasonable default - setting to assume that it is the optimization objective. - - """ - if len(self.outputs) == 1: - return self.outputs[0] - raise NotImplementedError - - def get_parameter_dict(self): - """Get model parameters. - - The parameter names are formed from positions of their owner bricks - in the bricks hierarchy. The variable names are used for the - parameters that do not belong to any brick. - - """ - return self._parameter_dict - - def get_top_bricks(self): return self.top_bricks diff --git a/blocks/roles.py b/blocks/roles.py index 18e72df..d672189 100644 --- a/blocks/roles.py +++ b/blocks/roles.py @@ -93,7 +93,14 @@ class CostRole(VariableRole): COST = CostRole() -class ParameterRole(VariableRole): +class PersistentRole(VariableRole): + pass + +# Any persistent quantity that should be saved as part of the model +PERSISTENT = PersistentRole() + + +class ParameterRole(PersistentRole): pass #: A parameter of the model @@ -154,3 +161,95 @@ class CollectorRole(ParameterRole): #: A collection of parameters combined into a single shared variable COLLECTOR = CollectorRole() + + +class AlgorithmStateRole(VariableRole): + pass + +#: Shared variables used in algorithms updates +ALGORITHM_STATE = AlgorithmStateRole() + + +class AlgorithmHyperparameterRole(AlgorithmStateRole): + pass + +#: hyperparameters accociated with algorithms +ALGORITHM_HYPERPARAMETER = AlgorithmHyperparameterRole() + + +class AlgorithmBufferRole(AlgorithmStateRole): + pass + +#: buffers accociated with algorithms +ALGORITHM_BUFFER = AlgorithmBufferRole() + + +class BatchNormPopulationStatisticsRole(PersistentRole): + pass + +#: base role for batch normalization population statistics +BATCH_NORM_POPULATION_STATISTICS = BatchNormPopulationStatisticsRole() + + +class BatchNormPopulationMeanRole(BatchNormPopulationStatisticsRole): + pass + +#: mean activations accumulated over the dataset +BATCH_NORM_POPULATION_MEAN = BatchNormPopulationMeanRole() + + +class BatchNormPopulationStdevRole(BatchNormPopulationStatisticsRole): + pass + +#: standard deviations of activations accumulated over the dataset +BATCH_NORM_POPULATION_STDEV = BatchNormPopulationStdevRole() + + +class BatchNormGraphVariableRole(VariableRole): + pass + +#: base for roles used for within-graph batch normalization replacement +BATCH_NORM_GRAPH_VARIABLE = BatchNormGraphVariableRole() + + +class BatchNormOffsetRole(BatchNormGraphVariableRole): + pass + +#: offset applied in a BatchNormalization application (or its +# batch-normalized replacement) +BATCH_NORM_OFFSET = BatchNormOffsetRole() + + +class BatchNormDivisorRole(BatchNormGraphVariableRole): + pass + +#: divisor applied in a BatchNormalization application (or its +# batch-normalized replacement) +BATCH_NORM_DIVISOR = BatchNormDivisorRole() + + +class BatchNormMinibatchEstimateRole(BatchNormGraphVariableRole): + pass + +#: role added to variables that are the result of a batch normalization +# replacement, rather than the original population statistics variables. +BATCH_NORM_MINIBATCH_ESTIMATE = BatchNormMinibatchEstimateRole() + + +class BatchNormScaleParameterRole(ParameterRole): + pass + +#: role given to the scale parameter, referred to as "scale" in the +# batch normalization manuscript, applied after normalizing. +BATCH_NORM_SCALE_PARAMETER = BatchNormScaleParameterRole() + + +class BatchNormShiftParameterRole(BiasRole): + pass + +#: role given to the shift parameter, referred to as "beta" in the +# batch normalization manuscript, applied after normalizing and scaling. +# Inherits from BIAS, because there really is no functional difference +# with a normal bias, and indeed these are the only biases present +# inside a BatchNormalizedMLP. +BATCH_NORM_SHIFT_PARAMETER = BatchNormShiftParameterRole() diff --git a/blocks/select.py b/blocks/select.py index 8d4ce80..55dd6d7 100644 --- a/blocks/select.py +++ b/blocks/select.py @@ -158,20 +158,42 @@ def select(self, path): return Selector(current_bricks) def get_parameters(self, parameter_name=None): - """Returns parameters the selected bricks and their ancestors. + r"""Returns parameters from selected bricks and their descendants. Parameters ---------- - parameter_name : :class:`Path.ParameterName` - If given, only parameters with the name `parameter_name` are - returned. + parameter_name : :class:`Path.ParameterName`, optional + If given, only parameters with a `name` attribute equal to + `parameter_name` are returned. Returns ------- parameters : OrderedDict A dictionary of (`path`, `parameter`) pairs, where `path` is - the string representation of the part to the parameter, - `parameter` is the parameter. + a string representation of the path in the brick hierarchy + to the parameter (i.e. the slash-delimited path to the brick + that owns the parameter, followed by a dot, followed by the + parameter's name), and `parameter` is the Theano variable + representing the parameter. + + Examples + -------- + >>> from blocks.bricks import MLP, Tanh + >>> mlp = MLP([Tanh(), Tanh(), Tanh()], [5, 7, 11, 2]) + >>> mlp.allocate() + >>> selector = Selector([mlp]) + >>> selector.get_parameters() # doctest: +NORMALIZE_WHITESPACE + OrderedDict([('/mlp/linear_0.W', W), ('/mlp/linear_0.b', b), + ('/mlp/linear_1.W', W), ('/mlp/linear_1.b', b), + ('/mlp/linear_2.W', W), ('/mlp/linear_2.b', b)]) + + Or, select just the weights of the MLP by passing the parameter + name `W`: + + >>> w_select = Selector([mlp]) + >>> w_select.get_parameters('W') # doctest: +NORMALIZE_WHITESPACE + OrderedDict([('/mlp/linear_0.W', W), ('/mlp/linear_1.W', W), + ('/mlp/linear_2.W', W)]) """ def recursion(brick): diff --git a/blocks/serialization.py b/blocks/serialization.py index 15b549f..cc18ee4 100644 --- a/blocks/serialization.py +++ b/blocks/serialization.py @@ -141,7 +141,7 @@ def dump(obj, file_handler, protocol=DEFAULT_PROTOCOL, >>> with open('model.zip', 'rb') as f: ... mlp2 = load(f) >>> mlp2 # doctest: +ELLIPSIS - + """ with closing(zipfile.ZipFile(file_handler, 'w', zipfile.ZIP_DEFLATED, @@ -178,7 +178,8 @@ def secure_dump(object_, path, dump_function=dump, **kwargs): """ try: - with tempfile.NamedTemporaryFile(delete=False) as temp: + with tempfile.NamedTemporaryFile(delete=False, + dir=config.temp_dir) as temp: dump_function(object_, temp, **kwargs) shutil.move(temp.name, path) except: diff --git a/blocks/utils/__init__.py b/blocks/utils/__init__.py index dad51e6..480fd5c 100644 --- a/blocks/utils/__init__.py +++ b/blocks/utils/__init__.py @@ -1,7 +1,7 @@ from __future__ import print_function import sys import contextlib -from collections import OrderedDict +from collections import OrderedDict, deque import numpy import six @@ -70,6 +70,35 @@ def unpack(arg, singleton=False): return arg +def shared_floatx_zeros_matching(shared_variable, name=None, **kwargs): + r"""Create another shared variable with matching shape and broadcast. + + Parameters + ---------- + shared_variable : :class:'tensor.TensorSharedVariable' + A Theano shared variable with the desired shape and broadcastable + flags. + name : :obj:`str`, optional + The name for the shared variable. Defaults to `None`. + \*\*kwargs + Keyword arguments to pass to the :func:`shared_floatx_zeros` function. + + Returns + ------- + :class:'tensor.TensorSharedVariable' + A new shared variable, initialized to all zeros, with the same + shape and broadcastable flags as `shared_variable`. + + + """ + if not is_shared_variable(shared_variable): + raise ValueError('argument must be a shared variable') + return shared_floatx_zeros(shared_variable.get_value().shape, + name=name, + broadcastable=shared_variable.broadcastable, + **kwargs) + + def shared_floatx_zeros(shape, **kwargs): r"""Creates a shared variable array filled with zeros. @@ -108,8 +137,8 @@ def shared_floatx_nans(shape, **kwargs): return shared_floatx(numpy.nan * numpy.zeros(shape), **kwargs) -def shared_floatx(value, name=None, borrow=False, dtype=None): - """Transform a value into a shared variable of type floatX. +def shared_floatx(value, name=None, borrow=False, dtype=None, **kwargs): + r"""Transform a value into a shared variable of type floatX. Parameters ---------- @@ -123,6 +152,8 @@ def shared_floatx(value, name=None, borrow=False, dtype=None): dtype : :obj:`str`, optional The `dtype` of the shared variable. Default value is :attr:`config.floatX`. + \*\*kwargs + Keyword arguments to pass to the :func:`~theano.shared` function. Returns ------- @@ -133,12 +164,11 @@ def shared_floatx(value, name=None, borrow=False, dtype=None): if dtype is None: dtype = theano.config.floatX return theano.shared(theano._asarray(value, dtype=dtype), - name=name, - borrow=borrow) + name=name, borrow=borrow, **kwargs) -def shared_like(variable, name=None): - """Construct a shared variable to hold the value of a tensor variable. +def shared_like(variable, name=None, **kwargs): + r"""Construct a shared variable to hold the value of a tensor variable. Parameters ---------- @@ -148,6 +178,8 @@ def shared_like(variable, name=None): name : :obj:`str` or :obj:`None` The name of the shared variable. If None, the name is determined based on variable's name. + \*\*kwargs + Keyword arguments to pass to the :func:`~theano.shared` function. """ variable = tensor.as_tensor_variable(variable) @@ -155,7 +187,7 @@ def shared_like(variable, name=None): name = "shared_{}".format(variable.name) return theano.shared(numpy.zeros((0,) * variable.ndim, dtype=variable.dtype), - name=name) + name=name, **kwargs) def reraise_as(new_exc): @@ -259,13 +291,6 @@ def check_theano_variable(variable, n_dim, dtype_prefix): dtype_prefix, variable.dtype)) -def named_copy(variable, new_name): - """Clones a variable and set a new name to the clone.""" - result = variable.copy() - result.name = new_name - return result - - def is_graph_input(variable): """Check if variable is a user-provided graph input. @@ -460,3 +485,94 @@ def change_recursion_limit(limit): sys.setrecursionlimit(limit) yield sys.setrecursionlimit(old_limit) + + +def extract_args(expected, *args, **kwargs): + r"""Route keyword and positional arguments to a list of names. + + A frequent situation is that a method of the class gets to + know its positional arguments only when an instance of the class + has been created. In such cases the signature of such method has to + be `*args, **kwargs`. The downside of such signatures is that the + validity of a call is not checked. + + Use :func:`extract_args` if your method knows at runtime, but not + at evaluation/compile time, what arguments it actually expects, + in order to check that they are correctly received. + + Parameters + ---------- + expected : list of str + A list of strings denoting names for the expected arguments, + in order. + args : iterable + Positional arguments that have been passed. + kwargs : Mapping + Keyword arguments that have been passed. + + Returns + ------- + routed_args : OrderedDict + An OrderedDict mapping the names in `expected` to values drawn + from either `args` or `kwargs` in the usual Python fashion. + + Raises + ------ + KeyError + If a keyword argument is passed, the key for which is not + contained within `expected`. + TypeError + If an expected argument is accounted for in both the positional + and keyword arguments. + ValueError + If certain arguments in `expected` are not assigned a value + by either a positional or keyword argument. + + """ + # Use of zip() rather than equizip() intentional here. We want + # to truncate to the length of args. + routed_args = dict(zip(expected, args)) + for name in kwargs: + if name not in expected: + raise KeyError('invalid input name: {}'.format(name)) + elif name in routed_args: + raise TypeError("got multiple values for " + "argument '{}'".format(name)) + else: + routed_args[name] = kwargs[name] + if set(expected) != set(routed_args): + raise ValueError('missing values for inputs: {}'.format( + [name for name in expected + if name not in routed_args])) + return OrderedDict((key, routed_args[key]) for key in expected) + + +def find_bricks(top_bricks, predicate): + """Walk the brick hierarchy, return bricks that satisfy a predicate. + + Parameters + ---------- + top_bricks : list + A list of root bricks to search downward from. + predicate : callable + A callable that returns `True` for bricks that meet the + desired criteria or `False` for those that don't. + + Returns + ------- + found : list + A list of all bricks that are descendants of any element of + `top_bricks` that satisfy `predicate`. + + """ + found = [] + visited = set() + to_visit = deque(top_bricks) + while len(to_visit) > 0: + current = to_visit.popleft() + if current not in visited: + visited.add(current) + if predicate(current): + found.append(current) + to_visit.extend(current.children) + return found diff --git a/blocks/utils/testing.py b/blocks/utils/testing.py new file mode 100644 index 0000000..a097edd --- /dev/null +++ b/blocks/utils/testing.py @@ -0,0 +1,128 @@ +import logging +import os +import sys +from functools import wraps +from importlib import import_module +from unittest.case import SkipTest + +from six import StringIO + +import blocks +from blocks.algorithms import TrainingAlgorithm +from blocks.config import config +from blocks.main_loop import MainLoop +from fuel.datasets import IterableDataset + + +def silence_printing(test): + @wraps(test) + def wrapper(*args, **kwargs): + stdout = sys.stdout + sys.stdout = StringIO() + logger = logging.getLogger(blocks.__name__) + old_level = logger.level + logger.setLevel(logging.ERROR) + try: + test(*args, **kwargs) + finally: + sys.stdout = stdout + logger.setLevel(old_level) + return wrapper + + +def skip_if_not_available(modules=None, datasets=None, configurations=None): + """Raises a SkipTest exception when requirements are not met. + + Parameters + ---------- + modules : list + A list of strings of module names. If one of the modules fails to + import, the test will be skipped. + datasets : list + A list of strings of folder names. If the data path is not + configured, or the folder does not exist, the test is skipped. + configurations : list + A list of of strings of configuration names. If this configuration + is not set and does not have a default, the test will be skipped. + + """ + if modules is None: + modules = [] + if datasets is None: + datasets = [] + if configurations is None: + configurations = [] + for module in modules: + try: + import_module(module) + except Exception: + raise SkipTest + if module == 'bokeh': + ConnectionError = import_module( + 'requests.exceptions').ConnectionError + session = import_module('bokeh.session').Session() + try: + session.execute('get', session.base_url) + except ConnectionError: + raise SkipTest + + if datasets and not hasattr(config, 'data_path'): + raise SkipTest + for dataset in datasets: + if not os.path.exists(os.path.join(config.data_path, dataset)): + raise SkipTest + for configuration in configurations: + if not hasattr(config, configuration): + raise SkipTest + + +def skip_if_configuration_set(configuration, value, message=None): + """Raise SkipTest if a configuration option has a certain value. + + Parameters + ---------- + configuration : str + Configuration option to check. + value : str + Value of `blocks.config.` which should cause + a `SkipTest` to be raised. + message : str, optional + Reason for skipping the test. + + """ + if getattr(config, configuration) == value: + if message is not None: + raise SkipTest(message) + else: + raise SkipTest + + +class MockAlgorithm(TrainingAlgorithm): + """An algorithm that only saves data. + + Also checks that the initialization routine is only called once. + + """ + def __init__(self): + self._initialized = False + + def initialize(self): + assert not self._initialized + self._initialized = True + + def process_batch(self, batch): + self.batch = batch + + +class MockMainLoop(MainLoop): + """Mock main loop with mock algorithm and simple data stream. + + Can be used with `main_loop = MagicMock(wraps=MockMainLoop())` to check + which calls were made. + + """ + def __init__(self, **kwargs): + kwargs.setdefault('data_stream', + IterableDataset(range(10)).get_example_stream()) + kwargs.setdefault('algorithm', MockAlgorithm()) + super(MockMainLoop, self).__init__(**kwargs) diff --git a/docs/_static/sequence_generator_scheme.png b/docs/_static/sequence_generator_scheme.png index f0d0caca6085e8ed2724d1505c8a7ceb21ca2ffe..956717d8504fa27f2fe57ba01ccaa44c4787ef2c 100644 GIT binary patch literal 189314 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

o@q^os+E|2=B#0=(S||a+Zkc&x3BeV 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 diff --git a/docs/_static/sequence_generator_scheme.svg b/docs/_static/sequence_generator_scheme.svg new file mode 100644 index 0000000..3d7e6f6 --- /dev/null +++ b/docs/_static/sequence_generator_scheme.svg @@ -0,0 +1,698 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + gi-1 si-1 + + + readout.readout + + transition.take_glimpses + + + readout.emit + + + readout.feedback + + + + transition.compute_states + + readout.cost + gi si yi-1 fi-1 + readout.feedback + ri yi ci fi + Legend:s - statesg - glimpsesr - readoutsy - outputsf - feedbackc - costs Dashed rectangle for outputs expresses the fact that they can be provided by the user (see BaseSequenceGenerator.cost method) fork.apply fork.apply + contexts + + + + transition.apply + + diff --git a/docs/api/log.rst b/docs/api/log.rst index ba1d4e6..916f46b 100644 --- a/docs/api/log.rst +++ b/docs/api/log.rst @@ -3,7 +3,26 @@ Logging ======= +Log has two different backends configurable in ``.blocksrc``, +see :doc:`../configuration`. + .. automodule:: blocks.log :members: :undoc-members: :show-inheritance: + +Dictionary backend +------------------ + +.. automodule:: blocks.log.log + :members: + :undoc-members: + :show-inheritance: + +Sqlite backend +-------------- + +.. automodule:: blocks.log.sqlite + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/bricks_overview.rst b/docs/bricks_overview.rst index d194992..1f3801d 100644 --- a/docs/bricks_overview.rst +++ b/docs/bricks_overview.rst @@ -174,7 +174,7 @@ automatically pushed the weight matrix and biases initialization configuration to its children. >>> mlp.initialize() - >>> mlp.children[1].parameters[0].get_value() # doctest: +SKIP + >>> mlp.children[0].parameters[0].get_value() # doctest: +SKIP array([[-0.38312393, -1.7718271 , 0.78074479, -0.74750996], ... [ 1.32390416, -0.56375355, -0.24268186, -2.06008577]]) diff --git a/docs/conf.py b/docs/conf.py index d108176..a726e04 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -42,7 +42,7 @@ extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.doctest', - 'sphinxcontrib.napoleon', + 'sphinx.ext.napoleon', 'sphinx.ext.todo', 'sphinx.ext.mathjax', 'sphinx.ext.graphviz', @@ -59,7 +59,6 @@ 'python': ('http://docs.python.org/3.4', None) } - class Mock(MagicMock): @classmethod def __getattr__(cls, name): @@ -95,9 +94,9 @@ def __getattr__(cls, name): # built documents. # # The short X.Y version. -version = '0.0' +version = '0.1' # The full version, including alpha/beta/rc tags. -release = '0.0.1' +release = '0.1.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/create_your_own_brick.rst b/docs/create_your_own_brick.rst new file mode 100644 index 0000000..e29911e --- /dev/null +++ b/docs/create_your_own_brick.rst @@ -0,0 +1,433 @@ +Create your own brick +===================== + +.. doctest:: + :hide: + + >>> import numpy + >>> + >>> import theano + >>> from theano import tensor + >>> + >>> from blocks.bricks import Brick, Initializable, Linear, Feedforward + >>> from blocks.bricks.base import lazy, application + >>> from blocks.bricks.parallel import Parallel + >>> from blocks.initialization import Constant + >>> from blocks.roles import add_role, WEIGHT + >>> from blocks.utils import shared_floatx_nans + +This tutorial explains how to create a custom brick, which is useful if you +want to group several specific operations (which can be bricks themselves) into +a single one so that you can easily reuse it. + +The first part of this tutorial lists the requirements and optional components +that a brick should/can implement while the second part describes the +construction of a simple toy brick. + +This tutorial assumes that you are already familiar with +:doc:`bricks ` and how to use them from a user point of view. + + +Bricks ingredients and recipe +----------------------------- + +All the bricks in Blocks inherit directly or indirectly from the +:class:`.Brick`. There is already a rich inheritance hierarchy of +bricks implemented in Blocks and thus, you should consider which brick level +to inherit from. Bear in mind that multiple inheritance is often possible and +advocated whenever it makes sense. + +Here are examples of possible bricks to inherit from: + +* :class:`.Sequence`: a sequence of bricks. +* :class:`.Initializable`: a brick that defines a same initialiation scheme + (weights and biases) for all its children. +* :class:`.Feedforward`: declares an interface for bricks with one input and + one output. +* :class:`.Linear`: a linear transformation with optional bias. Inherits from + :class:`.Initializable` and :class:`.Feedforward`. +* :class:`.BaseRecurrent`: the base class for recurrent bricks. Check the + :doc:`tutorial about rnns` for more information. +* many more! + +Let's say that you want to create a brick from scratch, simply inheriting +from :class:`.Brick`, then you should consider overwriting the following +methods (strictly speaking, all these methods are optional, check the docstring +of :class:`.Brick` for a precise description of the life-cycle of a brick): + +* :meth:`.Brick.__init__`: you should pass by argument the attributes of your + brick. It is also in this method that you should create the potential + "children bricks" that belongs to your brick (in that case, you have to put + the children bricks into ``self.children``). The initialiazation of the + attributes can be lazy as described later in the tutorial. +* :meth:`apply`: you need to implement a method that actually + implements the operation of the brick, taking as arguments the inputs + of the brick and returning its outputs. It can have any name and for simple + bricks is often named ``apply``. You should decorate it with the + :func:`.application` decorator, as explained in the next section. If you + design a recurrent brick, you should instead decorate it with the + :func:`.recurrent` decorator as explained in the + :doc:`tutorial about rnns`. +* :meth:`.Brick._allocate`: you should implement this method to allocate the + shared variables (often representing parameters) of the brick. In Blocks, + by convention, the built-in bricks allocate their shared variables with nan + values and we recommend you to do the same. +* :meth:`.Brick._initialize`: you should implement this method to initialize + the shared variables of your brick. This method is called after the + allocation. +* :meth:`.Brick._push_allocation_config`: you should consider overwriting + this method if you want to change configuration of the children bricks + before they allocate their parameters. +* :meth:`.Brick._push_initialization_config`: you should consider + overwriting this method if you want to change the initialization schemes of + the children before they get initialized. + If the children bricks need to be initialized with the same scheme, then you + should inherit your brick from :class:`.Initializable`, which + automatically pushes the initialization schemes of your brick (provided as + arguments ``weights_init`` and ``biases_init`` of the constructor) to the + children bricks. +* :meth:`.Brick.get_dim`: implementing this function is useful if you want + to provide a simple way to get the dimensions of the inputs and outputs of + the brick. + +If you want to inherit from a specific brick, check its docstring to +identify the particular methods to overwrite and the attributes to define. + +Application methods +~~~~~~~~~~~~~~~~~~~ + +The :meth:`apply` method listed above is probably the most +important method of your brick because it is the one that actually takes +theano tensors as inputs, process them and return output tensors. You should +decorate it with the :func:`.application` decorator, which names variables +and register auxiliary variables of the operation you implement. +It is used as follows: + + >>> class Foo(Brick): + ... @application(inputs=['input1', 'input2'], outputs=['output']) + ... def apply(self, input1, input2): + ... y = input1 + input2 + ... return y + +In the case above, it will automatically rename the theano tensor variable +``input1`` to ``Foo_apply_input1``, ``input2`` to ``Foo_apply_input2`` and the +output of the method to ``foo_apply_output``. It will also add roles and names +to the tag attributes of the variables, as shown below: + + >>> foo = Foo() + >>> i1 = tensor.matrix('i1') + >>> i2 = tensor.matrix('i2') + >>> y = foo.apply(i1, i2) + >>> theano.printing.debugprint(y) + Elemwise{identity} [id A] 'foo_apply_output' + |Elemwise{add,no_inplace} [id B] '' + |Elemwise{identity} [id C] 'foo_apply_input1' + | |i1 [id D] + |Elemwise{identity} [id E] 'foo_apply_input2' + |i2 [id F] + >>> print(y.name) + foo_apply_output + >>> print(y.tag.name) + output + >>> print(y.tag.roles) + [OUTPUT] + +Under the hood, the ``@application`` decorator creates an object of class +:class:`.Application`, named ``apply``, which becomes an attribute of the +brick class (by opposition to class instances): + + >>> print(type(Foo.apply)) + + + +Application properties +"""""""""""""""""""""" + +In the previous examples, the names of the arguments of the application methods +were directly provided as arguments of the ``@application`` decorator because +they were common to all instances of the classes. On the other hand, if these +names need to be defined differently for particular instances of the class, +you should use the ``apply.property`` decorator. Let's say that we want to +name our attribute inputs with the string ``self.fancy_name``, then we should +write: + + >>> class Foo(Brick): # doctest: +SKIP + ... def __init__(self, fancy_name): + ... self.fancy_name = fancy_name + ... @application + ... def apply(self, input) + ... ... + ... @apply.property('inputs') + ... def apply_inputs(self): + ... # Note that you can use any python code to define the name + ... return self.fancy_name + +Using application calls +""""""""""""""""""""""" + +You may want to save particular variables defined in the ``apply`` method in +order to use them later, for example to monitor them during training. For that, +you need to pass ``application_call`` as argument of your ``apply`` function +and use the ``add_auxiliary_variable`` function to register your variables of +interest, as shown in this example: + + >>> class Foo(Brick): + ... @application + ... def apply(self, x, application_call): + ... application_call.add_auxiliary_variable(x.mean()) + ... return x + 1 + +``add_auxiliary_variable`` annotates the variable ``x.mean()`` as an auxiliary +variable and you can thus later retrieve it with the computational graph +:class:`.ComputationGraph` and filters :class:`.VariableFilter`. In the +case of the ``Foo`` Brick defined above, we retrieve ``x.mean() as follows: + + >>> from blocks.graph import ComputationGraph + >>> x = tensor.fmatrix('x') + >>> y = Foo().apply(x) + >>> cg = ComputationGraph(y) + >>> print(cg.auxiliary_variables) + [mean] + +Lazy initialization +~~~~~~~~~~~~~~~~~~~ + +Instead of forcing the user to provide all the brick attributes as arguments +to the :meth:`.Brick.__init__` method, you could let him/her specify them +later, after the creation of the brick. To enable this mechanism, +called lazy initialization, you need to decorate the constructor with the +:func:`.lazy` decorator: + + >>> @lazy(allocation=['attr1', 'attr2']) # doctest: +SKIP + ... def __init__(self, attr1, attr1) + ... ... + +This allows the user to specify ``attr1`` and ``attr2`` after the creation of +the brick. For example, the following ``ChainOfTwoFeedforward`` brick is +composed of two :class:`.Feedforward` bricks for which you do not need to +specify the ``input_dim`` of ``brick2`` directly at its creation. + + >>> class ChainOfTwoFeedforward(Feedforward): + ... """Two sequential Feedforward bricks.""" + ... def __init__(self, brick1, brick2, **kwargs): + ... super(Feedforward, self).__init__(**kwargs) + ... self.brick1 = brick1 + ... self.brick2 = brick2 + ... self.children = [self.brick1, self.brick2] + ... + ... @property + ... def input_dim(self): + ... return self.brick1.input_dim + ... + ... @input_dim.setter + ... def input_dim(self, value): + ... self.brick1.input_dim = value + ... + ... @property + ... def output_dim(self): + ... return self.brick2.output_dim + ... + ... @output_dim.setter + ... def output_dim(self, value): + ... self.brick2.output_dim = value + ... + ... def _push_allocation_config(self): + ... self.brick2.input_dim = self.brick1.get_dim('output') + ... + ... @application + ... def apply(self, x): + ... return self.brick2.apply(self.brick1.apply(x)) + +Note how ``get_dim`` is used to retrieve the ``input_dim`` of ``brick1``. You +can now use a ``ChainOfTwoFeedforward`` brick as follows. + + >>> brick1 = Linear(input_dim=3, output_dim=2, use_bias=False, + ... weights_init=Constant(2)) + >>> brick2 = Linear(output_dim=4, use_bias=False, weights_init=Constant(2)) + >>> + >>> seq = ChainOfTwoFeedforward(brick1, brick2) + >>> seq.initialize() + >>> brick2.input_dim + 2 + + +Example +------- + +For the sake of the tutorial, let's consider a toy operation that takes two +batch inputs and multiplies them respectively by two matrices, resulting in two +outputs. + +The first step is to identify which brick to inherit from. Clearly we are +implementing a variant of the :class:`.Linear` brick. Contrary to +:class:`.Linear`, ours has two inputs and two outputs, which means that we can +not inherit from :class:`.Feedforward`, which requires a single input and a +single output. Our brick will have to manage two shared variables +representing the matrices to multiply the inputs with. As we want to initialize +them with the same scheme, we should inherit from :class:`.Initializable`, +which automatically push the initialization schemes to the children. The +initialization schemes are provided as arguments ``weights_init`` +and ``biases_init`` of the constructor of our brick (in the ``kwargs``). + + + >>> class ParallelLinear(Initializable): + ... r"""Two linear transformations without biases. + ... + ... Brick which applies two linear (affine) transformations by + ... multiplying its two inputs with two weight matrices, resulting in + ... two outputs. + ... The two inputs, weights and outputs can have different dimensions. + ... + ... Parameters + ... ---------- + ... input_dim{1,2} : int + ... The dimensions of the two inputs. + ... output_dim{1,2} : int + ... The dimension of the two outputs. + ... """ + ... @lazy(allocation=['input_dim1', 'input_dim2', + ... 'output_dim1', 'output_dim2']) + ... def __init__(self, input_dim1, input_dim2, output_dim1, output_dim2, + ... **kwargs): + ... super(ParallelLinear, self).__init__(**kwargs) + ... self.input_dim1 = input_dim1 + ... self.input_dim2 = input_dim2 + ... self.output_dim1 = output_dim1 + ... self.output_dim2 = output_dim2 + ... + ... def __allocate(self, input_dim, output_dim, number): + ... W = shared_floatx_nans((input_dim, output_dim), + ... name='W'+number) + ... add_role(W, WEIGHT) + ... self.parameters.append(W) + ... self.add_auxiliary_variable(W.norm(2), name='W'+number+'_norm') + ... + ... def _allocate(self): + ... self.__allocate(self.input_dim1, self.output_dim1, '1') + ... self.__allocate(self.input_dim2, self.output_dim2, '2') + ... + ... def _initialize(self): + ... W1, W2 = self.parameters + ... self.weights_init.initialize(W1, self.rng) + ... self.weights_init.initialize(W2, self.rng) + ... + ... @application(inputs=['input1_', 'input2_'], outputs=['output1', + ... 'output2']) + ... def apply(self, input1_, input2_): + ... """Apply the two linear transformations. + ... + ... Parameters + ... ---------- + ... input{1,2}_ : :class:`~tensor.TensorVariable` + ... The two inputs on which to apply the transformations + ... + ... Returns + ... ------- + ... output{1,2} : :class:`~tensor.TensorVariable` + ... The two inputs multiplied by their respective matrices + ... + ... """ + ... W1, W2 = self.parameters + ... output1 = tensor.dot(input1_, W1) + ... output2 = tensor.dot(input2_, W2) + ... return output1, output2 + ... + ... def get_dim(self, name): + ... if name == 'input1_': + ... return self.input_dim1 + ... if name == 'input2_': + ... return self.input_dim2 + ... if name == 'output1': + ... return self.output_dim1 + ... if name == 'output2': + ... return self.output_dim2 + ... super(ParallelLinear, self).get_dim(name) + +You can test the brick as follows: + + >>> input_dim1, input_dim2, output_dim1, output_dim2 = 10, 5, 2, 1 + >>> batch_size1, batch_size2 = 1, 2 + >>> + >>> x1_mat = 3 * numpy.ones((batch_size1, input_dim1), + ... dtype=theano.config.floatX) + >>> x2_mat = 4 * numpy.ones((batch_size2, input_dim2), + ... dtype=theano.config.floatX) + >>> + >>> x1 = theano.tensor.matrix('x1') + >>> x2 = theano.tensor.matrix('x2') + >>> parallel1 = ParallelLinear(input_dim1, input_dim2, output_dim1, + ... output_dim2, weights_init=Constant(2)) + >>> parallel1.initialize() + >>> output1, output2 = parallel1.apply(x1, x2) + >>> + >>> f1 = theano.function([x1, x2], [output1, output2]) + >>> f1(x1_mat, x2_mat) # doctest: +ELLIPSIS + [array([[ 60., 60.]]...), array([[ 40.], + [ 40.]]...)] + +One can also create the brick using :class:`Linear` children bricks, which + + >>> class ParallelLinear2(Initializable): + ... def __init__(self, input_dim1, input_dim2, output_dim1, output_dim2, + ... **kwargs): + ... super(ParallelLinear2, self).__init__(**kwargs) + ... self.linear1 = Linear(input_dim1, output_dim1, + ... use_bias=False, **kwargs) + ... self.linear2 = Linear(input_dim2, output_dim2, + ... use_bias=False, **kwargs) + ... self.children = [self.linear1, self.linear2] + ... + ... @application(inputs=['input1_', 'input2_'], outputs=['output1', + ... 'output2']) + ... def apply(self, input1_, input2_): + ... output1 = self.linear1.apply(input1_) + ... output2 = self.linear2.apply(input2_) + ... return output1, output2 + ... + ... def get_dim(self, name): + ... if name in ['input1_', 'output1']: + ... return self.linear1.get_dim(name) + ... if name in ['input2_', 'output2']: + ... return self.linear2.get_dim(name) + ... super(ParallelLinear2, self).get_dim(name) + +You can test this new version as follows: + + >>> parallel2 = ParallelLinear2(input_dim1, input_dim2, output_dim1, + ... output_dim2, weights_init=Constant(2)) + >>> parallel2.initialize() + >>> # The weights_init initialization scheme is pushed to the children + >>> # bricks. We can verify it as follows. + >>> w = parallel2.weights_init + >>> w0 = parallel2.children[0].weights_init + >>> w1 = parallel2.children[1].weights_init + >>> print(w == w0 == w1) + True + >>> + >>> output1, output2 = parallel2.apply(x1, x2) + >>> + >>> f2 = theano.function([x1, x2], [output1, output2]) + >>> f2(x1_mat, x2_mat) # doctest: +ELLIPSIS + [array([[ 60., 60.]]...), array([[ 40.], + [ 40.]]...)] + +Actually it was not even necessary to create a custom brick for this particular +operation as Blocks has a brick, called :class:``Parallel``, which +applies the same prototype brick to several inputs. In our case the prototype +brick we want to apply to our two inputs is a :class:``Linear`` brick with no +bias: + + >>> parallel3 = Parallel( + ... prototype=Linear(use_bias=False), + ... input_names=['input1_', 'input2_'], + ... input_dims=[input_dim1, input_dim2], + ... output_dims=[output_dim1, output_dim2], weights_init=Constant(2)) + >>> parallel3.initialize() + >>> + >>> output1, output2 = parallel3.apply(x1, x2) + >>> + >>> f3 = theano.function([x1, x2], [output1, output2]) + >>> f3(x1_mat, x2_mat) # doctest: +ELLIPSIS + [array([[ 60., 60.]]...), array([[ 40.], + [ 40.]]...)] + diff --git a/docs/development/docs.rst b/docs/development/docs.rst index 0d4efd4..83bc74e 100644 --- a/docs/development/docs.rst +++ b/docs/development/docs.rst @@ -4,15 +4,25 @@ Building documentation If you've made significant changes to the documentation, you can build a local to see how your changes are rendered. You will need to install Sphinx_, the Napoleon_ extension (to enable NumPy docstring support), and the `Read the Docs -theme`_. You can do this by installing the optional ``docs`` requirements: +theme`_. You can do this by installing the optional ``docs`` requirements. + +For Blocks: .. code-block:: bash $ pip install --upgrade git+git://github.com/user/blocks.git#egg=blocks[docs] + +For Fuel: + +.. code-block:: bash + + $ pip install --upgrade git+git://github.com/user/fuel.git#egg=fuel[docs] + + After the requirements have been installed, you can build a copy of the documentation by running the following command from the root ``blocks`` -directory. +(or ``fuel``) directory. .. code-block:: bash @@ -24,9 +34,9 @@ directory. Docstrings ---------- -Blocks follows the `NumPy docstring standards`_. For a quick introduction, have -a look at the NumPy_ or Napoleon_ examples of compliant docstrings. A few common -mistakes to avoid: +Blocks and Fuel follow the `NumPy docstring standards`_. For a quick +introduction, have a look at the NumPy_ or Napoleon_ examples of +compliant docstrings. A few common mistakes to avoid: * There is no line break after the opening quotes (``"""``). * There is an empty line before the closing quotes (``"""``). @@ -36,8 +46,9 @@ The docstrings are formatted using reStructuredText_, and can make use of all the formatting capabilities this provides. They are rendered into HTML documentation using the `Read the Docs`_ service. After code has been merged, please ensure that documentation was built successfully and that your docstrings -rendered as you intended by looking at the `online documentation`_, which is -automatically updated. +rendered as you intended by looking at the online documentation (for +`Blocks `_ or `Fuel `_, +which is automatically updated. Writing doctests_ is encouraged, and they are run as part of the test suite. They should use Python 3 syntax. @@ -48,7 +59,8 @@ They should use Python 3 syntax. .. _reStructuredText: http://docutils.sourceforge.net/rst.html .. _doctests: https://docs.python.org/2/library/doctest.html .. _Read the Docs: https://readthedocs.org/ -.. _online documentation: http://blocks.readthedocs.org/ +.. _Blocks online documentation: http://blocks.readthedocs.org/ +.. _Fuel online documentation: http://fuel.readthedocs.org/ .. _a bug in Napoleon: https://bitbucket.org/birkenfeld/sphinx-contrib/issue/82/napoleon-return-type-containing-colons-is .. _references_and_intersphinx: diff --git a/docs/development/index.rst b/docs/development/index.rst index 125a654..9b401a2 100644 --- a/docs/development/index.rst +++ b/docs/development/index.rst @@ -1,10 +1,10 @@ Development =========== -We want to encourage everyone to contribute to the development of Blocks. To -ensure the codebase is of high quality, we ask all new developers to have a -quick read through these rules to make sure that any code you contribute will be -easy to merge! +We want to encourage everyone to contribute to the development of Blocks +and Fuel. To ensure the codebase is of high quality, we ask all new +developers to have a quick read through these rules to make sure that +any code you contribute will be easy to merge! .. image:: /_static/code_quality.png @@ -13,7 +13,9 @@ easy to merge! Formatting guidelines --------------------- Blocks follows the `PEP8 style guide`_ closely, so please make sure you are -familiar with it. Our `Travis CI buildbot`_ runs flake8_ as part of every build, +familiar with it. Our Travis CI buildbots (for `Blocks `_, +`Fuel `_, and `Blocks-extras `_) +run flake8_ as part of every build, which checks for PEP8 compliance (using the pep8_ tool) and for some common coding errors using pyflakes_. You might want to install and run flake8_ on your code before submitting a PR to make sure that your build doesn't fail because of @@ -27,7 +29,7 @@ compliant! Some guidelines which aren't checked by flake8_: * Variable names should be explanatory and unambiguous. There are also some style guideline decisions that were made specifically for -Blocks: +Blocks and Fuel: * Do not rename imports i.e. do not use ``import theano.tensor as T`` or ``import numpy as np``. @@ -53,7 +55,9 @@ Blocks: self.baz = baz .. _PEP8 style guide: https://www.python.org/dev/peps/pep-0008/ -.. _Travis CI buildbot: https://travis-ci.org/mila-udem/blocks +.. _Blocks buildbot: https://travis-ci.org/mila-udem/blocks +.. _Blocks-extras buildbot: https://travis-ci.org/mila-udem/blocks-extras +.. _Fuel buildbot: https://travis-ci.org/mila-udem/fuel .. _flake8: https://pypi.python.org/pypi/flake8 .. _pep8: https://pypi.python.org/pypi/pep8 .. _pyflakes: https://pypi.python.org/pypi/pyflakes @@ -61,9 +65,10 @@ Blocks: Code guidelines --------------- -Some guidelines to keep in mind when coding for Blocks. Some of these are simply -preferences, others stem from particular requirements we have e.g. in order to -serialize training progress, support Python 2 and 3 simultaneously, etc. +Some guidelines to keep in mind when coding for Blocks or Fuel. Some of +these are simply preferences, others stem from particular requirements +we have, e.g., in order to serialize training progress, support Python 2 +and 3 simultaneously, etc. Validating function arguments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -119,10 +124,11 @@ building documentation. To prevent this, make sure to always use the Python 2 and 3 ~~~~~~~~~~~~~~ -Blocks aims to be both Python 2 and Python 3 compliant using a single code-base, -without using 2to3_. There are many online resources which discuss the writing -of compatible code. For a quick overview see `the cheatsheet from Python -Charmers`_. For non-trivial cases, we use the six_ compatibility library. +Blocks and Fuel aim to be both Python 2 and Python 3 compliant using a +single code-base, without using 2to3_. There are many online resources +which discuss the writing of compatible code. For a quick overview see +`the cheatsheet from Python Charmers`_. For non-trivial cases, we use +the six_ compatibility library. Documentation should be written to be Python 3 compliant. @@ -143,16 +149,18 @@ Not doing so `clobbers the original traceback`_, making it impossible to use Serialization ~~~~~~~~~~~~~ -To ensure the reproducibility of scientific experiments Blocks tries to make -sure that stopping and resuming training doesn't affect the final results. In -order to do so it takes a radical approach, serializing the entire training -state using pickle_. Some things cannot be pickled, so their use should be -avoided when the object will be pickled as part of the main loop: +To ensure the reproducibility of scientific experiments, Blocks and Fuel +try to make sure that stopping and resuming training doesn't affect +the final results. In order to do so it takes a radical approach, +serializing the entire training state using pickle_. Some things cannot +be pickled, so their use should be avoided when the object will be +pickled as part of the main loop: * Lambda functions * Iterators and generators (use picklable_itertools_) * References to methods as attributes -* Any variable that lies outside of the global namespace e.g. nested functions +* Any variable that lies outside of the global namespace, e.g., + nested functions * Dynamically generated classes (possible_ but complicated) .. _pickle: https://docs.python.org/3/library/pickle.html @@ -210,9 +218,11 @@ the error is being raised for. Unit testing ------------ -Blocks uses unit testing to ensure that individual parts of the library behave -as intended. It's also essential in ensuring that parts of the library are not -broken by proposed changes. +Blocks and Fuel use unit testing to ensure that individual parts of +the library behave as intended. It's also essential in ensuring that +parts of the library are not broken by proposed changes. Since Blocks +and Fuel were designed to be used together, it is important to make sure +changes in Fuel do not break Blocks. All new code should be accompanied by extensive unit tests. Whenever a pull request is made, the full test suite is run on `Travis CI`_, and pull requests @@ -236,8 +246,8 @@ The test suite can be executed locally using nose2_ [#]_. Writing and building documentation ---------------------------------- The :doc:`documentation guidelines ` outline how to write documentation -for Blocks, and how to build a local copy of the documentation for testing -purposes. +for Blocks and Fuel, and how to build a local copy of the documentation for +testing purposes. Internal API ------------ @@ -253,7 +263,65 @@ See the instructions at the bottom of the :doc:`installation instructions Sending a pull request ---------------------- See our :doc:`pull request workflow ` for a refresher on the -general recipe for sending a pull request to Blocks. +general recipe for sending a pull request to Blocks or Fuel. + +Making a new release +-------------------- +.. note: + This section is targeted for Blocks and Fuel administrators. + +Create an initial pull request and copy the following piece of markdown code. +This pull request should only change the version number. Then, create a pull +request to Fuel which refers the first PR. Follow the instruction carefully +and check the boxes in process. +``` +- **Stage 1**: Make changes in `master`: + - [ ] Freeze other PRs. + + After we agreed to initiate the process of releasing a new version, + other PRs shouldn't be merged. + - [ ] Increase the version number counter of Blocks. + + Change the version number in `blocks/__init__.py`. + - [ ] Increase the version number counter of Fuel. + + Change the version number in `fuel/version.py`. +- **Stage 2**: After two PRs merged to Blocks and Fuel: + - [ ] Create a pull request to merge `master` into `stable`. + + Add a link to the initial PR in order not to get lost in the numerous + pull requests. + - [ ] Create a pull request to Fuel. + + This will be a corresponding PR to Fuel which merges its `master` into + `stable`. Add a link to the initial PR. + - [ ] Check the Travis CI build log *on both the pull requests merging + `master` into `stable`*. + + Read carefully the Travis CI messages, check that it tests the + right version. + - [ ] Check the Theano version. + + The `req*.txt` should refer the last development Theano version + which is known not to have bugs. + - [ ] Check the Fuel version in `req*.txt` files. + + We should reference the stable version of Fuel. It can be seen + in the Travis CI output. + - [ ] Merge Fuel pull request. + - [ ] Merge this pull request. +- **Stage 3**: After the PRs are merged: + - [ ] Wait the build to pass. + - [ ] Check documentation build at ReadTheDocs. + - [ ] Double check that the version corresponds `__version__`. + - [ ] Create a release of Fuel by going to the + [releases page](https://github.com/mila-udem/fuel/releases) and + clicking "Draft new release". + - [ ] Create a release of Blocks by going to the + [releases page](https://github.com/mila-udem/blocks/releases) and + clicking "Draft new release". + +``` .. toctree:: :hidden: diff --git a/docs/index.rst b/docs/index.rst index c3f756c..25655bd 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -5,7 +5,8 @@ using Theano. Want to get try it out? Start by :doc:`installing ` Blocks and having a look at the :ref:`quickstart ` further down this page. Once you're -hooked, try your hand at the :ref:`tutorials `. +hooked, try your hand at the :ref:`tutorials ` and the +examples_. Blocks is developed in parallel with Fuel_, a dataset processing framework. @@ -23,6 +24,7 @@ Blocks is developed in parallel with Fuel_, a dataset processing framework. .. _mailing list: https://groups.google.com/forum/#!forum/blocks-users .. _making a GitHub issue: https://github.com/mila-udem/blocks/issues/new .. _Fuel: https://github.com/mila-udem/fuel +.. _examples: https://github.com/mila-udem/blocks-examples .. _tutorials: @@ -44,6 +46,7 @@ In-depth rnn configuration + create_your_own_brick serialization api/index.rst development/index.rst @@ -118,6 +121,9 @@ And train! ... +For a runnable version of this code, please see the MNIST demo +in our repository with examples_. + Features -------- diff --git a/docs/setup.rst b/docs/setup.rst index a062cad..e0bd7ad 100644 --- a/docs/setup.rst +++ b/docs/setup.rst @@ -1,9 +1,9 @@ Installation ============ -The easiest way to install Blocks using the Python package manager pip. Blocks -isn't listed yet on the Python Package Index (PyPI), so you will have to grab it -directly from GitHub. +The easiest way to install Blocks is using the Python package manager +``pip``. Blocks isn't listed yet on the Python Package Index (PyPI), so +you will have to grab it directly from GitHub. .. code-block:: bash @@ -60,6 +60,8 @@ Blocks' requirements are Bokeh_ is an optional requirement for if you want to use live plotting of your training progress (part of ``blocks-extras_``). +nose2_ is an optional requirement, used to run the tests. + We develop using the bleeding-edge version of Theano, so be sure to follow the `relevant installation instructions`_ to make sure that your Theano version is up to date if you didn't install it through Blocks. @@ -105,4 +107,3 @@ Documentation If you want to build a local copy of the documentation, follow the instructions at the :doc:`documentation development guidelines `. - diff --git a/docs/tutorial.rst b/docs/tutorial.rst index ff0b6b7..7237bb4 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -104,7 +104,7 @@ the parameters by adding a :math:`L2`-regularization term (also known as To get the weights from our model, we will use Blocks' annotation features (read more about them in the :doc:`cg` tutorial). ->>> from blocks.bricks import WEIGHT +>>> from blocks.roles import WEIGHT >>> from blocks.graph import ComputationGraph >>> from blocks.filter import VariableFilter >>> cg = ComputationGraph(cost) diff --git a/doctests/__init__.py b/doctests/__init__.py index 92155af..fd4c8a7 100644 --- a/doctests/__init__.py +++ b/doctests/__init__.py @@ -8,7 +8,7 @@ import blocks import blocks.bricks -from tests import skip_if_not_available +from blocks.utils.testing import skip_if_not_available def setup(testobj): diff --git a/req-rtd.txt b/req-rtd.txt index f99dd3f..3d8af08 100644 --- a/req-rtd.txt +++ b/req-rtd.txt @@ -1,13 +1,9 @@ ---allow-external picklable-itertools ---allow-unverified picklable-itertools -git+https://github.com/dwf/picklable_itertools.git#egg=picklable-itertools +picklable-itertools==0.1.1 +progressbar2==2.7.3 +pyyaml==3.11 +six==1.9.0 +toolz==0.7.2 --allow-external theano --allow-unverified theano git+https://github.com/Theano/Theano.git#egg=theano - -mock -toolz - -sphinx==1.2.2 # rq.filter: ==1.2.2 -sphinxcontrib-napoleon==0.2.11 # rq.filter: ==0.2.11 diff --git a/req-travis-conda.txt b/req-travis-conda.txt index 31a37da..c59823f 100644 --- a/req-travis-conda.txt +++ b/req-travis-conda.txt @@ -1,10 +1,12 @@ -coverage==3.7.1 -cython==0.22 +coverage==4.0 h5py==2.5.0 -mock==1.0.1 -numpy==1.9.2 -pillow=2.8.1 -pytables=3.1.1 -pyzmq==14.6.0 +mock==1.3.0 +nose==1.3.7 +numpy==1.9.3 +pillow==3.0.0 +pytables==3.2.1.1 +pyyaml==3.11 +pyzmq==14.7.0 +scipy==0.16.0 six==1.9.0 -toolz==0.7.2 +toolz==0.7.4 diff --git a/req-travis-pip.txt b/req-travis-pip.txt index 85e865c..0cce673 100644 --- a/req-travis-pip.txt +++ b/req-travis-pip.txt @@ -1,3 +1,12 @@ +coveralls==1.0 nose2[coverage-plugin]==0.5.0 -coveralls==0.5 +picklable-itertools==0.1.1 progressbar2==2.7.3 + +--allow-external theano +--allow-unverified theano +git+https://github.com/Theano/Theano.git#egg=theano + +--allow-external fuel +--allow-unverified fuel +git+https://github.com/mila-udem/fuel#egg=fuel diff --git a/requirements.txt b/requirements.txt index e3fd040..808fab8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,9 @@ -numpy==1.9.2 -six==1.9.0 +numpy==1.9.3 +picklable-itertools==0.1.1 progressbar2==2.7.3 -PyYaml==3.11 +pyyaml==3.11 +six==1.9.0 toolz==0.7.2 -picklable-itertools==0.1.0 --allow-external theano --allow-unverified theano diff --git a/setup.py b/setup.py index b1da658..a8e3638 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ install_requires=['numpy', 'six', 'pyyaml', 'toolz', 'theano', 'picklable-itertools', 'progressbar2', 'fuel'], extras_require={ - 'test': ['nose', 'nose2', 'mock'], - 'docs': ['sphinx', 'sphinxcontrib-napoleon', 'sphinx-rtd-theme'] + 'test': ['mock', 'nose', 'nose2'], + 'docs': ['sphinx', 'sphinx-rtd-theme'] }, zip_safe=False) diff --git a/tests/__init__.py b/tests/__init__.py index b100bc4..e69de29 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,107 +0,0 @@ -import logging -import os -import sys -from functools import wraps -from importlib import import_module -from unittest.case import SkipTest - -from six import StringIO - -import blocks -from blocks.algorithms import TrainingAlgorithm -from blocks.config import config -from blocks.main_loop import MainLoop -from fuel.datasets import IterableDataset - - -def silence_printing(test): - @wraps(test) - def wrapper(*args, **kwargs): - stdout = sys.stdout - sys.stdout = StringIO() - logger = logging.getLogger(blocks.__name__) - old_level = logger.level - logger.setLevel(logging.ERROR) - try: - test(*args, **kwargs) - finally: - sys.stdout = stdout - logger.setLevel(old_level) - return wrapper - - -def skip_if_not_available(modules=None, datasets=None, configurations=None): - """Raises a SkipTest exception when requirements are not met. - - Parameters - ---------- - modules : list - A list of strings of module names. If one of the modules fails to - import, the test will be skipped. - datasets : list - A list of strings of folder names. If the data path is not - configured, or the folder does not exist, the test is skipped. - configurations : list - A list of of strings of configuration names. If this configuration - is not set and does not have a default, the test will be skipped. - - """ - if modules is None: - modules = [] - if datasets is None: - datasets = [] - if configurations is None: - configurations = [] - for module in modules: - try: - import_module(module) - except Exception: - raise SkipTest - if module == 'bokeh': - ConnectionError = import_module( - 'requests.exceptions').ConnectionError - session = import_module('bokeh.session').Session() - try: - session.execute('get', session.base_url) - except ConnectionError: - raise SkipTest - - if datasets and not hasattr(config, 'data_path'): - raise SkipTest - for dataset in datasets: - if not os.path.exists(os.path.join(config.data_path, dataset)): - raise SkipTest - for configuration in configurations: - if not hasattr(config, configuration): - raise SkipTest - - -class MockAlgorithm(TrainingAlgorithm): - """An algorithm that only saves data. - - Also checks that the initialization routine is only called once. - - """ - def __init__(self): - self._initialized = False - - def initialize(self): - assert not self._initialized - self._initialized = True - - def process_batch(self, batch): - self.batch = batch - - -class MockMainLoop(MainLoop): - """Mock main loop with mock algorithm and simple data stream. - - Can be used with `main_loop = MagicMock(wraps=MockMainLoop())` to check - which calls were made. - - """ - def __init__(self, **kwargs): - kwargs.setdefault('data_stream', - IterableDataset(range(10)).get_example_stream()) - kwargs.setdefault('algorithm', MockAlgorithm()) - super(MockMainLoop, self).__init__(**kwargs) diff --git a/tests/algorithms/test_algorithms.py b/tests/algorithms/test_algorithms.py index f4df9ec..4d26d56 100644 --- a/tests/algorithms/test_algorithms.py +++ b/tests/algorithms/test_algorithms.py @@ -4,12 +4,33 @@ import theano from numpy.testing import assert_allclose, assert_raises from theano import tensor +from theano.compile.profiling import ProfileStats from blocks.algorithms import (GradientDescent, StepClipping, VariableClipping, CompositeRule, Scale, StepRule, BasicMomentum, Momentum, AdaDelta, BasicRMSProp, RMSProp, Adam, AdaGrad, RemoveNotFinite, Restrict) -from blocks.utils import shared_floatx +from blocks.utils import shared_floatx, shared_floatx_zeros + + +def verify_broadcastable_handling(step_rule): + def check(param): + grad = tensor.grad(param.sum(), wrt=param) + step, _ = step_rule.compute_steps(OrderedDict([(param, grad)])) + assert step[param].broadcastable == grad.broadcastable + + check(shared_floatx_zeros((5, 6, 1, 5), + broadcastable=(False, False, True, False))) + check(shared_floatx_zeros((2, 1, 3), + broadcastable=(False, True, False))) + check(shared_floatx_zeros((3, 4, 1), + broadcastable=(False, False, True))) + check(shared_floatx_zeros((1, 9, 6), + broadcastable=(True, False, False))) + check(shared_floatx_zeros((1, 1, 1), + broadcastable=(True, True, True))) + check(shared_floatx_zeros((1, 5, 1), + broadcastable=(True, False, True))) def test_gradient_descent(): @@ -68,6 +89,10 @@ def test_basic_momentum(): assert_allclose(f()[0], [10.5, 14.]) +def test_basic_momentum_broadcastable(): + verify_broadcastable_handling(BasicMomentum(0.5)) + + def test_momentum(): a = shared_floatx([3, 4]) cost = (a ** 2).sum() @@ -79,6 +104,10 @@ def test_momentum(): assert_allclose(f()[0], [1.05, 1.4]) +def test_momentum_broadcastable(): + verify_broadcastable_handling(Momentum(0.5)) + + def test_adadelta(): a = shared_floatx([3, 4]) cost = (a ** 2).sum() @@ -109,6 +138,10 @@ def test_basicrmsprop(): assert_allclose(f()[0], [0.6172134, 0.64699664]) +def test_basicrmsprop_broadcastable(): + verify_broadcastable_handling(BasicRMSProp(0.5, 1e5)) + + def test_basicrmsprop_max_scaling(): a = shared_floatx([1e-6, 1e-6]) cost = (a ** 2).sum() @@ -142,6 +175,10 @@ def test_rmsprop(): assert_allclose(f()[0], [0.06172134, 0.064699664]) +def test_rmsprop_broadcastable(): + verify_broadcastable_handling(RMSProp(0.1, 0.5, 1e5)) + + def test_step_clipping(): rule1 = StepClipping(4) rule2 = StepClipping(5) @@ -155,6 +192,10 @@ def test_step_clipping(): assert_allclose(clipped2[1].eval(), 4.0) +def test_step_clipping_broadcastable(): + verify_broadcastable_handling(StepClipping(0.4)) + + def test_variable_clipping(): # Test simple variable clipping with no axis. rule1 = VariableClipping(5) @@ -207,6 +248,10 @@ def test_variable_clipping(): assert_raises(ValueError, VariableClipping, 50, axis=(0, 0)) +def test_variable_clipping_broadcastable(): + verify_broadcastable_handling(VariableClipping(1)) + + def test_composite_rule(): rule = CompositeRule([StepClipping(4), Scale(0.1)]) gradients = {0: shared_floatx(3.0), 1: shared_floatx(4.0)} @@ -241,6 +286,10 @@ def test_adam(): assert_allclose(f()[0], [0.00178724, 0.0018223], rtol=rtol) +def test_adam_broadcastable(): + verify_broadcastable_handling(Adam()) + + def test_adagrad(): a = shared_floatx([3, 4]) cost = (a ** 2).sum() @@ -256,6 +305,10 @@ def test_adagrad(): assert_allclose(f()[0], [0.00053452, 0.0005747], rtol=rtol) +def test_adagrad_broadcastable(): + verify_broadcastable_handling(AdaGrad()) + + def test_remove_not_finite(): rule1 = RemoveNotFinite(0.1) rule2 = RemoveNotFinite() @@ -271,6 +324,11 @@ def test_remove_not_finite(): assert_allclose(rval2[2].eval(), 0.0) +def test_remove_not_finite_broadcastable(): + verify_broadcastable_handling(RemoveNotFinite()) + verify_broadcastable_handling(RemoveNotFinite(0.1)) + + class DummyUpdatesStepRule(StepRule): def compute_step(self, parameter, previous_step): return previous_step + 2, [(parameter * 10, parameter * 100)] @@ -299,3 +357,17 @@ def test_restrict(): assert_allclose(steps[5].eval(), 25.0) assert updates == [(10, 100), (40, 400)] + + +def test_theano_profile_for_sgd_function(): + W = shared_floatx(numpy.array([[1, 2], [3, 4]])) + W_start_value = W.get_value() + cost = tensor.sum(W ** 2) + + algorithm = GradientDescent( + cost=cost, parameters=[W], theano_func_kwargs={'profile': True}) + algorithm.step_rule.learning_rate.set_value(0.75) + algorithm.initialize() + algorithm.process_batch(dict()) + assert_allclose(W.get_value(), -0.5 * W_start_value) + assert isinstance(algorithm._function.profile, ProfileStats) diff --git a/tests/bricks/test_bn.py b/tests/bricks/test_bn.py new file mode 100644 index 0000000..dc853b8 --- /dev/null +++ b/tests/bricks/test_bn.py @@ -0,0 +1,306 @@ +import collections +import theano +from theano import tensor +import numpy +from numpy.testing import assert_raises, assert_allclose, assert_equal +from blocks.bricks import Tanh, Sequence +from blocks.bricks import (BatchNormalization, SpatialBatchNormalization, + BatchNormalizedMLP) +from blocks.bricks.conv import (Convolutional, ConvolutionalSequence, + MaxPooling, AveragePooling) +from blocks.initialization import Constant +from blocks.graph import (ComputationGraph, batch_normalization, + get_batch_normalization_updates) + + +def random_unif(rng, dim, low=1, high=10): + """Generate some floatX uniform random numbers.""" + return (rng.uniform(low, high, size=dim) + .astype(theano.config.floatX)) + + +def test_batch_normalization_allocation_initialization(): + """Sanity check allocation & initialization of BN bricks.""" + def check(input_dim, expected_shape, broadcastable=None, + conserve_memory=True): + bn = BatchNormalization(input_dim=input_dim, + broadcastable=broadcastable, + conserve_memory=conserve_memory) + if broadcastable is None: + if not isinstance(input_dim, collections.Sequence): + b_input_dim = (input_dim,) + else: + b_input_dim = input_dim + input_broadcastable = tuple(False for _ in range(len(b_input_dim))) + else: + input_broadcastable = broadcastable + bn.allocate() + assert conserve_memory == bn.conserve_memory + assert input_dim == bn.input_dim + assert bn.broadcastable == broadcastable + assert bn.scale.broadcastable == input_broadcastable + assert bn.shift.broadcastable == input_broadcastable + assert bn.population_mean.broadcastable == input_broadcastable + assert bn.population_stdev.broadcastable == input_broadcastable + assert_allclose(bn.population_mean.get_value(borrow=True), 0.) + assert_allclose(bn.population_stdev.get_value(borrow=True), 1.) + assert_equal(bn.scale.get_value(borrow=True).shape, expected_shape) + assert_equal(bn.shift.get_value(borrow=True).shape, expected_shape) + assert_equal(bn.population_mean.get_value(borrow=True).shape, + expected_shape) + assert_equal(bn.population_stdev.get_value(borrow=True).shape, + expected_shape) + assert numpy.isnan(bn.shift.get_value(borrow=True)).all() + assert numpy.isnan(bn.scale.get_value(borrow=True)).all() + bn.initialize() + assert_allclose(bn.shift.get_value(borrow=True), 0.) + assert_allclose(bn.scale.get_value(borrow=True), 1.) + + yield check, 5, (5,) + yield check, (6, 7, 9), (6, 7, 9), (False, False, False) + yield check, (7, 4, 3), (1, 4, 3), (True, False, False) + yield check, (9, 3, 6), (9, 1, 1), (False, True, True) + yield check, (7, 4, 5), (7, 1, 5), (False, True, False), False + + +def apply_setup(input_dim, broadcastable, conserve_memory): + """Common setup code.""" + bn = BatchNormalization(input_dim, broadcastable, conserve_memory, + epsilon=1e-4) + bn.initialize() + b_len = (len(input_dim) if isinstance(input_dim, collections.Sequence) + else 1) + x = tensor.TensorType(theano.config.floatX, + [False] * (b_len + 1))() + return bn, x + + +def test_batch_normalization_inference_apply(): + """Test that BatchNormalization.apply works in inference mode.""" + def check(input_dim, variable_dim, broadcastable=None, + conserve_memory=True): + bn, x = apply_setup(input_dim, broadcastable, conserve_memory) + y = bn.apply(x) + rng = numpy.random.RandomState((2015, 12, 16)) + input_ = random_unif(rng, + (9,) + + (input_dim + if isinstance(input_dim, collections.Sequence) + else (input_dim,))) + + # Upon initialization, should be just the identity function. + assert_allclose(y.eval({x: input_}), input_, rtol=1e-4) + + # Test population mean gets subtracted. + pop_mean = random_unif(rng, variable_dim) + bn.population_mean.set_value(pop_mean) + assert_allclose(y.eval({x: input_}), input_ - pop_mean, rtol=1e-4) + + # Test population stdev is divided out. + pop_stdev = random_unif(rng, variable_dim) + bn.population_stdev.set_value(pop_stdev) + assert_allclose(y.eval({x: input_}), (input_ - pop_mean) / pop_stdev, + rtol=1e-4) + + # Test learned scale is applied. + gamma = random_unif(rng, variable_dim) + bn.scale.set_value(gamma) + assert_allclose(y.eval({x: input_}), + (input_ - pop_mean) * (gamma / pop_stdev), + rtol=1e-4) + + # Test learned offset is applied. + beta = random_unif(rng, variable_dim) + bn.shift.set_value(beta) + assert_allclose(y.eval({x: input_}), + (input_ - pop_mean) * (gamma / pop_stdev) + beta, + rtol=1e-4) + + yield check, 9, (9,) + yield check, (5, 4), (5, 4), None, False + yield check, (2, 9, 7), (2, 1, 1), (False, True, True) + + +def test_batch_normalization_train_apply(): + def check(input_dim, variable_dim, broadcastable=None, + conserve_memory=True): + # Default epsilon value. + epsilon = numpy.cast[theano.config.floatX](1e-4) + bn, x = apply_setup(input_dim, broadcastable, conserve_memory) + with bn: + y_hat = bn.apply(x) + + rng = numpy.random.RandomState((2015, 12, 16)) + input_ = random_unif(rng, (9,) + + (input_dim + if isinstance(input_dim, collections.Sequence) + else (input_dim,))) + # i + 1 because the axes are all shifted one over when the batch + # axis is added. + axes = (0,) + tuple((i + 1) for i, b in + enumerate(bn.population_mean.broadcastable) if b) + + # NumPy implementation of the batch-normalization transform. + def normalize(x): + return ((x - x.mean(axis=axes, keepdims=True, + dtype=theano.config.floatX)) / + numpy.sqrt(numpy.var(x, axis=axes, keepdims=True, + dtype=theano.config.floatX) + + epsilon)) + + # Check that batch norm is doing what it should be. + assert_allclose(y_hat.eval({x: input_}), normalize(input_), + atol=(1e-3 if theano.config.floatX == 'float32' + else 1e-7)) + + # Check that the scale parameters are still getting applied. + gamma = random_unif(rng, variable_dim) + bn.scale.set_value(gamma) + assert_allclose(y_hat.eval({x: input_}), normalize(input_) * gamma, + atol=(1e-3 if theano.config.floatX == 'float32' + else 1e-7)) + + beta = random_unif(rng, variable_dim) + bn.shift.set_value(beta) + # Check that the shift parameters are still getting applied. + assert_allclose(y_hat.eval({x: input_}), + normalize(input_) * gamma + beta, + atol=(1e-3 if theano.config.floatX == 'float32' + else 1e-7)) + + # Double check that setting the population parameters doesn't + # affect anything. + bn.population_mean.set_value(numpy.nan * + bn.population_mean.get_value()) + bn.population_stdev.set_value(numpy.nan * + bn.population_mean.get_value()) + assert_allclose(y_hat.eval({x: input_}), + normalize(input_) * gamma + beta, + atol=(1e-3 if theano.config.floatX == 'float32' + else 1e-7)) + + yield check, 9, (9,) + yield check, (5, 4), (5, 4), None, False + yield check, (2, 9, 7), (2, 1, 1), (False, True, True) + + +def test_batch_normalization_image_size_setter(): + """Test that setting image_size on a BatchNormalization works.""" + bn = BatchNormalization() + bn.image_size = (5, 4) + assert bn.input_dim == (None, 5, 4) + bn.image_size = (4, 5) + assert bn.input_dim == (None, 4, 5) + + +def test_spatial_batch_normalization(): + """Smoke test for SpatialBatchNormalization.""" + def check(*input_dim): + sbn = SpatialBatchNormalization(input_dim) + sbn.initialize() + x = theano.tensor.TensorType(theano.config.floatX, + [False] * (len(input_dim) + 1))() + y = sbn.apply(x) + rng = numpy.random.RandomState((2015, 12, 17)) + input_ = random_unif(rng, (11,) + input_dim) + assert_equal(y.eval({x: input_}), input_) + + # Work around a stupid bug in nose2 by passing as *args. + yield check, 2, 3, 5 + yield check, 5, 3, 2, 3 + yield check, 1, 11 + + +def test_raise_exception_spatial(): + """Test that SpatialBatchNormalization raises an expected exception.""" + # Work around a stupid bug in nose2 that unpacks the tuple into + # separate arguments. + sbn1 = SpatialBatchNormalization((5,)) + yield assert_raises, (ValueError, sbn1.allocate) + sbn2 = SpatialBatchNormalization(3) + yield assert_raises, (ValueError, sbn2.allocate) + + def do_not_fail(*input_dim): + try: + sbn = SpatialBatchNormalization(input_dim) + sbn.allocate() + except ValueError: + assert False + + # Work around a stupid bug in nose2 by passing as *args. + yield do_not_fail, 5, 4, 3 + yield do_not_fail, 7, 6 + yield do_not_fail, 3, 9, 2, 3 + + +def test_batch_normalization_inside_convolutional_sequence(): + """Test that BN bricks work in ConvolutionalSequences.""" + conv_seq = ConvolutionalSequence( + [Convolutional(filter_size=(3, 3), num_filters=4), + BatchNormalization(broadcastable=(False, True, True)), + AveragePooling(pooling_size=(2, 2)), + BatchNormalization(broadcastable=(False, False, False)), + MaxPooling(pooling_size=(2, 2), step=(1, 1))], + weights_init=Constant(1.), + biases_init=Constant(2.), + image_size=(10, 8), num_channels=9) + + conv_seq_no_bn = ConvolutionalSequence( + [Convolutional(filter_size=(3, 3), num_filters=4), + AveragePooling(pooling_size=(2, 2)), + MaxPooling(pooling_size=(2, 2), step=(1, 1))], + weights_init=Constant(1.), + biases_init=Constant(2.), + image_size=(10, 8), num_channels=9) + + conv_seq.initialize() + conv_seq_no_bn.initialize() + rng = numpy.random.RandomState((2015, 12, 17)) + input_ = random_unif(rng, (2, 9, 10, 8)) + + x = theano.tensor.tensor4() + ybn = conv_seq.apply(x) + y = conv_seq_no_bn.apply(x) + yield (assert_equal, ybn.eval({x: input_}), y.eval({x: input_})) + + std = conv_seq.children[-2].population_stdev + std.set_value(3 * std.get_value(borrow=True)) + yield (assert_equal, ybn.eval({x: input_}), y.eval({x: input_}) / 3.) + + +def test_batch_normalized_mlp_construction(): + """Test that BatchNormalizedMLP performs construction correctly.""" + mlp = BatchNormalizedMLP([Tanh(), Tanh()], [5, 7, 9]) + assert all(isinstance(a, Sequence) for a in mlp.activations) + assert all(isinstance(a.children[0], BatchNormalization) + for a in mlp.activations) + assert all(isinstance(a.children[1], Tanh) + for a in mlp.activations) + + +def test_batch_normalized_mlp_allocation(): + """Test that BatchNormalizedMLP performs allocation correctly.""" + mlp = BatchNormalizedMLP([Tanh(), Tanh()], [5, 7, 9]) + mlp.allocate() + assert mlp.activations[0].children[0].input_dim == 7 + assert mlp.activations[1].children[0].input_dim == 9 + assert not any(l.use_bias for l in mlp.linear_transformations) + + +def test_batch_normalized_mlp_transformed(): + """Smoke test that a graph involving a BatchNormalizedMLP transforms.""" + x = tensor.matrix('x') + mlp = BatchNormalizedMLP([Tanh(), Tanh()], [5, 7, 9]) + with batch_normalization(mlp): + y = mlp.apply(x) + assert len(get_batch_normalization_updates(ComputationGraph([y]))) == 4 + + +def test_batch_normalized_mlp_conserve_memory_propagated(): + """Test that setting conserve_memory on a BatchNormalizedMLP works.""" + mlp = BatchNormalizedMLP([Tanh(), Tanh()], [5, 7, 9], + conserve_memory=False) + assert not any(act.children[0].conserve_memory for act in mlp.activations) + mlp.conserve_memory = True + assert mlp.conserve_memory + assert all(act.children[0].conserve_memory for act in mlp.activations) diff --git a/tests/bricks/test_bricks.py b/tests/bricks/test_bricks.py index 038c6da..6287f1d 100644 --- a/tests/bricks/test_bricks.py +++ b/tests/bricks/test_bricks.py @@ -5,7 +5,7 @@ from theano import tensor from blocks.bricks import (Identity, Linear, Maxout, LinearMaxout, MLP, Tanh, - Sequence, Random) + Sequence, Random, Logistic, Softplus, Softmax) from blocks.bricks.base import application, Brick, lazy, NoneAllocation from blocks.bricks.parallel import Parallel, Fork from blocks.filter import get_application_call, get_brick @@ -323,9 +323,17 @@ def test_maxout(): def test_activations(): x = tensor.vector() x_val = numpy.random.rand(8).astype(theano.config.floatX) + exp_x_val = numpy.exp(x_val) + assert_allclose(x_val, Identity().apply(x).eval({x: x_val})) assert_allclose(numpy.tanh(x_val), Tanh().apply(x).eval({x: x_val}), rtol=1e-06) + assert_allclose(numpy.log(1 + exp_x_val), + Softplus(x).apply(x).eval({x: x_val}), rtol=1e-6) + assert_allclose(exp_x_val / numpy.sum(exp_x_val), + Softmax(x).apply(x).eval({x: x_val}).flatten(), rtol=1e-6) + assert_allclose(1.0 / (1.0 + numpy.exp(-x_val)), + Logistic(x).apply(x).eval({x: x_val}), rtol=1e-6) def test_mlp(): diff --git a/tests/bricks/test_conv.py b/tests/bricks/test_conv.py index 653d919..312db89 100644 --- a/tests/bricks/test_conv.py +++ b/tests/bricks/test_conv.py @@ -1,14 +1,20 @@ +import pickle import numpy +from nose.tools import assert_raises_regexp import theano -from numpy.testing import assert_allclose, assert_equal +from numpy.testing import assert_allclose from theano import tensor from theano import function -from blocks.bricks import Rectifier -from blocks.bricks.conv import (Convolutional, ConvolutionalLayer, MaxPooling, - ConvolutionalActivation, ConvolutionalSequence) +from blocks.bricks import Rectifier, Tanh +from blocks.bricks.conv import (Convolutional, ConvolutionalTranspose, + MaxPooling, AveragePooling, + ConvolutionalActivation, + ConvolutionalTransposeActivation, + ConvolutionalSequence) from blocks.initialization import Constant +from blocks.graph import ComputationGraph def test_convolutional(): @@ -34,6 +40,72 @@ def test_convolutional(): assert conv.get_dim('output') == (num_filters, 15, 11) +def test_convolutional_transpose(): + x = tensor.tensor4('x') + num_channels = 4 + num_filters = 3 + image_size = (8, 6) + original_image_size = (17, 13) + batch_size = 5 + filter_size = (3, 3) + step = (2, 2) + conv = ConvolutionalTranspose( + original_image_size, filter_size, num_filters, num_channels, step=step, + image_size=image_size, weights_init=Constant(1.), + biases_init=Constant(5.)) + conv.initialize() + y = conv.apply(x) + func = function([x], y) + + x_val = numpy.ones((batch_size, num_channels) + image_size, + dtype=theano.config.floatX) + expected_value = num_channels * numpy.ones( + (batch_size, num_filters) + original_image_size) + expected_value[:, :, 2:-2:2, :] += num_channels + expected_value[:, :, :, 2:-2:2] += num_channels + expected_value[:, :, 2:-2:2, 2:-2:2] += num_channels + assert_allclose(func(x_val), expected_value + 5) + + +def test_border_mode_not_pushed(): + layers = [Convolutional(border_mode='full'), + ConvolutionalActivation(Rectifier().apply), + ConvolutionalActivation(Rectifier().apply, border_mode='valid'), + ConvolutionalActivation(Rectifier().apply, border_mode='full')] + stack = ConvolutionalSequence(layers) + stack.push_allocation_config() + assert stack.children[0].border_mode == 'full' + assert stack.children[1].border_mode == 'valid' + assert stack.children[2].border_mode == 'valid' + assert stack.children[3].border_mode == 'full' + stack2 = ConvolutionalSequence(layers, border_mode='full') + stack2.push_allocation_config() + assert stack2.children[0].border_mode == 'full' + assert stack2.children[1].border_mode == 'full' + assert stack2.children[2].border_mode == 'full' + assert stack2.children[3].border_mode == 'full' + + +def test_no_input_size(): + # suppose x is outputted by some RNN + x = tensor.tensor4('x') + filter_size = (1, 3) + num_filters = 2 + num_channels = 5 + c = Convolutional(filter_size, num_filters, num_channels, tied_biases=True, + weights_init=Constant(1.), biases_init=Constant(1.)) + c.initialize() + out = c.apply(x) + assert c.get_dim('output') == (2, None, None) + assert out.ndim == 4 + + c = Convolutional(filter_size, num_filters, num_channels, + tied_biases=False, weights_init=Constant(1.), + biases_init=Constant(1.)) + assert_raises_regexp(ValueError, 'Cannot infer bias size \S+', + c.initialize) + + def test_tied_biases(): x = tensor.tensor4('x') num_channels = 4 @@ -76,40 +148,108 @@ def test_max_pooling(): dtype=theano.config.floatX) assert_allclose(func(x_val), numpy.ones((batch_size, num_channels, - x_size / pool_size + 1, - y_size / pool_size + 1))) + x_size / pool_size, + y_size / pool_size))) pool.input_dim = (x_size, y_size) pool.get_dim('output') == (num_channels, x_size / pool_size + 1, y_size / pool_size + 1) -def test_convolutional_layer(): +def test_max_pooling_ignore_border_true(): x = tensor.tensor4('x') - num_channels = 4 - batch_size = 5 - pooling_size = 3 - num_filters = 3 - filter_size = (3, 3) - activation = Rectifier().apply + brick = MaxPooling((3, 4), ignore_border=True) + y = brick.apply(x) + out = y.eval({x: numpy.zeros((8, 3, 10, 13), dtype=theano.config.floatX)}) + assert out.shape == (8, 3, 3, 3) - conv = ConvolutionalLayer(activation, filter_size, num_filters, - (pooling_size, pooling_size), - num_channels, image_size=(17, 13), - batch_size=batch_size, - weights_init=Constant(1.), - biases_init=Constant(5.)) - conv.initialize() - y = conv.apply(x) - func = function([x], y) +def test_max_pooling_ignore_border_false(): + x = tensor.tensor4('x') + brick = MaxPooling((5, 7), ignore_border=False) + y = brick.apply(x) + out = y.eval({x: numpy.zeros((4, 6, 12, 15), dtype=theano.config.floatX)}) + assert out.shape == (4, 6, 3, 3) - x_val = numpy.ones((batch_size, num_channels, 17, 13), - dtype=theano.config.floatX) - assert_allclose(func(x_val), numpy.prod(filter_size) * num_channels * - numpy.ones((batch_size, num_filters, 5, 4)) + 5) - assert_equal(conv.convolution.batch_size, batch_size) - assert_equal(conv.pooling.batch_size, batch_size) +def test_max_pooling_padding(): + x = tensor.tensor4('x') + brick = MaxPooling((6, 2), padding=(3, 1), ignore_border=True) + y = brick.apply(x) + out = y.eval({x: numpy.zeros((2, 3, 6, 10), dtype=theano.config.floatX)}) + assert out.shape == (2, 3, 2, 6) + + +def test_max_pooling_old_pickle(): + brick = MaxPooling((3, 4)) + brick.allocate() + # Simulate old pickle, before #899. + del brick.ignore_border + del brick.mode + del brick.padding + # Pickle in this broken state and re-load. + broken_pickled = pickle.dumps(brick) + loaded = pickle.loads(broken_pickled) + # Same shape, same step. + assert brick.pooling_size == loaded.pooling_size + assert brick.step == loaded.step + # Check that the new attributes were indeed added. + assert hasattr(loaded, 'padding') and loaded.padding == (0, 0) + assert hasattr(loaded, 'mode') and loaded.mode == 'max' + assert hasattr(loaded, 'ignore_border') and not loaded.ignore_border + try: + loaded.apply(tensor.tensor4()) + except Exception: + raise AssertionError("failed to apply on unpickled MaxPooling") + # Make sure we're not overriding these attributes wrongly. + new_brick = MaxPooling((4, 3), padding=(2, 1)) + new_brick_unpickled = pickle.loads(pickle.dumps(new_brick)) + assert new_brick_unpickled.padding == (2, 1) + assert new_brick_unpickled.ignore_border + + +def test_average_pooling(): + x = tensor.tensor4('x') + brick = AveragePooling((2, 2)) + y = brick.apply(x) + tmp = numpy.arange(16, dtype=theano.config.floatX).reshape(1, 1, 4, 4) + x_ = numpy.tile(tmp, [2, 3, 1, 1]) + out = y.eval({x: x_}) + assert_allclose( + out - numpy.array([[10 / 4., 18 / 4.], [42 / 4., 50 / 4.]]), + numpy.zeros_like(out)) + + +def test_average_pooling_inc_padding(): + x = tensor.tensor4('x') + brick = AveragePooling((2, 2), ignore_border=True, padding=(1, 1), + include_padding=True) + y = brick.apply(x) + output = y.eval({x: 3 * numpy.ones((1, 1, 2, 2), + dtype=theano.config.floatX)}) + expected_out = numpy.array([0.75, 0.75, 0.75, 0.75]).reshape(1, 1, 2, 2) + assert_allclose(expected_out, output) + + +def test_average_pooling_exc_padding(): + x = tensor.tensor4('x') + brick = AveragePooling((2, 2), ignore_border=True, padding=(1, 1), + include_padding=False) + y = brick.apply(x) + x_ = 3 * numpy.ones((1, 1, 2, 2), dtype=theano.config.floatX) + output = y.eval({x: x_}) + assert_allclose(x_, output) + + +def test_pooling_works_in_convolutional_sequence(): + x = tensor.tensor4('x') + brick = ConvolutionalSequence([AveragePooling((2, 2), step=(2, 2)), + MaxPooling((4, 4), step=(2, 2), + ignore_border=True)], + image_size=(16, 32), num_channels=3) + brick.allocate() + y = brick.apply(x) + out = y.eval({x: numpy.empty((2, 3, 16, 32), dtype=theano.config.floatX)}) + assert out.shape == (2, 3, 3, 7) def test_convolutional_sequence(): @@ -119,14 +259,14 @@ def test_convolutional_sequence(): batch_size = 5 activation = Rectifier().apply - conv = ConvolutionalLayer(activation, (3, 3), 5, - (pooling_size, pooling_size), - weights_init=Constant(1.), - biases_init=Constant(5.)) + conv = ConvolutionalActivation(activation, (3, 3), 5, + weights_init=Constant(1.), + biases_init=Constant(5.)) + pooling = MaxPooling(pooling_size=(pooling_size, pooling_size)) conv2 = ConvolutionalActivation(activation, (2, 2), 4, weights_init=Constant(1.)) - seq = ConvolutionalSequence([conv, conv2], num_channels, + seq = ConvolutionalSequence([conv, pooling, conv2], num_channels, image_size=(17, 13)) seq.push_allocation_config() assert conv.num_channels == 4 @@ -137,6 +277,55 @@ def test_convolutional_sequence(): func = function([x], y) x_val = numpy.ones((batch_size, 4, 17, 13), dtype=theano.config.floatX) - y_val = (numpy.ones((batch_size, 4, 4, 3)) * + y_val = (numpy.ones((batch_size, 4, 4, 2)) * (9 * 4 + 5) * 4 * 5) assert_allclose(func(x_val), y_val) + + +def test_convolutional_activation_use_bias(): + act = ConvolutionalActivation(Rectifier().apply, (3, 3), 5, 4, + image_size=(9, 9), use_bias=False) + act.allocate() + assert not act.convolution.use_bias + assert len(ComputationGraph([act.apply(tensor.tensor4())]).parameters) == 1 + + +def test_convolutional_transpose_activation(): + x = tensor.tensor4('x') + num_channels = 4 + num_filters = 3 + image_size = (8, 6) + original_image_size = (17, 13) + batch_size = 5 + filter_size = (3, 3) + step = (2, 2) + conv = ConvolutionalTransposeActivation( + Tanh().apply, original_image_size, filter_size, num_filters, + num_channels, step=step, image_size=image_size, + weights_init=Constant(1.), biases_init=Constant(5.)) + conv.initialize() + y = conv.apply(x) + func = function([x], y) + + x_val = numpy.ones((batch_size, num_channels) + image_size, + dtype=theano.config.floatX) + expected_value = num_channels * numpy.ones( + (batch_size, num_filters) + original_image_size) + expected_value[:, :, 2:-2:2, :] += num_channels + expected_value[:, :, :, 2:-2:2] += num_channels + expected_value[:, :, 2:-2:2, 2:-2:2] += num_channels + assert_allclose(func(x_val), numpy.tanh(expected_value + 5)) + + +def test_convolutional_sequence_use_bias(): + cnn = ConvolutionalSequence( + [ConvolutionalActivation(activation=Rectifier().apply, + filter_size=(1, 1), num_filters=1) + for _ in range(3)], + num_channels=1, image_size=(1, 1), + use_bias=False) + cnn.allocate() + x = tensor.tensor4() + y = cnn.apply(x) + params = ComputationGraph(y).parameters + assert len(params) == 3 and all(param.name == 'W' for param in params) diff --git a/tests/bricks/test_recurrent.py b/tests/bricks/test_recurrent.py index bc7801f..f7fc45c 100644 --- a/tests/bricks/test_recurrent.py +++ b/tests/bricks/test_recurrent.py @@ -14,7 +14,8 @@ recurrent, BaseRecurrent, GatedRecurrent, SimpleRecurrent, Bidirectional, LSTM, RecurrentStack, RECURRENTSTACK_SEPARATOR) -from blocks.initialization import Constant, IsotropicGaussian, Orthogonal +from blocks.initialization import ( + Constant, IsotropicGaussian, Orthogonal, Identity) from blocks.filter import get_application_call, VariableFilter from blocks.graph import ComputationGraph from blocks.roles import INITIAL_STATE @@ -533,6 +534,50 @@ def test(self): assert_allclose(h_simple_rev, h_bidir[::-1, ..., 3:], rtol=1e-04) +class TestBidirectionalStack(unittest.TestCase): + def setUp(self): + prototype = SimpleRecurrent(dim=3, activation=Tanh()) + self.layers = [ + Bidirectional(weights_init=Orthogonal(), prototype=prototype) + for _ in range(3)] + self.stack = RecurrentStack(self.layers) + for fork in self.stack.forks: + fork.weights_init = Identity(1) + fork.biases_init = Constant(0) + self.stack.initialize() + + self.x_val = 0.1 * numpy.asarray( + list(itertools.permutations(range(4))), + dtype=theano.config.floatX) + self.x_val = (numpy.ones((24, 4, 3), dtype=theano.config.floatX) * + self.x_val[..., None]) + self.mask_val = numpy.ones((24, 4), dtype=theano.config.floatX) + self.mask_val[12:24, 3] = 0 + + def test_steps(self): + x = tensor.tensor3('x') + mask = tensor.matrix('mask') + + calc_stack_layers = [ + theano.function([x, mask], self.stack.apply(x, mask=mask)[i]) + for i in range(len(self.layers))] + stack_layers = [ + f(self.x_val, self.mask_val) for f in calc_stack_layers] + + h_val = self.x_val + for stack_layer_value, bidir_net in zip(stack_layers, self.layers): + calc = theano.function([x, mask], bidir_net.apply(x, mask=mask)) + simple_layer_value = calc(h_val, self.mask_val) + assert_allclose(stack_layer_value, simple_layer_value, rtol=1e-04) + h_val = simple_layer_value[..., :3] + + def test_dims(self): + self.assertEqual(self.stack.get_dim("inputs"), 3) + for i in range(len(self.layers)): + state_name = self.stack.suffix("states", i) + self.assertEqual(self.stack.get_dim(state_name), 6) + + def test_saved_inner_graph(): """Make sure that the original inner graph is saved.""" x = tensor.tensor3() diff --git a/tests/extensions/test_monitoring.py b/tests/extensions/test_monitoring.py index af42675..41d9b25 100644 --- a/tests/extensions/test_monitoring.py +++ b/tests/extensions/test_monitoring.py @@ -8,7 +8,7 @@ from blocks.extensions.monitoring import TrainingDataMonitoring from blocks.monitoring import aggregation from blocks.algorithms import GradientDescent, Scale -from blocks.utils import shared_floatx, named_copy +from blocks.utils import shared_floatx from blocks.main_loop import MainLoop @@ -24,7 +24,7 @@ def test_training_data_monitoring(): y = tensor.scalar('targets') W = shared_floatx([0, 0], name='W') V = shared_floatx(7, name='V') - W_sum = named_copy(W.sum(), 'W_sum') + W_sum = W.sum().copy(name='W_sum') cost = ((x * W).sum() - y) ** 2 cost.name = 'cost' diff --git a/tests/extensions/test_timing.py b/tests/extensions/test_timing.py index 2789e34..ce48c23 100644 --- a/tests/extensions/test_timing.py +++ b/tests/extensions/test_timing.py @@ -1,5 +1,5 @@ from blocks.extensions import Timing, FinishAfter -from tests import MockMainLoop +from blocks.utils.testing import MockMainLoop def test_timing(): diff --git a/tests/extensions/test_training.py b/tests/extensions/test_training.py index cf0c35a..607aac8 100644 --- a/tests/extensions/test_training.py +++ b/tests/extensions/test_training.py @@ -11,13 +11,14 @@ from theano.misc.pkl_utils import load from blocks.algorithms import GradientDescent, Scale +from blocks.config import config from blocks.extensions import FinishAfter, TrainingExtension from blocks.extensions.saveload import Checkpoint from blocks.extensions.training import SharedVariableModifier, TrackTheBest from blocks.extensions.predicates import OnLogRecord from blocks.main_loop import MainLoop from blocks.utils import shared_floatx -from tests import MockMainLoop +from blocks.utils.testing import MockMainLoop, skip_if_configuration_set def test_shared_variable_modifier(): @@ -128,8 +129,10 @@ def after_batch(self, batch): def test_save_the_best(): - with NamedTemporaryFile() as dst,\ - NamedTemporaryFile() as dst_best: + skip_if_configuration_set('log_backend', 'sqlite', + "Known to be flaky with SQLite log backend.") + with NamedTemporaryFile(dir=config.temp_dir) as dst,\ + NamedTemporaryFile(dir=config.temp_dir) as dst_best: track_cost = TrackTheBest("cost", after_epoch=False, after_batch=True) main_loop = MockMainLoop( extensions=[FinishAfter(after_n_epochs=1), diff --git a/tests/graph/test_bn.py b/tests/graph/test_bn.py new file mode 100644 index 0000000..f6b1c8b --- /dev/null +++ b/tests/graph/test_bn.py @@ -0,0 +1,133 @@ +import numpy +from numpy.testing import assert_allclose +import theano +from theano import tensor + +from blocks.bricks import (BatchNormalization, Sequence, Tanh, MLP, + BatchNormalizedMLP) +from blocks.filter import get_brick +from blocks.graph import (ComputationGraph, batch_normalization, + apply_batch_normalization, + get_batch_normalization_updates) +from blocks.initialization import Constant +from blocks.roles import (has_roles, BATCH_NORM_POPULATION_MEAN, + BATCH_NORM_POPULATION_STDEV) +from blocks.utils import is_shared_variable + + +def test_batch_normalization_simple(): + x = tensor.matrix() + eps = 1e-4 + bn = BatchNormalization(input_dim=4, epsilon=eps) + bn.initialize() + with batch_normalization(bn): + y = bn.apply(x) + rng = numpy.random.RandomState((2016, 1, 18)) + x_ = rng.uniform(size=(5, 4)).astype(theano.config.floatX) + y_ = y.eval({x: x_}) + y_expected = (x_ - x_.mean(axis=0)) / numpy.sqrt(x_.var(axis=0) + eps) + assert_allclose(y_, y_expected, rtol=1e-4) + + +def test_batch_normalization_nested(): + x = tensor.tensor4() + eps = 1e-4 + r_dims = (0, 2, 3) + batch_dims = (5, 4, 3, 2) + bn = BatchNormalization(input_dim=batch_dims[1:], + broadcastable=(False, True, True), + epsilon=eps) + seq = Sequence([bn.apply, Tanh().apply]) + seq.initialize() + with batch_normalization(seq): + y = seq.apply(x) + rng = numpy.random.RandomState((2016, 1, 18)) + x_ = rng.uniform(size=batch_dims).astype(theano.config.floatX) + y_ = y.eval({x: x_}) + y_expected = numpy.tanh((x_ - x_.mean(axis=r_dims, keepdims=True)) / + numpy.sqrt(x_.var(axis=r_dims, keepdims=True) + + eps)) + assert_allclose(y_, y_expected, rtol=1e-4) + + +def test_apply_batch_normalization_nested(): + x = tensor.matrix() + eps = 1e-8 + batch_dims = (3, 9) + bn = BatchNormalization(input_dim=5, epsilon=eps) + mlp = MLP([Sequence([bn.apply, Tanh().apply])], [9, 5], + weights_init=Constant(0.4), biases_init=Constant(1)) + mlp.initialize() + y = mlp.apply(x) + cg = apply_batch_normalization(ComputationGraph([y])) + y_bn = cg.outputs[0] + rng = numpy.random.RandomState((2016, 1, 18)) + x_ = rng.uniform(size=batch_dims).astype(theano.config.floatX) + y_ = y_bn.eval({x: x_}) + W_, b_ = map(lambda s: (getattr(mlp.linear_transformations[0], s) + .get_value(borrow=True)), ['W', 'b']) + z_ = numpy.dot(x_, W_) + b_ + y_expected = numpy.tanh((z_ - z_.mean(axis=0)) / + numpy.sqrt(z_.var(axis=0) + eps)) + assert_allclose(y_, y_expected, rtol=1e-3) + + +class TestSimpleGetBatchNormalizationUpdates(object): + def setUp(self): + self.mlp = BatchNormalizedMLP([Tanh(), Tanh()], [5, 7, 9]) + self.x = tensor.matrix() + + def simple_assertions(self, updates, num_bricks=2, num_updates=4): + """Shared assertions for simple tests.""" + assert len(updates) == num_updates + assert all(is_shared_variable(u[0]) for u in updates) + # This order is somewhat arbitrary and implementation_dependent + means = set(u[0] for u in updates + if has_roles(u[0], [BATCH_NORM_POPULATION_MEAN])) + stdevs = set(u[0] for u in updates + if has_roles(u[0], [BATCH_NORM_POPULATION_STDEV])) + assert means.isdisjoint(stdevs) + assert len(set(get_brick(v) for v in means)) == num_bricks + assert len(set(get_brick(v) for v in stdevs)) == num_bricks + + def test_get_batch_normalization_updates(self): + """Test that get_batch_normalization_updates works as expected.""" + with batch_normalization(self.mlp): + y_bn = self.mlp.apply(self.x) + graph = ComputationGraph([y_bn]) + updates = get_batch_normalization_updates(graph) + self.simple_assertions(updates) + + def test_get_batch_normalization_updates_non_training_applications(self): + """Test updates extracton in graph with non-training apply.""" + y = self.mlp.apply(self.x) + with batch_normalization(self.mlp): + y_bn = self.mlp.apply(self.x) + graph = ComputationGraph([y_bn, y]) + updates = get_batch_normalization_updates(graph) + self.simple_assertions(updates) + + def test_get_batch_normalization_updates_no_training(self): + """Test for exception if there are no training-mode nodes.""" + y = self.mlp.apply(self.x) + graph = ComputationGraph([y]) + numpy.testing.assert_raises(ValueError, + get_batch_normalization_updates, graph) + + def test_get_batch_normalization_updates_duplicates_error(self): + """Test that we get an error by default on multiple apply.""" + with batch_normalization(self.mlp): + y = self.mlp.apply(self.x) + y2 = self.mlp.apply(self.x) + graph = ComputationGraph([y, y2]) + numpy.testing.assert_raises(ValueError, + get_batch_normalization_updates, graph) + + def test_get_batch_normalization_updates_allow_duplicates(self): + """Test get_batch_normalization_updates(allow_duplicates=True).""" + with batch_normalization(self.mlp): + y = self.mlp.apply(self.x) + y2 = self.mlp.apply(self.x) + graph = ComputationGraph([y, y2]) + updates = get_batch_normalization_updates(graph, allow_duplicates=True) + self.simple_assertions(updates, num_bricks=2, num_updates=8) diff --git a/tests/test_graph.py b/tests/test_graph.py index 027640d..0c8eb70 100644 --- a/tests/test_graph.py +++ b/tests/test_graph.py @@ -8,7 +8,8 @@ from blocks.bricks import MLP, Identity, Logistic from blocks.bricks.cost import SquaredError from blocks.filter import VariableFilter -from blocks.graph import apply_noise, collect_parameters, ComputationGraph +from blocks.graph import (apply_dropout, apply_noise, collect_parameters, + ComputationGraph) from blocks.initialization import Constant from blocks.roles import add_role, COLLECTED, PARAMETER, AUXILIARY from tests.bricks.test_bricks import TestBrick @@ -140,6 +141,36 @@ def test_apply_noise(): 2 + MRG_RandomStreams(1).normal(tuple()).eval()) +def test_apply_dropout(): + x = tensor.vector() + y = tensor.vector() + z = x * y + cg = ComputationGraph([z]) + dropped_cg = apply_dropout(cg, [x], 0.4, seed=1) + + x_ = numpy.array([5., 6., 7.], dtype=theano.config.floatX) + y_ = numpy.array([1., 2., 3.], dtype=theano.config.floatX) + + assert_allclose( + dropped_cg.outputs[0].eval({x: x_, y: y_}), + x_ * y_ * MRG_RandomStreams(1).binomial((3,), p=0.6).eval() / 0.6) + + +def test_apply_dropout_custom_divisor(): + x = tensor.vector() + y = tensor.vector() + z = x - y + cg = ComputationGraph([z]) + scaled_dropped_cg = apply_dropout(cg, [y], 0.8, seed=2, custom_divisor=2.5) + + x_ = numpy.array([9., 8., 9.], dtype=theano.config.floatX) + y_ = numpy.array([4., 5., 6.], dtype=theano.config.floatX) + + assert_allclose( + scaled_dropped_cg.outputs[0].eval({x: x_, y: y_}), + x_ - (y_ * MRG_RandomStreams(2).binomial((3,), p=0.2).eval() / 2.5)) + + def test_snapshot(): x = tensor.matrix('x') linear = MLP([Identity(), Identity()], [10, 10, 10], diff --git a/tests/test_main_loop.py b/tests/test_main_loop.py index a8d7bea..78b8f5c 100644 --- a/tests/test_main_loop.py +++ b/tests/test_main_loop.py @@ -13,7 +13,7 @@ from blocks.extensions import TrainingExtension, FinishAfter, Printing from blocks.utils import unpack from blocks.config import config -from tests import MockAlgorithm, MockMainLoop +from blocks.utils.testing import MockAlgorithm, MockMainLoop class WriteBatchExtension(TrainingExtension): diff --git a/tests/test_model.py b/tests/test_model.py index 48f27a4..f65f05c 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -5,6 +5,8 @@ from blocks.bricks import MLP, Tanh from blocks.model import Model +from blocks.graph import add_role, PARAMETER +from blocks.utils import shared_floatx def test_model(): @@ -14,7 +16,7 @@ def test_model(): h1 = mlp1.apply(x) h2 = mlp2.apply(h1) - model = Model(h2.sum()) + model = Model(h2) assert model.get_top_bricks() == [mlp1, mlp2] # The order of parameters returned is deterministic but # not sensible. @@ -44,9 +46,27 @@ def test_model(): for name, value in parameter_values.items(): assert_allclose(value, got_parameter_values[name]) + # Test exception is raised if parameter shapes don't match + def helper(): + parameter_values = { + '/mlp/linear_0.W': 2 * numpy.ones((11, 11), + dtype=theano.config.floatX), + '/mlp/linear_0.b': 3 * numpy.ones(11, dtype=theano.config.floatX)} + model3.set_parameter_values(parameter_values) + assert_raises(ValueError, helper) + # Test name conflict handling mlp4 = MLP([Tanh()], [10, 10]) def helper(): Model(mlp4.apply(mlp3.apply(x))) assert_raises(ValueError, helper) + + +def test_model_handles_brickless_parameteres(): + x = tensor.matrix('x') + v = shared_floatx(numpy.zeros((10, 10)), name='V') + add_role(v, PARAMETER) + y = x.dot(v) + model = Model(y) + assert list(model.get_parameter_dict().items()) == [('V', v)] diff --git a/tests/test_serialization.py b/tests/test_serialization.py index 89c9f9c..d085cf7 100644 --- a/tests/test_serialization.py +++ b/tests/test_serialization.py @@ -7,6 +7,7 @@ from numpy.testing import assert_allclose, assert_raises from blocks.bricks import MLP +from blocks.config import config from blocks.initialization import Constant from blocks.serialization import load, dump, secure_dump, load_parameter_values @@ -23,7 +24,7 @@ def test_serialization(): W.set_value(W.get_value() * 2) # Check the data using numpy.load - with NamedTemporaryFile(delete=False) as f: + with NamedTemporaryFile(delete=False, dir=config.temp_dir) as f: dump(mlp, f) numpy_data = numpy.load(f.name) assert set(numpy_data.keys()) == \ @@ -38,7 +39,7 @@ def test_serialization(): # Ensure that only parameters are saved as NPY files mlp.random_data = numpy.random.rand(10) - with NamedTemporaryFile(delete=False) as f: + with NamedTemporaryFile(delete=False, dir=config.temp_dir) as f: dump(mlp, f) numpy_data = numpy.load(f.name) assert set(numpy_data.keys()) == \ @@ -52,7 +53,7 @@ def test_serialization(): # Ensure that duplicate names are dealt with for child in mlp.children: child.name = 'linear' - with NamedTemporaryFile(delete=False) as f: + with NamedTemporaryFile(delete=False, dir=config.temp_dir) as f: dump(mlp, f) numpy_data = numpy.load(f.name) assert set(numpy_data.keys()) == \ @@ -63,7 +64,7 @@ def test_serialization(): import __main__ __main__.__dict__['foo'] = foo mlp.foo = foo - with NamedTemporaryFile(delete=False) as f: + with NamedTemporaryFile(delete=False, dir=config.temp_dir) as f: with warnings.catch_warnings(record=True) as w: dump(mlp, f) assert len(w) == 1 @@ -73,7 +74,7 @@ def test_serialization(): def test_secure_dump(): foo = object() bar = lambda: None # flake8: noqa - with NamedTemporaryFile(delete=False) as f: + with NamedTemporaryFile(delete=False, dir=config.temp_dir) as f: secure_dump(foo, f.name) assert_raises(PicklingError, secure_dump, bar, f.name) with open(f.name, 'rb') as f: diff --git a/tests/utils/test_utils.py b/tests/utils/test_utils.py index 418fce6..f96341b 100644 --- a/tests/utils/test_utils.py +++ b/tests/utils/test_utils.py @@ -1,7 +1,8 @@ from numpy.testing import assert_raises from theano import tensor -from blocks.utils import check_theano_variable, unpack +from blocks.utils import check_theano_variable, unpack, find_bricks +from blocks.bricks import MLP, Sequence, Tanh, Identity, Logistic def test_unpack(): @@ -20,3 +21,60 @@ def test_check_theano_variable(): tensor.vector(), 2, 'float') assert_raises(ValueError, check_theano_variable, tensor.vector(), 1, 'int') + + +class TestFindBricks(object): + def setUp(self): + self.mlp = MLP([Sequence([Identity(name='id1').apply, + Tanh(name='tanh1').apply], + name='sequence1'), + Sequence([Logistic(name='logistic1').apply, + Identity(name='id2').apply, + Tanh(name='tanh2').apply], + name='sequence2'), + Logistic(name='logistic2'), + Sequence([Sequence([Logistic(name='logistic3').apply], + name='sequence4').apply], + name='sequence3')], + [10, 5, 9, 5, 9]) + + def test_find_zeroth_level(self): + found = find_bricks([self.mlp], lambda x: isinstance(x, MLP)) + assert len(found) == 1 + assert found[0] == self.mlp + + def test_find_zeroth_level_repeated(self): + found = find_bricks([self.mlp, self.mlp], lambda x: isinstance(x, MLP)) + assert len(found) == 1 + assert found[0] == self.mlp + + def test_find_all_unique(self): + found = find_bricks([self.mlp, self.mlp] + list(self.mlp.children), + lambda _: True) + assert len(found) == 16 # 12 activations plus 4 linear transformations + + def test_find_none(self): + found = find_bricks([self.mlp], lambda _: False) + assert len(found) == 0 + + def test_find_first_level(self): + found = set(find_bricks([self.mlp], lambda x: isinstance(x, Sequence))) + assert len(found) == 5 + assert self.mlp in found + found.remove(self.mlp) + sequences = set(self.mlp.activations[0:2] + + [self.mlp.activations[3], + self.mlp.activations[3].children[0]]) + assert sequences == found + + def test_find_second_and_third_level(self): + found = set(find_bricks([self.mlp], lambda x: isinstance(x, Identity))) + assert len(found) == 2 + assert self.mlp.activations[0].children[0] in found + assert self.mlp.activations[1].children[1] in found + + def test_find_first_and_second_and_third_level(self): + found = set(find_bricks([self.mlp], lambda x: isinstance(x, Logistic))) + assert self.mlp.activations[2] in found + assert self.mlp.activations[1].children[0] in found + assert self.mlp.activations[3].children[0].children[0]