Harry00 commited on
Commit
240bfd9
·
verified ·
1 Parent(s): aafc205

Upload mle/tests.py

Browse files
Files changed (1) hide show
  1. mle/tests.py +393 -0
mle/tests.py ADDED
@@ -0,0 +1,393 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Tests et benchmarks du MLE System
3
+
4
+ Vérifie :
5
+ 1. Apprendissage avec le temps (réduction de l'énergie)
6
+ 2. Généralisation (performance sur cas non vus)
7
+ 3. Cohérence sémantique (clusters plus nets)
8
+ 4. Performance CPU (temps d'inférence)
9
+ """
10
+
11
+ import numpy as np
12
+ import time
13
+ from typing import List, Dict, Tuple
14
+ import json
15
+
16
+ from .mle_system import MLESystem
17
+ from .memory import VECTOR_SIZE
18
+
19
+
20
+ def generate_related_vectors(n: int, base_sparsity: float = 0.05,
21
+ relatedness: float = 0.7) -> List[np.ndarray]:
22
+ """
23
+ Génère des vecteurs liés sémantiquement.
24
+ Ils partagent une fraction 'relatedness' de leurs bits actifs.
25
+ """
26
+ target_active = int(VECTOR_SIZE * base_sparsity)
27
+ n_shared = int(target_active * relatedness)
28
+ n_unique = target_active - n_shared
29
+
30
+ # Base partagée
31
+ shared_indices = np.random.choice(VECTOR_SIZE, size=n_shared, replace=False)
32
+
33
+ vectors = []
34
+ for i in range(n):
35
+ vec = np.zeros(VECTOR_SIZE, dtype=np.uint8)
36
+ vec[shared_indices] = 1
37
+
38
+ # Bits uniques
39
+ remaining = np.setdiff1d(np.arange(VECTOR_SIZE), shared_indices)
40
+ unique_indices = np.random.choice(remaining, size=n_unique, replace=False)
41
+ vec[unique_indices] = 1
42
+
43
+ vectors.append(vec)
44
+
45
+ return vectors
46
+
47
+
48
+ def generate_unrelated_vectors(n: int, base_sparsity: float = 0.05) -> List[np.ndarray]:
49
+ """Génère des vecteurs indépendants."""
50
+ target_active = int(VECTOR_SIZE * base_sparsity)
51
+ vectors = []
52
+ for i in range(n):
53
+ indices = np.random.choice(VECTOR_SIZE, size=target_active, replace=False)
54
+ vec = np.zeros(VECTOR_SIZE, dtype=np.uint8)
55
+ vec[indices] = 1
56
+ vectors.append(vec)
57
+ return vectors
58
+
59
+
60
+ def generate_query_from_base(base: np.ndarray, noise: float = 0.1) -> np.ndarray:
61
+ """Génère une requête bruitée à partir d'un vecteur de base."""
62
+ vec = base.copy()
63
+ active = np.where(vec)[0]
64
+ n_flip = int(len(active) * noise)
65
+
66
+ if n_flip > 0:
67
+ # Éteint des bits actifs
68
+ to_off = np.random.choice(active, size=min(n_flip, len(active)), replace=False)
69
+ vec[to_off] = 0
70
+
71
+ # Allume des bits aléatoires
72
+ inactive = np.where(vec == 0)[0]
73
+ to_on = np.random.choice(inactive, size=min(n_flip, len(inactive)), replace=False)
74
+ vec[to_on] = 1
75
+
76
+ return vec
77
+
78
+
79
+ class MLEBenchmark:
80
+ """Benchmark complet du système MLE."""
81
+
82
+ def __init__(self, system: MLESystem):
83
+ self.system = system
84
+ self.results: Dict[str, List[float]] = {
85
+ 'phase': [],
86
+ 'final_energy': [],
87
+ 'convergence_rate': [],
88
+ 'memory_size': [],
89
+ 'n_associations': [],
90
+ 'avg_inference_time_ms': [],
91
+ 'semantic_coherence': [],
92
+ 'generalization_score': [],
93
+ }
94
+
95
+ def run_learning_curve(
96
+ self,
97
+ n_train: int = 200,
98
+ n_test: int = 50,
99
+ n_batches: int = 5,
100
+ vectors_per_batch: int = 20,
101
+ ):
102
+ """
103
+ Exécute un benchmark d'apprentissage en courbe.
104
+
105
+ 1. Génère des concepts de base
106
+ 2. Entraîne sur plusieurs batches
107
+ 3. Teste la généralisation à chaque étape
108
+ """
109
+ print("\n" + "="*70)
110
+ print("BENCHMARK: Learning Curve & Generalization")
111
+ print("="*70)
112
+
113
+ # Génère les concepts de base (simulant des catégories sémantiques)
114
+ n_concepts = 10
115
+ concepts = []
116
+ for i in range(n_concepts):
117
+ # Chaque concept a une base sémantique
118
+ base = generate_related_vectors(1, relatedness=1.0)[0]
119
+ # Variantes du concept
120
+ variants = generate_related_vectors(5, relatedness=0.8)
121
+ concepts.append((base, variants))
122
+
123
+ # Crée les données d'entraînement et de test
124
+ train_data = []
125
+ test_data = []
126
+
127
+ for base, variants in concepts:
128
+ # Quelques requêtes bruitées pour entraînement
129
+ for v in variants[:3]:
130
+ train_data.append(v)
131
+ # Quelques requêtes très bruitées pour test
132
+ for v in variants[3:]:
133
+ test_data.append(v)
134
+ # Requêtes bruitées à partir de la base
135
+ for _ in range(3):
136
+ train_data.append(generate_query_from_base(base, noise=0.15))
137
+ for _ in range(2):
138
+ test_data.append(generate_query_from_base(base, noise=0.25))
139
+
140
+ np.random.shuffle(train_data)
141
+ np.random.shuffle(test_data)
142
+
143
+ # Phase d'entraînement par batches
144
+ for batch_idx in range(n_batches):
145
+ print(f"\n--- Training Batch {batch_idx + 1}/{n_batches} ---")
146
+
147
+ start_idx = batch_idx * vectors_per_batch
148
+ end_idx = min(start_idx + vectors_per_batch, len(train_data))
149
+ batch = train_data[start_idx:end_idx]
150
+
151
+ for i, vec in enumerate(batch):
152
+ result = self.system.process(vec)
153
+ if i % 10 == 0:
154
+ print(f" Processed {i}/{len(batch)} vectors, "
155
+ f"energy={result.energy_trajectory[-1]:.1f if result.energy_trajectory else 0:.1f}, "
156
+ f"converged={result.converged}")
157
+
158
+ # Évalue après chaque batch
159
+ self._evaluate("train", batch_idx)
160
+
161
+ # Phase de test (généralisation)
162
+ print(f"\n--- Testing Generalization ({len(test_data)} vectors) ---")
163
+ generalization_scores = []
164
+
165
+ for i, vec in enumerate(test_data):
166
+ result = self.system.process(vec)
167
+
168
+ # Score de généralisation : distance aux concepts originaux
169
+ # Plus l'énergie finale est basse, plus la généralisation est bonne
170
+ if result.energy_trajectory:
171
+ score = 1.0 / (1.0 + result.energy_trajectory[-1] / 1000.0)
172
+ generalization_scores.append(score)
173
+
174
+ if i % 10 == 0:
175
+ print(f" Tested {i}/{len(test_data)} vectors, "
176
+ f"energy={result.energy_trajectory[-1]:.1f if result.energy_trajectory else 0:.1f}")
177
+
178
+ self._evaluate("test", n_batches)
179
+
180
+ avg_gen = float(np.mean(generalization_scores)) if generalization_scores else 0.0
181
+ self.results['generalization_score'].append(avg_gen)
182
+ print(f"\nAverage Generalization Score: {avg_gen:.4f}")
183
+
184
+ def _evaluate(self, phase: str, step: int):
185
+ """Évalue et enregistre les métriques."""
186
+ summary = self.system.get_metrics_summary()
187
+
188
+ self.results['phase'].append(f"{phase}_{step}")
189
+ self.results['final_energy'].append(
190
+ summary.get('performance', {}).get('avg_final_energy', 0.0)
191
+ )
192
+ self.results['convergence_rate'].append(
193
+ summary.get('performance', {}).get('convergence_rate', 0.0)
194
+ )
195
+ self.results['memory_size'].append(
196
+ summary.get('memory', {}).get('size', 0)
197
+ )
198
+ self.results['n_associations'].append(
199
+ summary.get('energy', {}).get('n_associations', 0)
200
+ )
201
+ self.results['avg_inference_time_ms'].append(
202
+ summary.get('performance', {}).get('avg_inference_time_ms', 0.0)
203
+ )
204
+ self.results['semantic_coherence'].append(
205
+ summary.get('performance', {}).get('semantic_coherence', 0.0)
206
+ )
207
+
208
+ print(f" [Metrics] Energy={self.results['final_energy'][-1]:.1f}, "
209
+ f"Convergence={self.results['convergence_rate'][-1]:.2%}, "
210
+ f"Memory={self.results['memory_size'][-1]}, "
211
+ f"Assoc={self.results['n_associations'][-1]}, "
212
+ f"Coherence={self.results['semantic_coherence'][-1]:.3f}")
213
+
214
+ def run_stability_test(self, n_iterations: int = 100):
215
+ """
216
+ Test de stabilité : le système ne doit pas diverger
217
+ avec un flux continu de données.
218
+ """
219
+ print("\n" + "="*70)
220
+ print("BENCHMARK: Stability Test")
221
+ print("="*70)
222
+
223
+ # Génère un flux continu
224
+ base_vectors = generate_unrelated_vectors(5)
225
+
226
+ energies = []
227
+ memory_sizes = []
228
+
229
+ for i in range(n_iterations):
230
+ # Alterne entre vecteurs connus et nouveaux
231
+ if i % 3 == 0 and i > 0:
232
+ # Nouveau vecteur
233
+ vec = generate_unrelated_vectors(1)[0]
234
+ else:
235
+ # Vecteur lié à un existant
236
+ base = base_vectors[i % len(base_vectors)]
237
+ vec = generate_query_from_base(base, noise=0.2)
238
+
239
+ result = self.system.process(vec)
240
+
241
+ if result.energy_trajectory:
242
+ energies.append(result.energy_trajectory[-1])
243
+ memory_sizes.append(self.system.memory.size)
244
+
245
+ if i % 20 == 0:
246
+ print(f" Iteration {i}: energy={np.mean(energies[-20:]):.1f if energies else 0:.1f}, "
247
+ f"memory={self.system.memory.size}")
248
+
249
+ # Vérifie la stabilité
250
+ if len(energies) > 20:
251
+ early_mean = np.mean(energies[:20])
252
+ late_mean = np.mean(energies[-20:])
253
+
254
+ print(f"\n Early energy: {early_mean:.1f}")
255
+ print(f" Late energy: {late_mean:.1f}")
256
+
257
+ if late_mean < early_mean * 0.9:
258
+ print(" ✓ Energy decreased with experience (learning confirmed)")
259
+ elif late_mean < early_mean * 1.1:
260
+ print(" ✓ Energy stable (system stable)")
261
+ else:
262
+ print(" ✗ Energy increased (potential instability)")
263
+
264
+ def run_binding_test(self, n_trials: int = 20):
265
+ """
266
+ Test de binding/unbinding et composition.
267
+ """
268
+ print("\n" + "="*70)
269
+ print("BENCHMARK: Binding & Composition Test")
270
+ print("="*70)
271
+
272
+ # Crée des vecteurs pour role-filler
273
+ roles = generate_unrelated_vectors(3) # agent, action, patient
274
+ fillers = generate_unrelated_vectors(3) # john, run, ball
275
+
276
+ successes = 0
277
+ for trial in range(n_trials):
278
+ role_idx = trial % 3
279
+ filler_idx = (trial + 1) % 3
280
+
281
+ # Binding
282
+ bound = self.system.binder.bind_role_filler(
283
+ roles[role_idx],
284
+ fillers[filler_idx]
285
+ )
286
+
287
+ # Unbinding
288
+ recovered = self.system.binder.unbind_role_filler(bound, roles[role_idx])
289
+
290
+ # Vérifie la similarité
291
+ similarity = np.mean(recovered == fillers[filler_idx])
292
+ if similarity > 0.6:
293
+ successes += 1
294
+
295
+ print(f" Binding/Unbinding accuracy: {successes}/{n_trials} ({successes/n_trials:.1%})")
296
+
297
+ def run_abstraction_test(self, n_patterns: int = 10, n_instances: int = 5):
298
+ """
299
+ Test de formation d'abstractions.
300
+ Le système doit détecter des patterns récurrents et les compiler.
301
+ """
302
+ print("\n" + "="*70)
303
+ print("BENCHMARK: Abstraction Test")
304
+ print("="*70)
305
+
306
+ initial_size = self.system.memory.size
307
+
308
+ for p in range(n_patterns):
309
+ # Génère des instances d'un pattern
310
+ pattern_base = generate_related_vectors(1, relatedness=1.0)[0]
311
+
312
+ for i in range(n_instances):
313
+ instance = generate_query_from_base(pattern_base, noise=0.15)
314
+ self.system.process(instance)
315
+
316
+ final_size = self.system.memory.size
317
+ abstractions_created = final_size - initial_size - n_patterns * n_instances
318
+
319
+ print(f" Initial memory size: {initial_size}")
320
+ print(f" Final memory size: {final_size}")
321
+ print(f" Expected new vectors: {n_patterns * n_instances}")
322
+ print(f" Actual new vectors: {final_size - initial_size}")
323
+ print(f" Potential abstractions: {max(0, abstractions_created)}")
324
+
325
+ def run_all(self):
326
+ """Exécute tous les benchmarks."""
327
+ print("\n" + "="*70)
328
+ print("MLE SYSTEM COMPREHENSIVE BENCHMARK")
329
+ print("="*70)
330
+
331
+ self.run_learning_curve()
332
+ self.run_stability_test()
333
+ self.run_binding_test()
334
+ self.run_abstraction_test()
335
+
336
+ # Résumé final
337
+ print("\n" + "="*70)
338
+ print("FINAL SUMMARY")
339
+ print("="*70)
340
+ self.system.print_summary()
341
+
342
+ return self.results
343
+
344
+
345
+ def quick_test():
346
+ """Test rapide pour vérifier le fonctionnement de base."""
347
+ print("Quick functionality test...")
348
+
349
+ mle = MLESystem(
350
+ memory_capacity=1000,
351
+ online_learning=True,
352
+ )
353
+
354
+ # Test basique
355
+ vec = np.zeros(VECTOR_SIZE, dtype=np.uint8)
356
+ vec[np.random.choice(VECTOR_SIZE, size=200, replace=False)] = 1
357
+
358
+ result = mle.process(vec)
359
+ print(f" Basic inference: converged={result.converged}, "
360
+ f"iterations={result.n_iterations}, "
361
+ f"energy={result.energy_trajectory[-1]:.1f if result.energy_trajectory else 0:.1f}")
362
+
363
+ # Test binding
364
+ a = np.zeros(VECTOR_SIZE, dtype=np.uint8)
365
+ a[np.random.choice(VECTOR_SIZE, size=200, replace=False)] = 1
366
+ b = np.zeros(VECTOR_SIZE, dtype=np.uint8)
367
+ b[np.random.choice(VECTOR_SIZE, size=200, replace=False)] = 1
368
+
369
+ bound = mle.binder.bind(a, b)
370
+ recovered = mle.binder.unbind(bound, a)
371
+ similarity = np.mean(recovered == b)
372
+ print(f" Binding test: similarity={similarity:.3f}")
373
+
374
+ # Test requête
375
+ neighbors = mle.query(vec, k=3)
376
+ print(f" Query test: found {len(neighbors)} neighbors")
377
+
378
+ print(" ✓ All basic tests passed")
379
+ return mle
380
+
381
+
382
+ if __name__ == "__main__":
383
+ # Test rapide
384
+ mle = quick_test()
385
+
386
+ # Benchmark complet
387
+ benchmark = MLEBenchmark(mle)
388
+ results = benchmark.run_all()
389
+
390
+ # Sauvegarde
391
+ with open("benchmark_results.json", "w") as f:
392
+ json.dump(results, f, indent=2)
393
+ print("\nResults saved to benchmark_results.json")