
    ih                     \   d Z ddlZddlmZ ddlmc mZ ddlmZ ddl	m
Z
mZ ddlmZ ddlZddlmZ  G d d      ZddZdd	Z G d
 dej*                        Z G d dej*                        Z G d d      Zd Zd Zd Z ej8                         d        ZddZedk(  r
 ed       yy)a  
Entropy-Triggered Adaptive Depth (ETAD) MLP for CIFAR-10
- N-layer MLP with per-layer exit heads
- Learns entropy target profile per layer
- Skips computation when entropy drops below threshold
- Repeat batch iteration with fresh sample injection
- Single file: train + test
    N)datasets
transforms)
DataLoader)defaultdictc                       e Zd ZdZdZdZdZdZdZdZ	e
j                  j                         rdnd	ZdZd
ZdZdZdZdZdZdZdZdZy)Configi         
         gMbP?cudacpu      ?333333?g?g?   皙?g{Gz?d   *   N)__name__
__module____qualname__	INPUT_DIM
HIDDEN_DIMNUM_HIDDEN_LAYERS
OUTPUT_DIM
BATCH_SIZEEPOCHSLRtorchr   is_availableDEVICE	N_REPEATSFRESH_RATIO_STARTFRESH_RATIO_ENDENTROPY_TARGET_STARTENTROPY_TARGET_END	MIN_DEPTHLAMBDA_ENTROPYLAMBDA_DEPTHLOG_INTERVALSEED     ex03.pyr   r      s{    IJJ JF	Bzz..0VeF IO I NL LDr.   r   c                     t        j                  | |      }t        j                  |t        j                  ||z         z  |       S )z3Compute Shannon entropy of probability distributiondim)Fsoftmaxr    sumlog)logitsr2   epsprobss       r/   compute_entropyr:   9   s8    IIf#&EIIeeii44#>>>r.   c                 8    t        | |      j                         S )zMean entropy across batch)r8   )r:   mean)r7   r8   s     r/   batch_entropyr=   >   s    6s+0022r.   c                   8     e Zd Z fdZddZd Zd Zd Z xZS )ETADMLPc           	         t         |           || _        t        j                  |j
                  |j                        | _        t        j                  t        |j                        D cg c],  }t        j                  |j                  |j                        . c}      | _        t        j                  t        |j                  dz         D cg c],  }t        j                  |j                  |j                        . c}      | _        t        j                  t        j                   |j"                  |j$                  |j                  dz               | _        t)        t*              | _        y c c}w c c}w )N   )super__init__confignnLinearr   r   
input_proj
ModuleListranger   hidden_layersr   
exit_heads	Parameterr    linspacer&   r'   entropy_thresholdsr   intlayer_exit_counts)selfrD   _	__class__s      r/   rC   zETADMLP.__init__F   s'    ))F$4$4f6G6GH  ]]6334,
 IIf''):):;,
  --633a78)
 IIf''):):;)
  #%,,NN++))((1,#
 "-S!1-,
)
s   ,1E41E9c                    |j                  d      }t        j                  |fdt        j                  |j                        }d}g }g }t        j                  | j                  |            }	 | j                  d   |	      }
t        |
      }|j                  |j                                |j                  |
j                                ||| j                  d   k  |dk(  z  }nOd|k  r%t        j                  t        j                        n$t        j                  t        j                        }||dk(  z  }d||<   ||
j                         }t        j                   |j#                  d      |
|      }t%        | j&                        D ]O  \  }}|dk(  }|j)                         s n4t        j                   ||	            }	 | j                  |dz      |	      }
t        |
      }|j                  |j                                |j                  |
j                                |dz   }|/|| j*                  j,                  k\  }|| j                  |   k  |z  }nO||k  r%t        j                  |t        j                        n$t        j                  |t        j                        }||z  }|||<   t        j                   |j#                  d      |
|      }R |dk(  }| j*                  j.                  ||<   t        j                   |j#                  d      |
|      }| j0                  r:|j3                         j5                         D ]  }| j6                  |xx   dz  cc<    |r||||fS ||fS )a  
        Args:
            x: Input tensor [B, 3072]
            return_all: If True, return all layer outputs (for analysis)
            force_depth: If set, exit at this layer regardless of entropy
            
        Returns:
            logits: Final predictions
            exit_layers: Which layer each sample exited at
            entropies: Entropy at each layer (if return_all)
        r   )dtypedeviceNrV   rA   )sizer    fulllongrW   r3   relurG   rK   r:   appendclonerN   
zeros_likebool	ones_likewhere	unsqueeze	enumeraterJ   anyrD   r(   r   trainingr   numpyrP   )rQ   x
return_allforce_depth
batch_sizeexit_layersfinal_logitsall_entropies
all_logitshr7   entropyshould_exitexitedllayeractive_mask	layer_idxcan_exit	remainingidxs                        r/   forwardzETADMLP.forwardf   sO    VVAY
 jj*%**QXXV
 FF4??1%&##A&!&)W]]_-&,,.) "d&=&=a&@@[TVEVWKMNQ\_%**;ejjIbgbqbqr}  FK  FP  FP  cQKr 12F!<<>L{{Q
 "$"4"45 	HAu%+K??$uQx A+T__QU+A.F%f-G  1flln- AI"$(=(==&$*A*A)*LLPXXMVYdMde..wejjIjojyjy  {B  JO  JT  JT  kU ;.F"+K !;;  #L3	@  2%	!%!>!>I{{"
 =="(..0 1&&s+q0+1 mZGG[((r.   c                 n    | j                   j                         j                         j                         S )z7Return current entropy thresholds (the learned profile))rN   detachr   rg   rQ   s    r/   get_entropy_profilezETADMLP.get_entropy_profile   s)    &&--/335;;==r.   c                     t        | j                  j                               }|dk(  ri S t        | j                  j	                               D ci c]  \  }}|||z   c}}S c c}}w )z"Return distribution of exit layersr   )r5   rP   valuessorteditems)rQ   totalkvs       r/   get_exit_distributionzETADMLP.get_exit_distribution   s[    D**1134A:I)/0F0F0L0L0N)OPA1u9PPPs   A%c                 8    | j                   j                          y)z-Reset exit counters for new evaluation periodN)rP   clearr~   s    r/   reset_exit_countszETADMLP.reset_exit_counts   s    $$&r.   )FN)	r   r   r   rC   r{   r   r   r   __classcell__rS   s   @r/   r?   r?   E   s     2@\)|>Q'r.   r?   c                   &     e Zd Z fdZddZ xZS )ETADLossc                 b    t         |           || _        t        j                         | _        y N)rB   rC   rD   rE   CrossEntropyLossce)rQ   rD   rS   s     r/   rC   zETADLoss.__init__   s%    %%'r.   c                    | j                  ||      }d}|t        |      D ]  \  }}||k(  }	|	j                         s| j                  j                  t        j                  | dz        z  | j                  j                  z   }
||	   |
z
  j                         }||j                         z  } |t        |      z  }|j                         j                         }|| j                  j                  z  }|| j                  j                  |z  z   | j                  j                  |z  z   }||j                         ||j                         nd|j                         |j                         dfS )z
        Args:
            logits: Final predictions [B, 10]
            targets: Ground truth [B]
            exit_layers: Which layer each sample exited at [B]
            entropies: List of entropy tensors per layer (optional)
        r   r   )	task_lossentropy_lossdepth_penalty	avg_depth)r   rd   re   rD   r&   npexpr'   absr<   lenfloatr   r)   r*   item)rQ   r7   targetsrl   	entropiesr   r   rt   	entropy_lexited_masktarget_entropyentropy_diffr   r   
total_losss                  r/   r{   zETADLoss.forward   sn    GGFG,	   )) 4 89*a/??$%)[[%E%E$&FFA26N&359[[5S5S&TN %.k$:^$K#P#P#RL L$5$5$77L8 C	N*L  %%',,.	!DKK$A$AA KK&&56KK$$}45 	 ")3<3HL--/a*//1")	
 
 	
r.   r   )r   r   r   rC   r{   r   r   s   @r/   r   r      s    (
*
r.   r   c                   <    e Zd ZdZ	 	 d	dZd Zd Zd
dZd Zd Z	y)RepeatBatchTraineraD  
    Manages repeat batch iteration with fresh sample injection.
    
    Each epoch consists of N_REPEAT iterations where:
    - Early iterations: mostly fresh samples (explore)
    - Later iterations: mostly hard samples from history (exploit)
    
    This prevents memorization while focusing on difficult examples.
    c                 n   || _         || _        || _        || _        || _        t        j                  |      | _        t        j                  |t
        j                        | _	        t        j                  |t
        j                        | _        t        j                  j                  |      | _        y )NrX   )dataset_sizerk   	n_repeatsfresh_ratio_startfresh_ratio_endr    zerossample_lossesr[   sample_countsr`   	seen_maskr   randomRandomStaterng)rQ   r   rk   r   r   r   seeds          r/   rC   zRepeatBatchTrainer.__init__  s    ($"!2. #[[6"[[UZZH \D 99((.r.   c                     | j                   dk  r| j                  S || j                   dz
  z  }| j                  d|z
  z  | j                  |z  z   S )z>Linear interpolation from fresh_ratio_start to fresh_ratio_endrA   )r   r   r   )rQ   	iterationts      r/   get_fresh_ratioz"RepeatBatchTrainer.get_fresh_ratio'  sR    >>Q)))!+,%%Q/$2F2F2JJJr.   c                 N   | j                  |      }t        | j                  |z        }| j                  |z
  }| j                   j	                  d      d   }t        |      |k  rJ|}|t        |      z
  }t        j                  | j                        d| }t        j                  ||g      }n&t        j                  t        |            }	||	d|    }d| j                  |<   |dkD  r| j                  j                         }
d|
|<   t        |dz  | j                        }t        j                  |
|      j                  }|
|   }t        j                  |d      }|d	z   }||j!                         z  }| j"                  j%                  |j'                         ||j'                         d
      }t        j(                  |      }n%t        j*                  dt        j,                        }t        j                  ||g      }t        j                  t        |            }	||	   S )z
        Get batch indices mixing hard and fresh samples.
        
        Args:
            iteration: Current repeat iteration (0 to n_repeats-1)
            
        Returns:
            indices: Tensor of sample indices for this batch
        T)as_tupler   Ng      r   g        )minr   )rY   preplacerX   )r   rO   rk   r   nonzeror   r    randpermr   catr   r^   r   topkindicesclampr5   r   choicerg   
from_numpyemptyr[   )rQ   r   fresh_ration_freshn_hardunseen_indicesfresh_indicesn_extraextra_indicespermcandidate_lossesn_candidatestop_indiceshard_lossesweightshard_selectionhard_indicesr   s                     r/   get_next_indicesz#RepeatBatchTrainer.get_next_indices.  s    **95doo347*  >>/22D2A!D~(*MM 22G!NN4+<+<=hwGM!II}m&DEM>>#n"56D*4>:M )-}% A:#11779.2]+ v{D,=,=>L**%5|DLLK +;7K++ks;K!C'G-G!XX__!!#--/	 - N !++N;L ;;q

;L ))]L9:~~c'l+t}r.   c                     ||z  d|z
  | j                   |   z  z   | j                   |<   | j                  |xx   dz  cc<   y)z
        Update per-sample loss history with EMA.
        
        Args:
            indices: Sample indices that were in the batch
            losses: Per-sample loss values
            alpha: EMA update rate (higher = more weight to recent)
        rA   N)r   r   )rQ   r   lossesalphas       r/   update_sample_lossesz'RepeatBatchTrainer.update_sample_lossesj  sL     FNa%i4+=+=g+FFF 	7# 	7#q(#r.   c                 8    | j                   j                          y)zReset seen mask for new epochN)r   zero_r~   s    r/   reset_epochzRepeatBatchTrainer.reset_epochx  s    r.   c                    | j                   dkD  }|j                         r+| j                  |   j                         j	                         nd|j                         r+| j                  |   j                         j	                         nd|j                         j	                         | j                   |   dkD  j                         j	                         dS )z6Return statistics about sample difficulty distributionr   rA   )	mean_lossmax_lossn_seenn_seen_multiple)r   re   r   r<   r   maxr5   )rQ   seens     r/   get_difficulty_statsz'RepeatBatchTrainer.get_difficulty_stats|  s    !!A%CG88:++D1668==?STAE**40446;;=QRhhjoo' $ 2 24 81 <AACHHJ	
 	
r.   N)r   r   r   r   )r   )
r   r   r   __doc__rC   r   r   r   r   r   r-   r.   r/   r   r     s/     <>BD/$K:x)
r.   r   c                    t        j                  t        j                         t        j                  dd      t        j                  d       g      }t        j                  t        j                         t        j                  dd      t        j                  d       g      }t        j                  ddd|      }t        j                  ddd|      }t        || j                  dd	
      }t        || j                  dd	
      }|||fS )z'Load CIFAR-10 with proper preprocessing)gHPs?gec]?g~jt?)gۊe?ggDio?g|?5^?c                 $    | j                  d      S NrU   viewrh   s    r/   <lambda>z%get_cifar10_loaders.<locals>.<lambda>      AFF2J r.   c                 $    | j                  d      S r   r   r   s    r/   r   z%get_cifar10_loaders.<locals>.<lambda>  r   r.   z../dataT)roottraindownload	transformF   )rk   shufflenum_workers)	r   ComposeToTensor	NormalizeLambdar   CIFAR10r   r   )rD   transform_traintransform_testtrain_datasettest_datasettrain_loadertest_loaders          r/   get_cifar10_loadersr    s    ((53	5./	* O  ''53	5./	) N $$dT_M ##ednL m8I8I%)q:L\f6G6G$)q:K m33r.   c                 x   | j                          t        t        |      |j                  |j                  |j
                  |j                  |j                  |z         }|j                          d}d}d}	d}
t        t              }t        j                  t        t        |            D cg c]  }||   d   j                  d       c}      }t        j                  t        t        |            D cg c]
  }||   d    c}      }t!        d| d|j                   d       t        |j                        D ]  }|j#                  |      }||   j%                  |j&                        }||   j%                  |j&                        }|j)                           | |d	      \  }}}}t+        j,                  ||d
      }|j/                  ||j1                         j3                                 |||||      \  }}|j5                          |j7                          ||j9                         z  }|j;                  d      }||j=                  |      j?                         j9                         z  }|	|jA                  d      z  }	|
dz  }
|jC                         D ]  \  }}||xx   |z  cc<    |jE                  |      }|jG                         }|tI        d|j                  dz        z  dk(  s||j                  dz
  k(  st!        d|dd|j                   d|dd|j9                         ddd|z  |	z  dd|d   dd|d   d        | jK                          ||
z  d|z  |	z  d|jC                         D ci c]  \  }}|||
z   c}}d|jG                         iS c c}w c c}w c c}}w )a  
    Train one epoch using repeat batch iteration with fresh sample injection.
    
    Each epoch consists of N_REPEATS iterations over batches of size BATCH_SIZE.
    Early iterations use mostly fresh samples; later iterations focus on hard examples.
    )r   rk   r   r   r   r   r   rU   rA   u   
🔄 Epoch : z repeat iterationsTri   none)	reductionr1      z  Iter 2d/z (fresh=.0%z
) | Loss: .4f | Acc:       Y@.2fz% | AvgDepth: r   z | MeanDiff: r   .3flossaccuracydifficulty_stats)&r   r   r   r   r#   r$   r%   r,   r   r   r   r    stackrI   r   tensorprintr   tor"   	zero_gradr3   cross_entropyr   r}   r   backwardstepr   argmaxeqr5   rY   r   r   r   r   r   )modelr   	optimizerloss_fnrD   epochtrainerr   correctr   total_batchesmetricsiall_dataall_targetsr   r   datatargetr7   rl   r   rR   per_sample_lossesr  batch_metricspredr   r   r   
diff_statss                                  r/   train_epoch_with_repeatsr/    s    
KKM !'$$"" 22..[[5 G JGEM% G {{%MHZB[\QM!,Q/44R8\]H,,U3}CU=VWa 0 3WXK	M%6#3#3"44F
GH6++, ->	**95  ##FMM2W%((7 -2$4,H)Y OOFFfM 	$$W.?.F.F.H.L.L.NO &ffk9Mm 	diik!
}}}#4776?&&(--//Q!'') 	DAqAJ!OJ	 --i8113
s1f..!3449Y&JZJZ]^J^=^GIb>6+;+;*< =', -99;s+ ,w,u,S1 2,[9#> ?)+6s;= >Q->` 
 ]*7NU* -4MMO
<DAq1a-
< 	G88:	 o ]Wr =s   ,N,6N1N6c                     | j                          d}d}d}t        t              }	t        |      D ]n  \  }
\  }}|j	                  |j
                        |j	                  |j
                        }}|j                           | |d      \  }}}} |||||      \  }}|j                          |j                          ||j                         z  }|j                  d      }||j                  |      j                         j                         z  }||j                  d      z  }|j                         D ]  \  }}|	|xx   |z  cc<    |
|j                  z  dk(  s/t!        d| d|
 dt#        |       d	|j                         d
dd|z  |z  dd|d   d       q | j%                          |t#        |      z  d|z  |z  d|	j                         D ci c]  \  }}||t#        |      z   c}}S c c}}w )z:Train one epoch (original version, kept for compatibility)r   Tr  rA   r1   zEpoch z [r	  z] Loss: r  z Acc: r  r  z% AvgDepth: r   r  )r   r   r   rd   r  r"   r  r  r  r   r  r  r5   rY   r   r+   r  r   r   )r  loaderr  r   rD   r!  r   r#  r   r%  	batch_idxr)  r*  r7   rl   r   rR   r  r,  r-  r   r   s                         r/   train_epochr3     s	   	KKMJGE% G%.v%6 A!	>D&wwv}}-vyy/Gf -2$4,H)Y &ffk9Mm 	diik!
}}}#4776?&&(--//Q!'') 	DAqAJ!OJ	 v***a/F5'I;aF} =99;s+6$w,u2DS1I J,[9#>@ A1A: 
 S[(7NU* +2--/
:$!Q1a#f+o
:  ;s   G:c           
      2   | j                          d}d}d}t        t              }t        t              }t        d       }	|D ]  \  }
}|
j	                  |j
                        |j	                  |j
                        }}
 | |
d      \  }}}} |||||      \  }}||j                         z  }|j                  d      }||j                  |      j                         j                         z  }||j                  d      z  }|j                         j                         D ]  }||xx   dz  cc<    t        t        |||            D ][  \  }\  }}}|j                         }|	|   dxx   dz  cc<   |j                         |j                         k(  sL|	|   dxx   dz  cc<   ] |j                         D ]  \  }}||xx   |z  cc<     | j!                          i }|	j                         D ]  \  }}|d   dkD  sd	|d   z  |d   z  ||<   ! |t#        |      z  d	|z  |z  t%        |j                               D ci c]  \  }}|||z   c}}|d
|j                         D ci c]  \  }}||t#        |      z   c}}S c c}}w c c}}w )zEvaluate modelr   c                      dddS )Nr   )r#  r   r-   r-   r.   r/   r   zevaluate.<locals>.<lambda>6  s    Q-G r.   Tr  rA   r1   r   r#  r  )r  r  exit_distributionlayer_accuracy)evalr   r   rO   r  r"   r   r  r  r5   rY   r   rg   rd   zipr   r   r   r   )r  r1  r   rD   r   r#  r   r%  r6  accuracy_per_layerr)  r*  r7   rl   r   ro   r  r,  r-  rz   r&  rt   r   r   rw   r   r   r7  statss                                r/   evaluater<  -  s    
JJLJGE% G#C($%GH fwwv}}-vyy/Gf5:4D5Q2Y
%ffk9Mmdiik!
}}}#4776?&&(--//Q ??$**, 	(Cc"a'"	( &c+tV&DE 	>LAy1aIy)'2a72vvx1668#"9-i8A=8		> "'') 	DAqAJ!OJ	/4 
 N&,,. I5>A $uY'7 7%. HN1I
 S[(7NU*7=>O>U>U>W7XYtq!aUlY(	
 +2--/
:$!Q1a#f+o
:  Z
:s   J
/JTc           
      &	   t        j                  t        j                         t        j
                  j                  t        j                         t        d       t        dt        j                          t        dt        j                   d       t        dt        j                          | rKt        dt        j                          t        dt        j                  dd	t        j                  d       t                t        t              \  }}}t        t              j!                  t        j                        }t#        j$                  |j'                         t        j(                  
      }t"        j*                  j-                  |dd      }t/        t              }t        d       t        d       d}t1        dt        j2                  dz         D ]  }	| rt5        ||||t        |	      }
nt7        ||||t        |	      }
t9        |||t              }t        dd        t        d|	dd       t        d        t        d|
d   dd|
d   dd       t        d|d   dd|d   dd       t        d|d   dd t        j                          t        d!|j;                         j=                  d"              t        d#|d$           t        d%|d&           | r'd'|
v r#|
d'   }t        d(|d)   d*d+|d,    d-|d.           |d   |kD  r;|d   }t        j>                  |jA                         |j;                         |	d/d0       |jC                           t        dd        t        d1|dd       t        d        t        d2       t        d       d$   }t        d3       |jE                         D ]/  \  }}d4tG        |d5z        z  }t        d6|dd7|d8z  d9d:|        1 t        d;       tI        |d&   jE                               D ]  \  }}t        d6|dd7|dd        |d   }d|t        j                  z  z
  }t        d<|d8z  d=d>       t        d?|dd@t        j                   dA       yB)Cz
    Main training loop.
    
    Args:
        use_repeats: If True, use repeat batch iteration with fresh sample injection.
                    If False, use standard DataLoader iteration.
    u   🚀 ETAD MLP for CIFAR-10z   Device: z   Layers: z hidden + exit headsz   Min exit depth: u      🔄 Repeat iterations: u      📊 Fresh ratio: r
  u    → )lrr   g      ?)	step_sizegammau   📈 Training...zP================================================================================r   rA   
zEPOCH r  z SUMMARYzTrain Loss: r  r  r  r  r  %zTest  Loss: zAvg Depth: r   z / zEntropy Profile: r   zExit Distribution: r6  zLayer Accuracy: r7  r  zDifficulty Stats: mean_loss=r   r  z
 | n_seen=r   z | n_repeat=r   )r  rN   r!  zetad_mlp_best.ptu&   ✅ Training Complete! Best Test Acc: u   
📊 FINAL ANALYSISzExit Layer Distribution:u   █2   z  Layer r  r   z5.1fz% z
Accuracy by Exit Layer:u   
⚡ Efficiency Gain: z.1fz% fewer layers on averagez   (Avg depth: z vs max )N)%r    manual_seedr   r,   r   r   r   r  r"   r   r(   r#   r$   r%   r  r?   r  optimAdam
parametersr   lr_schedulerStepLRr   rI   r   r/  r3  r<  r   roundsave
state_dictr  r   rO   r   )use_repeatsr   r   r   r  r  	schedulerr   best_accr!  train_metricstest_metricsdiff	exit_distru   pctbaraccr   
efficiencys                       r/   mainrY  e  s    
fkk"IINN6;;	&(	K
'(	K0011E
FG	 0 01
23+F,<,<+=>?%f&>&>s%C5I_I_`cHdef	G 0C6/J,L+} FOv}}-E

5++-&))<I"")))r)MIvG 

	(OHq&--!+, '4}i&%M (|YM
  {GVD 	6(muRj)*]6237xj@YZ]?^^_`a\&1#6h|J?WX[>\\]^_L5c:#f>V>V=WXY!%";";"="C"CA"F!GHI#L1D$E#FGH .>!? @AB-> !34D0k1B30G H N+<=N8O7PR S 
#h.#J/HJJ))+&+&?&?&A "	# 	O'R 
Bvh-	28C.
BC	VH 

!"	(O 01I	$&oo' <
sc#(m#r
"SWTN"SE:;<
 
%'\*:;AACD 1
sr
"SIQ/01 [)Ii&":"::;J	#JsN3#77P
QR	OIc?(63K3K2LA
NOr.   __main__)rN  )rU   :0yE>)r[  )T) r   r    torch.nnrE   torch.nn.functional
functionalr3   torch.optimrF  torchvisionr   r   torch.utils.datar   rg   r   collectionsr   r   r:   r=   Moduler?   r   r   r  r/  r3  no_gradr<  rY  r   r-   r.   r/   <module>re     s         , '  #
 D?
3L'bii L'b0
ryy 0
jy
 y
|4<Wr+Z 2 2ndPL zT r.   