      def _set_carry_over(self, index, data):
          """ For live data, set carried over data from a partial Tensorflow Event read to
          :attr:`_carry_over`
  
          Parameters
          -------
          index: int
              The step index to carry over data for
          data: dict
              The raw partially read Tensorflow event log data
          """
          logger.debug("Setting carried over data: %s", data)
          self._carry_over[index] = data
  
      def _add_latest_live(self, session_id, loss, timestamps):
          """ Append the latest received live training data to the cached data.
  
          Parameters
 
</s> remove     def _set_carry_over(self, index, data):
        """ For live data, set carried over data from a partial Tensorflow Event read to
        :attr:`_carry_over`
 </s> add         if len(loss[-1]) != length_loss:
            logger.debug("Truncated loss found. loss count: %s", len(loss))
            idx = sorted(data)[-1]
            if is_live:
                logger.debug("Setting carried over data: %s", data)
                self._carry_over[idx] = data[idx]
            logger.debug("Removing truncated loss: (timestamp: %s, loss: %s)",
                         timestamps[-1], loss[-1])
            del loss[-1]
            del timestamps[-1] </s> remove         ids = sorted(data)

        if not all(len(step) == length_loss for step in loss):
            indices = [loss.index(step) for step in loss if len(step) != length_loss]
            logger.debug("Truncated loss found. loss count: %s, truncated indices: %s",
                         len(loss), indices)
            for idx in reversed(indices):  # Backwards so we can delete indices and still traverse
                if is_live:
                    self._set_carry_over(ids[idx], data[ids[idx]])
                logger.debug("Removing truncated loss: (timestamp: %s, loss: %s)",
                             timestamps[idx], loss[idx])
                del loss[idx]
                del timestamps[idx]

        return timestamps, loss
 </s> add  </s> remove     def _process_event(self, event, step):
 </s> add     def _parse_outputs(self, event):
        """ Parse the outputs from the stored model structure for mapping loss names to
        model outputs.

        Loss names are added to :attr:`_loss_labels`

        Parameters
        ----------
        event: :class:`tensorflow.core.util.event_pb2`
            The event data containing the keras model structure to be parsed
        """
        serializer = get_serializer("json")
        struct = event.summary.value[0].tensor.string_val[0]
        outputs = np.array(serializer.unmarshal(struct)["config"]["output_layers"])
        logger.debug("Obtained model outputs: %s, shape: %s", outputs, outputs.shape)
        if outputs.ndim == 2:  # Insert extra dimension for non learn mask models
            outputs = np.expand_dims(outputs, axis=1)
            logger.debug("Expanded dimensions for non-learn_mask model. outputs: %s, shape: %s",
                         outputs, outputs.shape)
        for side_outputs, side in zip(outputs, ("a", "b")):
            logger.debug("side: '%s', outputs: '%s'", side, side_outputs)
            for idx in range(len(side_outputs)):
                # First output is always face. Subsequent outputs are masks
                loss_name = f"face_{side}" if idx == 0 else f"mask_{side}"
                loss_name = loss_name if idx < 2 else f"{loss_name}_{idx}"
                if loss_name not in self._loss_labels:
                    logger.debug("Adding loss name: '%s'", loss_name)
                    self._loss_labels.append(loss_name)
        logger.debug("Collated loss labels: %s", self._loss_labels)

    @classmethod
    def _process_event(cls, event, step): </s> remove         self._tb_logs.set_training(False)
 </s> add         if self._tb_logs is not None:
            self._tb_logs.set_training(False) </s> remove 
    def _process_label(self, label):
        """ Check if the incoming loss label exists in :attr:`_loss_labels` and if not, add it.

        Parameters
        ----------
        label: str
            The label for the currently processing loss event
        """
        # tf2.3 stopped respecting loss names in tensorboard callback so rewrite
        lbl_split = label.replace("batch_", "").replace("_loss", "").split("_")
        if lbl_split[-1] in ("a", "b"):
            lbl = f"face_{lbl_split[-1]}"
        elif "both" in lbl_split:  # Combined decoders don't get face names
            lbl = f"face_{'b' if lbl_split[-1] == '1' else 'a'}"
        else:
            lbl = f"mask_{lbl_split[-2]}"  # TODO may not work for combined decoders
        if lbl not in self._loss_labels:
            logger.debug("Adding loss label: (original: '%s', rewritten: '%s')", label, lbl)
            self._loss_labels.append(lbl)
 </s> add 
        return timestamps, loss
      def _set_carry_over(self, index, data):
          """ For live data, set carried over data from a partial Tensorflow Event read to
          :attr:`_carry_over`
  
        return timestamps, loss
        return timestamps, loss
        return timestamps, loss
        return timestamps, loss
        return timestamps, loss
        return timestamps, loss
        return timestamps, loss
        return timestamps, loss
        return timestamps, loss
  
      def _add_latest_live(self, session_id, loss, timestamps):
          """ Append the latest received live training data to the cached data.
  
          Parameters
keep keep keep keep replace replace replace replace replace replace replace replace replace keep keep keep keep keep
 GUI - Bugfixes
  - Swallow OSErrors when failing to load preview image
  - Fix event_reader mapping for model output to loss names
  - stats - Ensure that _tb_logs exists prior to calling stop training 
lib/gui/analysis/event_reader.py