Harry00 commited on
Commit
8983e24
·
verified ·
1 Parent(s): bd05e61

Upload mle/mle_system.py

Browse files
Files changed (1) hide show
  1. mle/mle_system.py +502 -0
mle/mle_system.py ADDED
@@ -0,0 +1,502 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ MLE System - Intégration complète du Morpho-Logic Engine
3
+
4
+ Orchestre les modules :
5
+ - memory (SparseAddressTable)
6
+ - routing (HammingRouter)
7
+ - binding (CircularBinder)
8
+ - energy (EnergyLandscape)
9
+ - inference (InferenceEngine)
10
+
11
+ Ajoute :
12
+ - Pile sémantique pour traitement hiérarchique
13
+ - Méta-apprentissage sur la structure même du système
14
+ - Métriques et monitoring
15
+ - Stabilisation globale
16
+ """
17
+
18
+ import numpy as np
19
+ from typing import List, Dict, Tuple, Optional, Callable, Any
20
+ import logging
21
+ import time
22
+ import json
23
+
24
+ from .memory import SparseAddressTable, VECTOR_SIZE
25
+ from .routing import HammingRouter
26
+ from .binding import CircularBinder
27
+ from .energy import EnergyLandscape
28
+ from .inference import InferenceEngine, InferenceResult
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+
33
+ class SemanticStack:
34
+ """
35
+ Pile sémantique pour traitement hiérarchique.
36
+
37
+ Permet de représenter des structures imbriquées :
38
+ - Niveau 0 : tokens/bruts
39
+ - Niveau 1 : chunks/groupes
40
+ - Niveau 2 : phrases/propositions
41
+ - Niveau 3+: concepts abstraits
42
+ """
43
+
44
+ def __init__(self, max_depth: int = 4):
45
+ self.max_depth = max_depth
46
+ self.levels: List[List[int]] = [[] for _ in range(max_depth)]
47
+ self.level_bindings: Dict[int, Dict[Tuple[int, int], np.ndarray]] = {}
48
+
49
+ def push(self, vector_id: int, level: int = 0):
50
+ """Ajoute un vecteur à un niveau."""
51
+ if 0 <= level < self.max_depth:
52
+ self.levels[level].append(vector_id)
53
+
54
+ def pop(self, level: int = 0) -> Optional[int]:
55
+ """Retire le dernier vecteur d'un niveau."""
56
+ if 0 <= level < self.max_depth and self.levels[level]:
57
+ return self.levels[level].pop()
58
+ return None
59
+
60
+ def bind_level(self, level: int, binder: CircularBinder, memory: SparseAddressTable):
61
+ """
62
+ Combine les vecteurs d'un niveau en un vecteur composite,
63
+ puis le pousse au niveau supérieur.
64
+ """
65
+ if level >= self.max_depth - 1:
66
+ return None
67
+
68
+ ids = self.levels[level]
69
+ if len(ids) < 2:
70
+ return None
71
+
72
+ # Récupère les vecteurs
73
+ vectors = []
74
+ for vid in ids:
75
+ for idx, meta in memory.metadata.items():
76
+ if meta.id == vid and memory.active_mask[idx]:
77
+ vectors.append(memory.vectors[idx])
78
+ break
79
+
80
+ if len(vectors) < 2:
81
+ return None
82
+
83
+ # Binding de tous les vecteurs du niveau
84
+ composite = binder.bind_multiple(vectors)
85
+
86
+ # Stocke le composite
87
+ self.level_bindings[level] = {}
88
+ for i, vid in enumerate(ids):
89
+ for j, vid2 in enumerate(ids[i+1:], i+1):
90
+ self.level_bindings[level][(vid, vid2)] = composite
91
+
92
+ # Crée un nouveau vecteur pour le composite et le pousse au niveau supérieur
93
+ new_id = memory.create_vector(context=composite, abstraction_level=level+1)
94
+ self.levels[level] = []
95
+ self.push(new_id, level=level+1)
96
+
97
+ return new_id
98
+
99
+ def get_level_state(self, level: int, memory: SparseAddressTable) -> np.ndarray:
100
+ """Retourne l'état composite d'un niveau."""
101
+ if level >= self.max_depth:
102
+ return np.zeros(VECTOR_SIZE, dtype=np.uint8)
103
+
104
+ ids = self.levels[level]
105
+ if not ids:
106
+ return np.zeros(VECTOR_SIZE, dtype=np.uint8)
107
+
108
+ vectors = []
109
+ for vid in ids:
110
+ for idx, meta in memory.metadata.items():
111
+ if meta.id == vid and memory.active_mask[idx]:
112
+ vectors.append(memory.vectors[idx])
113
+ break
114
+
115
+ if not vectors:
116
+ return np.zeros(VECTOR_SIZE, dtype=np.uint8)
117
+
118
+ # Moyenne binaire
119
+ mean_vec = np.mean(vectors, axis=0)
120
+ return (mean_vec > 0.5).astype(np.uint8)
121
+
122
+ def clear(self):
123
+ """Vide toute la pile."""
124
+ self.levels = [[] for _ in range(self.max_depth)]
125
+ self.level_bindings = {}
126
+
127
+
128
+ class MLEMetrics:
129
+ """Collecte et agrège les métriques de performance du système."""
130
+
131
+ def __init__(self):
132
+ self.inference_times: List[float] = []
133
+ self.energy_trajectories: List[List[float]] = []
134
+ self.memory_sizes: List[int] = []
135
+ self.associations_counts: List[int] = []
136
+ self.creation_rates: List[float] = []
137
+ self.convergence_rates: List[float] = []
138
+
139
+ # Métriques de cohérence sémantique
140
+ self.semantic_coherence_scores: List[float] = []
141
+ self.clustering_coefficients: List[float] = []
142
+
143
+ # Suivi des améliorations
144
+ self.baseline_energy: Optional[float] = None
145
+ self.energy_improvement: List[float] = []
146
+
147
+ def record_inference(self, result: InferenceResult, memory: SparseAddressTable,
148
+ energy: EnergyLandscape):
149
+ self.inference_times.append(result.execution_time_ms)
150
+ self.energy_trajectories.append(result.energy_trajectory)
151
+ self.memory_sizes.append(memory.size)
152
+ self.associations_counts.append(len(energy.associations))
153
+
154
+ if result.energy_trajectory:
155
+ final_energy = result.energy_trajectory[-1]
156
+ if self.baseline_energy is None:
157
+ self.baseline_energy = final_energy
158
+ else:
159
+ improvement = (self.baseline_energy - final_energy) / max(abs(self.baseline_energy), 1.0)
160
+ self.energy_improvement.append(improvement)
161
+
162
+ self.convergence_rates.append(1.0 if result.converged else 0.0)
163
+
164
+ def compute_coherence(self, memory: SparseAddressTable) -> float:
165
+ """
166
+ Calcule un score de cohérence sémantique :
167
+ les vecteurs proches en distance de Hamming doivent avoir des usages similaires.
168
+ """
169
+ if memory.size < 10:
170
+ return 0.0
171
+
172
+ active = memory.active_vectors
173
+ ids = [meta.id for idx, meta in memory.metadata.items() if memory.active_mask[idx]]
174
+
175
+ if len(active) < 10:
176
+ return 0.0
177
+
178
+ # Échantillonne
179
+ n_sample = min(50, len(active))
180
+ sample_idx = np.random.choice(len(active), size=n_sample, replace=False)
181
+
182
+ coherence_scores = []
183
+ for i in sample_idx:
184
+ dists = np.sum(active != active[i], axis=1)
185
+ nearest = np.argsort(dists)[1:6] # 5 plus proches
186
+
187
+ # Compare les niveaux d'abstraction
188
+ my_level = memory.metadata[i].abstraction_level if i in memory.metadata else 0
189
+ neighbor_levels = [
190
+ memory.metadata[ids[j]].abstraction_level
191
+ for j in nearest
192
+ ]
193
+
194
+ # Cohérence = variance faible des niveaux dans le voisinage
195
+ level_variance = np.var(neighbor_levels + [my_level])
196
+ coherence_scores.append(1.0 / (1.0 + level_variance))
197
+
198
+ return float(np.mean(coherence_scores)) if coherence_scores else 0.0
199
+
200
+ def get_summary(self) -> Dict:
201
+ if not self.inference_times:
202
+ return {}
203
+
204
+ recent_energies = [
205
+ traj[-1] for traj in self.energy_trajectories[-50:]
206
+ if traj
207
+ ]
208
+
209
+ return {
210
+ 'avg_inference_time_ms': float(np.mean(self.inference_times[-100:])),
211
+ 'avg_final_energy': float(np.mean(recent_energies)) if recent_energies else 0.0,
212
+ 'memory_size': self.memory_sizes[-1] if self.memory_sizes else 0,
213
+ 'n_associations': self.associations_counts[-1] if self.associations_counts else 0,
214
+ 'convergence_rate': float(np.mean(self.convergence_rates[-100:])),
215
+ 'energy_improvement_trend': float(np.mean(self.energy_improvement[-50:])) if self.energy_improvement else 0.0,
216
+ 'semantic_coherence': float(np.mean(self.semantic_coherence_scores[-50:])) if self.semantic_coherence_scores else 0.0,
217
+ }
218
+
219
+
220
+ class MLESystem:
221
+ """
222
+ Système MLE complet intégrant tous les modules avec apprentissage organique.
223
+
224
+ Usage:
225
+ mle = MLESystem()
226
+ result = mle.process(input_vector)
227
+ metrics = mle.get_metrics()
228
+ """
229
+
230
+ def __init__(
231
+ self,
232
+ memory_capacity: int = 10000,
233
+ k_neighbors: int = 10,
234
+ temperature: float = 0.5,
235
+ online_learning: bool = True,
236
+ enable_stack: bool = True,
237
+ enable_metrics: bool = True,
238
+ ):
239
+ self.k_neighbors = k_neighbors
240
+ self.enable_stack = enable_stack
241
+ self.enable_metrics = enable_metrics
242
+
243
+ # Modules
244
+ self.memory = SparseAddressTable(
245
+ initial_capacity=memory_capacity,
246
+ max_capacity=memory_capacity * 5,
247
+ )
248
+ self.router = HammingRouter(
249
+ use_index=True,
250
+ learn_routes=True,
251
+ )
252
+ self.binder = CircularBinder()
253
+ self.energy = EnergyLandscape()
254
+ self.inference = InferenceEngine(
255
+ temperature=temperature,
256
+ online_learning=online_learning,
257
+ )
258
+
259
+ # Stack sémantique
260
+ self.stack = SemanticStack() if enable_stack else None
261
+
262
+ # Métriques
263
+ self.metrics = MLEMetrics() if enable_metrics else None
264
+
265
+ # Historique d'expérience
266
+ self.experience_buffer: List[Dict] = []
267
+ self.experience_buffer_size = 1000
268
+
269
+ # Initialisation : crée quelques vecteurs de base
270
+ self._initialize_base_vectors()
271
+
272
+ logger.info(f"MLE System initialized with capacity {memory_capacity}")
273
+
274
+ def _initialize_base_vectors(self, n_base: int = 10):
275
+ """Crée des vecteurs de base pour démarrer le système."""
276
+ for i in range(n_base):
277
+ vec = self.memory._create_sparse_vector()
278
+ vid = self.memory.create_vector()
279
+ # Trouve l'index
280
+ for idx, meta in self.memory.metadata.items():
281
+ if meta.id == vid:
282
+ self.router.add_vector(idx, vec)
283
+ break
284
+
285
+ def process(
286
+ self,
287
+ input_vector: np.ndarray,
288
+ stack_level: int = 0,
289
+ external_callback: Optional[Callable] = None,
290
+ ) -> InferenceResult:
291
+ """
292
+ Traite un vecteur d'entrée par inférence + apprentissage.
293
+
294
+ Args:
295
+ input_vector: (4096,) uint8
296
+ stack_level: niveau de la pile sémantique
297
+ external_callback: callback par itération
298
+
299
+ Returns:
300
+ InferenceResult
301
+ """
302
+ # Maintenance de la mémoire
303
+ self.memory.tick()
304
+
305
+ # Requête ou création du vecteur d'entrée
306
+ input_id, input_idx, created = self.memory.query_or_create(input_vector)
307
+
308
+ if created and input_idx >= 0:
309
+ # Nouveau vecteur : ajoute au routeur
310
+ self.router.add_vector(input_idx, input_vector)
311
+
312
+ # Ajoute à la pile sémantique
313
+ if self.stack:
314
+ self.stack.push(input_id, level=stack_level)
315
+
316
+ # Inférence
317
+ result = self.inference.infer(
318
+ initial_state=input_vector,
319
+ memory_table=self.memory,
320
+ router=self.router,
321
+ energy_landscape=self.energy,
322
+ binder=self.binder,
323
+ k_neighbors=self.k_neighbors,
324
+ external_callback=external_callback,
325
+ )
326
+
327
+ # Stocke l'expérience
328
+ experience = {
329
+ 'input_id': input_id,
330
+ 'created': created,
331
+ 'final_state': result.final_state.copy() if result.final_state is not None else None,
332
+ 'energy_trajectory': result.energy_trajectory.copy(),
333
+ 'converged': result.converged,
334
+ 'learning_events': result.learning_events.copy(),
335
+ }
336
+ self.experience_buffer.append(experience)
337
+ if len(self.experience_buffer) > self.experience_buffer_size:
338
+ self.experience_buffer.pop(0)
339
+
340
+ # Métriques
341
+ if self.metrics:
342
+ self.metrics.record_inference(result, self.memory, self.energy)
343
+ # Coherence périodique
344
+ if self.inference.total_inferences % 50 == 0:
345
+ coherence = self.metrics.compute_coherence(self.memory)
346
+ self.metrics.semantic_coherence_scores.append(coherence)
347
+
348
+ # Met à jour le routeur pour le vecteur final
349
+ if result.final_state is not None:
350
+ # Requête ou création de l'état final
351
+ final_id, final_idx, final_created = self.memory.query_or_create(result.final_state)
352
+ if final_created and final_idx >= 0:
353
+ self.router.add_vector(final_idx, result.final_state)
354
+
355
+ # Renforce la route input -> final
356
+ if not created and not final_created:
357
+ pair = tuple(sorted((input_id, final_id)))
358
+ current = self.energy.associations.get(pair, 0.0)
359
+ self.energy.associations[pair] = min(1.0, current + 0.05)
360
+
361
+ return result
362
+
363
+ def process_sequence(
364
+ self,
365
+ vectors: List[np.ndarray],
366
+ bind_levels: bool = False,
367
+ ) -> List[InferenceResult]:
368
+ """
369
+ Traite une séquence de vecteurs.
370
+
371
+ Args:
372
+ vectors: liste de (4096,) uint8
373
+ bind_levels: si True, bind les niveaux de la pile périodiquement
374
+
375
+ Returns:
376
+ Liste de InferenceResult
377
+ """
378
+ results = []
379
+ for i, vec in enumerate(vectors):
380
+ result = self.process(vec, stack_level=0)
381
+ results.append(result)
382
+
383
+ # Bind périodique des niveaux
384
+ if bind_levels and self.stack and i > 0 and i % 3 == 0:
385
+ self.stack.bind_level(0, self.binder, self.memory)
386
+
387
+ return results
388
+
389
+ def query(
390
+ self,
391
+ query_vector: np.ndarray,
392
+ k: int = 5,
393
+ ) -> List[Tuple[int, float, int]]:
394
+ """
395
+ Requête simple (sans inférence) pour retrouver les voisins.
396
+
397
+ Returns:
398
+ [(vector_id, distance, index)]
399
+ """
400
+ return self.memory.find_nearest(query_vector, k=k)
401
+
402
+ def bind_vectors(self, ids: List[int]) -> Optional[np.ndarray]:
403
+ """
404
+ Binding explicite de vecteurs par ID.
405
+
406
+ Returns:
407
+ Vecteur composé ou None
408
+ """
409
+ vectors = []
410
+ for vid in ids:
411
+ for idx, meta in self.memory.metadata.items():
412
+ if meta.id == vid and self.memory.active_mask[idx]:
413
+ vectors.append(self.memory.vectors[idx])
414
+ break
415
+
416
+ if len(vectors) < 2:
417
+ return None
418
+
419
+ return self.binder.bind_multiple(vectors)
420
+
421
+ def get_vector(self, vector_id: int) -> Optional[np.ndarray]:
422
+ """Retourne un vecteur par son ID."""
423
+ for idx, meta in self.memory.metadata.items():
424
+ if meta.id == vector_id and self.memory.active_mask[idx]:
425
+ return self.memory.vectors[idx].copy()
426
+ return None
427
+
428
+ def get_semantic_clusters(self, n_clusters: int = 5) -> Dict[int, List[int]]:
429
+ """
430
+ Retourne des clusters sémantiques basés sur la distance de Hamming.
431
+ """
432
+ if self.memory.size < n_clusters * 2:
433
+ return {}
434
+
435
+ active = self.memory.active_vectors
436
+ ids = [meta.id for idx, meta in self.memory.metadata.items() if self.memory.active_mask[idx]]
437
+
438
+ # Clustering simple par distance
439
+ # 1. Choix des graines aléatoires
440
+ seeds = np.random.choice(len(active), size=min(n_clusters, len(active)), replace=False)
441
+ clusters: Dict[int, List[int]] = {ids[s]: [] for s in seeds}
442
+
443
+ # 2. Assignation par plus proche graine
444
+ for i, vec in enumerate(active):
445
+ dists = [np.sum(vec != active[s]) for s in seeds]
446
+ nearest_seed = seeds[np.argmin(dists)]
447
+ clusters[ids[nearest_seed]].append(ids[i])
448
+
449
+ return clusters
450
+
451
+ def get_metrics_summary(self) -> Dict:
452
+ """Résumé des métriques."""
453
+ summary = {}
454
+
455
+ if self.metrics:
456
+ summary['performance'] = self.metrics.get_summary()
457
+
458
+ summary['memory'] = self.memory.get_stats()
459
+ summary['routing'] = self.router.get_stats()
460
+ summary['energy'] = self.energy.get_stats()
461
+ summary['inference'] = self.inference.get_stats()
462
+
463
+ return summary
464
+
465
+ def print_summary(self):
466
+ """Affiche un résumé lisible."""
467
+ summary = self.get_metrics_summary()
468
+ print("\n" + "="*60)
469
+ print("MLE SYSTEM SUMMARY")
470
+ print("="*60)
471
+
472
+ for section, data in summary.items():
473
+ print(f"\n--- {section.upper()} ---")
474
+ if isinstance(data, dict):
475
+ for key, value in data.items():
476
+ if isinstance(value, float):
477
+ print(f" {key}: {value:.4f}")
478
+ else:
479
+ print(f" {key}: {value}")
480
+ else:
481
+ print(f" {data}")
482
+
483
+ print("\n" + "="*60)
484
+
485
+ def save_state(self, filepath: str):
486
+ """Sauvegarde l'état du système."""
487
+ state = {
488
+ 'memory_stats': self.memory.get_stats(),
489
+ 'energy_stats': self.energy.get_stats(),
490
+ 'inference_stats': self.inference.get_stats(),
491
+ 'router_stats': self.router.get_stats(),
492
+ }
493
+ with open(filepath, 'w') as f:
494
+ json.dump(state, f, indent=2)
495
+
496
+ def reset_metrics(self):
497
+ """Réinitialise les métriques."""
498
+ if self.metrics:
499
+ self.metrics = MLEMetrics()
500
+ self.inference.total_inferences = 0
501
+ self.inference.total_iterations = 0
502
+ self.inference.total_converged = 0