Home | History | Annotate | Download | only in training
      1 # Copyright 2017 The TensorFlow Authors. All Rights Reserved.
      2 #
      3 # Licensed under the Apache License, Version 2.0 (the "License");
      4 # you may not use this file except in compliance with the License.
      5 # You may obtain a copy of the License at
      6 #
      7 #     http://www.apache.org/licenses/LICENSE-2.0
      8 #
      9 # Unless required by applicable law or agreed to in writing, software
     10 # distributed under the License is distributed on an "AS IS" BASIS,
     11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 # See the License for the specific language governing permissions and
     13 # limitations under the License.
     14 # ==============================================================================
     15 """Functional tests for Python wrappers around warm-starting."""
     16 from __future__ import absolute_import
     17 from __future__ import division
     18 from __future__ import print_function
     19 
     20 import os
     21 import numpy as np
     22 
     23 from tensorflow.python.framework import constant_op
     24 from tensorflow.python.framework import dtypes
     25 from tensorflow.python.framework import ops
     26 from tensorflow.python.framework import test_util
     27 from tensorflow.python.ops import array_ops
     28 from tensorflow.python.ops import init_ops
     29 from tensorflow.python.ops import partitioned_variables
     30 from tensorflow.python.ops import variable_scope
     31 from tensorflow.python.ops import variables
     32 from tensorflow.python.platform import test
     33 from tensorflow.python.training import checkpoint_ops
     34 from tensorflow.python.training import saver as saver_lib
     35 
     36 
     37 @test_util.run_v1_only('b/120545219')
     38 class LoadAndRemapWrappersTest(test.TestCase):
     39   """Tests for the functionality of the Python wrappers."""
     40 
     41   def setUp(self):
     42     ops.reset_default_graph()
     43     # Create the checkpoint file in a temporary directory.
     44     checkpoint_prefix = os.path.join(self.get_temp_dir(), 'model')
     45     # 0., 1., ..., 79. reshaped into [5, 16].
     46     initializer = init_ops.constant_initializer(
     47         np.reshape(np.linspace(0.0, 79, 5 * 16), (5, 16)))
     48     with self.cached_session() as sess:
     49       with variable_scope.variable_scope('some_scope'):
     50         variable_scope.get_variable(name='embeddings', shape=[5, 16],
     51                                     initializer=initializer)
     52       self.evaluate(variables.global_variables_initializer())
     53       saver = saver_lib.Saver()
     54       saver.save(sess, checkpoint_prefix, global_step=5)
     55     self.checkpoint_file = '{}-5'.format(checkpoint_prefix)
     56 
     57     # Create the vocabulary files.
     58     self.new_feature_vocab_file = os.path.join(
     59         self.get_temp_dir(), 'new_feature_vocab.txt')
     60     with open(self.new_feature_vocab_file, 'w') as f:
     61       f.write('\n'.join(['zero', 'one', 'two', 'three', 'four']) + '\n')
     62 
     63     self.old_feature_vocab_file = os.path.join(
     64         self.get_temp_dir(), 'old_feature_vocab.txt')
     65     with open(self.old_feature_vocab_file, 'w') as f:
     66       f.write('\n'.join(['zero', 'one', 'two', 'three']) + '\n')
     67 
     68     self.new_class_vocab_file = os.path.join(
     69         self.get_temp_dir(), 'new_class_vocab.txt')
     70     with open(self.new_class_vocab_file, 'w') as f:
     71       f.write('\n'.join(['MISSING', 'knitting', 'flask', 'eminem']) + '\n')
     72 
     73     self.old_class_vocab_file = os.path.join(
     74         self.get_temp_dir(), 'old_class_vocab.txt')
     75     with open(self.old_class_vocab_file, 'w') as f:
     76       f.write('\n'.join(['knitting', 'eminem', 'MISSING']) + '\n')
     77 
     78     self.init_val = 42
     79 
     80     def _init_val_initializer(shape, dtype=None, partition_info=None):
     81       del dtype, partition_info  # Unused by this unit-testing initializer.
     82       return array_ops.tile(
     83           constant_op.constant([[self.init_val]], dtype=dtypes.float32), shape)
     84 
     85     self.initializer = _init_val_initializer
     86 
     87   def test_load_and_remap_matrix(self):
     88     """Tests the end-to-end loading / remapping of weights."""
     89     # _load_and_remap_matrix() is the generalized wrapper that takes in row and
     90     # column vocabulary files, calls the relevant remappings, and returns the
     91     # weight matrix.  Take this example to be linear multi-class by providing
     92     # both row and column vocabularies.
     93     remapped_matrix = checkpoint_ops._load_and_remap_matrix(
     94         new_row_vocab_file=self.new_feature_vocab_file,
     95         old_row_vocab_file=self.old_feature_vocab_file,
     96         num_rows_to_load=4,
     97         new_col_vocab_file=self.new_class_vocab_file,
     98         old_col_vocab_file=self.old_class_vocab_file,
     99         new_col_vocab_size=4,
    100         old_tensor_name='some_scope/embeddings',
    101         ckpt_path=[self.checkpoint_file],
    102         new_row_vocab_offset=1,
    103         initializer=self.initializer,
    104         num_row_oov_buckets=1,
    105         num_col_oov_buckets=1)
    106 
    107     # [4 in vocab + 1 oov features, 4 in vocab + 1 oov classes].  The offset
    108     # means we read from the first line.
    109     expected_remapped_matrix = np.concatenate(
    110         [
    111             np.reshape([18, 34, 50, self.init_val, self.init_val], [5, 1]),
    112             np.reshape([16, 32, 48, self.init_val, self.init_val], [5, 1]),
    113             np.reshape([self.init_val] * 5, [5, 1]),
    114             np.reshape([17, 33, 49, self.init_val, self.init_val], [5, 1]),
    115             np.reshape([self.init_val] * 5, [5, 1])
    116         ],
    117         axis=1)
    118 
    119     with self.cached_session():
    120       self.assertAllClose(expected_remapped_matrix,
    121                           self.evaluate(remapped_matrix))
    122 
    123   def test_load_and_remap_output_layer_weight_initializer_linear(self):
    124     """Tests for the output layer initializer in the linear multi-class case."""
    125     loading_initializer = (checkpoint_ops._load_and_remap_matrix_initializer(
    126         new_row_vocab_size=5,
    127         new_col_vocab_file=self.new_class_vocab_file,
    128         old_col_vocab_file=self.old_class_vocab_file,
    129         new_col_vocab_size=4,
    130         old_tensor_name='some_scope/embeddings',
    131         ckpt_path=[self.checkpoint_file],
    132         new_row_vocab_file=self.new_feature_vocab_file,
    133         old_row_vocab_file=self.old_feature_vocab_file,
    134         num_row_oov_buckets=1,
    135         num_col_oov_buckets=1,
    136         initializer=self.initializer))
    137 
    138     # The new weight matrix is of size
    139     # [5 feature vocab + 1 feature OOV, 4 class vocab + 1 class OOV].  Use a
    140     # partitioned variable to confirm that the offset logic works.
    141     expected_remapped_matrix = np.concatenate(
    142         [
    143             np.reshape([2, 18, 34, 50, self.init_val, self.init_val], [6, 1]),
    144             np.reshape([0, 16, 32, 48, self.init_val, self.init_val], [6, 1]),
    145             np.reshape([self.init_val] * 6, [6, 1]),
    146             np.reshape([1, 17, 33, 49, self.init_val, self.init_val], [6, 1]),
    147             np.reshape([self.init_val] * 6, [6, 1])
    148         ],
    149         axis=1)
    150     remapped_matrix = variable_scope.get_variable(
    151         name='linear/obtained_weight_matrix',
    152         shape=[6, 5],
    153         initializer=loading_initializer,
    154         partitioner=partitioned_variables.fixed_size_partitioner(2))
    155 
    156     with self.cached_session():
    157       self.evaluate(variables.global_variables_initializer())
    158       self.assertAllClose(expected_remapped_matrix,
    159                           remapped_matrix.as_tensor().eval())
    160 
    161   def test_load_and_remap_output_layer_weight_initializer_dnn_output(self):
    162     """Tests for the output layer initializer in the DNN output case."""
    163     loading_initializer = (checkpoint_ops._load_and_remap_matrix_initializer(
    164         new_row_vocab_size=5,
    165         new_col_vocab_file=self.new_class_vocab_file,
    166         old_col_vocab_file=self.old_class_vocab_file,
    167         new_col_vocab_size=4,
    168         old_tensor_name='some_scope/embeddings',
    169         ckpt_path=[self.checkpoint_file],
    170         num_col_oov_buckets=1,
    171         initializer=self.initializer))
    172 
    173     # The new weight matrix is of size
    174     # [5-sized input layer, 4 class vocab + 1 class OOV].
    175     expected_remapped_matrix = np.concatenate(
    176         [
    177             np.reshape([2, 18, 34, 50, 66], [5, 1]),
    178             np.reshape([0, 16, 32, 48, 64], [5, 1]),
    179             np.reshape([self.init_val] * 5, [5, 1]),
    180             np.reshape([1, 17, 33, 49, 65], [5, 1]),
    181             np.reshape([self.init_val] * 5, [5, 1])
    182         ],
    183         axis=1)
    184     remapped_matrix = variable_scope.get_variable(
    185         name='dnn_output/obtained_weight_matrix',
    186         shape=[5, 5],
    187         initializer=loading_initializer,
    188         partitioner=partitioned_variables.fixed_size_partitioner(2))
    189 
    190     with self.cached_session():
    191       self.evaluate(variables.global_variables_initializer())
    192       self.assertAllClose(expected_remapped_matrix,
    193                           remapped_matrix.as_tensor().eval())
    194 
    195   def test_initializer_with_oov_only_partition(self):
    196     """Tests for the output layer initializer where one partition is all OOV."""
    197     loading_initializer = (checkpoint_ops._load_and_remap_matrix_initializer(
    198         new_row_vocab_size=5,
    199         new_col_vocab_file=self.new_class_vocab_file,
    200         old_col_vocab_file=self.old_class_vocab_file,
    201         new_col_vocab_size=4,
    202         old_tensor_name='some_scope/embeddings',
    203         ckpt_path=[self.checkpoint_file],
    204         new_row_vocab_file=self.new_feature_vocab_file,
    205         old_row_vocab_file=self.old_feature_vocab_file,
    206         num_row_oov_buckets=5,
    207         num_col_oov_buckets=1,
    208         initializer=self.initializer))
    209 
    210     # The new weight matrix is of size
    211     # [5 feature vocab + 5 feature OOV, 4 class vocab + 1 class OOV].  The
    212     # second partition has only OOV.
    213     expected_remapped_matrix = np.concatenate(
    214         [
    215             np.reshape([2, 18, 34, 50] + [self.init_val] * 6, [10, 1]),
    216             np.reshape([0, 16, 32, 48] + [self.init_val] * 6, [10, 1]),
    217             np.reshape([self.init_val] * 10, [10, 1]),
    218             np.reshape([1, 17, 33, 49] + [self.init_val] * 6, [10, 1]),
    219             np.reshape([self.init_val] * 10, [10, 1]),
    220         ],
    221         axis=1)
    222     remapped_matrix = variable_scope.get_variable(
    223         name='linear_all_oov/obtained_weight_matrix',
    224         shape=[10, 5],
    225         initializer=loading_initializer,
    226         partitioner=partitioned_variables.fixed_size_partitioner(2))
    227 
    228     with self.cached_session():
    229       self.evaluate(variables.global_variables_initializer())
    230       self.assertAllClose(expected_remapped_matrix,
    231                           remapped_matrix.as_tensor().eval())
    232 
    233   def test_load_and_remap_linear_multiclass_initializer_default_init(self):
    234     """Tests where the zeros_initializer default is used for linear."""
    235     loading_initializer = (checkpoint_ops._load_and_remap_matrix_initializer(
    236         new_row_vocab_size=5,
    237         new_col_vocab_file=self.new_class_vocab_file,
    238         old_col_vocab_file=self.old_class_vocab_file,
    239         new_col_vocab_size=4,
    240         old_tensor_name='some_scope/embeddings',
    241         ckpt_path=[self.checkpoint_file],
    242         new_row_vocab_file=self.new_feature_vocab_file,
    243         old_row_vocab_file=self.old_feature_vocab_file,
    244         num_row_oov_buckets=1,
    245         num_col_oov_buckets=1))
    246 
    247     # Same as test_initializer_with_oov_only_partition, but with zero
    248     # initialization.
    249     expected_remapped_matrix = np.concatenate(
    250         [
    251             np.reshape([2, 18, 34, 50, 0, 0], [6, 1]),
    252             np.reshape([0, 16, 32, 48, 0, 0], [6, 1]),
    253             np.reshape([0] * 6, [6, 1]),
    254             np.reshape([1, 17, 33, 49, 0, 0], [6, 1]),
    255             np.reshape([0] * 6, [6, 1])
    256         ],
    257         axis=1)
    258     remapped_matrix = variable_scope.get_variable(
    259         name='linear_init_fallback/obtained_weight_matrix',
    260         shape=[6, 5],
    261         initializer=loading_initializer,
    262         partitioner=partitioned_variables.fixed_size_partitioner(2))
    263 
    264     with self.cached_session():
    265       self.evaluate(variables.global_variables_initializer())
    266       self.assertAllClose(expected_remapped_matrix,
    267                           remapped_matrix.as_tensor().eval())
    268 
    269   def test_load_embedding_initializer(self):
    270     """Tests for the load_embedding_initializer wrapper."""
    271     embedding_loading_initializer = (checkpoint_ops._load_embedding_initializer(
    272         new_vocab_file=self.new_feature_vocab_file,
    273         old_vocab_file=self.old_feature_vocab_file,
    274         new_vocab_size=5,
    275         embedding_dim=16,
    276         embedding_tensor_name='some_scope/embeddings',
    277         ckpt_path=[self.checkpoint_file],
    278         num_oov_buckets=1,
    279         initializer=self.initializer))
    280 
    281     # The new weight matrix is of size
    282     # [5 feature vocab + 1 feature OOV, 16 (embedding dimension)], where the
    283     # last vocab row (2nd last row) is newly initialized (wasn't found in
    284     # previous vocab) and the actual last row is OOV and also newly initialized.
    285     # Use a partitioned variable to confirm that the offset logic works.
    286     expected_remapped_embeddings = np.concatenate(
    287         [
    288             np.reshape(range(64), [4, 16]),
    289             np.reshape([self.init_val] * 32, [2, 16]),
    290         ],
    291         axis=0)
    292     remapped_embeddings = variable_scope.get_variable(
    293         name='embedding/obtained_embedding_matrix',
    294         shape=[6, 16],
    295         initializer=embedding_loading_initializer,
    296         partitioner=partitioned_variables.fixed_size_partitioner(2))
    297 
    298     with self.cached_session():
    299       self.evaluate(variables.global_variables_initializer())
    300       self.assertAllClose(expected_remapped_embeddings,
    301                           remapped_embeddings.as_tensor().eval())
    302 
    303   def test_load_embedding_initializer_large_oov(self):
    304     """Tests for the large OOV case for load_embedding_initializer wrapper."""
    305     self.new_feature_vocab_file = os.path.join(
    306         self.get_temp_dir(), 'new_feature_vocab.txt')
    307     with open(self.new_feature_vocab_file, 'w') as f:
    308       f.write('\n'.join(['one', 'zero', 'two', 'four']) + '\n')
    309 
    310     # Checkpoint has 5 entries, 3 of which correspond to OOV.
    311     self.old_feature_vocab_file = os.path.join(
    312         self.get_temp_dir(), 'old_feature_vocab.txt')
    313     with open(self.old_feature_vocab_file, 'w') as f:
    314       f.write('\n'.join(['zero', 'one']) + '\n')
    315 
    316     embedding_loading_initializer = (checkpoint_ops._load_embedding_initializer(
    317         new_vocab_file=self.new_feature_vocab_file,
    318         old_vocab_file=self.old_feature_vocab_file,
    319         new_vocab_size=4,
    320         embedding_dim=16,
    321         embedding_tensor_name='some_scope/embeddings',
    322         ckpt_path=[self.checkpoint_file],
    323         num_oov_buckets=5,
    324         initializer=self.initializer))
    325 
    326     # The new weight matrix is of size
    327     # [4 feature vocab + 5 feature OOV, 16 (embedding dimension)], where the
    328     # 3rd and 4th rows are not found in the old vocabulary and therefore newly
    329     # initialized.  The last five rows are OOV and also newly initialized.
    330     # Use a partitioned variable to confirm that the offset logic works.
    331     expected_remapped_embeddings = np.concatenate(
    332         [
    333             np.reshape(range(16, 32), [1, 16]),
    334             np.reshape(range(16), [1, 16]),
    335             np.reshape([self.init_val] * 112, [7, 16]),
    336         ],
    337         axis=0)
    338     remapped_embeddings = variable_scope.get_variable(
    339         name='embedding/obtained_embedding_matrix',
    340         shape=[9, 16],
    341         initializer=embedding_loading_initializer,
    342         partitioner=partitioned_variables.fixed_size_partitioner(2))
    343 
    344     with self.cached_session():
    345       self.evaluate(variables.global_variables_initializer())
    346       self.assertAllClose(expected_remapped_embeddings,
    347                           remapped_embeddings.as_tensor().eval())
    348 
    349   def test_load_embedding_initializer_old_row_vocab(self):
    350     """Tests for load_embedding_initializer where we constrain old vocab."""
    351     embedding_loading_initializer = (
    352         checkpoint_ops._load_embedding_initializer(
    353             new_vocab_file=self.new_feature_vocab_file,
    354             old_vocab_file=self.old_feature_vocab_file,
    355             # Considered old vocabulary becomes ['zero', 'one', 'two'].  This
    356             # means 'three' in the new vocabulary is newly initialized.
    357             old_vocab_size=3,
    358             new_vocab_size=5,
    359             embedding_dim=16,
    360             embedding_tensor_name='some_scope/embeddings',
    361             ckpt_path=[self.checkpoint_file],
    362             num_oov_buckets=1,
    363             initializer=self.initializer))
    364 
    365     # The new weight matrix is of size
    366     # [5 feature vocab + 1 feature OOV, 16 (embedding dimension)], where the
    367     # last vocab row (2nd last row) is newly initialized (wasn't found in
    368     # previous vocab) and the actual last row is OOV and also newly initialized.
    369     # Use a partitioned variable to confirm that the offset logic works.
    370     expected_remapped_embeddings = np.concatenate(
    371         [
    372             np.reshape(range(48), [3, 16]),
    373             np.reshape([self.init_val] * 48, [3, 16]),
    374         ],
    375         axis=0)
    376     remapped_embeddings = variable_scope.get_variable(
    377         name='embedding/obtained_embedding_matrix',
    378         shape=[6, 16],
    379         initializer=embedding_loading_initializer,
    380         partitioner=partitioned_variables.fixed_size_partitioner(2))
    381 
    382     with self.cached_session():
    383       self.evaluate(variables.global_variables_initializer())
    384       self.assertAllClose(expected_remapped_embeddings,
    385                           remapped_embeddings.as_tensor().eval())
    386 
    387 if __name__ == '__main__':
    388   test.main()
    389