class_docstr: This criterion computes losses from input (monolingual data in
  translation) with two components:
  1. Reconstruction loss:
  2. LM loss:
  The total loss is a weighted sum of these two components.
class_docstr_tok: ["This","criterion","computes","losses","from","input","(","monolingual","data","in","translation",")","with","two","components",":","1",".","Reconstruction","loss",":","2",".","LM","loss",":","The","total","loss","is","a","weighted","sum","of","these","two","components","."]
docstr: Compute the reconstruction and LM loss from forward and backward
  models.
  
  Args:
      sample: original input.
      hypos: psudo labels generated by the forward model. They are used
          as approximation of the target space to do importance sampling.
      forward_model: the model used to generate psuedo labels.
      backward_model: the model to reconstruct original input using
          psuedo labels.
      lm_scorer: an LM model eval mode to score psuedo labels in target
          space.
docstr_tok: ["Compute","the","reconstruction","and","LM","loss","from","forward","and","backward","models",".","Args",":","sample",":","original","input",".","hypos",":","psudo","labels","generated","by","the","forward","model",".","They","are","used","as","approximation","of","the","target","space","to","do","importance","sampling",".","forward_model",":","the","model","used","to","generate","psuedo","labels",".","backward_model",":","the","model","to","reconstruct","original","input","using","psuedo","labels",".","lm_scorer",":","an","LM","model","eval","mode","to","score","psuedo","labels","in","target","space","."]
code: def forward(
      self,
      sample,
      forward_model,
      forward_optimizer,
      tgt_dict,
      backward_model,
      backward_optimizer,
      src_dict,
      lm_scorer=None,
      reduce=True,
      **generate_kwargs,
  ):
      """Compute the reconstruction and LM loss from forward and backward
      models.
  
      Args:
          sample: original input.
          hypos: psudo labels generated by the forward model. They are used
              as approximation of the target space to do importance sampling.
          forward_model: the model used to generate psuedo labels.
          backward_model: the model to reconstruct original input using
              psuedo labels.
          lm_scorer: an LM model eval mode to score psuedo labels in target
              space.
      """
      # Generate translations
      nbest_translations = self._generate_translation(
          forward_model,
          tgt_dict,
          sample,
          self.args.beam,
          **generate_kwargs,
      )
  
      forward_samples = []
      backward_samples = {}
      # TODO (T36875783): load pretrained lm to score
      lm_score = 0.0
      for sample_id, src_processed, tgt_hypos in nbest_translations:
          # compute each model's reward
          forward_reward = lm_score
          # construct the sample; compute the ce loss
          # backward_samples need to handle EOS
          src = self._maybe_reverse_source(src_processed)
          src = self._maybe_add_eos(src, src_dict.eos())
          assert len(tgt_hypos) == self.args.beam
          for tgt_hypo_i, tgt_hypo_struct in enumerate(tgt_hypos):
              dual_sample_id = (
                  sample_id.item() * self.args.beam + tgt_hypo_i
              )
              tgt_hypo = tgt_hypo_struct["tokens"]
              # add EOS to the target, i.e. original source, since it'll be used
              # as target
              # remove EOS in the src is optional
              if self.remove_eos_at_src:
                  tgt_hypo = tgt_hypo[:-1]
              tgt_hypo_processed = self._maybe_reverse_source(tgt_hypo)
  
              backward_sample = {
                  "id": dual_sample_id,
                  "source": tgt_hypo_processed.cpu(),
                  "target": src.cpu(),
                  "weight": 1.0 - self.alpha,
              }
              assert dual_sample_id not in backward_samples
              backward_samples[dual_sample_id] = backward_sample
  
      bwd_model_input = utils.move_to_cuda(
          WeightedLanguagePairDataset.collate(
              samples=list(backward_samples.values()),
              pad_idx=src_dict.pad(),
              eos_idx=src_dict.eos(),
          )
      )
      reconstructed_source = self._generate_translation(
          backward_model,
          src_dict,
          bwd_model_input,
          1,
          **generate_kwargs,
      )
      for (
          dual_sample_id,
          tgt_hypo_processed,
          src_hypos,
      ) in reconstructed_source:
          backward_sample = backward_samples[dual_sample_id.item()]
          src = backward_sample["target"]
          tgt_hypo = self._maybe_reverse_source(tgt_hypo_processed)
  
          # use bleu score as reward
          scorer = bleu.Scorer(
              bleu.BleuConfig(
                  pad=src_dict.pad(),
                  eos=src_dict.eos(),
                  unk=src_dict.unk(),
              )
          )
          assert len(src_hypos) == 1
          src_hypo = src_hypos[0]["tokens"][:-1]
          scorer.add(src.int().cpu(), src_hypo.int().cpu())
          backward_reward = (
              scorer.score(order=self.args.reconstruction_bleu_order)
              / 100.0
          )
  
          original_stc = " ".join(src_dict[tid] for tid in src.tolist())
          translated_stc = " ".join(tgt_dict[tid] for tid in tgt_hypo)
          recon_stc = " ".join(
              src_dict[tid] for tid in src_hypo.tolist()
          )
  
          if int(dual_sample_id / self.args.beam) % 100 == 0:
              print("--------")
              print(
                  "original sentence:",
                  original_stc.replace(
                      self.args.source_bpe_end_marker, ""
                  ),
              )
              print(
                  "translated sentence:",
                  translated_stc.replace(
                      self.args.source_bpe_end_marker, ""
                  ),
              )
              print(
                  "reconstructed sentence:",
                  recon_stc.replace(
                      self.args.source_bpe_end_marker, ""
                  ),
              )
              print("reward:", backward_reward)
              print("--------")
  
          total_reward = (
              self.alpha * forward_reward
              + (1.0 - self.alpha) * backward_reward
          )
          src_processed = self._maybe_reverse_source(src)
          tgt_hypo = self._maybe_add_eos(tgt_hypo, tgt_dict.eos())
          forward_samples.append(
              {
                  "id": dual_sample_id,
                  "source": src_processed.cpu(),
                  "target": tgt_hypo.cpu(),  # first hypo is best hypo
                  "weight": total_reward,
              }
          )
  
      # Now combine pseudo labelled examples to corresponding batch with
      # rewards factored to weighting of each task's loss
      agg_loss, agg_sample_size, agg_logging_output = 0.0, 0.0, {}
      forward_model.train()
      forward_loss, sample_size, logging_output = self.task.criterion(
          forward_model,
          utils.move_to_cuda(
              WeightedLanguagePairDataset.collate(
                  samples=forward_samples,
                  pad_idx=tgt_dict.pad(),
                  eos_idx=tgt_dict.eos(),
              )
          ),
      )
      agg_loss += forward_loss.detach().item()
      agg_sample_size += sample_size
      agg_logging_output["primal"] = logging_output
      # grad would be further scaled when passed back to trainer,
      # which will do the update
      forward_optimizer.backward(forward_loss)
  
      backward_model.train()
      backward_loss, sample_size, logging_output = self.task.criterion(
          backward_model, bwd_model_input
      )
  
      agg_loss += backward_loss.data.item()
      agg_sample_size += sample_size
      agg_logging_output["dual"] = logging_output
      backward_optimizer.backward(backward_loss)
      return agg_loss, agg_sample_size, agg_logging_output
  
file_path: translate/pytorch_translate/dual_learning/dual_learning_criterion.py
imports: import math
  import torch
  from fairseq import utils
  from fairseq.criterions import LegacyFairseqCriterion, register_criterion
  from fairseq.scoring import bleu
  from pytorch_translate import beam_decode
  from pytorch_translate.data.weighted_data import WeightedLanguagePairDataset
label_desc: Use image node_id 5 for calling the UnsupervisedCriterion obj's underlying member method code with example usage: obj.forward(sample, forward_model, forward_optimizer, tgt_dict, backward_model, backward_optimizer, src_dict, lm_scorer, reduce) and returns: agg_loss,  agg_sample_size,  agg_logging_output