mshaiel2004 commited on
Commit
7e6d950
·
verified ·
1 Parent(s): 728be4b

Create aeg.py

Browse files
Files changed (1) hide show
  1. core/aeg.py +146 -0
core/aeg.py ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ import os
4
+
5
+ # ==========================================
6
+ # 1. CONFIGURATION (V5.4 Standard)
7
+ # ==========================================
8
+ AEG_CONFIG = {
9
+ "num_phonemes": 64,
10
+ "num_errors": 9,
11
+ "num_major_types": 3,
12
+ "num_positions": 3,
13
+ "num_structures": 8,
14
+ "max_seq_len": 10,
15
+ "hidden_size": 128
16
+ }
17
+
18
+ class AEGPolicyNetwork_V5_4(nn.Module):
19
+ def __init__(self):
20
+ super(AEGPolicyNetwork_V5_4, self).__init__()
21
+
22
+ # Embeddings for categorical inputs
23
+ self.emb_error = nn.Embedding(AEG_CONFIG['num_errors'], 8)
24
+ self.emb_major = nn.Embedding(AEG_CONFIG['num_major_types'], 4)
25
+ self.emb_pos = nn.Embedding(AEG_CONFIG['num_positions'], 4)
26
+
27
+ # Input Dimension Calculation:
28
+ # Cluster(64) + Trap(64) + Feat(8) + Emb_Err(8) + Emb_Maj(4) + Emb_Pos(4) + Flags(6) + Stats(4) = 162
29
+ input_dim = 64 + 64 + 8 + 8 + 4 + 4 + 6 + 4
30
+
31
+ # Shared Encoder (Patient Embedding)
32
+ self.encoder = nn.Sequential(
33
+ nn.Linear(input_dim, 256),
34
+ nn.BatchNorm1d(256), nn.ReLU(), nn.Dropout(0.2),
35
+ nn.Linear(256, AEG_CONFIG['hidden_size']), nn.ReLU()
36
+ )
37
+
38
+ # Head A: Volume (Scalar)
39
+ self.head_volume = nn.Linear(AEG_CONFIG['hidden_size'], 1)
40
+
41
+ # Head B: Difficulty Progression (LSTM Sequence)
42
+ self.lstm_diff = nn.LSTM(input_size=AEG_CONFIG['hidden_size'], hidden_size=64, batch_first=True)
43
+ self.fc_diff = nn.Linear(64, 5) # 5 Difficulty levels
44
+ self.emb_diff_cond = nn.Embedding(5, 8) # Embedding for the predicted difficulty to feed into other heads
45
+
46
+ # Head D: Format Selector
47
+ self.head_format = nn.Sequential(
48
+ nn.Linear(AEG_CONFIG['hidden_size'] + 8 + 8 + 4, 64),
49
+ nn.ReLU(),
50
+ nn.Linear(64, 8) # 8 Activity Formats
51
+ )
52
+
53
+ # Head E: Targets (Residual Anchor)
54
+ self.head_targets = nn.Sequential(
55
+ nn.Linear(64 + 8 + 64, 128),
56
+ nn.ReLU(),
57
+ nn.Linear(128, 64)
58
+ )
59
+
60
+ # Head F: Structure Scaffolding
61
+ self.head_struct = nn.Sequential(
62
+ nn.Linear(64 + 8 + 8, 64),
63
+ nn.ReLU(),
64
+ nn.Linear(64, AEG_CONFIG['num_structures'])
65
+ )
66
+
67
+ # Head C: Safety/Forbidden (Residual Bypass)
68
+ self.head_forbidden = nn.Linear(AEG_CONFIG['hidden_size'], 64)
69
+
70
+ # Head C2: Dynamic Parameters
71
+ self.head_dynamic = nn.Linear(AEG_CONFIG['hidden_size'], 5)
72
+
73
+ def forward(self, error_idx, major_idx, pos_idx, cluster, trap, feat, flags, stats):
74
+ # 1. Embed Categoricals
75
+ e_err = self.emb_error(error_idx)
76
+ e_maj = self.emb_major(major_idx)
77
+ e_pos = self.emb_pos(pos_idx)
78
+
79
+ # 2. Shared Encoding
80
+ x_cat = torch.cat([cluster, trap, feat, e_err, e_maj, e_pos, flags, stats], dim=1)
81
+ h_shared = self.encoder(x_cat)
82
+
83
+ # 3. Head A: Volume
84
+ pred_vol = self.head_volume(h_shared)
85
+
86
+ # 4. Head B: Difficulty (LSTM)
87
+ # Repeat shared vector for sequence length
88
+ lstm_input = h_shared.unsqueeze(1).repeat(1, AEG_CONFIG['max_seq_len'], 1)
89
+ lstm_out, _ = self.lstm_diff(lstm_input)
90
+ pred_diff_logits = self.fc_diff(lstm_out)
91
+
92
+ # Get predicted difficulty indices to condition other heads
93
+ cond_diff = torch.argmax(pred_diff_logits, dim=2)
94
+ cond_vec = self.emb_diff_cond(cond_diff)
95
+
96
+ # Prepare repeated embeddings for sequence generation
97
+ h_rep = h_shared.unsqueeze(1).repeat(1, AEG_CONFIG['max_seq_len'], 1)
98
+ e_err_rep = e_err.unsqueeze(1).repeat(1, AEG_CONFIG['max_seq_len'], 1)
99
+ e_maj_rep = e_maj.unsqueeze(1).repeat(1, AEG_CONFIG['max_seq_len'], 1)
100
+ cluster_rep = cluster.unsqueeze(1).repeat(1, AEG_CONFIG['max_seq_len'], 1)
101
+
102
+ # 5. Head D: Format
103
+ fmt_input = torch.cat([h_rep, cond_vec, e_err_rep, e_maj_rep], dim=2)
104
+ pred_fmt = self.head_format(fmt_input)
105
+
106
+ # 6. Head E: Targets (With Residual Anchor)
107
+ # Output = Net(x) + (Input_Cluster * 5.0)
108
+ target_input = torch.cat([lstm_out, cond_vec, cluster_rep], dim=2)
109
+ pred_targ = self.head_targets(target_input) + (cluster_rep * 5.0)
110
+
111
+ # 7. Head F: Structure
112
+ struct_input = torch.cat([lstm_out, cond_vec, e_err_rep], dim=2)
113
+ pred_struct = self.head_struct(struct_input)
114
+
115
+ # 8. Head C: Forbidden (With Residual Bypass)
116
+ # Output = Net(x) + (Input_Trap * 10.0)
117
+ pred_forbid = self.head_forbidden(h_shared) + (trap * 10.0)
118
+
119
+ # 9. Head C2: Dynamic
120
+ pred_dyn = self.head_dynamic(h_shared)
121
+
122
+ return pred_vol, pred_diff_logits, pred_fmt, pred_targ, pred_struct, pred_forbid, pred_dyn
123
+
124
+ # ==========================================
125
+ # 2. LOADER UTILITY
126
+ # ==========================================
127
+ def load_aeg_model(model_path, device):
128
+ """
129
+ Initializes the model and loads weights safely.
130
+ """
131
+ print(f"--- Loading AEG V5.4 from {model_path} ---")
132
+ model = AEGPolicyNetwork_V5_4().to(device)
133
+
134
+ if os.path.exists(model_path):
135
+ try:
136
+ state_dict = torch.load(model_path, map_location=device)
137
+ model.load_state_dict(state_dict)
138
+ model.eval() # Set to inference mode
139
+ print("✅ AEG Weights Loaded Successfully")
140
+ except Exception as e:
141
+ print(f"❌ CRITICAL: Failed to load AEG weights: {e}")
142
+ raise e
143
+ else:
144
+ print(f"⚠️ WARNING: Model file not found at {model_path}. Running with random weights (Debugging Mode).")
145
+
146
+ return model