Architecture MLflow CVNTrade - Document de Conception¶
Table des Matières¶
- Vue d'ensemble
- Architecture Technique
- Organisation des Expériences
- Gestion des Runs
- Architecture de Cache
- Stockage des Artefacts
- MLflow Model Registry
- Tags et Métadonnées
- CVNTrade_MLFlowManager
- Patterns d'Utilisation
- Stratégies de Fallback
- Bonnes Pratiques
- Troubleshooting
Vue d'ensemble¶
MLflow dans CVNTrade est structuré comme une architecture de cache sémantique robuste qui gère l'ensemble du cycle de vie du machine learning pour le trading crypto. L'infrastructure MLflow sert à la fois de système de tracking d'expériences et de cache intelligent pour 6 types d'entités métier.
Objectifs Architecturaux¶
- Traçabilité complète : Suivi de chaque étape du pipeline ML
- Cache intelligent : Réutilisation optimale des calculs coûteux
- Fallbacks robustes : Stratégies de récupération automatique
- Scalabilité : Support de multiple cryptos/timeframes/stratégies
- Reproductibilité : Versioning complet des données et modèles
Architecture Technique¶
Stack Infrastructure¶
┌─────────────────────────────────────────────────────────────┐
│ CVNTrade Application │
├─────────────────────────────────────────────────────────────┤
│ CVNTrade_MLFlowManager │
├─────────────────────────────────────────────────────────────┤
│ MLflow Server │
├─────────────────────────────────────────────────────────────┤
│ Tracking Backend │ Artifact Store │
│ PostgreSQL │ AWS S3 │
│ localhost:5432/ │ s3://bucket-name/ │
│ champollion │ │
└─────────────────────────────────────────────────────────────┘
Configuration¶
- Tracking URI:
postgresql://mlflow:mlflow@localhost:5432/champollion - Artifact Store:
s3://cvntrade-mlflow-artifacts/ - Backend: PostgreSQL pour les métadonnées, S3 pour les artefacts
- Authentication: Gestion via variables d'environnement
Organisation des Expériences¶
Mapping Entités → Expériences¶
Le projet utilise 6 expériences MLflow spécialisées pour organiser logiquement les différents types d'entités:
| Entité | Expérience MLflow | Description |
|---|---|---|
FEATURE_STORE |
CVNTrade_FeatureStores |
Données brutes crypto enrichies |
LABELS |
CVNTrade_Labels |
Étiquetages stratégies de trading |
FEATURE_ENGINEERING |
CVNTrade_FeatureEng |
Features transformées spécialisées |
FEATURE_SELECTION |
CVNTrade_FeatureSelect |
Sélection optimale de features |
HPO_PARAMS |
CVNTrade_HPO |
Paramètres d'hyperoptimisation |
TRAINED_MODEL |
CVNTrade_Models |
Modèles entraînés finaux |
Hiérarchie des Dépendances¶
graph TD
A[FEATURE_STORE] --> B[LABELS]
A --> C[FEATURE_ENGINEERING]
B --> C
C --> D[FEATURE_SELECTION]
D --> E[HPO_PARAMS]
D --> F[TRAINED_MODEL]
E --> F
Gestion des Runs¶
Types de Runs¶
- Runs Principaux : Processus d'entraînement complet
- Runs Nested : Sous-processus (HPO, validation, etc.)
- Runs de Cache : Stockage d'entités intermédiaires
Structure de Nommage¶
{entity_type}_{crypto}_{timeframe}_{strategy}_{timestamp}
Exemple: hpo_params_BTCUSDT_15m_SL1.2_TP1.3_H4_20251003
Gestion des Conflits¶
Le CVNTrade_MLFlowManager inclut une détection automatique des runs actifs :
# Auto-détection : Run principal vs Nested
if self.mlflow.active_run() is not None:
return self.mlflow.start_run(run_name=run_name, nested=True, tags=tags)
else:
return self.mlflow.start_run(run_name=run_name, tags=tags)
Architecture de Cache¶
Stratégies de Fallback (4 Niveaux)¶
Le système implémente des stratégies de fallback intelligentes pour maximiser la réutilisation:
1. Exact Match (Niveau 1)¶
exact_tags = {
'cache.entity_type': entity_type.value,
'cache.crypto_symbol': criteria.get('crypto_symbol'),
'cache.timeframe': criteria.get('timeframe'),
'cache.strategy': criteria.get('strategy'),
'cache.model_type': criteria.get('model_type'),
'cache.version': criteria.get('version'),
'cache.created_date': criteria.get('data_date')
}
2. Context Match (Niveau 2)¶
- Même crypto, timeframe, stratégie, modèle
- Ignore la date exacte et version
- Prend le plus récent non-archivé
3. Best Available (Niveau 3)¶
- Même crypto et timeframe
- Trie par
performance_score DESC - Favorise les runs marqués
production_ready
4. Default Config (Niveau 4)¶
- Configuration par défaut hardcodée
- Paramètres optimaux par type de modèle
- Utilisé en dernier recours
Cache d'Entités¶
Chaque entité a une clé sémantique unique :
def get_semantic_key(self) -> str:
return f"{self.crypto_symbol}_{self.timeframe}_{self.data_date}_{self.version}"
Stockage des Artefacts¶
Organisation S3¶
s3://cvntrade-mlflow-artifacts/
├── CVNTrade_FeatureStores/
│ ├── {run_id}/
│ │ ├── raw_data.parquet
│ │ └── metadata.json
├── CVNTrade_Models/
│ ├── {run_id}/
│ │ ├── model/
│ │ ├── requirements.txt
│ │ └── metrics.json
└── CVNTrade_HPO/
├── {run_id}/
│ ├── best_params.json
│ ├── study_data.pkl
│ └── optimization_history.json
Types d'Artefacts par Entité¶
| Entité | Artefacts Principaux |
|---|---|
| FeatureStore | raw_data.parquet, metadata.json |
| Labels | labels_data.parquet, strategy_stats.json |
| FeatureEngineering | features_data.parquet, feature_names.json |
| FeatureSelection | selected_features.json, selection_metrics.json |
| HpoParams | best_params.json, study_data.pkl |
| TrainedModel | model/, metrics.json, feature_importance.json |
MLflow Model Registry¶
Vue d'ensemble¶
Le projet CVNTrade utilise le MLflow Model Registry pour la gestion centralisée des modèles de machine learning en production. Cette composante assure le versioning, le déploiement et la gouvernance des modèles.
Architecture Model Registry¶
┌─────────────────────────────────────────────────────────────┐
│ MLflow Model Registry │
├─────────────────────────────────────────────────────────────┤
│ Registered Models │ Model Versions │
│ ┌─────────────────────┐ │ ┌─────────────────────────┐ │
│ │ CVNTrade_XGBoost │────┼──│ Version 1: Staging │ │
│ │ CVNTrade_LightGBM │ │ │ Version 2: Production │ │
│ │ CVNTrade_CatBoost │ │ │ Version 3: Archived │ │
│ │ CVNTrade_Ensemble │ │ └─────────────────────────┘ │
│ └─────────────────────┘ │ │
├─────────────────────────────────────────────────────────────┤
│ Stages de Cycle de Vie │
│ None → Staging → Production → Archived │
└─────────────────────────────────────────────────────────────┘
Modèles Enregistrés¶
Convention de Nommage¶
Modèles Standards du Registry¶
| Nom du Modèle | Description | Algorithme |
|---|---|---|
CVNTrade_XGBoost_Base |
Modèle XGBoost générique | XGBoost Classifier |
CVNTrade_LightGBM_Base |
Modèle LightGBM générique | LightGBM Classifier |
CVNTrade_CatBoost_Base |
Modèle CatBoost générique | CatBoost Classifier |
CVNTrade_Ensemble_Stacking |
Ensemble par stacking | Meta-classifier |
CVNTrade_RandomForest_Base |
Random Forest de référence | RandomForest Classifier |
Gestion des Versions¶
Stages de Cycle de Vie¶
- None : Version nouvellement créée
- Staging : En cours de validation
- Production : Approuvée pour utilisation
- Archived : Ancienne version archivée
Métadonnées de Version¶
Chaque version stocke :
version_metadata = {
"crypto_symbol": "BTCUSDT",
"strategy": "SL1.2_TP1.3_H4",
"timeframe": "15m",
"model_type": "xgboost",
"f1_macro": 0.77,
"auc_score": 0.93,
"training_date": "2025-10-03",
"features_count": 371,
"training_samples": 105211
}
Intégration avec CVNTrade_MLFlowManager¶
Enregistrement de Modèles¶
# Enregistrement automatique dans log_pipeline
manager.log_pipeline(
pipeline=trained_model,
artifact_path="model",
input_example=X_sample,
registered_model_name="CVNTrade_XGBoost_BTCUSDT_SL1.2_TP1.3_H4"
)
Chargement de Modèles en Production¶
# Chargement du modèle en production
model_uri = f"models:/CVNTrade_XGBoost_BTCUSDT_SL1.2_TP1.3_H4/Production"
loaded_model = mlflow.pyfunc.load_model(model_uri)
# Alternative avec version spécifique
model_uri = f"models:/CVNTrade_XGBoost_BTCUSDT_SL1.2_TP1.3_H4/2"
loaded_model = mlflow.pyfunc.load_model(model_uri)
Workflow de Déploiement¶
1. Entraînement et Enregistrement¶
with manager.start_run("training_btc_h4") as run:
# Entraînement du modèle
model = train_xgboost_model(X_train, y_train)
# Évaluation
metrics = evaluate_model(model, X_test, y_test)
manager.log_metrics(metrics)
# Enregistrement dans le registry
manager.log_pipeline(
pipeline=model,
artifact_path="model",
registered_model_name=f"CVNTrade_XGBoost_{crypto}_{strategy}"
)
2. Transition vers Staging¶
from mlflow.tracking import MlflowClient
client = MlflowClient()
latest_version = client.get_latest_versions(
"CVNTrade_XGBoost_BTCUSDT_SL1.2_TP1.3_H4",
stages=["None"]
)[0]
# Transition vers Staging
client.transition_model_version_stage(
name="CVNTrade_XGBoost_BTCUSDT_SL1.2_TP1.3_H4",
version=latest_version.version,
stage="Staging"
)
3. Validation et Production¶
# Après validation réussie
client.transition_model_version_stage(
name="CVNTrade_XGBoost_BTCUSDT_SL1.2_TP1.3_H4",
version=latest_version.version,
stage="Production"
)
# Archiver l'ancienne version Production
old_production_versions = client.get_latest_versions(
"CVNTrade_XGBoost_BTCUSDT_SL1.2_TP1.3_H4",
stages=["Production"]
)
for version in old_production_versions[:-1]: # Garde le plus récent
client.transition_model_version_stage(
name="CVNTrade_XGBoost_BTCUSDT_SL1.2_TP1.3_H4",
version=version.version,
stage="Archived"
)
Patterns d'Utilisation Registry¶
Pattern 1: Déploiement A/B Testing¶
# Modèle en production
model_prod = mlflow.pyfunc.load_model(
"models:/CVNTrade_XGBoost_BTCUSDT_SL1.2_TP1.3_H4/Production"
)
# Modèle en staging pour comparaison
model_staging = mlflow.pyfunc.load_model(
"models:/CVNTrade_XGBoost_BTCUSDT_SL1.2_TP1.3_H4/Staging"
)
# Routing 90/10
if random.random() < 0.9:
prediction = model_prod.predict(features)
else:
prediction = model_staging.predict(features)
# Log pour comparaison
mlflow.log_metric("staging_prediction", prediction)
Pattern 2: Rollback Automatique¶
def rollback_model(model_name: str, reason: str):
client = MlflowClient()
# Archiver la version Production actuelle
prod_version = client.get_latest_versions(model_name, ["Production"])[0]
client.transition_model_version_stage(
name=model_name,
version=prod_version.version,
stage="Archived"
)
# Promouvoir la version Staging
staging_version = client.get_latest_versions(model_name, ["Staging"])[0]
client.transition_model_version_stage(
name=model_name,
version=staging_version.version,
stage="Production"
)
# Logger l'incident
with mlflow.start_run():
mlflow.log_param("rollback_reason", reason)
mlflow.log_param("old_version", prod_version.version)
mlflow.log_param("new_version", staging_version.version)
Pattern 3: Monitoring de Performance¶
def monitor_model_performance(model_name: str):
client = MlflowClient()
# Récupérer toutes les versions Production
versions = client.search_model_versions(
f"name='{model_name}' and current_stage='Production'"
)
for version in versions:
# Charger les métriques de la version
run = client.get_run(version.run_id)
metrics = run.data.metrics
# Vérifier la dégradation
current_f1 = metrics.get('f1_macro', 0)
if current_f1 < 0.70: # Seuil critique
logger.warning(f"Performance dégradée pour {model_name} v{version.version}")
# Déclencher alerte ou rollback automatique
Integration avec Model Registry CVNTrade¶
Le projet utilise également un Model Registry interne (CVNTrade_ModelRegistry) pour les configurations par défaut :
from commun.utils.cvntrade_model_registry import ModelRegistry
registry = ModelRegistry()
# Récupérer la configuration XGBoost optimisée
xgb_config = registry.get_model('xgboost_direct')
model = xgb_config['class'](**xgb_config['default_params'])
# Entraîner et enregistrer dans MLflow Registry
with manager.start_run() as run:
model.fit(X_train, y_train)
manager.log_pipeline(
pipeline=model,
artifact_path="model",
registered_model_name="CVNTrade_XGBoost_Direct"
)
Gouvernance et Sécurité¶
Contrôles d'Accès¶
- Lecture : Tous les utilisateurs autorisés
- Écriture : Équipe ML uniquement
- Transition Production : Validation manuelle requise
- Archivage : Automated après 6 mois
Audit Trail¶
Toutes les transitions sont loggées :
transition_log = {
"timestamp": datetime.now().isoformat(),
"user": os.getenv("USER"),
"model_name": model_name,
"version": version,
"from_stage": old_stage,
"to_stage": new_stage,
"reason": transition_reason
}
Tags et Métadonnées¶
Tags Standardisés¶
Tous les runs utilisent un système de tags standardisé :
standard_tags = {
"cache.entity_type": entity_type.value,
"cache.crypto_symbol": metadata.get('crypto_symbol', 'UNKNOWN'),
"cache.timeframe": metadata.get('timeframe', '15m'),
"cache.strategy": metadata.get('strategy'),
"cache.model_type": metadata.get('model_type'),
"cache.version": metadata.get('version', 'v1'),
"cache.created_date": datetime.now().strftime('%Y-%m-%d'),
"cache.data_date": metadata.get('data_date'),
"cache.production_ready": str(metadata.get('production_ready', False)).lower(),
"cache.experimental": str(metadata.get('experimental', True)).lower()
}
Métriques Principales¶
- Performance Score : Score composite de performance
- F1 Macro : Métrique principale de classification
- AUC Score : Score de probabilité
- Action Rate : Taux d'actions de trading
- Validation Score : Performance sur validation
Paramètres Trackés¶
- Model Hyperparameters : Tous les hyperparamètres optimisés
- Feature Engineering Config : Configuration des transformations
- Strategy Parameters : SL, TP, Hold time
- Data Parameters : Timeframe, crypto, date range
CVNTrade_MLFlowManager¶
Interface Principale¶
Le CVNTrade_MLFlowManager est la couche d'abstraction centrale UNIQUE qui gère toutes les interactions MLflow.
RÈGLE ARCHITECTURALE CRITIQUE: Aucun autre module ne doit importer
mlflowdirectement. Toutes les opérations MLflow (log_model, load_model, etc.) DOIVENT passer par ce gestionnaire centralisé pour garantir que les artefacts sont stockés dans PostgreSQL/S3 et non dans./mlruns/local.
Initialisation¶
manager = CVNTrade_MLFlowManager(
experiment_name="CVNTrade_Models",
tracking_uri="postgresql://mlflow:mlflow@localhost:5432/champollion"
)
# Le tracking URI est configuré automatiquement dans le constructeur
# via mlflow.set_tracking_uri() pour éviter les écritures locales
Méthodes Principales¶
# Gestion des runs
start_run(run_name, tags) -> Run
start_nested_run(run_name, tags) -> Run
start_experiment_run(coin_pair, run_name, tags) -> Run
# Logging avec protection anti-doublons
log_params(params: Dict[str, Any], run_id: str = None)
log_metrics(metrics: Dict[str, float], run_id: str = None)
log_artifact(local_path: str, artifact_path: str, run_id: str = None)
# NOUVEAU: Méthodes centralisées pour modèles (évite les écritures locales)
log_model(model, artifact_path: str, run_id: str,
registered_model_name: str = None,
model_flavor: str = "sklearn") # sklearn, xgboost, lightgbm, catboost
load_model(model_uri: str, model_flavor: str = "sklearn")
# Gestion des modèles (wrappers utilisant log_model/load_model)
log_pipeline(pipeline, artifact_path, input_example, registered_model_name, run_id)
load_latest_pipeline(coin, strategy_tag, artifact_path, model_flavor)
load_latest_artifact(coin, run_type, artifact_path)
# Model Registry
log_model_with_registry(model, artifact_path, model_name, description, metadata, model_flavor)
load_model_from_registry(model_name, stage, version, model_flavor)
# Sérialisation centralisée (évite imports pickle/joblib dans autres modules)
serialize_object(obj, path, method="joblib") # Sérialise localement
deserialize_object(path, method="joblib") # Désérialise localement
log_serialized_artifact(obj, artifact_name, run_id, artifact_path, method="pickle")
download_and_deserialize_artifact(run_id, artifact_path, method="pickle")
# Accès au client MLflow (pour modules nécessitant des opérations avancées)
get_client() -> MlflowClient # Retourne le client configuré
search_runs(experiment_ids, filter_string, ...) # Recherche de runs
download_artifacts(run_id, artifact_path) # Téléchargement d'artefacts
get_experiment_by_name(name) # Récupération d'expérience
Note: Les modules externes peuvent utiliser
get_client()pour accéder au client MLflow sans avoir à importerMlflowClientdirectement. Cela garantit que la configuration du tracking URI est toujours correcte.
Fonctionnalités Avancées¶
- Protection Anti-Doublons : Évite le double logging de paramètres/métriques
- Gestion des Runs Nested : Détection automatique des runs parents
- Artifact Location Management : Configuration automatique S3
- Centralisation MLflow : Toutes les opérations MLflow passent par ce manager
- Support Multi-Flavor : sklearn, xgboost, lightgbm, catboost
- Tracking URI Centralisé : Configuration unique garantissant l'écriture vers PostgreSQL/S3
- Sérialisation Centralisée : Méthodes pickle/joblib centralisées
- Accès Client Contrôlé : Client MLflow fourni via le manager
Patterns d'Utilisation¶
Pattern 1: Entraînement Complet¶
# 1. Démarrer un run principal
with manager.start_run("training_BTCUSDT_20251003") as run:
# 2. Logger la configuration
manager.log_params({
"crypto_symbol": "BTCUSDT",
"strategy": "SL1.2_TP1.3_H4",
"model_type": "xgboost"
})
# 3. Runs nested pour sous-processus
with manager.start_nested_run("feature_engineering") as fe_run:
# Feature engineering...
manager.log_artifact("features.parquet")
with manager.start_nested_run("hyperopt") as hpo_run:
# HPO process...
manager.log_params(best_params)
manager.log_metrics({"best_score": 0.85})
# 4. Logger le modèle final
manager.log_pipeline(final_model, "model")
manager.log_metrics({
"f1_macro": 0.77,
"auc_score": 0.93
})
Pattern 2: Cache d'Entité¶
# Recherche avec fallbacks automatiques
cache_result = cache_manager.get_entity(
EntityType.HPO_PARAMS,
criteria={
'crypto_symbol': 'BTCUSDT',
'timeframe': '15m',
'strategy': 'SL1.2_TP1.3_H4',
'model_type': 'xgboost'
}
)
if cache_result.found:
print(f"Trouvé avec stratégie: {cache_result.level}")
hpo_params = cache_result.entity
else:
# Générer nouveaux paramètres
hpo_params = run_hyperopt()
# Stocker dans cache
cache_manager.store_entity(
EntityType.HPO_PARAMS,
hpo_params,
metadata={
'crypto_symbol': 'BTCUSDT',
'performance_score': 0.85
}
)
Pattern 3: Chargement de Modèle¶
# Chargement du meilleur modèle pour une stratégie
try:
model = manager.load_latest_pipeline(
coin="BTCUSDT",
strategy_tag="SL1.2_TP1.3_H4",
artifact_path="model"
)
print("Modèle chargé avec succès")
except FileNotFoundError:
print("Aucun modèle trouvé, entraînement requis")
Stratégies de Fallback¶
Algorithme de Résolution¶
def resolve_cache_entity(entity_type, criteria):
# 1. Tentative Exact Match
result = exact_match_strategy.search(entity_type, criteria)
if result.found:
return result
# 2. Tentative Context Match
result = context_match_strategy.search(entity_type, criteria)
if result.found:
return result
# 3. Tentative Best Available
result = best_available_strategy.search(entity_type, criteria)
if result.found:
return result
# 4. Fallback Default Config
return default_config_strategy.search(entity_type, criteria)
Métriques de Performance Cache¶
- Cache Hit Rate : Pourcentage de succès niveau 1-2
- Fallback Usage : Distribution des niveaux utilisés
- Cache Age : Âge moyen des entités réutilisées
- Storage Efficiency : Ratio réutilisation/stockage
Bonnes Pratiques¶
1. Naming Conventions¶
# Runs principaux
"training_{crypto}_{timeframe}_{strategy}_{date}"
# Runs nested
"hpo_{model_type}_{crypto}_{date}"
"feature_eng_{crypto}_{version}"
# Expériences
"CVNTrade_{EntityType}"
2. Tags Essentiels¶
essential_tags = {
"crypto_symbol": "BTCUSDT",
"timeframe": "15m",
"strategy": "SL1.2_TP1.3_H4",
"model_type": "xgboost",
"version": "v2.1",
"production_ready": "true"
}
3. Métriques Minimales¶
required_metrics = {
"f1_macro": 0.77,
"performance_score": 0.85,
"training_time": 120.5,
"data_points": 105211
}
4. Artifact Organization¶
# Structure recommandée pour artefacts
model/
├── MLmodel
├── conda.yaml
├── model.pkl
├── python_env.yaml
└── requirements.txt
data/
├── features.parquet
├── labels.parquet
└── metadata.json
metrics/
├── validation_results.json
├── feature_importance.json
└── confusion_matrix.png
Troubleshooting¶
Problèmes Courants¶
1. Run Conflicts¶
Solution : Le MLflow Manager gère automatiquement viamlflow.end_run() dans le cache manager
2. Artifact Not Found¶
Solutions : - Vérifier leartifact_path dans log_artifact()
- Confirmer que l'artifact store S3 est accessible
- Utiliser list_artifacts() pour debug
3. Experiment Creation Failed¶
Solutions : - Vérifier permissions PostgreSQL - Confirmer que l'URI de tracking est correct - Vérifier la connectivité à S34. Cache Miss Systematic¶
Diagnostic :
# Vérifier les tags de recherche
print(f"Critères: {search_criteria}")
# Examiner les tags existants
runs = mlflow.search_runs(experiment_id)
print(runs[['tags.cache.crypto_symbol', 'tags.cache.strategy']])
Monitoring et Debug¶
Scripts de Diagnostic¶
# Statistiques cache
cache_stats = cache_manager.get_cache_statistics()
print(f"Cache hit rate: {cache_stats['hit_rate']:.2%}")
# Audit des expériences
for exp_name in MLFLOW_EXPERIMENTS.values():
exp = mlflow.get_experiment_by_name(exp_name)
print(f"{exp_name}: {len(mlflow.search_runs(exp.experiment_id))} runs")
# Validation des dépendances
registry = EntityRegistry()
for entity in EntityType:
deps = registry.get_dependencies(entity)
print(f"{entity.name}: depends on {[d.name for d in deps]}")
Optimisations Performance¶
- Nettoyage Périodique : Archiver les runs anciens
- Index Database : Optimiser les requêtes PostgreSQL sur tags
- S3 Lifecycle : Configurer l'archivage automatique
- Connection Pooling : Réutiliser les connexions MLflow
Évolutions Futures¶
Roadmap Architecture¶
- ✅ Model Registry Integration : MLflow Model Registry implémenté avec gouvernance complète
- Distributed Training : Support multi-GPU/multi-node
- Real-time Inference : Cache des prédictions en temps réel
- A/B Testing Framework : Comparaison automatique de modèles (partiellement implémenté)
- Data Lineage : Traçabilité complète des datasets
- Model Monitoring : Détection automatique de drift des modèles
- CI/CD Pipeline : Intégration continue pour modèles ML
Métriques et KPIs¶
- Time to Market : Réduction du temps de développement via cache
- Resource Efficiency : Optimisation des coûts de calcul
- Model Performance : Amélioration continue via versioning
- Operational Reliability : Réduction des erreurs de déploiement
Document généré automatiquement basé sur l'architecture MLflow CVNTrade Sprint7 V2 Dernière mise à jour: 2025-10-03 Ajout MLflow Model Registry: 2025-10-03