o
    }o™iÈ  ã                   @   s6   d dl Z d dlZd dlmZ dgZG dd„ deƒZdS )é    N)ÚMetricÚMultiBinaryAccuracyc                	       sT   e Zd ZdZdZd‡ fdd„	Z	ddejdejdejdejfd	d
„Zdd„ Z	‡  Z
S )r   a  
    This metric computes accuracies that are needed to evaluate multiple binary outputs.
    For example, if a model returns a set of multiple sigmoid outputs per each sample or at each time step,
    F1 score can be calculated to monitor Type 1 error and Type 2 error together.

    Example:
        def validation_step(self, batch, batch_idx):
            ...
            signals, signal_lengths, targets = batch
            preds, _ = self.forward(input_signal=signals,
                                    signal_lengths=signal_lengths,
                                    targets=targets)
            loss = self.loss(logits=preds, labels=targets)
            self._accuracy_valid(preds, targets, signal_lengths)
            f1_acc = self._accuracy.compute()
            self.val_outputs = {'val_loss': loss, 'val_f1_acc': f1_acc}
            return self.val_outputs

        def on_validation_epoch_end(self):
            ...
            val_loss_mean = torch.stack([x['val_loss'] for x in self.val_outputs]).mean()
            correct_counts = torch.stack([x['val_correct_counts'] for x in self.val_outputs]).sum(axis=0)
            total_counts = torch.stack([x['val_total_counts'] for x in self.val_outputs]).sum(axis=0)

            self._accuracy_valid.correct_counts_k = correct_counts
            self._accuracy_valid.total_counts_k = total_counts
            f1_acc = self._accuracy_valid.compute()
            self._accuracy_valid.reset()

            self.log('val_loss', val_loss_mean)
            self.log('val_f1_acc', f1_acc)
            self.val_outputs.clear()  # free memory
            return {'val_loss': val_loss_mean, 'val_f1_acc': f1_acc}

    Args:
        preds (torch.Tensor):
            Predicted values which should be in range of [0, 1].
        targets (torch.Tensor):
            Target values which should be in range of [0, 1].
        signal_lengths (torch.Tensor):
            Length of each sequence in the batch input. signal_lengths values are used to
            filter out zero-padded parts in each sequence.

    Returns:
        f1_score (torch.Tensor):
            F1 score calculated from the predicted value and binarized target values.
    Fc                    s¨   t ƒ j|d | jdt d¡ddd | jdt d¡ddd | jdt d¡ddd | jd	t d¡ddd | jd
t d¡ddd | jdt d¡ddd d| _d S )N)Údist_sync_on_stepÚtotal_correct_countsr   ÚsumF)ÚdefaultÚdist_reduce_fxÚ
persistentÚtotal_sample_countsÚtrue_positive_countÚfalse_positive_countÚfalse_negative_countÚpositive_countgíµ ÷Æ°>)ÚsuperÚ__init__Ú	add_stateÚtorchÚtensorÚeps)Úselfr   ©Ú	__class__© úa/home/ubuntu/.local/lib/python3.10/site-packages/nemo/collections/asr/metrics/multi_binary_acc.pyr   J   s   
zMultiBinaryAccuracy.__init__ÚpredsÚtargetsÚsignal_lengthsÚreturnc              	      sp  t  ¡ ( ‡ ‡fdd„tˆ jd ƒD ƒ}‡‡fdd„tˆjd ƒD ƒ}t j|dd| _t j|dd| _| j ¡  ¡ | j ¡  ¡ k| _	| j ¡  ¡ | j ¡  ¡ k| _
| j ¡  ¡ dk| _| j ¡  ¡ dk| _|rÏ|  jt  | j ¡  ¡ dk¡7  _|  jt  t  | j	| j¡¡7  _|  jt  t  | j
| j¡¡7  _|  jt  t  | j
| j¡¡7  _|  jt  | j ¡  ¡ | j ¡  ¡ k¡7  _|  jt  t  | jj¡¡7  _nVt  | j ¡  ¡ dk¡| _t  t  | j	| j¡¡| _t  t  | j
| j¡¡| _t  t  | j
| j¡¡| _t  | j ¡  ¡ | j ¡  ¡ k¡| _t  t  | jj¡¡| _W d  ƒ dS W d  ƒ dS 1 s1w   Y  dS )	a‹  
        Update the metric with the given predictions, targets, and signal lengths to the metric instance.

        Args:
            preds (torch.Tensor): Predicted values.
            targets (torch.Tensor): Target values.
            signal_lengths (torch.Tensor): Length of each sequence in the batch input.
            cumulative (bool): Whether to accumulate the values over time.
        c                    s&   g | ]}ˆ |d ˆ| …d d …f ‘qS ©Nr   ©Ú.0Úk)r   r   r   r   Ú
<listcomp>a   ó   & z.MultiBinaryAccuracy.update.<locals>.<listcomp>r   c                    s&   g | ]}ˆ|d ˆ | …d d …f ‘qS r   r   r   )r   r   r   r   r"   b   r#   )Údimé   TN)r   Úno_gradÚrangeÚshapeÚcatr   r   ÚroundÚboolÚtrueÚfalseÚpositiveÚnegativer   r   r   Úlogical_andr   r   r   r
   Úprodr   )r   r   r   r   Ú
cumulativeÚ
preds_listÚtargets_listr   )r   r   r   r   ÚupdateT   s2   "   , $è$ïzMultiBinaryAccuracy.updatec                 C   s€   | j | j | j | j  }| j | j | j | j  }d| | || | j   ¡  ¡ }t |¡r5t 	d¡ d}| 
¡ | 
¡ | 
¡ fS )as  
        Compute F1 score from the accumulated values. Return -1 if the F1 score is NaN.

        Returns:
            f1_score (torch.Tensor): F1 score calculated from the accumulated values.
            precision (torch.Tensor): Precision calculated from the accumulated values.
            recall (torch.Tensor): Recall calculated from the accumulated values.
        é   zDself.f1_score contains NaN value. Returning -1 instead of NaN value.éÿÿÿÿ)r   r   r   r   ÚdetachÚcloner   ÚisnanÚloggingÚwarnÚfloat)r   Ú	precisionÚrecallÚf1_scorer   r   r   Úcomputez   s   	"

zMultiBinaryAccuracy.compute)F)Ú__name__Ú
__module__Ú__qualname__Ú__doc__Úfull_state_updater   r   ÚTensorr5   rA   Ú__classcell__r   r   r   r   r      s    0ÿÿÿÿ
þ&)r;   r   Útorchmetricsr   Ú__all__r   r   r   r   r   Ú<module>   s
   