
    6biB                     
   S r SSKJs  Jr  SSKJr  SSKJr  SSK	J
r
  SSKJr  \" S/ S9 " S	 S
5      5       r SS jrSS jrS rS r\R$                  4S jr\" S/ S9S\R$                  S4S j5       rS rS rS rS rS rg)z$Utilities related to loss functions.    N)backend)keras_tensor)tf_utils)keras_exportzkeras.losses.Reduction)v1c                   H    \ rS rSrSrSrSrSrSr\	S 5       r
\	S 5       rS	rg
)ReductionV2   a  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                 ^    U R                   U R                  U R                  U R                  4$ N)AUTONONESUMSUM_OVER_BATCH_SIZE)clss    Y/home/james-whalen/.local/lib/python3.13/site-packages/tf_keras/src/utils/losses_utils.pyallReductionV2.allQ   s#    #((CGGS-D-DEE    c                 j    XR                  5       ;  a   [        SU SU R                  5        S35      eg )NzInvalid Reduction Key: z. Expected keys are "")r   
ValueError)r   keys     r   validateReductionV2.validateU   s9    ggi)#.CCGGI;aP   r    N)__name__
__module____qualname____firstlineno____doc__r   r   r   r   classmethodr   r   __static_attributes__r    r   r   r	   r	      sG    ,\ DD
C/F F  r   r	   c                   ^ ^ [         R                  " U=(       d    S5         [        R                  " T5      (       d  [        R
                  " T5      m[        R                  " T 5      (       d  [        R
                  " T 5      m TR                  nUR                  nT R                  nUR                  nUb  Ub  XW-
  nXS-   :X  a<  UR                  S   R                  S5      (       a  [        R                  " TS/5      mOCXS-
  :X  a;  UR                  S   R                  S5      (       a  [        R                  " T S/5      m T T4sSSS5        $ [        R                  " T5      [        R                  " T 5      -
  nUb#  UR                  S   R                  S5      (       a8  [        R                  " [        R                  " US-   U5      U4S jU4S j5      mUb#  UR                  S   R                  S5      (       a8  [        R                  " [        R                  " US-
  U5      U 4S jU 4S j5      m T T4sSSS5        $ ! , (       d  f       g= f)	a   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                  4   > [         R                  " T S/5      $ Nr+   tfsqueezepredictionss   r   <lambda>.remove_squeezable_dimensions.<locals>.<lambda>   s    

;5r   c                     > T $ r   r    r1   s   r   r3   r4      s    r   c                  4   > [         R                  " T S/5      $ r-   r.   labelss   r   r3   r4      s    

6B40r   c                     > T $ r   r    r7   s   r   r3   r4      s    r   )r   
name_scoper   is_tensor_or_extension_typer/   convert_to_tensorshapendimsdimsis_compatible_withr0   rankcondequal)	r8   r2   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;1449J9J:  #:$ FRD1;&) 
D	C. GGK(2776?:	$""2&99!<<''+a/;5#K
 b!44Q77WW+a/;0F
 {"Q 
D	C	Cs   D'I	C,I		
Ic                   ^ ^^^^^^^ T R                   nUR                  nTb  TR                   nUR                  nUb$  Ub!  XF-
  S:w  d	  US   S:X  a  [        TT 5      u  mm O[        R                  " T 5      [        R                  " T5      -
  mU U4S jm[        R
                  " S[        R                   " T 5      S   5      mUUU U4S jn[        R                  " [        R
                  " ST5      UT5      u  mm Tc  T T4$ TR                   nUR                  n	U	S:X  a  T TT4$ UbI  U	bF  X-
  S:X  a  [        R                  " TS/5      mO XI-
  S:X  a  [        R                  " TS/5      mT TT4$ [        R                  " T5      n
U
[        R                  " T 5      -
  mU4S jmUU4S jmUUU4S jn[        R                  " [        R
                  " U
S5      U4S	 jU5      mT TT4$ )
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T 5      $ r   )r)   y_predy_trues   r   r3   .squeeze_or_expand_dimensions.<locals>.<lambda>   s    #?#Or   c                  >   > [         R                  " T TUU4S j5      $ )Nc                     > TT 4$ r   r    rM   s   r   r3   @squeeze_or_expand_dimensions.<locals>.<lambda>.<locals>.<lambda>   s
    ff5Er   )r/   rB   )is_last_dim_1squeeze_dimsrN   rO   s   r   r3   rP      s    |-E*r   r   c                  4   > [         R                  " T S/5      $ r-   r.   sample_weights   r   r3   rP      s    BJJ}rd$Cr   c                  r   > U4S jn [         R                  " [         R                  " TS5      U U4S j5      $ )Nc                  4   > [         R                  " T S/5      $ r-   )r/   expand_dimsrW   s   r   r3   Msqueeze_or_expand_dimensions.<locals>._maybe_expand_weights.<locals>.<lambda>   s    t!Dr   r+   c                     > T $ r   r    rW   s   r   r3   r\      s    ]r   r/   rB   rC   )expand_weightsrJ   rX   s    r   _maybe_expand_weights;squeeze_or_expand_dimensions.<locals>._maybe_expand_weights   s,    DwwHHY#^5J
 	
r   c                  ^   > [         R                  " [         R                  " TS5      TT 5      $ )Nr*   r^   )r`   maybe_squeeze_weightsrJ   s   r   _maybe_adjust_weights;squeeze_or_expand_dimensions.<locals>._maybe_adjust_weights   s(    wwHHY"$9;P
 	
r   c                     > T $ r   r    rW   s   r   r3   rP      s    r   )	r=   r>   r)   r/   rA   rC   rB   r0   r[   )rN   rO   rX   y_pred_shapey_pred_ranky_true_shapey_true_rankmaybe_squeeze_dimsweights_shapeweights_rankweights_rank_tensorrd   r`   rT   rc   rJ   rU   s   ```         @@@@@r   squeeze_or_expand_dimensionsro      s   0 <<L$$K
 ||"((#+*A)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                 j    [         R                  " U 5      n[         R                  R                  X!SS9$ )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rE   )r/   
reduce_summathdivide_no_nan)lossesnum_present
total_losss      r   
_safe_meanry      s,     v&J77  w GGr   c                     [         R                  " S5       n[        R                  " [        R                  " XS9U R
                  S9sSSS5        $ ! , (       d  f       g= f)z3Computes the number of elements in `losses` tensor.num_elementsrr   )dtypeN)r   r:   r/   castsizer|   )rv   scopes     r   _num_elementsr     s9    			N	+uwwrwwv2&,,G 
,	+	+s   1A
A c                     U[         R                  :X  a  U nU$ [        R                  " U 5      nU[         R                  :X  a  [        U[        U 5      5      nU$ )z2Reduces the individual weighted loss measurements.)r	   r   r/   rs   r   ry   r   )weighted_losses	reductionlosss      r   reduce_weighted_lossr     sR     K$$$
 K }}_-777dM/$BCDKr   z/keras.__internal__.losses.compute_weighted_lossc                    [         R                  U5        U[         R                  :X  a  [         R                  nUc  Sn[        R
                  " U=(       d    S5         U[        R                  R                  R                  5       l
        [        U [        R                  [        R                  45      (       d  [        R                  " U 5      n [        U[        R                  [        R                  45      (       d  [        R                  " U5      nU R                   R"                  (       d&  U R                   n[        R$                  " U S5      n SnOSn[        R$                  " XR                   5      n['        U SU5      u  n nn[        R(                  " X5      n[+        Xr5      nU(       a  [        R$                  " UW5      nUsSSS5        $ ! , (       d  f       g= f)al  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   r:   r/   compatr   get_default_graph_last_loss_reduction
isinstancer   KerasTensorRaggedTensorr<   r|   is_floatingr}   ro   multiplyr   )	rv   rX   r   rE   input_dtypeinput_casted_r   r   s	            r   compute_weighted_lossr      sd   2 # K$$$33				D3O	4 AJ		&&(=&<#;#;R__"MNN))&1FL44booF
 
 00?M ||'' ,,KWWVY/FL L||< )}E		
++f< $O?774-DI 
5	4	4s   E,G
G"c                 r    [         R                  R                  5       R                  nUS:  a  U SU-  -  n U $ )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                    SnU  H  nUR                   R                  (       aJ  Ub$  UR                   R                  UR                  :  a  UR                   nOUR                   U1SS1:X  a  SnUR                   R                  (       d  M  U s  $    U(       a&  U  Vs/ s H  n[        R
                  " X!5      PM     n nU $ s  snf )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   )r|   r   r~   
is_complexr/   r}   )rv   highest_floatr   s      r   cast_losses_to_common_dtyper   p  s     M::!!$

-:L:L(L $

**m,Y0GG )::     ;AB64"''$.6BM Cs    B?c                     [        U SS5      $ )z"Returns TF-Keras mask from tensor._keras_maskN)getattr)y_ps    r   get_maskr     s    3t,,r   c                     UbW  [         R                  " X R                  5      nUb2  [         R                  " XR                  5      n[        X!S9u  p#nX-  nU$ UnU$ )z2Applies any mask on predictions to sample weights.rW   )r/   r}   r|   ro   )r   swmaskr   s       r   
apply_maskr     sY    wwtYY'>ZZ(B6tNKDRJB I BIr   c                 L   Ub  [         R                  " X R                  5      nU[        R                  [        R
                  4;   aR  [         R                  " [         R                  " U5      U R                  5      n[         R                  " U5      nX$U-  -  n[        XU5      $ )z;Redistribute sample weights considering only valid entries.)	r/   r}   r|   r	   r   r   r~   rs   r   )rv   r   r   r   totalvalids         r   apply_valid_maskr     sx    wwt\\*));+J+JKK GGBGGDM6<<8EMM$'EEM!Df$''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)   ro   ry   r   r   r   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   