  
      def print_loss(self, loss):
          """ Override for specific model loss formatting """
          logger.trace(loss)
          output = [", ".join(["{}_{}: {:.5f}".format(self.model.state.loss_names[side][idx],
                                                      side.capitalize(),
                                                      this_loss)
                               for idx, this_loss in enumerate(loss[side])])
                    for side in sorted(list(loss.keys()))]
          output = ", ".join(output)
          print("[{}] [#{:05d}] {}".format(self.timestamp, self.model.iterations, output), end='\r')
  
      def train_one_step(self, viewer, timelapse_kwargs):
 
</s> remove         return tuple(out[0])
 </s> add         return [tuple(shape) for shape in out]

    @property
    def output_shape(self):
        """ The output shape of the model (shape of largest face output) """
        return self.output_shapes[self.largest_face_index]

    @property
    def largest_face_index(self):
        """ Return the index from model.outputs of the largest face
            Required for multi-output model prediction. The largest face
            is assumed to be the final output
        """
        sizes = [shape[1] for shape in self.output_shapes if shape[2] == 3]
        if not sizes:
            return None
        max_face = max(sizes)
        retval = [idx for idx, shape in enumerate(self.output_shapes)
                  if shape[1] == max_face and shape[2] == 3][0]
        logger.debug(retval)
        return retval

    @property
    def largest_mask_index(self):
        """ Return the index from model.outputs of the largest mask
            Required for multi-output model prediction. The largest face
            is assumed to be the final output
        """
        sizes = [shape[1] for shape in self.output_shapes if shape[2] == 1]
        if not sizes:
            return None
        max_mask = max(sizes)
        retval = [idx for idx, shape in enumerate(self.output_shapes)
                  if shape[1] == max_mask and shape[2] == 1][0]
        logger.debug(retval)
        return retval </s> remove         logger.debug(output_names)
 </s> add         logger.debug("Overridden output_names: %s", output_names) </s> remove         if self.is_output and not any(name == "face_out" for name in output_names):
 </s> add         if self.is_output and not any(name.startswith("face_out") for name in output_names): </s> remove     def output_shape(self):
 </s> add     def output_shapes(self): </s> remove     def loss_function(self, mask, side, initialize):
        """ Set the loss function
            Side is input so we only log once """
        if self.config.get("dssim_loss", False):
            if side == "a" and not self.predict and initialize:
                logger.verbose("Using DSSIM Loss")
            loss_func = DSSIMObjective()
        else:
            if side == "a" and not self.predict and initialize:
                logger.verbose("Using Mean Absolute Error Loss")
            loss_func = losses.mean_absolute_error

        if mask and self.config.get("penalized_mask_loss", False):
            loss_mask = mask[0]
            if side == "a" and not self.predict and initialize:
                logger.verbose("Penalizing mask for Loss")
            loss_func = PenalizedLoss(loss_mask, loss_func)
        return loss_func

    def mask_loss_function(self, side, initialize):
        """ Set the mask loss function
            Side is input so we only log once """
        if side == "a" and not self.predict and initialize:
            logger.verbose("Using Mean Squared Error Loss for mask")
        mask_loss_func = losses.mean_squared_error
        return mask_loss_func

 </s> add 
        output = ["Loss {}: {:.5f}".format(side.capitalize(), loss[side][0])
  
      def print_loss(self, loss):
          """ Override for specific model loss formatting """
          logger.trace(loss)
        output = ["Loss {}: {:.5f}".format(side.capitalize(), loss[side][0])
        output = ["Loss {}: {:.5f}".format(side.capitalize(), loss[side][0])
        output = ["Loss {}: {:.5f}".format(side.capitalize(), loss[side][0])
        output = ["Loss {}: {:.5f}".format(side.capitalize(), loss[side][0])
                    for side in sorted(list(loss.keys()))]
          output = ", ".join(output)
          print("[{}] [#{:05d}] {}".format(self.timestamp, self.model.iterations, output), end='\r')
  
      def train_one_step(self, viewer, timelapse_kwargs):
keep keep keep keep replace replace replace replace keep keep keep keep keep
 Training: Add Multi-Output Support

- Name output nodes of models
- Add support for multiple outputs in models and in training
- Update loss output format for cli and gui
- Sort graphs tabs on training
- Fix analysis graph for new loss names
- Multi output support in convert 
plugins/train/trainer/_base.py