
    BVh ;                         d Z ddlmZ ddlmZ ddlmZ ddlmZ ddlm	Z	 ddl
mZ ddl
mZ dd	l
mZ dd
lmZ  G d de      Z	 ddZddZd Zd Zej,                  fdZdej,                  dfdZd Zd Zy)z$Utilities related to loss functions.    )distribute_lib)ops)tensor_conversion)backend)keras_tensor)	array_opscond)math_ops)ragged_tensorc                   @    e Zd ZdZdZdZdZdZed        Z	ed        Z
y)	ReductionV2ai  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 defaults to `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 your 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    Z/home/dcms/DCMS/lib/python3.12/site-packages/tensorflow/python/keras/utils/losses_utils.pyallzReductionV2.allP   s#    HHchh)@)@AA    c                 D    || j                         vrt        d|z        y )NzInvalid Reduction Key %s.)r   
ValueError)r   keys     r   validatezReductionV2.validateT   s&    
#'')2S899 r   N)__name__
__module____qualname____doc__r   r   r   r   classmethodr   r     r   r   r   r      sG    +Z 
$	$#-B B : :r   r   Nc                 P    t        j                  |xs d      5  t        t        j                        st        j                        t         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)	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                  2    t        j                   dg      S Nr*   r   squeezepredictionss   r   <lambda>z.remove_squeezable_dimensions.<locals>.<lambda>   s    )##K"6 r   c                       S r   r&   r/   s   r   r1   z.remove_squeezable_dimensions.<locals>.<lambda>   s    + r   c                  2    t        j                   dg      S r,   r-   labelss   r   r1   z.remove_squeezable_dimensions.<locals>.<lambda>   s    )##FRD1 r   c                       S r   r&   r4   s   r   r1   z.remove_squeezable_dimensions.<locals>.<lambda>   s    & r   )r   
name_scope
isinstancer   RaggedTensorr   "convert_to_tensor_v2_with_dispatchshapendimsdimsis_compatible_withr   r.   rankr
   r   equal)	r5   r0   expected_rank_diffnamepredictions_shapepredictions_ranklabels_shapelabels_rank	rank_diffs	   ``       r   r(   r(   Z   s   4 $@"@A $k=#=#=>%HH
k fm889 CCFKf#))(..<<L$$K&6&B"[0i
)A-
-

 
 
$
7
7
:''bT:+a//b!44Q7""6B40[ )$ $. {+innV.DDI r"55a8II
..+a/
;
6
k 	"003yy
..+a/
;
1
f ;I$ $ $s   DH<C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   r1   z.squeeze_or_expand_dimensions.<locals>.<lambda>   s    9
& r   c                  :    t        j                    fd      S )Nc                       fS r   r&   rJ   s   r   r1   z@squeeze_or_expand_dimensions.<locals>.<lambda>.<locals>.<lambda>   s    /? r   r	   )is_last_dim_1squeeze_dimsrK   rL   s   r   r1   z.squeeze_or_expand_dimensions.<locals>.<lambda>   s    499
'?$A r   r   c                  2    t        j                   dg      S r,   r-   sample_weights   r   r1   z.squeeze_or_expand_dimensions.<locals>.<lambda>   s    )"3"3MB4"H r   c                  j    fd} t        j                   t        j                  d      | fd      S )Nc                  2    t        j                   dg      S r,   )r   expand_dimsrR   s   r   r1   zMsqueeze_or_expand_dimensions.<locals>._maybe_expand_weights.<locals>.<lambda>   s    Y22=2$G r   r*   c                       S r   r&   rR   s   r   r1   zMsqueeze_or_expand_dimensions.<locals>._maybe_expand_weights.<locals>.<lambda>   s    } r   r
   r   r@   )expand_weightsrG   rS   s    r   _maybe_expand_weightsz;squeeze_or_expand_dimensions.<locals>._maybe_expand_weights   s.    GN99y"%~7LN Nr   c                  Z    t        j                   t        j                  d             S )Nr)   rX   )rZ   maybe_squeeze_weightsrG   s   r   _maybe_adjust_weightsz;squeeze_or_expand_dimensions.<locals>._maybe_adjust_weights   s(    99y!$&; r   c                       S r   r&   rR   s   r   r1   z.squeeze_or_expand_dimensions.<locals>.<lambda>   s    m r   )
r;   r<   r(   r   r?   r   r@   r
   r.   rV   )rK   rL   rS   y_pred_shapey_pred_ranky_true_shapey_true_rankmaybe_squeeze_dimsweights_shapeweights_rankweights_rank_tensorr]   rZ   rO   r\   rG   rP   s   ```         @@@@@r   squeeze_or_expand_dimensionsrg      s   0 ,""+ <<L$$Kk&=

#q
(\"-=-B5F ..(9>>&+AAilnnQ	(?(CDmAyy
..I
&(:LJnff 6>%%-$$,Q6=((L$<k!Q&''t<m	|	#q	(++MB4@m6=(( "}5!INN6$::)HN
 ))nn(!,.C- 
	&&r   c                 \    t        j                  |       }t        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rB   )r   
reduce_sum
div_no_nan)lossesnum_present
total_losss      r   
_safe_meanrp      s)     ""6**			Z7	CCr   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_elementsrj   )dtypeN)r   r7   r   castr   sizers   )rm   scopes     r   _num_elementsrw     sH    .) QU==U;6<<PQ Q Q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   rk   r   rp   rw   )weighted_losses	reductionlosss      r   reduce_weighted_lossr|   	  sT     +"""D
 
+ /DK333mO<=d	+r   c                    t         j                  |       |t         j                  k(  rt         j                  }|d}t	        j
                  |xs d      5  |t        j                         _        t        | t        j                  t        j                  f      st        j                  |       } | j                   }t        |t        j                        st        j                  |      }t#        j$                  | d      } t#        j$                  |d      }t'        | d|      \  } }}t#        j(                  | |      }t+        ||      }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float32)r   r    r   r   r   r7   r   get_default_graph_last_loss_reductionr8   r   KerasTensorr   r9   r   r:   rs   r   rt   rg   multiplyr|   )rm   rS   rz   rB   input_dtype_ry   r{   s           r   compute_weighted_lossr     s;   * y! +"""//IM$1/2  4=C0f#//1K1KLN CCFKf,,Km\%=%=>'JJ
m ]]69-FMM-;M;m %FA}''>O  ;D=={+D9  s   D
E**E3c                 \    t        j                         j                  }|dkD  r| d|z  z  } | S )zBScales and returns the given loss value by the number of replicas.r)   r~   )r   get_strategynum_replicas_in_sync)
loss_valuenum_replicass     r   scale_loss_for_distributionr   Q  s8     !!#88 A2$%J	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 )ab  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   )rs   is_floatingru   
is_complexr   rt   )rm   highest_floatr{   s      r   cast_losses_to_common_dtyper   Z  s     - dzz		$**//M4F4F"F

JJ&:y*AA!zzm =CDThmmD-0DFD	- Es   B()r   N)NN)r$   tensorflow.python.distributer   tensorflow.python.frameworkr   r   tensorflow.python.kerasr   tensorflow.python.keras.enginer   tensorflow.python.opsr   r
   r   tensorflow.python.ops.raggedr   objectr   r(   rg   rp   rw   r   r|   r   r   r   r&   r   r   <module>r      s     + 7 + 9 + 7 + & * 6::& ::| 59>BV'rDQ $/#B#B	 )-$/$C$C#9xr   