
    0hB                        d Z ddlmc mZ ddlmZ ddlmZ ddl	m
Z
 ddlmZ  edg        G d	 d
             Z	 ddZddZd Zd Zej$                  fdZ edg       dej$                  dfd       Zd Zd Zd Zd Zd Zy)z$Utilities related to loss functions.    N)backend)keras_tensor)tf_utils)keras_exportzkeras.losses.Reduction)v1c                   @    e Zd ZdZdZdZdZdZed        Z	ed        Z
y)	ReductionV2a  Types of loss reduction.

    Contains the following values:

    * `AUTO`: Indicates that the reduction option will be determined by the
      usage context. For almost all cases this uses `SUM_OVER_BATCH_SIZE`.
      When used with `tf.distribute.Strategy`, outside of built-in training
      loops such as `tf.keras` `compile` and `fit`, we expect reduction
      value to be `SUM` or `NONE`. Using `AUTO` in that case will raise an
      error.
    * `NONE`: No **additional** reduction is applied to the output of the
      wrapped loss function. When non-scalar losses are returned to Keras
      functions like `fit`/`evaluate`, the unreduced vector loss is passed to
      the optimizer but the reported loss will be a scalar value.

       Caution: **Verify the shape of the outputs when using** `Reduction.NONE`.
       The builtin loss functions wrapped by the loss classes reduce one
       dimension (`axis=-1`, or `axis` if specified by loss function).
       `Reduction.NONE` just means that no **additional** reduction is applied
       by the class wrapper. For categorical losses with an example input shape
       of `[batch, W, H, n_classes]` the `n_classes` dimension is reduced. For
       pointwise losses you must include a dummy axis so that `[batch, W, H, 1]`
       is reduced to `[batch, W, H]`. Without the dummy axis `[batch, W, H]`
       will be incorrectly reduced to `[batch, W]`.

    * `SUM`: Scalar sum of weighted losses.
    * `SUM_OVER_BATCH_SIZE`: Scalar `SUM` divided by number of elements in
       losses.  This reduction type is not supported when used with
       `tf.distribute.Strategy` outside of built-in training loops like
       `tf.keras` `compile`/`fit`.

       You can implement 'SUM_OVER_BATCH_SIZE' using global batch size like:
       ```
       with strategy.scope():
         loss_obj = tf.keras.losses.CategoricalCrossentropy(
             reduction=tf.keras.losses.Reduction.NONE)
         ....
         loss = tf.reduce_sum(loss_obj(labels, predictions)) *
             (1. / global_batch_size)
       ```

    Please see the [custom training guide](
    https://www.tensorflow.org/tutorials/distribute/custom_training) for more
    details on this.
    autononesumsum_over_batch_sizec                 ^    | j                   | j                  | j                  | j                  fS N)AUTONONESUMSUM_OVER_BATCH_SIZE)clss    Y/var/www/html/engine/venv/lib/python3.12/site-packages/tf_keras/src/utils/losses_utils.pyallzReductionV2.allQ   s#    #((CGGS-D-DEE    c                 h    || j                         vr t        d| d| j                          d      y )NzInvalid Reduction Key: z. Expected keys are "")r   
ValueError)r   keys     r   validatezReductionV2.validateU   s;    cggi)#.CCGGI;aP   r   N)__name__
__module____qualname____doc__r   r   r   r   classmethodr   r    r   r   r	   r	      sG    ,\ DD
C/F F  r   r	   c                 <    t        j                  |xs d      5  t        j                        st	        j
                        t        j                         st	        j
                          j                  }|j                  } j                  }|j                  }||||z
  }||dz   k(  r6|j                  d   j                  d      rt	        j                  dg      n=||dz
  k(  r5|j                  d   j                  d      rt	        j                   dg        fcddd       S t	        j                        t	        j                         z
  }||j                  d   j                  d      r4t	        j                  t	        j                  |dz   |      fdfd      ||j                  d   j                  d      r4t	        j                  t	        j                  |dz
  |       fd fd        fcddd       S # 1 sw Y   yxY w)	aH  Squeeze last dim if ranks differ from expected by exactly 1.

    In the common case where we expect shapes to match, `expected_rank_diff`
    defaults to 0, and we squeeze the last dimension of the larger rank if they
    differ by 1.

    But, for example, if `labels` contains class IDs and `predictions` contains
    1 probability per class, we expect `predictions` to have 1 more dimension
    than `labels`, so `expected_rank_diff` would be 1. In this case, we'd
    squeeze `labels` if `rank(predictions) - rank(labels) == 0`, and
    `predictions` if `rank(predictions) - rank(labels) == 2`.

    This will use static shape if available. Otherwise, it will add graph
    operations, which could result in a performance hit.

    Args:
      labels: Label values, a `Tensor` whose dimensions match `predictions`.
      predictions: Predicted values, a `Tensor` of arbitrary dimensions.
      expected_rank_diff: Expected result of `rank(predictions) - rank(labels)`.
      name: Name of the op.

    Returns:
      Tuple of `labels` and `predictions`, possibly with last dim squeezed.
    remove_squeezable_dimensionsN   c                  2    t        j                   dg      S Nr&   tfsqueezepredictionss   r   <lambda>z.remove_squeezable_dimensions.<locals>.<lambda>   s    

;5 r   c                       S r   r"   r,   s   r   r.   z.remove_squeezable_dimensions.<locals>.<lambda>   s     r   c                  2    t        j                   dg      S r(   r)   labelss   r   r.   z.remove_squeezable_dimensions.<locals>.<lambda>   s    

6B40 r   c                       S r   r"   r1   s   r   r.   z.remove_squeezable_dimensions.<locals>.<lambda>   s     r   )r   
name_scoper   is_tensor_or_extension_typer*   convert_to_tensorshapendimsdimsis_compatible_withr+   rankcondequal)	r2   r-   expected_rank_diffnamepredictions_shapepredictions_ranklabels_shapelabels_rank	rank_diffs	   ``       r   r$   r$   ]   s   6 
		DB$B	C (#33K@..{;K33F;))&1F'--,22||"((#*:*F(;6I.227H7M7M8  #8$ !jjrd;01449J9J:  #:$ FRD1;&)(# (#. GGK(2776?:	$""2&99!<''+a/;5#K
 b!44Q7WW+a/;0F
 {"Q(# (# (#s   DH2CHHc                      j                   }|j                  }ωj                   }|j                  }|"| ||z
  dk7  s|d   dk(  rt               \   nt        j                         t        j                        z
   fdt        j
                  dt        j                          d          fd}t        j                  t        j
                  d      |      \    fS j                   }|j                  }	|	dk(  r fS |F|	D|	|z
  dk(  rt        j                  dg      n||	z
  dk(  rt        j                  dg       fS t        j                        }
|
t        j                         z
  fdfdfd}t        j                  t        j
                  |
d      fd	|       fS )
a  Squeeze or expand last dimension if needed.

    1. Squeezes last dim of `y_pred` or `y_true` if their rank differs by 1
    (using `remove_squeezable_dimensions`).
    2. Squeezes or expands last dim of `sample_weight` if its rank differs by 1
    from the new rank of `y_pred`.
    If `sample_weight` is scalar, it is kept scalar.

    This will use static shape if available. Otherwise, it will add graph
    operations, which could result in a performance hit.

    Args:
      y_pred: Predicted values, a `Tensor` of arbitrary dimensions.
      y_true: Optional label `Tensor` whose dimensions match `y_pred`.
      sample_weight: Optional weight scalar or `Tensor` whose dimensions match
        `y_pred`.

    Returns:
      Tuple of `y_pred`, `y_true` and `sample_weight`. Each of them possibly has
      the last dimension squeezed,
      `sample_weight` could be extended by one dimension.
      If `sample_weight` is None, (y_pred, y_true) is returned.
    r%   r&   c                      t               S r   )r$   y_predy_trues   r   r.   z.squeeze_or_expand_dimensions.<locals>.<lambda>   s    #?#O r   c                  :    t        j                   fd      S )Nc                       fS r   r"   rG   s   r   r.   z@squeeze_or_expand_dimensions.<locals>.<lambda>.<locals>.<lambda>   s    ff5E r   )r*   r<   )is_last_dim_1squeeze_dimsrH   rI   s   r   r.   z.squeeze_or_expand_dimensions.<locals>.<lambda>   s    |-E* r   r   c                  2    t        j                   dg      S r(   r)   sample_weights   r   r.   z.squeeze_or_expand_dimensions.<locals>.<lambda>   s    BJJ}rd$C r   c                  j    fd} t        j                  t        j                  d      | fd      S )Nc                  2    t        j                   dg      S r(   )r*   expand_dimsrO   s   r   r.   zMsqueeze_or_expand_dimensions.<locals>._maybe_expand_weights.<locals>.<lambda>   s    t!D r   r&   c                       S r   r"   rO   s   r   r.   zMsqueeze_or_expand_dimensions.<locals>._maybe_expand_weights.<locals>.<lambda>   s    ] r   r*   r<   r=   )expand_weightsrD   rP   s    r   _maybe_expand_weightsz;squeeze_or_expand_dimensions.<locals>._maybe_expand_weights   s,    DwwHHY#^5J
 	
r   c                  Z    t        j                  t        j                  d             S )Nr%   rU   )rW   maybe_squeeze_weightsrD   s   r   _maybe_adjust_weightsz;squeeze_or_expand_dimensions.<locals>._maybe_adjust_weights   s(    wwHHY"$9;P
 	
r   c                       S r   r"   rO   s   r   r.   z.squeeze_or_expand_dimensions.<locals>.<lambda>   s     r   )	r7   r8   r$   r*   r;   r=   r<   r+   rS   )rH   rI   rP   y_pred_shapey_pred_ranky_true_shapey_true_rankmaybe_squeeze_dimsweights_shapeweights_rankweights_rank_tensorrZ   rW   rL   rY   rD   rM   s   ```         @@@@@r   squeeze_or_expand_dimensionsrd      s   0 <<L$$K
 ||"((#+*Ak)Q.<3Cq3H!=ff!M "''&/9IOLHHQ(8(<=M"  WWI&(:LNFF v~!''M &&Lqv},,l&>+%*JJ}rd;M<'1,NN=2$?Mv},, ''-0#bggfo5IC

 GG
$a(M
 6=((r   c                 p    t        j                  |       }t         j                  j                  ||d      S )a:  Computes a safe mean of the losses.

    Args:
      losses: `Tensor` whose elements contain individual loss measurements.
      num_present: The number of measurable elements in `losses`.

    Returns:
      A scalar representing the mean of `losses`. If `num_present` is zero,
        then zero is returned.
    valuer?   )r*   
reduce_summathdivide_no_nan)lossesnum_present
total_losss      r   
_safe_meanrn      s.     v&J77  [w GGr   c                     t        j                  d      5 }t        j                  t        j                  | |      | j
                        cddd       S # 1 sw Y   yxY w)z3Computes the number of elements in `losses` tensor.num_elementsrg   )dtypeN)r   r4   r*   castsizerq   )rk   scopes     r   _num_elementsru     sH    			N	+ HuwwrwwvE2&,,GH H Hs   6AAc                     |t         j                  k(  r| }|S t        j                  |       }|t         j                  k(  rt        |t        |             }|S )z2Reduces the individual weighted loss measurements.)r	   r   r*   rh   r   rn   ru   )weighted_losses	reductionlosss      r   reduce_weighted_lossrz     sR     K$$$
 K }}_-777dM/$BCDKr   z/keras.__internal__.losses.compute_weighted_lossc                    t         j                  |       |t         j                  k(  rt         j                  }|d}t	        j
                  |xs d      5  |t        j                  j                  j                         _
        t        | t        j                  t        j                  f      st        j                  |       } t        |t        j                  t        j                  f      st        j                  |      }| j                   j"                  s%| j                   }t        j$                  | d      } d}nd}t        j$                  || j                         }t'        | d|      \  } }}t        j(                  | |      }t+        ||      }|rt        j$                  |      }|cddd       S # 1 sw Y   yxY w)a  Computes the weighted loss.

    Args:
      losses: `Tensor` of shape `[batch_size, d1, ... dN]`.
      sample_weight: Optional `Tensor` whose rank is either 0, or the same rank
        as `losses`, or be broadcastable to `losses`.
      reduction: (Optional) Type of `tf.keras.losses.Reduction` to apply to
        loss. Default value is `SUM_OVER_BATCH_SIZE`.
      name: Optional name for the op.

    Raises:
      ValueError: If the shape of `sample_weight` is not compatible with
        `losses`.

    Returns:
      Weighted loss `Tensor` of the same type as `losses`. If `reduction` is
      `NONE`, this has the same shape as `losses`; otherwise, it is scalar.
    N      ?weighted_lossfloat32TF)r	   r   r   r   r   r4   r*   compatr   get_default_graph_last_loss_reduction
isinstancer   KerasTensorRaggedTensorr6   rq   is_floatingrr   rd   multiplyrz   )	rk   rP   rx   r?   input_dtypeinput_casted_rw   ry   s	            r   compute_weighted_lossr      sk   2 # K$$$33				D3O	4 $ AJ		&&(=&<#;#;R__"MN))&1FL44booF
 00?M ||'' ,,KWWVY/FL Lv||< )}E		
++fm< $OY?774-DI$ $ $s   EF55F>c                 p    t         j                  j                         j                  }|dkD  r| d|z  z  } | S )zBScales and returns the given loss value by the number of replicas.r%   r|   )r*   
distributeget_strategynum_replicas_in_sync)
loss_valuenum_replicass     r   scale_loss_for_distributionr   h  s7    ==--/DDLacL((
r   c                 Z   d}| D ]y  }|j                   j                  rG|#|j                   j                  |j                  kD  r|j                   }n|j                   |hddhk(  rd}|j                   j                  sw| c S  |r#| D cg c]  }t	        j
                  ||       } }| S c c}w )ar  Cast a list of losses to a common dtype.

    If any loss is floating-point, they will all be casted to the most-precise
    floating-point loss. Otherwise the losses are not casted. We also skip
    casting losses if there are any complex losses.

    Args:
      losses: A list of losses.

    Returns:
      `losses`, but they have been casted to a common dtype.
    Nbfloat16float16r~   )rq   r   rs   
is_complexr*   rr   )rk   highest_floatry   s      r   cast_losses_to_common_dtyper   p  s     M 	::!!$

-:L:L(L $

**m,Y0GG )::  	 ;AB4"''$.BBM Cs   B(c                     t        | dd      S )z"Returns TF-Keras mask from tensor._keras_maskN)getattr)y_ps    r   get_maskr     s    3t,,r   c                     |\t        j                  || j                        }|8t        j                  ||j                        }t        ||      \  }}}||z  }|S |}|S )z2Applies any mask on predictions to sample weights.rO   )r*   rr   rq   rd   )r   swmaskr   s       r   
apply_maskr     sc    wwtSYY'>TZZ(B6t2NKD!R$JB I BIr   c                 D   |t        j                  || j                        }|t        j                  t        j
                  fv rPt        j                  t        j                  |      | j                        }t        j                  |      }|||z  z  }t        | ||      S )z;Redistribute sample weights considering only valid entries.)	r*   rr   rq   r	   r   r   rs   rh   r   )rk   r   r   rx   totalvalids         r   apply_valid_maskr     s~    wwtV\\*));+J+JKK GGBGGDM6<<8EMM$'EEEM!Dfb$''r   )r   N)NN)r    tensorflow.compat.v2r   v2r*   tf_keras.srcr   tf_keras.src.enginer   tf_keras.src.utilsr    tensorflow.python.util.tf_exportr   r	   r$   rd   rn   ru   r   rz   r   r   r   r   r   r   r"   r   r   <module>r      s     + ! !   , ' : &2.= = /=B 59C#LX)vHH  +>>
 ?BG --		D HDN:-

(r   