o
    }oi3                     @   s   d dl Z d dlZd dlZd dlZd dlmZmZmZ d dlm	Z
 d dlZd dlmZ d dlmZ d dlmZ G dd deZe dd	 Zdd
dZG dd dejjZdS )    N)AnyDictIterable)Callback)MisconfigurationException)rank_zero_infoc                	   @   s   e Zd ZdZ			d(dedededefdd	Zd)ddZd)ddZ	d)ddZ
d)ddZd)ddZd
ddefddZd
ddefddZd*d
ddefdd Zejd+d!d"Zejd+d#d$Zd
dddd%eeef ddfd&d'ZdS ),EMAaj  
    Implements Exponential Moving Averaging (EMA).

    When training a model, this callback will maintain moving averages of the trained parameters.
    When evaluating, we use the moving averages copy of the trained parameters.
    When saving, we save an additional set of parameters with the prefix `ema`.

    Args:
        decay: The exponential decay used when calculating the moving average. Has to be between 0-1.
        validate_original_weights: Validate the original weights, as apposed to the EMA weights.
        every_n_steps: Apply EMA every N steps.
        cpu_offload: Offload weights to CPU.
    F   decayvalidate_original_weightsevery_n_stepscpu_offloadc                 C   s@   d|  krdkst d t d|| _|| _|| _|| _d S )Nr   r	   z'EMA decay value must be between 0 and 1)r   r
   r   r   r   )selfr
   r   r   r    r   Y/home/ubuntu/.local/lib/python3.10/site-packages/nemo/collections/common/callbacks/ema.py__init__*   s   
zEMA.__init__trainer
pl.Trainer	pl_modulepl.LightningModulereturnNc                    s4   j s|jntd  fddjD _d S )Ncpuc              	      s.   g | ]}t |tst| jjjd qS ))devicer
   r   current_step)
isinstanceEMAOptimizerr
   r   global_step).0optimr   r   r   r   r   
<listcomp>:   s    z$EMA.on_fit_start.<locals>.<listcomp>)r   r   torch
optimizersr   r   r   r   r   r   on_fit_start8   s   zEMA.on_fit_startc                 C      |  |r| | d S d S N_should_validate_ema_weightsswap_model_weightsr#   r   r   r   on_validation_startF      
zEMA.on_validation_startc                 C   r%   r&   r'   r#   r   r   r   on_validation_endJ   r+   zEMA.on_validation_endc                 C   r%   r&   r'   r#   r   r   r   on_test_startN   r+   zEMA.on_test_startc                 C   r%   r&   r'   r#   r   r   r   on_test_endR   r+   zEMA.on_test_endc                 C   s   | j  o| |S r&   )r   _ema_initializedr   r   r   r   r   r(   V   s   z EMA._should_validate_ema_weightsc                 C   s   t dd |jD S )Nc                 s   s    | ]}t |tV  qd S r&   )r   r   )r   	optimizerr   r   r   	<genexpr>Z   s    z'EMA._ema_initialized.<locals>.<genexpr>)anyr"   r0   r   r   r   r/   Y   s   zEMA._ema_initializedsaving_ema_modelc                 C   s(   |j D ]}t|tsJ || qd S r&   )r"   r   r   switch_main_parameter_weights)r   r   r4   r1   r   r   r   r)   \   s   
zEMA.swap_model_weightsc              
   c   s<    | j |dd zdV  W | j |dd dS | j |dd w )zS
        Saves an EMA copy of the model + EMA optimizer states for resume.
        T)r4   NF)r)   r0   r   r   r   save_ema_modela   s
   "zEMA.save_ema_modelc              	   c   sV    |j D ]}t|tsJ d|_qzd V  W |j D ]}d|_qd S |j D ]}d|_q$w )NTF)r"   r   r   save_original_optimizer_state)r   r   r1   r   r   r   r7   l   s   

z!EMA.save_original_optimizer_state
checkpointc           	      C   s   |j }|j}|rR|d urTdt|jv rV|j}|d| r$td d S ||d| }tj	
|rKtj|tddd}|d |d< ~td d S td	| d S d S d S )
NNeMoz-EMAzloading EMA based weights. The callback will treat the loaded EMA weights as the main weights and create a new EMA copy when training.r   F)map_locationweights_onlyoptimizer_stateszEMA state has been restored.z}Unable to find the associated EMA weights when re-loading, training will start with new EMA weights. Expected them to be at: )checkpoint_callback	ckpt_pathtype__name__FILE_EXTENSIONendswithr   replaceospathexistsr!   loadr   r   )	r   r   r   r8   r=   r>   extema_pathema_state_dictr   r   r   on_load_checkpointw   s*   zEMA.on_load_checkpoint)Fr	   F)r   r   r   r   r   NF)r   r   )r@   
__module____qualname____doc__floatboolintr   r$   r*   r,   r-   r.   r(   r/   r)   
contextlibcontextmanagerr6   r7   r   strr   rK   r   r   r   r   r      sF    








r   c                 C   s$   t | | t j| |d| d d S )Ng      ?)alpha)r!   _foreach_mul__foreach_add_)ema_model_tuplecurrent_model_tupler
   r   r   r   
ema_update   s   
r[   c                 C   s    |d ur|   t| || d S r&   )synchronizer[   )rY   rZ   r
   pre_sync_streamr   r   r   run_ema_update_cpu   s   r^   c                   @   s   e Zd ZdZ			d*dejjdejdede	d	e	f
d
dZ
deej fddZd+ddZdefddZe dd Zdd Zd,defddZejd-defddZd d! Zd"d# Zd$d% Zd&d' Zd(d) ZdS ).r   a  
    EMAOptimizer is a wrapper for torch.optim.Optimizer that computes
    Exponential Moving Average of parameters registered in the optimizer.

    EMA parameters are automatically updated after every step of the optimizer
    with the following formula:

        ema_weight = decay * ema_weight + (1 - decay) * training_weight

    To access EMA parameters, use ``swap_ema_weights()`` context manager to
    perform a temporary in-place swap of regular parameters with EMA
    parameters.

    Notes:
        - EMAOptimizer is not compatible with APEX AMP O2.

    Args:
        optimizer (torch.optim.Optimizer): optimizer to wrap
        device (torch.device): device for EMA parameters
        decay (float): decay factor

    Returns:
        returns an instance of torch.optim.Optimizer that computes EMA of
        parameters

    Example:
        model = Model().to(device)
        opt = torch.optim.Adam(model.parameters())

        opt = EMAOptimizer(opt, device, 0.9999)

        for epoch in range(epochs):
            training_loop(model, opt)

            regular_eval_accuracy = evaluate(model)

            with opt.swap_ema_weights():
                ema_eval_accuracy = evaluate(model)
    H.?r	   r   r1   r   r
   r   r   c                 C   sL   || _ || _|| _|| _|| _d| _d| _d| _d | _d | _	d| _
d| _d S )NFTr   )r1   r
   r   r   r   r7   first_iterationrebuild_ema_paramsstreamthread
ema_paramsin_saving_ema_model_context)r   r1   r   r
   r   r   r   r   r   r      s   
zEMAOptimizer.__init__r   c                 C   s   dd | j D S )Nc                 s   s"    | ]}|d  D ]}|V  qqdS )paramsNr   )r   groupparamr   r   r   r2      s     z.EMAOptimizer.all_parameters.<locals>.<genexpr>)param_groupsr   r   r   r   all_parameters      zEMAOptimizer.all_parametersNc                    s        jrtdd   D rtj  _d _ jr>t	  }  j
t fdd|t j
d  D 7  _
d _t jddrR|d urR jj||d}n j|}  r`     jd7  _|S )Nc                 s   s    | ]}|j V  qd S r&   )is_cuda)r   pr   r   r   r2      s    z$EMAOptimizer.step.<locals>.<genexpr>Fc                 3   s(    | ]}t |j  jV  qd S r&   )copydeepcopydatadetachtor   r   rh   rj   r   r   r2      s    
_step_supports_amp_scaling)closuregrad_scalerr	   )joinr`   r3   rk   r!   cudaStreamrb   ra   listrd   tuplelengetattrr1   step_should_update_at_stepupdater   )r   rv   rw   kwargs
opt_paramslossr   rj   r   r      s$   zEMAOptimizer.stepc                 C   s   | j | j dkS )Nr   )r   r   rj   r   r   r   r     rl   z#EMAOptimizer._should_update_at_stepc                    s    j d ur j tj  tj  j # t fdd  D } jjdkr1t	 j
| j W d    n1 s;w   Y   jjdkr\tjt j
| j j fd _ j  d S d S )Nc                 3   s"    | ]}|j j jd dV  qdS )T)non_blockingN)rq   rs   r   rt   rj   r   r   r2     s    
z&EMAOptimizer.update.<locals>.<genexpr>ry   r   )targetargs)rb   wait_streamr!   ry   current_streamr|   rk   r   r?   r[   rd   r
   	threadingThreadr^   rc   start)r   current_model_stater   rj   r   r     s*   
	zEMAOptimizer.updatec                 C   s,   t |}|| || || d S r&   )r!   
empty_likecopy_)r   tensor1tensor2tmpr   r   r   swap_tensors!  s   


zEMAOptimizer.swap_tensorsFr4   c                 C   s:   |    || _t|  | jD ]\}}| |j| qd S r&   )rx   re   ziprk   rd   r   rq   )r   r4   rh   	ema_paramr   r   r   r5   '  s
   z*EMAOptimizer.switch_main_parameter_weightsTenabledc                 c   s<    |r|    zdV  W |r|    dS dS |r|    w w )a  
        A context manager to in-place swap regular parameters with EMA
        parameters.
        It swaps back to the original regular parameters on context manager
        exit.

        Args:
            enabled (bool): whether the swap should be performed
        N)r5   )r   r   r   r   r   swap_ema_weights-  s   
zEMAOptimizer.swap_ema_weightsc                 C   s   t | j|S r&   )r~   r1   )r   namer   r   r   __getattr__A  s   zEMAOptimizer.__getattr__c                 C   s0   | j d ur
| j   | jd ur| j  d S d S r&   )rb   r\   rc   rx   rj   r   r   r   rx   D  s
   


zEMAOptimizer.joinc                 C   sP   |    | jr| j S | js| jnt|  }| j || j| j	| j
d}|S )N)optemar   r
   r   )rx   r7   r1   
state_dictre   rd   r{   rk   r   r
   r   )r   rd   r   r   r   r   r   K  s   
zEMAOptimizer.state_dictc                    sb        j|d  t fddt|d D  _|d  _|d  _|d  _	d _
d S )	Nr   c                 3   s    | ]	}|  jV  qd S r&   )rs   r   rt   rj   r   r   r2   `  s    z/EMAOptimizer.load_state_dict.<locals>.<genexpr>r   r   r
   r   F)rx   r1   load_state_dictr|   ro   rp   rd   r   r
   r   ra   )r   r   r   rj   r   r   \  s   "



zEMAOptimizer.load_state_dictc                 C   s   | j | d| _d S )NT)r1   add_param_groupra   )r   param_groupr   r   r   r   f  s   
zEMAOptimizer.add_param_group)r_   r	   r   )NNrL   )T)r@   rM   rN   rO   r!   r   	Optimizerr   rP   rR   r   r   Tensorrk   r   rQ   r   no_gradr   r   r5   rS   rT   r   r   rx   r   r   r   r   r   r   r   r      s<    ,



r   r&   )rS   ro   rD   r   typingr   r   r   lightning.pytorchpytorchplr!   r   &lightning.pytorch.utilities.exceptionsr   %lightning.pytorch.utilities.rank_zeror   r   r   r[   r^   r   r   r   r   r   r   r   <module>   s   {

	