o
    i eV                     @   s~  d Z ddlm  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 dd
lmZ G dd dZdd Zdd Zdd Zdd ZG dd dejjjZdZdZ G dd de!Z"edG dd de"dZ#G dd  d ejjj$ej%e#Z&G d!d" d"ejjj$ej'e#Z(G d#d$ d$ejjjZ)d%d& Z*ejj+,ej%e*e& d'd( Z-d)d* Z.d+d, Z/dS )-z*Contains the loss scaling optimizer class.    N)backend)
optimizers)utils)	optimizer)optimizer_v2)serialization_lib)
tf_logging)keras_exportc                   @   s   e Zd ZdZdgZdd ZdS )_UnwrapPreventera  Wrapper that DistributionStrategy will not unwrap.

    Typically, DistributionStrategy will unwrap values when going from a cross-
    replica context to a replica context via `call_for_each_replica`. This class
    is a wrapper that DistributionStrategy will not unwrap, so it can be used to
    prevent it from unwrapping a value.

    TODO(reedwm): Find/implement a better way of preventing values from being
    unwrapped by DistributionStrategy
    valuec                 C   
   || _ d S N)r   selfr    r   ]/var/www/myenv/lib/python3.10/site-packages/keras/src/mixed_precision/loss_scale_optimizer.py__init__.      
z_UnwrapPreventer.__init__N)__name__
__module____qualname____doc__	__slots__r   r   r   r   r   r
       s    r
   c                    s$   dd   fdd| D }t |S )zKReturns a scalar boolean tensor indicating if all gradients are
    finite.c                 S   s   t | tjr	| jS | S r   )
isinstancetfIndexedSlicesvalues)gr   r   r   
raw_values6   s   z"_is_all_finite.<locals>.raw_valuesc                    s*   g | ]}|d urt t j |qS r   )r   
reduce_allmath	is_finite.0r   r   r   r   
<listcomp>9   s
    z"_is_all_finite.<locals>.<listcomp>)r   r   )gradsis_finite_per_gradr   r$   r   _is_all_finite2   s
   

r(   c                 C   s   t  r| S | jS )a6  Returns the tensor's op in graph mode, or the tensor in eager mode.

    This is useful because sometimes an op is needed in graph mode instead of a
    tensor. In eager mode, there are no ops.

    Args:
      tensor: A tensor.

    Returns:
      The tensor's op in graph mode. The tensor in eager mode.
    )r   executing_eagerlyop)tensorr   r   r   _op_in_graph_modeA   s   r,   c                    s"   t t j  fddt jS )z5Assigns a value to a variable if the value is finite.c                      s   t  S r   )r,   assignr   r   varr   r   <lambda>V   s    z#_assign_if_finite.<locals>.<lambda>)r   condr    r!   no_op)r/   r   r   r.   r   _assign_if_finiteR   s
   
r3   c                 C   sf   | r|rdS d}| s|st d| d dS | s$t d| d dS |s1t d| d dS dS )z=Warn if the loss or gradients hasn't been scaled or unscaled.Nz
    with tf.GradientTape() as tape:
      loss = loss_fn()
      scaled_loss = opt.get_scaled_loss(loss)
    scaled_grads = tape.gradient(scaled_loss, vars)
    grads = opt.get_unscaled_gradients(scaled_grads)
    opt.apply_gradients([(grads, var)])a  You forgot to call LossScaleOptimizer.get_scaled_loss() and LossScaleOptimizer.get_unscaled_gradients() before calling LossScaleOptimizer.apply_gradients(). This will likely result in worse model quality, so please call them in the correct places! For example:zq
For more information, see https://www.tensorflow.org/api_docs/python/tf/keras/mixed_precision/LossScaleOptimizera  You forgot to call LossScaleOptimizer.get_scaled_loss() before calling LossScaleOptimizer.apply_gradients() (you did call get_unscaled_gradients() however). This will likely result in worse model quality, so please call get_scaled_loss() in the correct place! For example:a  You forgot to call LossScaleOptimizer.get_unscaled_gradients() before calling LossScaleOptimizer.apply_gradients() (you did call get_scaled_loss() however). This will likely result in worse model quality, so please call get_unscaled_gradients() in the correct place! For example:)r   warning)loss_has_been_scaledgradients_have_been_unscaledexample_coder   r   r   _maybe_warn_about_scaling[   s,   	r8   c                       s   e Zd ZdZ fddZdddZd fdd		Zd fd
d	Zedd Z	edd Z
edd Zedd Zedd Zdd Zdd Z  ZS )_DynamicLossScaleStatez"The state of a dynamic loss scale.c                    s\   t    t|| _t|| _t|| _i | _| jdt	j
| jd| _| jdt	jdd| _dS )zCreates the dynamic loss scale.current_loss_scale)namedtypeinitial_value
good_stepsr   N)superr   float_initial_loss_scaleint_growth_steps_multiplier_weights_add_weightr   float32_current_loss_scaleint64_counter)r   initial_loss_scalegrowth_steps
multiplier	__class__r   r   r      s   



z_DynamicLossScaleState.__init__Nc                 C   sl   t j|||dt jjt jjd}t  rd}n	t jj	 }|j
}||f}|| j|< | j||d t| |S )a;  Adds a weight to this loss scale.

        Args:
          name: Variable name.
          initial_value: The variable's initial value.
          dtype: The type of the variable.

        Returns:
          A variable.

        Raises:
          RuntimeError: If a weight with `name` has already been added.
        F)r=   r;   r<   	trainablesynchronizationaggregationN)r;   	trackable)r   VariableVariableSynchronizationAUTOVariableAggregationNONEr)   compatv1get_default_graph
_graph_keyrE   _handle_deferred_dependenciesr   track_variable)r   r;   r=   r<   variable	graph_keygraphkeyr   r   r   rF      s"   


z"_DynamicLossScaleState._add_weight
checkpointc           	         sv   t  rd}n	t jj }|j}i }t| j dd dD ]\\}}}||kr+|||< q|	t
 j|fi | |S )z6From Trackable. Gather graph-specific weights to save.Nc                 S   s   | d d S Nr   r   )ir   r   r   r0      s    z<_DynamicLossScaleState._trackable_children.<locals>.<lambda>)rb   )r   r)   rY   rZ   r[   r\   sortedrE   itemsupdater?   _trackable_children)	r   	save_typekwargsr`   ra   weightsr;   r   vrN   r   r   ri      s   z*_DynamicLossScaleState._trackable_childrenc                    s^   |dur
| |}nt |}|dur|S t rd}n	tjj }|j}| j	 ||fdS )z3From Trackable. Find a weight in the current graph.N)
getr?   _lookup_dependencyr   r)   rY   rZ   r[   r\   rE   )r   r;   cached_dependenciesunconditionalr`   ra   rN   r   r   ro      s   z)_DynamicLossScaleState._lookup_dependencyc                 C      | j S r   )rA   r   r   r   r   rK         z)_DynamicLossScaleState.initial_loss_scalec                 C   rr   r   )rC   rs   r   r   r   rL      rt   z#_DynamicLossScaleState.growth_stepsc                 C   rr   r   )rD   rs   r   r   r   rM      rt   z!_DynamicLossScaleState.multiplierc                 C   rr   )z:Returns the current loss scale as a float32 `tf.Variable`.)rH   rs   r   r   r   r:         z)_DynamicLossScaleState.current_loss_scalec                 C   rr   )z/Returns the counter as a float32 `tf.Variable`.)rJ   rs   r   r   r   counter   ru   z_DynamicLossScaleState.counterc                 C   s   t | jS )z<Returns the current loss scale as a scalar `float32` tensor.)r   convert_to_tensorrH   rs   r   r   r   __call__      z_DynamicLossScaleState.__call__c           	         s   t j|}t j r&t j r&t j }|jjt	|fd}|
|d }nt	|} fdd} fdd}t |||}|}||fS )a  Updates the value of the loss scale.

        Args:
          grads: A nested structure of unscaled gradients, each which is an
            all-reduced gradient of the loss with respect to a weight.

        Returns:
          update_op: In eager mode, None. In graph mode, an op to update the
            loss scale.
          should_apply_gradients: Either a bool or a scalar boolean tensor. If
            False, the caller should skip applying `grads` to the variables this
            step.
        argsr   c                     s.    fdd} t  jd  jk|  fddS )z)Update assuming the gradients are finite.c                     s(    j  j } tt j |  jdS rd   )r:   rM   r   groupr3   rv   r-   new_loss_scalers   r   r   incr_loss_scale   s
   

zV_DynamicLossScaleState.update.<locals>.update_if_finite_grads.<locals>.incr_loss_scale   c                      s   t  jdS )Nr   )r,   rv   
assign_addr   rs   r   r   r0   *  s    zO_DynamicLossScaleState.update.<locals>.update_if_finite_grads.<locals>.<lambda>)r   r1   rv   rL   )r   rs   r   r   update_if_finite_grads  s   
z=_DynamicLossScaleState.update.<locals>.update_if_finite_gradsc                     s0   t  j j d} t  jd j| S )z,Update assuming the gradients are nonfinite.r   r   )r   maximumr:   rM   r|   rv   r-   r}   rs   r   r   update_if_not_finite_grads-  s   

zA_DynamicLossScaleState.update.<locals>.update_if_not_finite_grads)r   nestflatten
distributehas_strategyin_cross_replica_contextget_strategyextendedcall_for_each_replicar(   experimental_local_resultsr1   )	r   r&   distributionis_finite_per_replicar!   r   r   	update_opshould_apply_gradientsr   rs   r   rh      s,   
z_DynamicLossScaleState.updater   )rc   )r   r   r   r   r   rF   ri   ro   propertyrK   rL   rM   r:   rv   rx   rh   __classcell__r   r   rN   r   r9      s$    
$




r9   i   i  c                       s    e Zd ZdZ fddZ  ZS )LossScaleOptimizerMetaclassa  Metaclass that delegates LossScaleOptimizer instance creation.

    This metaclass causes a LossScaleOptimizer or LossScaleOptimizerV3 to be
    created when a BaseLossScaleOptimizer is constructed. As a result, when a
    user creates a loss scale optimizer with
    `tf.keras.mixed_precision.LossScaleOptimizer(opt)`, either a
    LossScaleOptimizer or LossScaleOptimizerV3 will be created, depending on the
    type of `opt`.
    c                    s~   | t urtt| j|g|R i |S t|tjr$t|g|R i |S t|tj	r5t
|g|R i |S d| d}t|)Nz"inner_optimizer" must be an instance of `tf.keras.optimizers.Optimizer` or `tf.keras.optimizers.experimental.Optimizer`, but got: .)BaseLossScaleOptimizerr?   r   rx   r   r   OptimizerV2LossScaleOptimizerr   	OptimizerLossScaleOptimizerV3	TypeError)clsinner_optimizerr{   rk   msgrN   r   r   rx   P  s"   
z$LossScaleOptimizerMetaclass.__call__)r   r   r   r   rx   r   r   r   rN   r   r   E  s    
r   z(keras.mixed_precision.LossScaleOptimizerc                   @   sh   e Zd ZdZedd Zedd Zedd Zedd	 Zed
d Z	edd Z
dd Zdd ZdS )r   ai  An optimizer that applies loss scaling to prevent numeric underflow.

    Loss scaling is a technique to prevent numeric underflow in intermediate
    gradients when float16 is used. To prevent underflow, the loss is multiplied
    (or "scaled") by a certain factor called the "loss scale", which causes
    intermediate gradients to be scaled by the loss scale as well. The final
    gradients are divided (or "unscaled") by the loss scale to bring them back
    to their original value.

    `LossScaleOptimizer` wraps another optimizer and applies loss scaling to it.
    By default, the loss scale is dynamically updated over time so you do not
    have to choose the loss scale. The `minimize` method automatically scales
    the loss, unscales the gradients, and updates the loss scale so all you have
    to do is wrap your optimizer with a `LossScaleOptimizer` if you use
    `minimize`. For example:

    >>> opt = tf.keras.optimizers.experimental.SGD(0.25)
    >>> opt = tf.keras.mixed_precision.LossScaleOptimizer(opt)
    >>> var = tf.Variable(1.)
    >>> loss_fn = lambda: var ** 2
    >>> # 'minimize' applies loss scaling and updates the loss sale.
    >>> opt.minimize(loss_fn, var_list=[var])
    >>> var.numpy()
    0.5

    If a `tf.GradientTape` is used to compute gradients instead of `minimize`,
    you must scale the loss and gradients manually. This can be done with the
    `LossScaleOptimizer.get_scaled_loss` and
    `LossScaleOptimizer.get_unscaled_gradients` methods. For example:

    >>> with tf.GradientTape() as tape:
    ...   loss = loss_fn()
    ...   scaled_loss = opt.get_scaled_loss(loss)
    >>> scaled_grad = tape.gradient(scaled_loss, var)
    >>> (grad,) = opt.get_unscaled_gradients([scaled_grad])
    >>> opt.apply_gradients([(grad, var)])  # Loss scale is updated here
    >>> var.numpy()
    0.25

    Warning: If you forget to call `get_scaled_loss` or `get_unscaled_gradients`
    (or both) when using a `tf.GradientTape`, the model will likely converge to
    a worse quality. Please make sure you call each function exactly once.

    When mixed precision with float16 is used, there is typically no risk of
    underflow affecting model quality if loss scaling is properly used. See
    [the mixed precision guide](
    https://www.tensorflow.org/guide/keras/mixed_precision) for more information
    on how to use mixed precision.

    Args:
      inner_optimizer: The `tf.keras.optimizers.Optimizer` or
        `tf.keras.optimizers.experimental.Optimizer` instance to wrap.
      dynamic: Bool indicating whether dynamic loss scaling is used. If `True`,
        the loss scale will be dynamically updated over time using an algorithm
        that keeps the loss scale at approximately its optimal value. If False,
        a single fixed loss scale is used and  `initial_scale` must be
        specified, which is used as the loss scale.
        Recommended to keep as True, as choosing a fixed loss scale can be
        tricky. Currently, there is a small performance overhead to dynamic loss
        scaling compared to fixed loss scaling. Defaults to `True`.
      initial_scale: The initial loss scale. If `dynamic` is True, this defaults
        to `2 ** 15`. If `dynamic` is False, this must be specified and acts as
        the sole loss scale, as the loss scale does not change over time. When
        dynamic loss scaling is used, is better for this to be a very high
        number, because a loss scale that is too high gets lowered far more
        quickly than a loss scale that is too low gets raised.
      dynamic_growth_steps: With dynamic loss scaling, every
        `dynamic_growth_steps` steps with finite gradients, the loss scale is
        doubled. If a nonfinite gradient is encountered, the
        count is reset back to zero, gradients are skipped that step, and the
        loss scale is halved. The count can be queried with
        `LossScaleOptimizer.dynamic_counter`. This argument can only be
        specified if `dynamic` is True. Defaults to `2000`.

    `LossScaleOptimizer` will occasionally skip applying gradients to the
    variables, in which case the trainable variables will not change that step.
    This is done because the dynamic loss scale will sometimes be raised too
    high, causing overflow in the gradients. Typically, the first 2 to 15 steps
    of the model are skipped as the initial loss scale is very high, but
    afterwards steps will only be skipped on average 0.05% of the time (the
    fraction of steps skipped is `1 / dynamic_growth_steps`).

    `LossScaleOptimizer` delegates all public `Optimizer` methods to the inner
    optimizer. Additionally, in methods `minimize` and `get_gradients`, it
    scales the loss and unscales the gradients. In methods `minimize` and
    `apply_gradients`, it additionally updates the loss scale and skips applying
    gradients if any gradient has a nonfinite value.

    ### Hyperparameters

    If wrapping a `tf.keras.optimizers.Optimizer`, hyperparameters can be
    accessed and set on the LossScaleOptimizer, which will be delegated to the
    wrapped optimizer.

    >>> opt = tf.keras.optimizers.legacy.Adam(beta_1=0.8, epsilon=1e-5)
    >>> opt = tf.keras.mixed_precision.LossScaleOptimizer(opt)
    >>> opt.beta_1  # Equivalent to `opt.inner_optimizer.beta_1`
    0.8
    >>> opt.beta_1 = 0.7  # Equivalent to `opt.inner_optimizer.beta_1 = 0.7`
    >>> opt.beta_1
    0.7
    >>> opt.inner_optimizer.beta_1
    0.7

    However, accessing or setting non-hyperparameters is not delegated to the
    LossScaleOptimizer. In an Adam optimizer, `beta_1` is a hyperparameter but
    `epsilon` is not, as the Adam optimizer only calls `Optimizer._set_hyper` on
    `beta_1`.

    >>> opt.inner_optimizer.epsilon
    1e-5
    >>> opt.epsilon
    Traceback (most recent call last):
    ...
    AttributeError: 'LossScaleOptimizer' object has no attribute 'epsilon'
    >>> opt.epsilon = 1e-4  # This does NOT set epsilon on `opt.inner_optimizer`
    >>> opt.inner_optimizer.epsilon
    >>> 1e-5

    In the above example, despite epsilon being set on the LossScaleOptimizer,
    the old epsilon value will still be used when training as epsilon was not
    set on the inner optimizer.
    c                 C      t )z5Bool indicating whether dynamic loss scaling is used.NotImplementedErrorrs   r   r   r   dynamic     zBaseLossScaleOptimizer.dynamicc                 C   r   )z2The current loss scale as a float32 scalar tensor.r   rs   r   r   r   
loss_scale  r   z!BaseLossScaleOptimizer.loss_scalec                 C   r   )a  The number of steps since the loss scale was last increased or
        decreased.

        This is None if `LossScaleOptimizer.dynamic` is False.

        The counter is incremented every step. Once it reaches
        `LossScaleOptimizer.dynamic_growth_steps`, the loss scale will be
        doubled and the counter will be reset back to zero. If nonfinite
        gradients are encountered, the loss scale will be halved and the counter
        will be reset back to zero.
        r   rs   r   r   r   dynamic_counter  s   z&BaseLossScaleOptimizer.dynamic_counterc                 C   r   )zThe initial loss scale.

        If `LossScaleOptimizer.dynamic` is False, this is the same number as
        `LossScaleOptimizer.loss_scale`, as the loss scale never changes.
        r   rs   r   r   r   initial_scale  s   z$BaseLossScaleOptimizer.initial_scalec                 C   r   )zThe number of steps it takes to increase the loss scale.

        This is None if `LossScaleOptimizer.dynamic` is False.

        Every `dynamic_growth_steps` consecutive steps with finite gradients,
        the loss scale is increased.
        r   rs   r   r   r   dynamic_growth_steps  s   	z+BaseLossScaleOptimizer.dynamic_growth_stepsc                 C   r   )z7The optimizer that this LossScaleOptimizer is wrapping.r   rs   r   r   r   r     r   z&BaseLossScaleOptimizer.inner_optimizerc                 C   r   )a;  Scales the loss by the loss scale.

        This method is only needed if you compute gradients manually, e.g. with
        `tf.GradientTape`. In that case, call this method to scale the loss
        before passing the loss to `tf.GradientTape`. If you use
        `LossScaleOptimizer.minimize` or `LossScaleOptimizer.get_gradients`,
        loss scaling is automatically applied and this method is unneeded.

        If this method is called, `get_unscaled_gradients` should also be
        called.  See the `tf.keras.mixed_precision.LossScaleOptimizer` doc for
        an example.

        Args:
          loss: The loss, which will be multiplied by the loss scale. Can either
            be a tensor or a callable returning a tensor.

        Returns:
          `loss` multiplied by `LossScaleOptimizer.loss_scale`.
        r   )r   lossr   r   r   get_scaled_loss  s   z&BaseLossScaleOptimizer.get_scaled_lossc                 C   r   )a  Unscales the gradients by the loss scale.

        This method is only needed if you compute gradients manually, e.g. with
        `tf.GradientTape`. In that case, call this method to unscale the
        gradients after computing them with `tf.GradientTape`. If you use
        `LossScaleOptimizer.minimize` or `LossScaleOptimizer.get_gradients`,
        loss scaling is automatically applied and this method is unneeded.

        If this method is called, `get_scaled_loss` should also be called. See
        the `tf.keras.mixed_precision.LossScaleOptimizer` doc for an
        example.

        Args:
          grads: A list of tensors, each which will be divided by the loss
            scale. Can have None values, which are ignored.

        Returns:
          A new list the same size as `grads`, where every non-None value in
          `grads` is divided by `LossScaleOptimizer.loss_scale`.
        r   r   r&   r   r   r   get_unscaled_gradients0  s   z-BaseLossScaleOptimizer.get_unscaled_gradientsN)r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   g  s     |






r   )	metaclassc                       s  e Zd ZdZdZ			dQddZedd Zedd	 Zed
d Z	edd Z
edd Zedd Zdd Zdd ZdRddZdd Zdd Z	dSddZdd Zd d! ZedTd"d#Zed$d% Zejd&d% Zd'd( Zd)d* Zed+d, Zd-d. Zd/d0 Zed1d2 Zejd3d2 Zed4d5 Zejd6d5 Zed7d8 Z e jd9d8 Z d:d; Z!d<d= Z"d>d? Z#d@dA Z$dUdCdDZ%dEdF Z& fdGdHZ' fdIdJZ(edKdL Z)e)jdMdL Z)edNdO Z*e*jdPdO Z*  Z+S )Vr   zDAn optimizer that applies loss scaling to prevent numeric underflow.TNc                 C   s0  t |tjst |tjrtd| d| }t|t |ts'td|f t |tr3td|f t  t	|ddr@t
d|| _d| j_tjjj| | j |ro|d u rYt}|d u r_t}t||d	d
| _| | jd n|d u rwt
dt|| _|d urt
d|f d| _d| _| t| jd d S )Na  You passed an instance of the new experimental optimizer, `optimizer.Optimizer`, to LossScaleOptimizer, but only the classic optimizers subclassing from `tf.keras.optimizers.Optimizer` can be passed. Please use `loss_scale_optimizer.LossScaleOptimizerV3` instead of `tf.keras.mixed_precision.LossScaleOptimizer`, as the former supports wrapping instances of the new experimental optimizer. Got optimizer: zW"inner_optimizer" must be an instance of `tf.keras.optimizers.Optimizer`, but got: %s. zM"dynamic" argument to LossScaleOptimizer.__init__ must be a bool, but got: %rzFLossScaleOptimizer cannot wrap another LossScaleOptimizer, but got: %s#_is_wrapped_by_loss_scale_optimizerF}"inner_optimizer" is already wrapped by a LossScaleOptimizer. An optimizer can only be wrapped by a single LossScaleOptimizerT   rM   r   7"initial_scale" must be specified if "dynamic" is FalsezF"dynamic_growth_steps" must be None if "dynamic" is False, but got: %sbase_optimizer)r   r   r   r   r   r   boolr   _raise_if_strategy_unsupportedgetattr
ValueError
_optimizerr   r   __internal__trackingDelegatingTrackableMixinr   _DEFAULT_INITIAL_SCALE_DEFAULT_GROWTH_STEPSr9   _loss_scale_track_trackabler@   _loss_has_been_scaled_gradients_have_been_unscaledFakeOptimizerForRestoration)r   r   r   r   r   r   r   r   r   r   T  sz   





zLossScaleOptimizer.__init__c                 C      t | jtS r   r   r   r9   rs   r   r   r   r     ry   zLossScaleOptimizer.dynamicc                 C   &   t | jtrt| jjS t| jS r   r   r   r9   r   rw   r:   rs   r   r   r   r        zLossScaleOptimizer.loss_scalec                 C      t | jtr
| jjS d S r   r   r   r9   rv   rs   r   r   r   r        z"LossScaleOptimizer.dynamic_counterc                 C      t | jtr
| jjS | jS r   r   r   r9   rK   rs   r   r   r   r        z LossScaleOptimizer.initial_scalec                 C   r   r   r   r   r9   rL   rs   r   r   r   r     r   z'LossScaleOptimizer.dynamic_growth_stepsc                 C   rr   r   r   rs   r   r   r   r     rt   z"LossScaleOptimizer.inner_optimizerc                    4   d_ t r fdd}|S  tj j S )NTc                          } | t j| j S r   r   castr   r<   loss_valr   r   r   r   new_loss     z4LossScaleOptimizer.get_scaled_loss.<locals>.new_lossr   callabler   r   r   r<   r   r   r   r   r   r   r     
   z"LossScaleOptimizer.get_scaled_lossc                    "   d| _ d| j   fdd|D S )NT      ?c                    "   g | ]}|d urt | nd qS r   _multiply_gradientr"   loss_scale_reciprocalr   r   r%         
z=LossScaleOptimizer.get_unscaled_gradients.<locals>.<listcomp>r   r   r   r   r   r   r     
   

z)LossScaleOptimizer.get_unscaled_gradientsc           	      C   s   |d u rt  n|}| | |}W d    n1 sw   Y  | jj||||d}dd |D }dd |D }| |}tt||S )Ntapec                 S      g | ]\}}|qS r   r   r#   r   _r   r   r   r%         z9LossScaleOptimizer._compute_gradients.<locals>.<listcomp>c                 S      g | ]\}}|qS r   r   r#   r   rm   r   r   r   r%     r   )r   GradientTaper   r   _compute_gradientsr   listzip)	r   r   var_list	grad_lossr   grads_and_varsr&   rl   unscaled_gradsr   r   r   r     s   
z%LossScaleOptimizer._compute_gradientsc                 C   s"   |  |}| j||}| |S r   )r   r   get_gradientsr   )r   r   paramsr&   r   r   r   r     s   

z LossScaleOptimizer.get_gradientsc                 C      | j | d S r   )r   _create_all_weightsr   r   r   r   r   r        z&LossScaleOptimizer._create_all_weightsc           	         s   t j r	tdt  tjj t	|}|r&j
|}j
|}t|}dd |D tdd |D fddfdd t jj rj \}}fd	d
}t jj||}t ||S  fdd}t j j|fdS )N6apply_gradients() must be called in a replica context.c                 S   r   r   r   r   r   r   r   r%      r   z6LossScaleOptimizer.apply_gradients.<locals>.<listcomp>c                 S   r   r   r   r   r   r   r   r%   &  r   c                      s    j jjdddS Nr   F)
read_valuer   
iterationsr   r   rs   r   r   do_not_apply_fn(  s   z;LossScaleOptimizer.apply_gradients.<locals>.do_not_apply_fnc                    s$   t  jtr j| S t dfS NT)r   r   r9   rh   r   r2   )r&   rs   r   r   _if_should_apply_grads.  s   zBLossScaleOptimizer.apply_gradients.<locals>._if_should_apply_gradsc                      s     S r   _apply_gradientsr   )r&   r;   r   wrapped_varsr   r   apply_fn9     z4LossScaleOptimizer.apply_gradients.<locals>.apply_fnc                    s>   \}} fdd}t jj||}t ||S )Nc                      s    j jjfdS Nrz   r   r   r  r   )r   r&   r;   r   r  r   r   r  K  s   z\LossScaleOptimizer.apply_gradients.<locals>._apply_gradients_cross_replica.<locals>.apply_fn)r   r   
smart_condr|   )r   r&   r  r;   loss_scale_update_opshould_apply_gradsr  maybe_apply_opr  r  r   )r   r&   r;   r  r   _apply_gradients_cross_replicaC  s   	zJLossScaleOptimizer.apply_gradients.<locals>._apply_gradients_cross_replicarz   )r   r   r   r   r   r8   r   r   optimizer_utilsfilter_empty_gradientsr   !_transform_unaggregated_gradients_aggregate_gradientstupler
   r   strategy_supports_no_merge_callr  r|   get_replica_context
merge_call)	r   r   r;    experimental_aggregate_gradientsr  r  r  r  r  r   )r  r  r&   r;   r   r  r   apply_gradients  sD   



z"LossScaleOptimizer.apply_gradientsc                 C   s   | j jtt||j|ddS )NF)r;   r  r   r  r   r   r   )r   r&   r  r;   r   r   r   r  ]  s
   z#LossScaleOptimizer._apply_gradientsc                 C       t | j}|| j| j| jdS N)r   r   r   r   r   	serializer   r   r   r   r   serialized_optimizerr   r   r   
get_configi     zLossScaleOptimizer.get_configc                 C   s  |  }d|v r`tj|dtjjjjtjjjj	ddd}t
|tjjjjr1d|d< |j|d< n(t
|tjjjj	rUd|d< |j|d< |j|d	< |jd
krTtd|f ntd|d|d< t
|d tjrm|d }n
tj|d |dd}|d= | |fi |S )Nr   )FixedLossScaleDynamicLossScalez
loss scale)module_objectsprintable_module_nameFr   r   Tr   r   zqCannot deserialize LossScaleOptimizer with a DynamicLossScale whose multiplier is not 2. Got DynamicLossScale: %szSerialized LossScaleOptimizers with a LossScale that is neither a FixedLossScale nor a DynamicLossScale can no longer be deserializedr   r   custom_objectsuse_legacy_optimizer)copyr   deserialize_keras_objectpopr   rY   rZ   mixed_precisionr$  r%  r   _loss_scale_valuerK   increment_periodrM   r   r   r   r   deserialize)r   configr)  r   r   r   r   r   from_configr  sP   

	



zLossScaleOptimizer.from_configc                 C      | j jS r   r   r   rs   r   r   r   r        zLossScaleOptimizer.iterationsc                 C      || j _d S r   r5  r   r_   r   r   r   r     ry   c                 C   
   | j  S r   r   get_slot_namesrs   r   r   r   r;    r   z!LossScaleOptimizer.get_slot_namesc                 C   r9  r   r   	variablesrs   r   r   r   r=    r   zLossScaleOptimizer.variablesc                 C   r4  r   )r   rl   rs   r   r   r   rl     r6  zLossScaleOptimizer.weightsc                 C   r9  r   )r   get_weightsrs   r   r   r   r>    r   zLossScaleOptimizer.get_weightsc                 C      | j |S r   )r   set_weights)r   rl   r   r   r   r@       zLossScaleOptimizer.set_weightsc                 C   r4  r   r   clipnormrs   r   r   r   rC    r6  zLossScaleOptimizer.clipnormc                 C   r7  r   rB  r   valr   r   r   rC    ry   c                 C   r4  r   r   global_clipnormrs   r   r   r   rG    r6  z"LossScaleOptimizer.global_clipnormc                 C   r7  r   rF  rD  r   r   r   rG    ry   c                 C   r4  r   r   	clipvaluers   r   r   r   rI    r6  zLossScaleOptimizer.clipvaluec                 C   r7  r   rH  rD  r   r   r   rI    ry   c                 C   r?  r   )r   r  )r   r   r   r   r   r    rA  z'LossScaleOptimizer._aggregate_gradientsc                 C      | j |||S r   )r   _restore_slot_variable)r   	slot_namer_   slot_variabler   r   r   rK    s
   z)LossScaleOptimizer._restore_slot_variablec                 C   rJ  r   r    _create_or_restore_slot_variabler   slot_variable_positionrL  r_   r   r   r   rO       z3LossScaleOptimizer._create_or_restore_slot_variablec                 C   s   | j ||S r   )r   get_slot)r   r/   rL  r   r   r   rS    r  zLossScaleOptimizer.get_slotzerosc                 C   rJ  r   )r   add_slot)r   r/   rL  initializerr   r   r   rU    r   zLossScaleOptimizer.add_slotc              
   C   sp   zt | |W S  ty7 } z$|dks|dkr||dkrd}|| jjv r1| j|W  Y d }~S |d }~ww )Nr   _hyperlrlearning_rate)object__getattribute__AttributeErrorr   rW  
_get_hyper)r   r;   er   r   r   r[    s   z#LossScaleOptimizer.__getattribute__c                    sH   t t  }d|v r || jj O }d| jj v r |d t|S )Nr   rY  rX  )setr?   __dir__r   rW  keysaddr   )r   resultrN   r   r   r`    s   
zLossScaleOptimizer.__dir__c                    s|   |dkrd}z|dkrt | | d}W n ty   d}Y nw |dkr5|| jjv r5|s5| j|| d S t || d S )NrX  rY  r   TFr   )rZ  r[  r\  r   rW  
_set_hyperr?   __setattr__)r   r;   r   has_attributerN   r   r   re    s   zLossScaleOptimizer.__setattr__c                 C   r4  r   r   rY  rs   r   r   r   rY  !  r6  z LossScaleOptimizer.learning_ratec                 C   r7  r   rg  r   r   r   r   rY  %  ry   c                 C   r4  r   rg  rs   r   r   r   rX  )  r6  zLossScaleOptimizer.lrc                 C   r7  r   )r   rX  r   r   r   r   rX  -  ry   TNN)NNr  r   )rT  ),r   r   r   r   _HAS_AGGREGATE_GRADr   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r"  classmethodr3  r   setterr;  r=  rl   r>  r@  rC  rG  rI  r  rK  rO  rS  rU  r[  r`  re  rY  rX  r   r   r   rN   r   r   K  s    
`








[	5












r   c                   @   s:  e Zd ZdZejjj			d3ddZe	dd Z
e	dd	 Ze	d
d Ze	dd Ze	dd Ze	dd Zdd Zdd Zd4ddZ	d5ddZdd Zdd Zed4dd Ze	d!d" Zejd#d" Ze	d$d% Zd&d' Ze	d(d) Zejd*d) Ze	d+d, Zejd-d, Ze	d.d/ Zejd0d/ Zd1d2 ZdS )6r   aP  An optimizer that applies loss scaling to prevent numeric underflow.

    This is a copy of the `mixed_precision.LossScaleOptimizer` class
    defined above, except it subclasses and wraps the new experimental Optimizer
    class instead of the `tf.keras.optimizers.Optimizer` class. Some of the
    methods this class defines and calls are different compared to
    LossScaleOptimizer due to the differences between the two Optimizer base
    classes. Additionally, this class does not support the legacy graph mode,
    but LossScaleOptimizer does.

    Since the new experimental Optimizer does not have a hyperparameter concept,
    LossScaleOptimizerV3 does not delegate arbitrary hyperparameter accesses to
    the inner optimizer, unlike LossScaleOptimizer. LossScaleOptimizerV3 does
    delegate the "learning_rate" attribute, however.
    TNc                 C   s"  t |tjst |tjrtd| td| dt |ts)tdt| t |tr5td| t	  t
|ddrBtd|| _d	| j_tjjj| | j |rq|d u r[t}|d u rat}t||d
d| _| | jd n|d u rytdt|| _|d urtd| d| _d| _d S )Na]  You passed a `tf.keras.optimizers.Optimizer` instance to LossScaleOptimizerV3, but only the new experimental optimizer defined in keras/optimizer_expeirmental/optimizer.py can be passed. Please use `tf.keras.mixed_precision.LossScaleOptimizer` instead of LossScaleOptimizerV3, as the former supports `tf.keras.optimizers.Optimizer`s. Got optimizer: z="inner_optimizer" must be an instance of Optimizer, but got: r   zK"dynamic" argument to LossScaleOptimizer.__init__ must be a bool, but got: zDLossScaleOptimizer cannot wrap another LossScaleOptimizer, but got: r   Fr   Tr   r   r   r   zD"dynamic_growth_steps" must be None if "dynamic" is False, but got: )r   r   r   r   r   r   r   reprr   r   r   r   r   r   r   r   r   r   r   r   r   r9   r   r   r@   r   r   )r   r   r   r   r   r   r   r   r   U  st   




zLossScaleOptimizerV3.__init__c                 C   r   r   r   rs   r   r   r   r     ry   zLossScaleOptimizerV3.dynamicc                 C   r   r   r   rs   r   r   r   r     r   zLossScaleOptimizerV3.loss_scalec                 C   r   r   r   rs   r   r   r   r     r   z$LossScaleOptimizerV3.dynamic_counterc                 C   r   r   r   rs   r   r   r   r     r   z"LossScaleOptimizerV3.initial_scalec                 C   r   r   r   rs   r   r   r   r     r   z)LossScaleOptimizerV3.dynamic_growth_stepsc                 C   rr   r   r   rs   r   r   r   r     rt   z$LossScaleOptimizerV3.inner_optimizerc                    r   )NTc                     r   r   r   r   r   r   r   r     r   z6LossScaleOptimizerV3.get_scaled_loss.<locals>.new_lossr   r   r   r   r   r     r   z$LossScaleOptimizerV3.get_scaled_lossc                    r   )NTr   c                    r   r   r   r"   r   r   r   r%     r   z?LossScaleOptimizerV3.get_unscaled_gradients.<locals>.<listcomp>r   r   r   r   r   r     r   z+LossScaleOptimizerV3.get_unscaled_gradientsc                 C   s   |d u rt  n|}| | |}W d    n1 sw   Y  | jj|||d}dd |D }dd |D }| |}tt||S )Nr   c                 S   r   r   r   r   r   r   r   r%     r   z:LossScaleOptimizerV3.compute_gradients.<locals>.<listcomp>c                 S   r   r   r   r   r   r   r   r%     r   )r   r   r   r   compute_gradientsr   r   r   )r   r   r   r   r   r&   rl   r   r   r   r   rm    s   
z&LossScaleOptimizerV3.compute_gradientsFc           
         sP  t |}t| \}t  | W d    n1 sw   Y  tj r,tdt  t	j
j t|}|dd}jjpHjj}|sU|rU|sUj|}t|}dd |D tdd |D fddfd	d
 tjj r }fdd}tjj|| d S  fdd}	tj j|	fd d S )Nr   r  Tc                 S   r   r   r   r   r   r   r   r%   $  r   z8LossScaleOptimizerV3.apply_gradients.<locals>.<listcomp>c                 S   r   r   r   r   r   r   r   r%   *  r   c                      s    j jjddd d S r   r   r   rs   r   r   r  ,  s   z=LossScaleOptimizerV3.apply_gradients.<locals>.do_not_apply_fnc                    s$   t  jtr j| \}}|S dS r  )r   r   r9   rh   )r&   r   should_apply_gradrs   r   r   r  2  s   zDLossScaleOptimizerV3.apply_gradients.<locals>._if_should_apply_gradsc                      s     S r   r  r   )r&   r   r  r   r   r  <  rA  z6LossScaleOptimizerV3.apply_gradients.<locals>.apply_fnc                    s0   } fdd}t jj|| d S )Nc                      s    j jjfd d S r	  r
  r   )r   r&   r   r  r   r   r  I  s   

z^LossScaleOptimizerV3.apply_gradients.<locals>._apply_gradients_cross_replica.<locals>.apply_fn)r   r   r  )r   r&   r  r  r  r  )r   r&   r  r   r  D  s
   	zLLossScaleOptimizerV3.apply_gradients.<locals>._apply_gradients_cross_replicarz   )r   r   r   
init_scopebuildr   r   r   r   r8   r   r   r  r  r-  r   _run_with_dtensor_meshaggregate_gradientsr  r
   r   r  r  r  r  )
r   r   skip_gradients_aggregationrk   trainable_variablesr  run_with_dtensorr  r  r  r   )r  r  r&   r   r  r   r    sT   


	


z$LossScaleOptimizerV3.apply_gradientsc                 C   s    | j jtt||jdd d S )NT)rt  r  )r   r&   r  r   r   r   r  Z  s   
z%LossScaleOptimizerV3._apply_gradientsc                 C   r  r  r  r   r   r   r   r"  b  r#  zLossScaleOptimizerV3.get_configc                 C   sL   |  }t|d tjr|d }n
tj|d |dd}|d= | |fi |S )Nr   Fr(  )r+  r   r   r   r   r1  )r   r2  r)  r   r   r   r   r3  k  s   
z LossScaleOptimizerV3.from_configc                 C   r4  r   r5  rs   r   r   r   r   y  r6  zLossScaleOptimizerV3.iterationsc                 C   r7  r   r5  r8  r   r   r   r   }  ry   c                 C   r4  r   r<  rs   r   r   r   r=    r6  zLossScaleOptimizerV3.variablesc                 C   r?  r   )r   rp  r   r   r   r   rp    rA  zLossScaleOptimizerV3.buildc                 C   r4  r   rg  rs   r   r   r   rY    r6  z"LossScaleOptimizerV3.learning_ratec                 C   r7  r   rg  )r   rY  r   r   r   rY    ry   c                 C   r4  r   r   use_emars   r   r   r   rx    r6  zLossScaleOptimizerV3.use_emac                 C   r7  r   rw  )r   rx  r   r   r   rx    ry   c                 C   r4  r   r   ema_momentumrs   r   r   r   rz    r6  z!LossScaleOptimizerV3.ema_momentumc                 C   r7  r   ry  )r   rz  r   r   r   rz    ry   c                 C   r   r   )r   finalize_variable_valuesr   r   r   r   r{    r   z-LossScaleOptimizerV3.finalize_variable_valuesrh  r   )F) r   r   r   r   r   r   r    no_automatic_dependency_trackingr   r   r   r   r   r   r   r   r   r   rm  r  r  r"  rj  r3  r   rk  r=  rp  rY  rx  rz  r{  r   r   r   r   r   @  s`    W








g	








r   c                   @   s(   e Zd ZdZdd Zdd Zdd ZdS )	r   a;  A fake optimizer used to support restoring TensorFlow 2.2 checkpoints.

    The checkpoint format for LossScaleOptimizers changed after TF 2.2. This
    class exists to support restoring TF 2.2 checkpoints in newer version of
    TensorFlow.

    In TF 2.2, LossScaleOptimizer would track the wrapped optimizer by calling
    the following in LossScaleOptimizer.__init__

    ```
    self._track_trackable(self._optimizer, 'base_optimizer')
    ```

    This means a dependency from the LossScaleOptimizer to the wrapped optimizer
    would be stored in the checkpoint. However now, the checkpoint format with a
    LossScaleOptimizer is the same as the format without a LossScaleOptimizer,
    except the loss scale is also stored. This means there is no dependency from
    the LossScaleOptimizer to the wrapped optimizer. Instead, the
    LossScaleOptimizer acts as if it is the wrapped optimizer, from a
    checkpoint's perspective, by overriding all Trackable methods and delegating
    them to the wrapped optimizer.

    To allow restoring TF 2.2. checkpoints, LossScaleOptimizer adds a dependency
    on this class instead of the inner optimizer. When restored, this class will
    instead restore the slot variables of the inner optimizer. Since this class
    has no variables, it does not affect the checkpoint when saved.
    c                 C   r   r   r   )r   r   r   r   r   r     r   z$FakeOptimizerForRestoration.__init__c                 C   r9  r   r:  rs   r   r   r   r;    r   z*FakeOptimizerForRestoration.get_slot_namesc                 C   rJ  r   rN  rP  r   r   r   rO    rR  z<FakeOptimizerForRestoration._create_or_restore_slot_variableN)r   r   r   r   r   r;  rO  r   r   r   r   r     s
    r   c                 C   s   t |ttfrt| d|dS t |tjjjjr!|j	}t| d|dS |dkr)t| S t |tjjjj
rG|jdkr>td| t| |j|jdS t |tjjjjrWtd| td| )	aF  Creates an LSO from a tf.compat.v1.mixed_precision.LossScale.

    This is only used to pass to
    `tf.__internal__.mixed_precision.register_loss_scale_wrapper` below, which
    is called so that
    `tf.compat.v1.mixed_precision.enable_mixed_precision_graph_rewrite` can
    wrap a Keras optimizer with a LossScaleOptimizer.

    Args:
      optimizer: An OptimizerV2 instance.
      loss_scale: A `tf.compat.v1.mixed_precision.LossScale` instance

    Returns:
      A LossScaleOptimizer that wraps `optimizer` and uses the same loss scaling
      algorithm as `loss_scale`.
    F)r   r   r   r   z]When passing a DynamicLossScale to "loss_scale", DynamicLossScale.multiplier must be 2. Got: )r   r   z^Passing a LossScale that is not a FixedLossScale or a DynamicLossScale is not supported. Got: zInvalid value passed to loss_scale. loss_scale must be the string "dynamic" (recommended), an int, a float, a FixedLossScale, or a DynamicLossScale. Got value: )r   rB   r@   r   r   rY   rZ   r.  r$  r/  r%  rM   r   rK   r0  	LossScaler   )r   r   ls_valr   r   r   /_create_loss_scale_optimizer_from_v1_loss_scale  sD   
r  c                 C   s<   t || j}t| t jrt j| j| | j| jdS | | S )z@Multiply a (possibly sparse) gradient by the given scale factor.)dense_shape)r   r   r<   r   r   r   indicesr  )gradientscaler   r   r   r     s   r   c                  C   s\   t j sdS t j } t| t jjt jjjjjt jj	t jjjj	t jj
t jjjj
fp-t S )z;Returns True if the current Strategy supports loss scaling.T)r   r   r   r   r   MultiWorkerMirroredStrategyrY   rZ   experimentalOneDeviceStrategyMirroredStrategydtensor_utilsrunning_with_dtensor_strategystrategyr   r   r   strategy_supports_loss_scaling  s   




r  c                  C   sT   t  s(tj } t| tjjjtjjjjjtjjfrt	dt	d| j
j ddS )zLRaise an exception if the current strategy doesn't support loss
    scaling.zLoss scaling is not supported with TPUStrategy. Loss scaling is unnecessary with TPUs, since they support bfloat16 instead of float16 and bfloat16 does not require loss scaling. You should remove the use of the LossScaleOptimizer when TPUs are used.z?Loss scaling is not supported with the tf.distribute.Strategy: z9. Try using a different Strategy, e.g. a MirroredStrategyN)r  r   r   r   r   r  TPUStrategyrY   rZ   r   rO   r   r  r   r   r   r   6  s$   
r   )0r   tensorflow.compat.v2rY   v2r   	keras.srcr   r   keras.src.dtensorr   r  keras.src.optimizersr   r  keras.src.optimizers.legacyr   keras.src.savingr   tensorflow.python.platformr    tensorflow.python.util.tf_exportr	   r
   r(   r,   r3   r8   r   r   	Trackabler9   r   r   typer   r   r   r   r   r   r   r   r  r.  register_loss_scale_wrapperr   r  r   r   r   r   r   <module>   sb   	. 8" 
d   
x  f+6