Ë
    ëÓ^j$  ã                  ó~   — d Z ddlmZ ddlZddlZddlmZ ddlmZm	Z	 ddl
Z
ddlmZmZmZ ddlmZ  G d„ d	e«      Zy)
zAExponential Moving Average callback compatible with ``ModelEma``.é    )ÚannotationsN)Údeepcopy)ÚAnyÚOptional)ÚCallbackÚLightningModuleÚTrainer)ÚAveragedModelc                  óØ   ‡ — e Zd ZdZ	 	 	 	 d	 	 	 	 	 	 	 	 	 dˆ fd„Z	 	 	 	 	 	 	 	 dd„Zdd„Z	 	 d	 	 	 	 	 dd„Zdd„Z	 	 	 	 	 	 	 	 	 	 	 	 dd„Z	dd„Z
dd	„Zdd
„Zdd„Zdd„Zdd„Zdd„Zˆ xZS )ÚRFDETREMACallbacka/  Exponential Moving Average with optional tau-based warm-up.

    Drop-in replacement for ``rfdetr.util.utils.ModelEma`` implemented as a plain Lightning callback around
    :class:`torch.optim.swa_utils.AveragedModel`. The ``_avg_fn`` reproduces the exact same formula as ``ModelEma``
    (1-indexed ``updates`` counter, optional ``tau`` warm-up).

    Args:
        decay: Base EMA decay factor. Corresponds to ``TrainConfig.ema_decay``.
        tau: Warm-up time constant (in optimizer steps). When > 0 the
            effective decay ramps from 0 towards *decay* following ``decay * (1 - exp(-updates / tau))``. Corresponds to
            ``TrainConfig.ema_tau``.
        use_buffers: Whether buffers are averaged in addition to parameters.
        update_interval_steps: Update EMA every N optimizer steps.
    c                óÆ   •— t         ‰| �  «        || _        || _        || _        t        dt        |«      «      | _        d | _        d| _	        d| _
        d | _        d | _        y )Né   r   éÿÿÿÿ)ÚsuperÚ__init__Ú_decayÚ_tauÚ_use_buffersÚmaxÚintÚ_update_interval_stepsÚ_average_modelÚ_latest_update_stepÚ_latest_update_epochÚ_swapped_state_dictÚ_pending_average_state_dict)ÚselfÚdecayÚtauÚuse_buffersÚupdate_interval_stepsÚ	__class__s        €úh/var/www/ramen.bs-engineer-server.com/venv/lib/python3.12/site-packages/rfdetr/training/callbacks/ema.pyr   zRFDETREMACallback.__init__$   sc   ø€ ô 	‰ÑÔØˆŒØˆŒ	Ø'ˆÔÜ&)¨!¬SÐ1FÓ-GÓ&HˆÔ#à7;ˆÔØ#$ˆÔ Ø$&ˆÔ!ØFJˆÔ ØEIˆÕ(ó    c                óÆ   — |dz   }| j                   dkD  r4| j                  dt        j                  | | j                   z  «      z
  z  }n| j                  }||z  |d|z
  z  z   S )aN  Compute the EMA update for a single parameter tensor.

        Matches the ``ModelEma`` formula where ``updates`` is 1-indexed: PTL's ``num_averaged`` starts at 0 (incremented
        *after* calling ``avg_fn``), so ``updates = num_averaged + 1`` reproduces the same sequence of effective decay
        values.

        Args:
            averaged_param: Current EMA parameter value.
            model_param: Corresponding live model parameter value.
            num_averaged: Number of models averaged so far (0-indexed).

        Returns:
            Updated EMA parameter tensor.
        r   r   g      ð?)r   r   ÚmathÚexp)r   Úaveraged_paramÚmodel_paramÚnum_averagedÚupdatesÚeffective_decays         r#   Ú_avg_fnzRFDETREMACallback._avg_fn7   sc   € ð(  Ñ"ˆØ�9‰9�qŠ=Ø"Ÿk™k¨Q´·±¸7¸(ÀTÇYÁYÑ:NÓ1OÑ-OÑP‰Oà"Ÿk™kˆOØ Ñ/°+ÀÀÑAVÑ2WÑWÐWr$   c                ó¨  — |dk7  ryt        ||j                  | j                  | j                  ¬«      | _        | j                  j                  «        | j                  �-| j                  j                  | j                  «       d| _        yt        |d«      r»t        |d«      }t        |t        «      r’| j                  j                  j                  j                  |d¬«      }|j                  s|j                  rIt!        j"                  dt%        |j                  «      › dt%        |j                  «      › d	�t&        d
¬«       t)        |d«       yy)a  Initialise the averaged model at fit start.

        Args:
            trainer: The Lightning Trainer instance.
            pl_module: The ``RFDETRModelModule`` being trained.
            stage: Current trainer stage (``"fit"``, ``"validate"``, ...).
        ÚfitN)ÚmodelÚdevicer    Úavg_fnÚ_pending_legacy_ema_stateF©Ústrictz?Legacy EMA checkpoint loaded with non-exact key match; missing=z unexpected=ú.é   )Ú
stacklevel)r
   r1   r   r-   r   Úevalr   Úload_state_dictÚhasattrÚgetattrÚ
isinstanceÚdictÚmoduler0   Úmissing_keysÚunexpected_keysÚwarningsÚwarnÚlenÚUserWarningÚdelattr)r   ÚtrainerÚ	pl_moduleÚstageÚlegacy_ema_stateÚincompatibles         r#   ÚsetupzRFDETREMACallback.setupR   s2  € ð �EŠ>Øä+ØØ×#Ñ#Ø×)Ñ)Ø—<‘<ô	
ˆÔð 	×Ñ× Ñ Ô"à×+Ñ+Ð7Ø×Ñ×/Ñ/°×0PÑ0PÔQØ/3ˆDÕ,Ü�YÐ ;Ô<Ü& yÐ2MÓNÐÜÐ*¬DÔ1Ø#×2Ñ2×9Ñ9×?Ñ?×OÑOÐP`ÐinÐOÓo�Ø×,Ò,°×0LÒ0LÜ—M‘Mð#Ü#& |×'@Ñ'@Ó#AÐ"Bð C&Ü&)¨,×*FÑ*FÓ&GÐ%HÈðKô $Ø#$õô �IÐ:Õ;ð =r$   c                ó   — |duxs |duS )aÐ  Return ``True`` after every optimizer step and every epoch end.

        The base ``WeightAveraging`` only updates on steps. This override also triggers an update at epoch boundaries,
        matching RF-DETR's existing EMA behaviour.

        Args:
            step_idx: Index of the last optimizer step, or ``None``.
            epoch_idx: Index of the last epoch, or ``None``.

        Returns:
            Whether the averaged model should be updated.
        N© )r   Ústep_idxÚ	epoch_idxs      r#   Úshould_updatezRFDETREMACallback.should_updatey   s   € ð" ˜tÐ#Ò< y¸Ð'<Ð<r$   c                ó&  — | j                   €y| j                  €Tt        |j                  «       «      | _        |j	                  | j                   j
                  j                  «       d¬«       y|j	                  | j                  d¬«       d| _        y)z2Swap live model weights with averaged EMA weights.NTr4   )r   r   r   Ú
state_dictr:   r?   )r   rH   s     r#   Ú_swap_modelszRFDETREMACallback._swap_modelsŒ   s€   € à×ÑÐ&ØØ×#Ñ#Ð+Ü'/°	×0DÑ0DÓ0FÓ'GˆDÔ$Ø×%Ñ% d×&9Ñ&9×&@Ñ&@×&KÑ&KÓ&MÐVZÐ%Ô[ØØ×!Ñ! $×":Ñ":À4Ð!ÔHØ#'ˆÕ r$   c                ó,  — | j                   €y|j                  dz
  }|j                  | j                  k  ry|j                  | _        |j                  | j                  z  dk(  }|r/| j	                  |¬«      r| j                   j                  |«       yyy)z!Update EMA after optimizer steps.Nr   r   )rO   )r   Úglobal_stepr   r   rQ   Úupdate_parameters)r   rG   rH   ÚoutputsÚbatchÚ	batch_idxrO   Úshould_update_steps           r#   Úon_train_batch_endz$RFDETREMACallback.on_train_batch_end—   s•   € ð ×ÑÐ&ØØ×&Ñ&¨Ñ*ˆØ×Ñ $×":Ñ":Ò:Øà#*×#6Ñ#6ˆÔ Ø$×0Ñ0°4×3NÑ3NÑNÐRSÑSÐÙ $×"4Ñ"4¸hÐ"4Ô"GØ×Ñ×1Ñ1°)Õ<ð #HÐr$   c                óä   — | j                   €y|j                  | j                  kD  rJ| j                  |j                  ¬«      r-| j                   j	                  |«       |j                  | _        yyy)z*Optionally update EMA at epoch boundaries.N)rP   )r   Úcurrent_epochr   rQ   rW   ©r   rG   rH   s      r#   Úon_train_epoch_endz$RFDETREMACallback.on_train_epoch_end«   sh   € à×ÑÐ&ØØ× Ñ  4×#<Ñ#<Ò<À×ASÑASÐ^e×^sÑ^sÐASÔAtØ×Ñ×1Ñ1°)Ô<Ø(/×(=Ñ(=ˆDÕ%ð BuÐ<r$   c                ó&   — | j                  |«       y)z*Evaluate tests using averaged EMA weights.N©rT   r_   s      r#   Úon_test_epoch_startz%RFDETREMACallback.on_test_epoch_start³   ó   € à×Ñ˜)Õ$r$   c                ó&   — | j                  |«       y)z+Restore live weights after test evaluation.Nrb   r_   s      r#   Úon_test_epoch_endz#RFDETREMACallback.on_test_epoch_end·   rd   r$   c                ó”   — | j                   �5|j                  | j                   j                  j                  «       d¬«       d| _        y)z6Leave the module in EMA state after training finishes.NTr4   )r   r:   r?   rS   r   r_   s      r#   Úon_train_endzRFDETREMACallback.on_train_end»   s?   € à×ÑÐ*Ø×%Ñ% d×&9Ñ&9×&@Ñ&@×&KÑ&KÓ&MÐVZÐ%Ô[Ø#'ˆÕ r$   c                óŠ   — | j                   | j                  dœ}| j                  �| j                  j                  «       |d<   |S )z(Return callback state for checkpointing.)Úlatest_update_stepÚlatest_update_epochÚaverage_model_state_dict)r   r   r   rS   )r   Ústates     r#   rS   zRFDETREMACallback.state_dictÁ   sJ   € ð #'×":Ñ":Ø#'×#<Ñ#<ñ
ˆð ×ÑÐ*Ø04×0CÑ0C×0NÑ0NÓ0PˆEÐ,Ñ-Øˆr$   c                óŒ   — |j                  dd«      | _        |j                  dd«      | _        |j                  d«      | _        y)z(Restore callback state from checkpoints.rj   r   rk   r   rl   N)Úgetr   r   r   )r   rS   s     r#   r:   z!RFDETREMACallback.load_state_dictË   s<   € à#-§>¡>Ð2FÈÓ#JˆÔ Ø$.§N¡NÐ3HÈ"Ó$MˆÔ!Ø+5¯>©>Ð:TÓ+UˆÕ(r$   c                ó@  — | j                   � t        | j                   j                  d«      sy| j                   j                  j                  j	                  «       j                  «       D ��ci c]$  \  }}||j                  «       j                  «       “Œ& c}}S c c}}w )z;Expose EMA model weights for external checkpoint callbacks.Nr0   )r   r;   r?   r0   rS   ÚitemsÚdetachÚclone)r   ÚkÚvs      r#   Úget_ema_model_state_dictz*RFDETREMACallback.get_ema_model_state_dictÑ   sw   € à×ÑÐ&¬g°d×6IÑ6I×6PÑ6PÐRYÔ.ZØØ26×2EÑ2E×2LÑ2L×2RÑ2R×2]Ñ2]Ó2_×2eÑ2eÓ2g×h©$¨!¨Q��1—8‘8“:×#Ñ#Ó%Ñ%ÓhÐhùÓhs   Á-)B)g-²�ï§Æï?éd   Tr   )
r   Úfloatr   r   r    Úboolr!   r   ÚreturnÚNone)r(   útorch.Tensorr)   r|   r*   r   rz   r|   )rG   r	   rH   r   rI   Ústrrz   r{   )NN)rO   úOptional[int]rP   r~   rz   ry   )rH   r   rz   r{   )rG   r	   rH   r   rX   r   rY   r   rZ   r   rz   r{   )rG   r	   rH   r   rz   r{   )rz   údict[str, Any])rS   r   rz   r{   )rz   z!Optional[dict[str, torch.Tensor]])Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r-   rL   rQ   rT   r\   r`   rc   rf   rh   rS   r:   rv   Ú__classcell__)r"   s   @r#   r   r      s  ø„ ñð" ØØ Ø%&ðJàðJð ðJð ð	Jð
  #ðJð 
õJð&Xà$ðXð "ðXð ð	Xð
 
óXó6%<ðR #'Ø#'ð=àð=ð !ð=ð 
ó	=ó&	(ð=àð=ð #ð=ð ð	=ð
 ð=ð ð=ð 
ó=ó(>ó%ó%ó(óóV÷ir$   r   )rƒ   Ú
__future__r   r&   rB   Úcopyr   Útypingr   r   ÚtorchÚpytorch_lightningr   r   r	   Útorch.optim.swa_utilsr
   r   rN   r$   r#   ú<module>r‹      s6   ðñ Hå "ã Û Ý ß  ã ß @Ñ @Ý /ôAi˜õ Air$   