prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Ensemble decoding is broken
  @flauted 
  Ensemble decoding is not tested I think and it is broken on master.
  onmt/decoders/ensemble.py", line 133, in load_test_model
      if field is not None and 'vocab' in field.__dict__:
  AttributeError: 'list' object has no attribute '__dict__'
  
  To replicate, just run the translate command with the same model twice in argument -model x x
  
  Thanks.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `onmt/decoders/ensemble.py`
  Content:
  ```
  1 """
  2 Ensemble decoding.
  3 
  4 Decodes using multiple models simultaneously,
  5 combining their prediction distributions by averaging.
  6 All models in the ensemble must share a target vocabulary.
  7 """
  8 
  9 import torch
  10 import torch.nn as nn
  11 
  12 from onmt.encoders.encoder import EncoderBase
  13 from onmt.models import NMTModel
  14 import onmt.model_builder
  15 
  16 
  17 class EnsembleDecoderOutput(object):
  18     """ Wrapper around multiple decoder final hidden states """
  19     def __init__(self, model_dec_outs):
  20         self.model_dec_outs = tuple(model_dec_outs)
  21 
  22     def squeeze(self, dim=None):
  23         """
  24         Delegate squeeze to avoid modifying
  25         :obj:`Translator.translate_batch()`
  26         """
  27         return EnsembleDecoderOutput([
  28             x.squeeze(dim) for x in self.model_dec_outs])
  29 
  30     def __getitem__(self, index):
  31         return self.model_dec_outs[index]
  32 
  33 
  34 class EnsembleEncoder(EncoderBase):
  35     """ Dummy Encoder that delegates to individual real Encoders """
  36     def __init__(self, model_encoders):
  37         super(EnsembleEncoder, self).__init__()
  38         self.model_encoders = nn.ModuleList(model_encoders)
  39 
  40     def forward(self, src, lengths=None):
  41         enc_hidden, memory_bank, _ = zip(*[
  42             model_encoder(src, lengths)
  43             for model_encoder in self.model_encoders])
  44         return enc_hidden, memory_bank, lengths
  45 
  46 
  47 class EnsembleDecoder(nn.Module):
  48     """ Dummy Decoder that delegates to individual real Decoders """
  49     def __init__(self, model_decoders):
  50         super(EnsembleDecoder, self).__init__()
  51         self.model_decoders = nn.ModuleList(model_decoders)
  52 
  53     def forward(self, tgt, memory_bank, memory_lengths=None, step=None):
  54         """ See :obj:`RNNDecoderBase.forward()` """
  55         # Memory_lengths is a single tensor shared between all models.
  56         # This assumption will not hold if Translator is modified
  57         # to calculate memory_lengths as something other than the length
  58         # of the input.
  59         dec_outs, attns = zip(*[
  60             model_decoder(
  61                 tgt, memory_bank[i], memory_lengths, step=step)
  62             for i, model_decoder in enumerate(self.model_decoders)])
  63         mean_attns = self.combine_attns(attns)
  64         return EnsembleDecoderOutput(dec_outs), mean_attns
  65 
  66     def combine_attns(self, attns):
  67         result = {}
  68         for key in attns[0].keys():
  69             result[key] = torch.stack([attn[key] for attn in attns]).mean(0)
  70         return result
  71 
  72     def init_state(self, src, memory_bank, enc_hidden):
  73         """ See :obj:`RNNDecoderBase.init_state()` """
  74         for i, model_decoder in enumerate(self.model_decoders):
  75             model_decoder.init_state(src, memory_bank[i], enc_hidden[i])
  76 
  77     def map_state(self, fn):
  78         for model_decoder in self.model_decoders:
  79             model_decoder.map_state(fn)
  80 
  81 
  82 class EnsembleGenerator(nn.Module):
  83     """
  84     Dummy Generator that delegates to individual real Generators,
  85     and then averages the resulting target distributions.
  86     """
  87     def __init__(self, model_generators, raw_probs=False):
  88         super(EnsembleGenerator, self).__init__()
  89         self.model_generators = nn.ModuleList(model_generators)
  90         self._raw_probs = raw_probs
  91 
  92     def forward(self, hidden, attn=None, src_map=None):
  93         """
  94         Compute a distribution over the target dictionary
  95         by averaging distributions from models in the ensemble.
  96         All models in the ensemble must share a target vocabulary.
  97         """
  98         distributions = torch.stack(
  99                 [mg(h) if attn is None else mg(h, attn, src_map)
  100                  for h, mg in zip(hidden, self.model_generators)]
  101             )
  102         if self._raw_probs:
  103             return torch.log(torch.exp(distributions).mean(0))
  104         else:
  105             return distributions.mean(0)
  106 
  107 
  108 class EnsembleModel(NMTModel):
  109     """ Dummy NMTModel wrapping individual real NMTModels """
  110     def __init__(self, models, raw_probs=False):
  111         encoder = EnsembleEncoder(model.encoder for model in models)
  112         decoder = EnsembleDecoder(model.decoder for model in models)
  113         super(EnsembleModel, self).__init__(encoder, decoder)
  114         self.generator = EnsembleGenerator(
  115             [model.generator for model in models], raw_probs)
  116         self.models = nn.ModuleList(models)
  117 
  118 
  119 def load_test_model(opt):
  120     """ Read in multiple models for ensemble """
  121     shared_fields = None
  122     shared_model_opt = None
  123     models = []
  124     for model_path in opt.models:
  125         fields, model, model_opt = \
  126             onmt.model_builder.load_test_model(opt, model_path=model_path)
  127         if shared_fields is None:
  128             shared_fields = fields
  129         else:
  130             for key, field in fields.items():
  131                 if field is not None and 'vocab' in field.__dict__:
  132                     assert field.vocab.stoi == shared_fields[key].vocab.stoi, \
  133                         'Ensemble models must use the same preprocessed data'
  134         models.append(model)
  135         if shared_model_opt is None:
  136             shared_model_opt = model_opt
  137     ensemble_model = EnsembleModel(models, opt.avg_raw_probs)
  138     return shared_fields, ensemble_model, shared_model_opt
  139
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/onmt/decoders/ensemble.py b/onmt/decoders/ensemble.py
  --- a/onmt/decoders/ensemble.py
  +++ b/onmt/decoders/ensemble.py
  @@ -58,7 +58,8 @@
           # of the input.
           dec_outs, attns = zip(*[
               model_decoder(
  -                tgt, memory_bank[i], memory_lengths, step=step)
  +                tgt, memory_bank[i],
  +                memory_lengths=memory_lengths, step=step)
               for i, model_decoder in enumerate(self.model_decoders)])
           mean_attns = self.combine_attns(attns)
           return EnsembleDecoderOutput(dec_outs), mean_attns
  @@ -130,9 +131,25 @@
               shared_fields = fields
           else:
               for key, field in fields.items():
  -                if field is not None and 'vocab' in field.__dict__:
  -                    assert field.vocab.stoi == shared_fields[key].vocab.stoi, \
  -                        'Ensemble models must use the same preprocessed data'
  +                assert len(field) == 1
  +                n, f = field[0]
  +                try:
  +                    f_iter = iter(f)
  +                except TypeError:
  +                    f_iter = [(n, f)]
  +                for sn, sf in f_iter:
  +                    if sf is not None and 'vocab' in sf.__dict__:
  +                        sh_field = shared_fields[key]
  +                        assert len(sh_field) == 1
  +                        sh_n, sh_f = sh_field[0]
  +                        try:
  +                            sh_f_iter = iter(sh_f)
  +                        except TypeError:
  +                            sh_f_iter = [(sh_n, sh_f)]
  +                        sh_f_dict = dict(sh_f_iter)
  +                        assert sf.vocab.stoi == sh_f_dict[sn].vocab.stoi, \
  +                            "Ensemble models must use the same " \
  +                            "preprocessed data"
           models.append(model)
           if shared_model_opt is None:
               shared_model_opt = model_opt
  
verification_info: {"golden_diff": "diff --git a/onmt/decoders/ensemble.py b/onmt/decoders/ensemble.py\n--- a/onmt/decoders/ensemble.py\n+++ b/onmt/decoders/ensemble.py\n@@ -58,7 +58,8 @@\n         # of the input.\n         dec_outs, attns = zip(*[\n             model_decoder(\n-                tgt, memory_bank[i], memory_lengths, step=step)\n+                tgt, memory_bank[i],\n+                memory_lengths=memory_lengths, step=step)\n             for i, model_decoder in enumerate(self.model_decoders)])\n         mean_attns = self.combine_attns(attns)\n         return EnsembleDecoderOutput(dec_outs), mean_attns\n@@ -130,9 +131,25 @@\n             shared_fields = fields\n         else:\n             for key, field in fields.items():\n-                if field is not None and 'vocab' in field.__dict__:\n-                    assert field.vocab.stoi == shared_fields[key].vocab.stoi, \\\n-                        'Ensemble models must use the same preprocessed data'\n+                assert len(field) == 1\n+                n, f = field[0]\n+                try:\n+                    f_iter = iter(f)\n+                except TypeError:\n+                    f_iter = [(n, f)]\n+                for sn, sf in f_iter:\n+                    if sf is not None and 'vocab' in sf.__dict__:\n+                        sh_field = shared_fields[key]\n+                        assert len(sh_field) == 1\n+                        sh_n, sh_f = sh_field[0]\n+                        try:\n+                            sh_f_iter = iter(sh_f)\n+                        except TypeError:\n+                            sh_f_iter = [(sh_n, sh_f)]\n+                        sh_f_dict = dict(sh_f_iter)\n+                        assert sf.vocab.stoi == sh_f_dict[sn].vocab.stoi, \\\n+                            \"Ensemble models must use the same \" \\\n+                            \"preprocessed data\"\n         models.append(model)\n         if shared_model_opt is None:\n             shared_model_opt = model_opt\n", "issue": "Ensemble decoding is broken\n@flauted \r\nEnsemble decoding is not tested I think and it is broken on master.\r\nonmt/decoders/ensemble.py\", line 133, in load_test_model\r\n    if field is not None and 'vocab' in field.__dict__:\r\nAttributeError: 'list' object has no attribute '__dict__'\r\n\r\nTo replicate, just run the translate command with the same model twice in argument -model x x\r\n\r\nThanks.\n", "before_files": [{"content": "\"\"\"\nEnsemble decoding.\n\nDecodes using multiple models simultaneously,\ncombining their prediction distributions by averaging.\nAll models in the ensemble must share a target vocabulary.\n\"\"\"\n\nimport torch\nimport torch.nn as nn\n\nfrom onmt.encoders.encoder import EncoderBase\nfrom onmt.models import NMTModel\nimport onmt.model_builder\n\n\nclass EnsembleDecoderOutput(object):\n    \"\"\" Wrapper around multiple decoder final hidden states \"\"\"\n    def __init__(self, model_dec_outs):\n        self.model_dec_outs = tuple(model_dec_outs)\n\n    def squeeze(self, dim=None):\n        \"\"\"\n        Delegate squeeze to avoid modifying\n        :obj:`Translator.translate_batch()`\n        \"\"\"\n        return EnsembleDecoderOutput([\n            x.squeeze(dim) for x in self.model_dec_outs])\n\n    def __getitem__(self, index):\n        return self.model_dec_outs[index]\n\n\nclass EnsembleEncoder(EncoderBase):\n    \"\"\" Dummy Encoder that delegates to individual real Encoders \"\"\"\n    def __init__(self, model_encoders):\n        super(EnsembleEncoder, self).__init__()\n        self.model_encoders = nn.ModuleList(model_encoders)\n\n    def forward(self, src, lengths=None):\n        enc_hidden, memory_bank, _ = zip(*[\n            model_encoder(src, lengths)\n            for model_encoder in self.model_encoders])\n        return enc_hidden, memory_bank, lengths\n\n\nclass EnsembleDecoder(nn.Module):\n    \"\"\" Dummy Decoder that delegates to individual real Decoders \"\"\"\n    def __init__(self, model_decoders):\n        super(EnsembleDecoder, self).__init__()\n        self.model_decoders = nn.ModuleList(model_decoders)\n\n    def forward(self, tgt, memory_bank, memory_lengths=None, step=None):\n        \"\"\" See :obj:`RNNDecoderBase.forward()` \"\"\"\n        # Memory_lengths is a single tensor shared between all models.\n        # This assumption will not hold if Translator is modified\n        # to calculate memory_lengths as something other than the length\n        # of the input.\n        dec_outs, attns = zip(*[\n            model_decoder(\n                tgt, memory_bank[i], memory_lengths, step=step)\n            for i, model_decoder in enumerate(self.model_decoders)])\n        mean_attns = self.combine_attns(attns)\n        return EnsembleDecoderOutput(dec_outs), mean_attns\n\n    def combine_attns(self, attns):\n        result = {}\n        for key in attns[0].keys():\n            result[key] = torch.stack([attn[key] for attn in attns]).mean(0)\n        return result\n\n    def init_state(self, src, memory_bank, enc_hidden):\n        \"\"\" See :obj:`RNNDecoderBase.init_state()` \"\"\"\n        for i, model_decoder in enumerate(self.model_decoders):\n            model_decoder.init_state(src, memory_bank[i], enc_hidden[i])\n\n    def map_state(self, fn):\n        for model_decoder in self.model_decoders:\n            model_decoder.map_state(fn)\n\n\nclass EnsembleGenerator(nn.Module):\n    \"\"\"\n    Dummy Generator that delegates to individual real Generators,\n    and then averages the resulting target distributions.\n    \"\"\"\n    def __init__(self, model_generators, raw_probs=False):\n        super(EnsembleGenerator, self).__init__()\n        self.model_generators = nn.ModuleList(model_generators)\n        self._raw_probs = raw_probs\n\n    def forward(self, hidden, attn=None, src_map=None):\n        \"\"\"\n        Compute a distribution over the target dictionary\n        by averaging distributions from models in the ensemble.\n        All models in the ensemble must share a target vocabulary.\n        \"\"\"\n        distributions = torch.stack(\n                [mg(h) if attn is None else mg(h, attn, src_map)\n                 for h, mg in zip(hidden, self.model_generators)]\n            )\n        if self._raw_probs:\n            return torch.log(torch.exp(distributions).mean(0))\n        else:\n            return distributions.mean(0)\n\n\nclass EnsembleModel(NMTModel):\n    \"\"\" Dummy NMTModel wrapping individual real NMTModels \"\"\"\n    def __init__(self, models, raw_probs=False):\n        encoder = EnsembleEncoder(model.encoder for model in models)\n        decoder = EnsembleDecoder(model.decoder for model in models)\n        super(EnsembleModel, self).__init__(encoder, decoder)\n        self.generator = EnsembleGenerator(\n            [model.generator for model in models], raw_probs)\n        self.models = nn.ModuleList(models)\n\n\ndef load_test_model(opt):\n    \"\"\" Read in multiple models for ensemble \"\"\"\n    shared_fields = None\n    shared_model_opt = None\n    models = []\n    for model_path in opt.models:\n        fields, model, model_opt = \\\n            onmt.model_builder.load_test_model(opt, model_path=model_path)\n        if shared_fields is None:\n            shared_fields = fields\n        else:\n            for key, field in fields.items():\n                if field is not None and 'vocab' in field.__dict__:\n                    assert field.vocab.stoi == shared_fields[key].vocab.stoi, \\\n                        'Ensemble models must use the same preprocessed data'\n        models.append(model)\n        if shared_model_opt is None:\n            shared_model_opt = model_opt\n    ensemble_model = EnsembleModel(models, opt.avg_raw_probs)\n    return shared_fields, ensemble_model, shared_model_opt\n", "path": "onmt/decoders/ensemble.py"}], "after_files": [{"content": "\"\"\"\nEnsemble decoding.\n\nDecodes using multiple models simultaneously,\ncombining their prediction distributions by averaging.\nAll models in the ensemble must share a target vocabulary.\n\"\"\"\n\nimport torch\nimport torch.nn as nn\n\nfrom onmt.encoders.encoder import EncoderBase\nfrom onmt.models import NMTModel\nimport onmt.model_builder\n\n\nclass EnsembleDecoderOutput(object):\n    \"\"\" Wrapper around multiple decoder final hidden states \"\"\"\n    def __init__(self, model_dec_outs):\n        self.model_dec_outs = tuple(model_dec_outs)\n\n    def squeeze(self, dim=None):\n        \"\"\"\n        Delegate squeeze to avoid modifying\n        :obj:`Translator.translate_batch()`\n        \"\"\"\n        return EnsembleDecoderOutput([\n            x.squeeze(dim) for x in self.model_dec_outs])\n\n    def __getitem__(self, index):\n        return self.model_dec_outs[index]\n\n\nclass EnsembleEncoder(EncoderBase):\n    \"\"\" Dummy Encoder that delegates to individual real Encoders \"\"\"\n    def __init__(self, model_encoders):\n        super(EnsembleEncoder, self).__init__()\n        self.model_encoders = nn.ModuleList(model_encoders)\n\n    def forward(self, src, lengths=None):\n        enc_hidden, memory_bank, _ = zip(*[\n            model_encoder(src, lengths)\n            for model_encoder in self.model_encoders])\n        return enc_hidden, memory_bank, lengths\n\n\nclass EnsembleDecoder(nn.Module):\n    \"\"\" Dummy Decoder that delegates to individual real Decoders \"\"\"\n    def __init__(self, model_decoders):\n        super(EnsembleDecoder, self).__init__()\n        self.model_decoders = nn.ModuleList(model_decoders)\n\n    def forward(self, tgt, memory_bank, memory_lengths=None, step=None):\n        \"\"\" See :obj:`RNNDecoderBase.forward()` \"\"\"\n        # Memory_lengths is a single tensor shared between all models.\n        # This assumption will not hold if Translator is modified\n        # to calculate memory_lengths as something other than the length\n        # of the input.\n        dec_outs, attns = zip(*[\n            model_decoder(\n                tgt, memory_bank[i],\n                memory_lengths=memory_lengths, step=step)\n            for i, model_decoder in enumerate(self.model_decoders)])\n        mean_attns = self.combine_attns(attns)\n        return EnsembleDecoderOutput(dec_outs), mean_attns\n\n    def combine_attns(self, attns):\n        result = {}\n        for key in attns[0].keys():\n            result[key] = torch.stack([attn[key] for attn in attns]).mean(0)\n        return result\n\n    def init_state(self, src, memory_bank, enc_hidden):\n        \"\"\" See :obj:`RNNDecoderBase.init_state()` \"\"\"\n        for i, model_decoder in enumerate(self.model_decoders):\n            model_decoder.init_state(src, memory_bank[i], enc_hidden[i])\n\n    def map_state(self, fn):\n        for model_decoder in self.model_decoders:\n            model_decoder.map_state(fn)\n\n\nclass EnsembleGenerator(nn.Module):\n    \"\"\"\n    Dummy Generator that delegates to individual real Generators,\n    and then averages the resulting target distributions.\n    \"\"\"\n    def __init__(self, model_generators, raw_probs=False):\n        super(EnsembleGenerator, self).__init__()\n        self.model_generators = nn.ModuleList(model_generators)\n        self._raw_probs = raw_probs\n\n    def forward(self, hidden, attn=None, src_map=None):\n        \"\"\"\n        Compute a distribution over the target dictionary\n        by averaging distributions from models in the ensemble.\n        All models in the ensemble must share a target vocabulary.\n        \"\"\"\n        distributions = torch.stack(\n                [mg(h) if attn is None else mg(h, attn, src_map)\n                 for h, mg in zip(hidden, self.model_generators)]\n            )\n        if self._raw_probs:\n            return torch.log(torch.exp(distributions).mean(0))\n        else:\n            return distributions.mean(0)\n\n\nclass EnsembleModel(NMTModel):\n    \"\"\" Dummy NMTModel wrapping individual real NMTModels \"\"\"\n    def __init__(self, models, raw_probs=False):\n        encoder = EnsembleEncoder(model.encoder for model in models)\n        decoder = EnsembleDecoder(model.decoder for model in models)\n        super(EnsembleModel, self).__init__(encoder, decoder)\n        self.generator = EnsembleGenerator(\n            [model.generator for model in models], raw_probs)\n        self.models = nn.ModuleList(models)\n\n\ndef load_test_model(opt, dummy_opt):\n    \"\"\" Read in multiple models for ensemble \"\"\"\n    shared_fields = None\n    shared_model_opt = None\n    models = []\n    for model_path in opt.models:\n        fields, model, model_opt = \\\n            onmt.model_builder.load_test_model(opt,\n                                               dummy_opt,\n                                               model_path=model_path)\n        if shared_fields is None:\n            shared_fields = fields\n        else:\n            for key, field in fields.items():\n                assert len(field) == 1\n                n, f = field[0]\n                try:\n                    f_iter = iter(f)\n                except TypeError:\n                    f_iter = [(n, f)]\n                for sn, sf in f_iter:\n                    if sf is not None and 'vocab' in sf.__dict__:\n                        sh_field = shared_fields[key]\n                        assert len(sh_field) == 1\n                        sh_n, sh_f = sh_field[0]\n                        try:\n                            sh_f_iter = iter(sh_f)\n                        except TypeError:\n                            sh_f_iter = [(sh_n, sh_f)]\n                        sh_f_dict = dict(sh_f_iter)\n                        assert sf.vocab.stoi == sh_f_dict[sn].vocab.stoi, \\\n                            \"Ensemble models must use the same \" \\\n                            \"preprocessed data\"\n        models.append(model)\n        if shared_model_opt is None:\n            shared_model_opt = model_opt\n    ensemble_model = EnsembleModel(models, opt.avg_raw_probs)\n    return shared_fields, ensemble_model, shared_model_opt\n", "path": "onmt/decoders/ensemble.py"}]}