From f809d8970fcdf41fb633fa98f13a40617f48f244 Mon Sep 17 00:00:00 2001 From: Tobias Domhan Date: Wed, 23 Sep 2020 17:09:31 +0200 Subject: [PATCH] Fix backwards incompatibility introduced with 2.1.17 (#885) * Adding a test for backwards compatability. * Backwards compatability. * Space * Changelog. --- CHANGELOG.md | 6 + MANIFEST.in | 6 + sockeye/__init__.py | 2 +- sockeye/transformer.py | 9 + test/data/model_2.1.x/README.md | 22 +++ test/data/model_2.1.x/config | 157 ++++++++++++++++++ test/data/model_2.1.x/model_input | 10 ++ test/data/model_2.1.x/params.best | Bin 0 -> 33643 bytes test/data/model_2.1.x/version | 1 + test/data/model_2.1.x/vocab.src.0.json | 17 ++ test/data/model_2.1.x/vocab.trg.0.json | 17 ++ .../test_backwards_compatibility.py | 55 ++++++ 12 files changed, 301 insertions(+), 1 deletion(-) create mode 100644 test/data/model_2.1.x/README.md create mode 100644 test/data/model_2.1.x/config create mode 100644 test/data/model_2.1.x/model_input create mode 100644 test/data/model_2.1.x/params.best create mode 100644 test/data/model_2.1.x/version create mode 100644 test/data/model_2.1.x/vocab.src.0.json create mode 100644 test/data/model_2.1.x/vocab.trg.0.json create mode 100644 test/integration/test_backwards_compatibility.py diff --git a/CHANGELOG.md b/CHANGELOG.md index d0e694f93..f47f1c94d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,12 @@ Note that Sockeye has checks in place to not translate with an old model that wa Each version section may have have subsections for: _Added_, _Changed_, _Removed_, _Deprecated_, and _Fixed_. +## [2.1.26] + +### Fixed + +- Fixes a backwards incompatibility introduced in 2.1.17, which would prevent models trained with prior versions to be used for inference. + ## [2.1.25] ### Changed diff --git a/MANIFEST.in b/MANIFEST.in index f8ba0012b..e307a5fa7 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -7,6 +7,12 @@ include pylintrc include .flake8 include typechecked-files include test/data/config_with_missing_attributes.yaml +include test/data/model_2.1.x/config +include test/data/model_2.1.x/params.best +include test/data/model_2.1.x/model_input +include test/data/model_2.1.x/vocab* +include test/data/model_2.1.x/version +include test/data/model_2.1.x/README.md include sockeye/git_version.py include *.bib recursive-include .github * diff --git a/sockeye/__init__.py b/sockeye/__init__.py index e2782f453..34f6d9f04 100644 --- a/sockeye/__init__.py +++ b/sockeye/__init__.py @@ -11,4 +11,4 @@ # express or implied. See the License for the specific language governing # permissions and limitations under the License. -__version__ = '2.1.25' +__version__ = '2.1.26' diff --git a/sockeye/transformer.py b/sockeye/transformer.py index d764b0ec5..fdc5fc799 100644 --- a/sockeye/transformer.py +++ b/sockeye/transformer.py @@ -158,6 +158,15 @@ def __init__(self, dropout=config.dropout_prepost, prefix=self.autoregr_layer.prefix + "post_", num_hidden=config.model_size) + # TODO (tdomhan): Remove with next major version bump. + # For backwards compatibility with versions prior to 2.1.17 we also store the layers under to previous + # attribute name. This way parameters can be loaded as either decoder.layers.0.autoregr_layer.ff_out.weight + # or decoder.layers.0.self_attention.ff_out.weight. Parameter deduplication makes sure parameters are stored + # and loaded once only. + if self.decoder_type == C.TRANSFORMER_TYPE: + self.self_attention = self.autoregr_layer + self.pre_self_attention = self.pre_autoregr_layer + self.post_self_attention = self.post_autoregr_layer self.pre_enc_attention = TransformerProcessBlock(sequence=config.preprocess_sequence, dropout=config.dropout_prepost, diff --git a/test/data/model_2.1.x/README.md b/test/data/model_2.1.x/README.md new file mode 100644 index 000000000..8b78b6b06 --- /dev/null +++ b/test/data/model_2.1.x/README.md @@ -0,0 +1,22 @@ +The model was generated with the following command: +``` +-s docs/tutorials/seqcopy/data/dev.source +-t docs/tutorials/seqcopy/data/dev.target +-vs docs/tutorials/seqcopy/data/dev.source +-vt docs/tutorials/seqcopy/data/dev.target +--transformer-model-size 16 +--num-layers 1:1 +--transformer-attention-heads 2 +--transformer-feed-forward-num-hidden 16 +--overwrite-output +--use-cpu +--batch-type sentence +--batch-size 32 +--decode-and-evaluate 400 +--checkpoint-interval 500 +--initial-learning-rate 0.01 +--max-num-checkpoint-not-improved 4 +-o model +``` + +The model_input is just `head dev.source`. diff --git a/test/data/model_2.1.x/config b/test/data/model_2.1.x/config new file mode 100644 index 000000000..2d644b413 --- /dev/null +++ b/test/data/model_2.1.x/config @@ -0,0 +1,157 @@ +!ModelConfig +config_data: !DataConfig + data_statistics: !DataStatistics + average_len_target_per_bucket: + - null + - 13.44736842105263 + - 20.568421052631578 + - 28.053672316384176 + - null + - null + - null + - null + - null + - null + - null + - null + buckets: + - !!python/tuple + - 8 + - 8 + - !!python/tuple + - 16 + - 16 + - !!python/tuple + - 24 + - 24 + - !!python/tuple + - 32 + - 32 + - !!python/tuple + - 40 + - 40 + - !!python/tuple + - 48 + - 48 + - !!python/tuple + - 56 + - 56 + - !!python/tuple + - 64 + - 64 + - !!python/tuple + - 72 + - 72 + - !!python/tuple + - 80 + - 80 + - !!python/tuple + - 88 + - 88 + - !!python/tuple + - 96 + - 96 + length_ratio_mean: 1.0 + length_ratio_stats_per_bucket: + - &id001 !!python/tuple + - null + - null + - !!python/tuple + - 1.0 + - 0.0 + - !!python/tuple + - 1.0 + - 0.0 + - !!python/tuple + - 1.0 + - 0.0 + - *id001 + - *id001 + - *id001 + - *id001 + - *id001 + - *id001 + - *id001 + - *id001 + length_ratio_std: 0.0 + max_observed_len_source: 31 + max_observed_len_target: 31 + num_discarded: 0 + num_sents: 1000 + num_sents_per_bucket: + - 0 + - 266 + - 380 + - 354 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + num_tokens_source: 21324 + num_tokens_target: 21324 + num_unks_source: 0 + num_unks_target: 0 + size_vocab_source: 15 + size_vocab_target: 15 + max_seq_len_source: 96 + max_seq_len_target: 96 + num_source_factors: 1 +config_decoder: !TransformerConfig + act_type: relu + attention_heads: 2 + depth_key_value: 16 + dropout_act: 0.1 + dropout_attention: 0.1 + dropout_prepost: 0.1 + feed_forward_num_hidden: 16 + lhuc: false + max_seq_len_source: 96 + max_seq_len_target: 96 + model_size: 16 + num_layers: 1 + positional_embedding_type: fixed + postprocess_sequence: dr + preprocess_sequence: n + use_lhuc: false +config_embed_source: !EmbeddingConfig + allow_sparse_grad: true + dropout: 0.0 + factor_configs: null + num_embed: 16 + num_factors: 1 + vocab_size: 15 +config_embed_target: !EmbeddingConfig + allow_sparse_grad: true + dropout: 0.0 + factor_configs: null + num_embed: 16 + num_factors: 1 + vocab_size: 15 +config_encoder: !TransformerConfig + act_type: relu + attention_heads: 2 + depth_key_value: 0 + dropout_act: 0.1 + dropout_attention: 0.1 + dropout_prepost: 0.1 + feed_forward_num_hidden: 16 + lhuc: false + max_seq_len_source: 96 + max_seq_len_target: 96 + model_size: 16 + num_layers: 1 + positional_embedding_type: fixed + postprocess_sequence: dr + preprocess_sequence: n + use_lhuc: false +config_length_task: null +dtype: float32 +intgemm_custom_lib: /Volumes/CaseSensitive/Projects/CoreMT/sockeye-github/sockeye/libintgemm.so +lhuc: false +vocab_source_size: 15 +vocab_target_size: 15 +weight_tying_type: src_trg_softmax diff --git a/test/data/model_2.1.x/model_input b/test/data/model_2.1.x/model_input new file mode 100644 index 000000000..8e94f7366 --- /dev/null +++ b/test/data/model_2.1.x/model_input @@ -0,0 +1,10 @@ +6 3 0 9 3 2 0 5 3 8 0 1 0 4 1 6 2 8 9 10 3 7 0 4 9 7 5 2 7 7 +2 1 7 7 5 0 5 7 1 7 10 4 0 9 10 5 0 5 +6 8 4 6 1 8 1 3 2 4 0 1 6 4 6 1 0 6 5 4 7 0 5 +8 1 7 6 9 10 10 3 4 7 8 1 9 6 9 5 2 3 1 1 +0 6 4 2 0 6 8 1 0 8 3 7 4 0 8 0 1 2 0 0 8 9 4 1 7 4 +10 1 8 2 3 4 2 3 7 6 6 +7 7 0 1 5 8 8 8 10 1 7 6 7 4 4 0 9 4 2 7 6 3 8 2 +5 7 3 5 3 7 5 5 9 9 7 5 5 5 8 0 10 8 8 5 3 10 5 6 2 9 8 3 7 7 +6 9 4 4 7 6 9 4 5 9 10 1 8 2 +7 0 1 6 0 6 9 7 2 4 3 diff --git a/test/data/model_2.1.x/params.best b/test/data/model_2.1.x/params.best new file mode 100644 index 0000000000000000000000000000000000000000..13e33ec10ca00e297ca39b032edf0d260bb827cf GIT binary patch literal 33643 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 Oz|Uv=Z_)qvIsYH33b%#; literal 0 HcmV?d00001 diff --git a/test/data/model_2.1.x/version b/test/data/model_2.1.x/version new file mode 100644 index 000000000..91dbb1711 --- /dev/null +++ b/test/data/model_2.1.x/version @@ -0,0 +1 @@ +2.1.16 \ No newline at end of file diff --git a/test/data/model_2.1.x/vocab.src.0.json b/test/data/model_2.1.x/vocab.src.0.json new file mode 100644 index 000000000..b2e392270 --- /dev/null +++ b/test/data/model_2.1.x/vocab.src.0.json @@ -0,0 +1,17 @@ +{ + "": 0, + "": 1, + "": 2, + "": 3, + "5": 4, + "3": 5, + "9": 6, + "1": 7, + "10": 8, + "6": 9, + "7": 10, + "2": 11, + "4": 12, + "0": 13, + "8": 14 +} \ No newline at end of file diff --git a/test/data/model_2.1.x/vocab.trg.0.json b/test/data/model_2.1.x/vocab.trg.0.json new file mode 100644 index 000000000..b2e392270 --- /dev/null +++ b/test/data/model_2.1.x/vocab.trg.0.json @@ -0,0 +1,17 @@ +{ + "": 0, + "": 1, + "": 2, + "": 3, + "5": 4, + "3": 5, + "9": 6, + "1": 7, + "10": 8, + "6": 9, + "7": 10, + "2": 11, + "4": 12, + "0": 13, + "8": 14 +} \ No newline at end of file diff --git a/test/integration/test_backwards_compatibility.py b/test/integration/test_backwards_compatibility.py new file mode 100644 index 000000000..7dd15b997 --- /dev/null +++ b/test/integration/test_backwards_compatibility.py @@ -0,0 +1,55 @@ +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You may not +# use this file except in compliance with the License. A copy of the License +# is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is distributed on +# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +import logging +import os +import sys +from tempfile import TemporaryDirectory + +import sockeye.translate +from unittest.mock import patch + +logger = logging.getLogger(__name__) + +EXPECTED_OUTPUT = """6 3 0 9 3 2 0 5 3 8 0 1 0 4 1 6 2 8 9 10 3 7 0 4 9 7 5 2 7 7 +2 1 7 7 5 0 5 7 1 7 10 4 0 9 10 5 0 5 +6 8 4 6 1 8 1 3 2 4 0 1 6 4 6 1 0 6 5 4 7 0 5 +8 1 7 6 9 10 10 3 4 7 8 1 9 6 9 5 2 3 1 1 +0 6 4 2 0 6 8 1 0 8 3 7 4 0 8 0 1 2 0 0 8 9 4 1 7 4 +10 1 8 2 3 4 2 3 7 6 6 +7 7 0 1 5 8 8 8 10 1 7 6 7 4 4 0 9 4 2 7 6 3 8 2 +5 7 3 5 3 7 5 5 9 9 7 5 5 5 8 0 10 8 8 5 3 10 5 6 2 9 8 3 7 7 +6 9 4 4 7 6 9 4 5 9 10 1 8 2 +7 0 1 6 0 6 9 7 2 4 3 +""" + + +def test_backwards_compatibility(): + """ + This test checks whether the current code can still produce translations with a model that was trained with the + same major version. + """ + with TemporaryDirectory() as work_dir: + output_file = os.path.join(work_dir, "out") + params = """{sockeye} --use-cpu --models {model} --input {input} --output {output} """.format( + sockeye=sockeye.translate.__file__, + model="test/data/model_2.1.x", + input="test/data/model_2.1.x/model_input", + output=output_file + ) + logger.info("Translating with params %s", params) + with patch.object(sys, "argv", params.split()): + sockeye.translate.main() + + with open(output_file) as model_out: + assert model_out.read() == EXPECTED_OUTPUT