Une model card décrit objectifs, périmètre, données, métrriques, limites et usages prévus d’un modèle. Une system card étend la vue à l’application: RAG, prompts, filtres, mesures, monitoring, coûts, responsables. L’enjeu est la confiance opérationnelle, pas la paperasse. Ce guide propose des gabarits concrets, un flux de validation en CI et des exemples prêts à adapter.
prérequis
- un dépôt Git pour stocker cartes, schémas et exemples.
- une convention de nommage et de version (semver) partagée.
- un validateur (JSON Schema ou script) exécuté en CI.
- un emplacement de publication interne (docs, catalogue data/ML).
aperçu rapide
- écrire court et utile: 1 page + un schéma, pas un roman.
- lier cartes et artefacts réels: données, code, jeux d’évaluation.
- valider automatiquement la présence des champs clés en CI.
- tenir à jour à chaque version majeure et lors de changements de données.
- exposer métriques et limites par segments pour éviter les angles morts.
- rattacher les cartes aux journaux d’usage et aux incidents.
où commence et finit chaque carte
modèle de model card (gabarit YAML)
model_card:
id: "ticket_classifier_fr_v1"
title: "Classification de tickets FR (priorite et theme)"
owner: "@ml-support"
version: "1.3.0"
objective: "Aider au routage initial des tickets en français"
scope:
inputs: ["titre", "description"]
outputs: ["priorite in {basse, moyenne, haute}", "theme in {facturation, technique, compte, autre}"]
exclusions: ["dossiers juridiques", "tickets non FR"]
data:
training_sources: ["tickets_2022_2023_fr_clean.parquet"]
eval_set: "eval_fr_q1_2024.parquet"
pii_handling: "emails et tels masques en amont"
drift_signals: ["lang_detect", "distribution_themes"]
method:
family: "transformer distille"
compression: ["distillation", "quantization int8"]
seed: 42
metrics:
global:
accuracy: 0.89
macro_f1: 0.86
by_segment:
"priorite:haute": {f1: 0.91}
"theme:facturation": {f1: 0.88}
limitations:
- "performances plus faibles sur tickets mixtes FR/EN"
- "erreurs sur textes très courts (< 5 mots)"
fairness_notes:
segments_compares: ["region", "canal"]
ecarts_acceptables: "delta f1 <= 3 points"
risks:
- "faux negatif sur priorite:haute"
- "biais canal email vs chat"
monitoring:
metrics: ["latence_p95_ms", "macro_f1_sem", "delta_segment_f1"]
thresholds: {"latence_p95_ms": 120, "delta_segment_f1": 3}
changelog:
- "1.3.0: ajout quantization int8, latence -35% p95; f1 stable"
- "1.2.0: nouveau jeu d'evaluation Q1 2024"
modèle de system card (gabarit YAML)
system_card:
id: "assistant_support_fr"
title: "Assistant de reponse support FR"
owners: ["@support", "@ml-platform"]
architecture:
diagram: "ui -> orchestrateur -> modele + rag -> filtres -> ui"
components:
- "retriever dense + bm25"
- "index faq_support_fr_v2"
- "modele: ticket_classifier_fr_v1"
context:
inputs: ["question_agent", "contexte_faq"]
outputs: ["suggestion_reponse", "tags"]
languages: ["fr"]
sources:
allowlist: ["faq_support_fr_v2", "wiki_support_fr"]
blocklist: ["dossiers_sensibles"]
prompts:
style: "formel, concis"
guardrails: ["ne pas inventer", "referencer source"]
filters:
inbound: ["normalisation unicode", "suppression html", "limite longueur"]
outbound: ["filtre pii", "liste interdits", "seuil confiance"]
quality:
kpi: ["exactitude@top1 sur 200 questions", "taux d'acceptation agent"]
target: {"exactitude_top1": 0.78, "acceptation": 0.7}
security:
policies: ["masquage pii", "quotas", "logs"]
operations:
latency_p95_ms_target: 150
cost_per_1000: "0.04 eur inference locale"
rollout: {"canary": 10, "rollback_on_error": true}
risks:
- "reponses trop assures sur contenus obsoletes"
- "prompt injection via piece jointe"
evidence:
eval_report_uri: "s3://ml-reports/assistant_support_fr/2024Q2.html"
tutoriel pas-à-pas
étape 1: créer les gabarits
Placez des gabarits YAML et un schéma de validation dans le dépôt.
# cards/schema/model_card.schema.yaml (extrait)
type: object
required: ["id","title","owner","version","objective","data","metrics","limitations","monitoring"]
properties:
id: {type: "string", minLength: 3}
version: {type: "string", pattern: "^[0-9]+\\.[0-9]+\\.[0-9]+$"}
metrics:
type: object
properties:
global:
type: object
properties:
accuracy: {type: "number", minimum: 0, maximum: 1}
macro_f1: {type: "number", minimum: 0, maximum: 1}
étape 2: lier artefacts et jeux d’évaluation
Ajoutez des URI vérifiables vers jeux d’évaluation, notebooks et rapports.
evidence:
eval_set_uri: "s3://ml-eval/eval_fr_q1_2024.parquet"
notebook_uri: "s3://ml-eval/notebooks/ticket_cls_eval.ipynb"
report_uri: "s3://ml-reports/ticket_cls/1.3.0.html"
étape 3: valider en CI
Utilisez un validateur simple (Python) pour bloquer les PR si des champs manquent.
# tools/validate_cards.py
import sys, json, yaml, jsonschema, glob
from jsonschema import validate
def load_schema(path):
with open(path) as f:
return yaml.safe_load(f)
def check_card(card_path, schema):
with open(card_path) as f:
card = yaml.safe_load(f)
try:
validate(instance=card.get("model_card") or card.get("system_card"), schema=schema)
return True, None
except jsonschema.exceptions.ValidationError as e:
return False, f"{card_path}: {e.message}"
def main():
ok = True
schema = load_schema(sys.argv[1])
for p in glob.glob("cards/**/*.yaml", recursive=True):
valid, err = check_card(p, schema)
if not valid:
ok = False
print(err)
sys.exit(0 if ok else 1)
if __name__ == "__main__":
main()
étape 4: publier et relier
Exposez les cartes dans le catalogue interne et reliez-les aux pages produit.
# pseudo-ci
python tools/validate_cards.py cards/schema/model_card.schema.yaml
python tools/validate_cards.py cards/schema/system_card.schema.yaml
# si OK, publier
rsync -av cards/ docs/cards/
étape 5: boucler avec la production
Connectez cartes et métriques réelles (latence, coûts, qualité) pour rester crédibles.
-- table de suivi des KPI par version
CREATE TABLE IF NOT EXISTS ml.cards_kpi (
day DATE,
card_id TEXT,
version TEXT,
metric TEXT,
value DOUBLE PRECISION
);
diagrammes utiles
flux de revue et publication
séquence de mise à jour contrôlée
exemples complets
exemple 1: model card concrète
model_card:
id: "sentiment_fr_v2"
title: "Analyse de sentiment FR (avis clients)"
owner: "@marketing-ml"
version: "2.0.0"
objective: "Classer avis en {negatif, neutre, positif} pour prioriser la moderation"
scope:
inputs: ["avis_texte"]
outputs: ["sentiment"]
data:
training_sources: ["avis_2019_2023_fr.parquet"]
eval_set: "eval_fr_retail_q1_2024.parquet"
pii_handling: "masquage email/tel"
method:
family: "cnn text + embeddings fr"
compression: ["pruning light"]
metrics:
global: {accuracy: 0.92, macro_f1: 0.90}
by_segment:
"canal:web": {f1: 0.91}
"canal:magasin": {f1: 0.88}
limitations:
- "ironies détectées partiellement"
- "mauvaise qualité sur messages très courts"
fairness_notes:
segments_compares: ["genre_estime", "region"]
ecarts_acceptables: "delta f1 <= 3 points"
risks:
- "mauvaise priorisation des cas urgents"
monitoring:
metrics: ["latence_p95_ms","macro_f1_sem","drift_kl"]
thresholds: {"latence_p95_ms": 80}
changelog:
- "2.0.0: nouveau backbone, f1 +2 pts; latence +5 ms"
exemple 2: system card concrète
system_card:
id: "assistant_support_fr"
title: "Assistant de reponse support FR"
owners: ["@support", "@ml-platform"]
architecture:
diagram: "ui -> orchestrateur -> modele + rag -> filtres -> ui"
components:
- "retriever hybrid"
- "index faq_support_fr_v2"
- "modele: sentiment_fr_v2 (tonalité)"
sources:
allowlist: ["faq_support_fr_v2", "wiki_support_fr"]
prompts:
style: "clair et sourcé"
guardrails: ["ne pas inventer", "indiquer sources"]
filters:
inbound: ["normalisation", "limite longueur"]
outbound: ["filtre pii", "liste interdits"]
quality:
kpi: ["exactitude@top1 sur 200 questions", "taux d'acceptation agent"]
operations:
latency_p95_ms_target: 150
cost_per_1000: "0.03 eur"
risks:
- "reponses hors périmètre"
- "documents obsoletes"
evidence:
eval_report_uri: "s3://ml-reports/assistant_support_fr/2024Q2.html"
rattacher cartes et mesures
Reliez chaque card à des métriques vivantes pour éviter l’écart entre promesse et réalité.
-- joindre kpi de production à la carte
SELECT c.card_id, c.version, k.day, k.metric, k.value
FROM ml.cards_kpi k
JOIN LATERAL (
VALUES ('sentiment_fr_v2','2.0.0'),
('ticket_classifier_fr_v1','1.3.0')
) AS c(card_id,version) ON true
WHERE k.card_id = c.card_id AND k.version = c.version
ORDER BY k.day DESC;
pratiques recommandées
- stocker cartes avec le code: même MR, même revue, même historique.
- valider en CI la présence des champs et la cohérence des valeurs.
- mettre à jour à chaque version majeure et quand les données changent.
- raccrocher aux tests et aux journaux: preuves et diagnostics à portée de main.
- écrire pour les lecteurs: 1 page, un schéma, des liens vers les détails.
erreurs courantes et solutions
- roman illisible -> personne ne lit -> viser 1 page utile + schéma clair.
- cartes isolées -> dérive dans le temps -> lier aux métriques et aux incidents.
- champs manquants -> ambiguïtés -> valider en CI avec schémas stricts.
- métriques flatteuses -> surprises en prod -> publier par segments et suivre en continu.
- version floue -> confusion -> semver, changelog, liens vers artefacts.
faq
-
A quoi sert une model card au-delà du marketing ? A cadrer l’usage, exposer les limites et fournir des références vérifiables sur données et métriques. C’est un contrat technique lisible.
-
Quand faut-il créer ou mettre à jour une system card ? A l’introduction d’un nouveau composant, à chaque changement de source, d’index ou de politique de filtrage, et à chaque version majeure.
-
Faut-il tout standardiser avec un schéma unique ? Oui pour les champs essentiels (objectif, données, métriques, limites, owners). Laissez des sections libres pour les spécificités.
-
Comment éviter que les cartes deviennent obsolètes ? Connectez-les au monitoring, exigez un lien vers le dernier rapport d’évaluation, et rendez le check obligatoire en CI.
-
Où les publier pour qu’elles soient réellement utiles ? Dans le catalogue interne au côté des jeux de données et des APIs, avec des permaliens et des extraits lisibles.