Passe de 95/3/2 (lookups/raisonnement/règles) à ~31/49/20. Dataset cible ~16K exemples denses (vs 66K de lookups avant). Modifiés : - 03_convert_cache.py : cache complet 1840 entrées (actuel + backup) - 04_build_dataset.py : subsampling agressif (CIM-10 1.5K, CCAM 1.5K, CoCoA 2K) + sélection intelligente priorisant le raisonnement - 12_generate_pipeline_examples.py : 3 templates (court + long + CPAM), cache actuel, cible ~2800 exemples Créés : - 13_generate_fascicule_reasoning.py : parsing 10 fascicules ATIH, génération Q&A raisonnement via Claude Opus 4.6 (~450 exemples) - 14_generate_negative_examples.py : 1000 exemples négatifs (symptômes/DP, redondances sémantiques, DAS non significatifs) - 15_generate_discrimination.py : 800 exercices de discrimination entre codes siblings CIM-10 via Claude Opus 4.6 - 16_parse_guide_metho.py : extraction Guide Méthodologique MCO 2026, Q&A directes + raisonnement via Claude Opus 4.6 (~500 exemples) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
397 lines
21 KiB
Python
397 lines
21 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Génère des exemples négatifs : enseigner au modèle quand NE PAS coder.
|
|
|
|
3 types d'exemples :
|
|
a) Codes rejetés (500) — symptômes couverts par le DP
|
|
b) Redondances sémantiques (200) — paires dominé/dominant
|
|
c) DAS non significatifs (300) — antécédents sans ressources consommées
|
|
|
|
Sources :
|
|
- Cache Ollama (textes diagnostics réels)
|
|
- Règles PMSI (SEMANTIC_REDUNDANCIES, symptômes R00-R99)
|
|
- Templates + CIM-10 FHIR
|
|
|
|
Produit : data/processed/negative_chatml.jsonl
|
|
|
|
Usage :
|
|
python scripts/14_generate_negative_examples.py
|
|
"""
|
|
|
|
import json
|
|
import random
|
|
from pathlib import Path
|
|
|
|
random.seed(42)
|
|
|
|
BASE = Path(__file__).resolve().parent.parent
|
|
T2A = BASE.parent / "t2a"
|
|
RAW = BASE / "data" / "raw"
|
|
OUTPUT = BASE / "data" / "processed" / "negative_chatml.jsonl"
|
|
OUTPUT.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
SYSTEM_PROMPT = (
|
|
"Tu es un médecin DIM expert en codage CIM-10 pour le PMSI français. "
|
|
"Tu sais quand un diagnostic ne doit PAS être codé."
|
|
)
|
|
|
|
|
|
def make_chatml(user: str, assistant: str) -> dict:
|
|
return {
|
|
"messages": [
|
|
{"role": "system", "content": SYSTEM_PROMPT},
|
|
{"role": "user", "content": user},
|
|
{"role": "assistant", "content": assistant},
|
|
]
|
|
}
|
|
|
|
|
|
def load_cim10_dict() -> dict[str, str]:
|
|
"""Charge le dictionnaire CIM-10."""
|
|
d = {}
|
|
for path in [T2A / "data" / "cim10_dict.json", T2A / "data" / "cim10_supplements.json"]:
|
|
if path.exists():
|
|
d.update(json.loads(path.read_text()))
|
|
return d
|
|
|
|
|
|
def load_fhir_concepts() -> dict[str, dict]:
|
|
"""Charge les concepts FHIR indexés par code."""
|
|
fhir_path = RAW / "smt_cim10_fhir.json"
|
|
if not fhir_path.exists():
|
|
return {}
|
|
data = json.loads(fhir_path.read_text())
|
|
by_code = {}
|
|
for c in data.get("concept", []):
|
|
by_code[c["code"]] = c
|
|
return by_code
|
|
|
|
|
|
# ─── Type A : Symptômes couverts par le DP ────────────────────────────────────
|
|
|
|
# Symptômes R00-R99 fréquemment codés à tort comme DAS quand le DP les explique
|
|
SYMPTOM_DP_PAIRS = [
|
|
# (symptôme_code, symptôme_label, dp_code, dp_label, explication)
|
|
("R50.9", "Fièvre, sans précision", "A41.9", "Sepsis, sans précision",
|
|
"La fièvre est un symptôme cardinal du sepsis. Elle est couverte par le DP A41.9."),
|
|
("R50.9", "Fièvre, sans précision", "J18.9", "Pneumonie, sans précision",
|
|
"La fièvre est un symptôme habituel de la pneumonie. Le DP J18.9 la couvre."),
|
|
("R50.9", "Fièvre, sans précision", "N10", "Néphrite tubulo-interstitielle aiguë",
|
|
"La fièvre accompagne habituellement la pyélonéphrite aiguë (N10)."),
|
|
("R06.0", "Dyspnée", "J44.1", "BPCO avec exacerbation aiguë",
|
|
"La dyspnée est le symptôme principal de l'exacerbation de BPCO."),
|
|
("R06.0", "Dyspnée", "I50.9", "Insuffisance cardiaque, sans précision",
|
|
"La dyspnée est un symptôme majeur de l'insuffisance cardiaque."),
|
|
("R06.0", "Dyspnée", "J96.0", "Insuffisance respiratoire aiguë",
|
|
"La dyspnée est couverte par le DP d'insuffisance respiratoire aiguë."),
|
|
("R07.4", "Douleur thoracique, sans précision", "I21.9", "IDM aigu, sans précision",
|
|
"La douleur thoracique est le symptôme principal de l'IDM."),
|
|
("R07.4", "Douleur thoracique, sans précision", "I20.0", "Angor instable",
|
|
"La douleur thoracique est le symptôme cardinal de l'angor instable."),
|
|
("R10.4", "Douleur abdominale, sans précision", "K80.1", "Lithiase vésiculaire avec cholécystite",
|
|
"La douleur abdominale est couverte par le DP de cholécystite."),
|
|
("R10.4", "Douleur abdominale, sans précision", "K35.8", "Appendicite aiguë, autre et sans précision",
|
|
"La douleur abdominale est le symptôme principal de l'appendicite."),
|
|
("R11.0", "Nausées", "K29.1", "Gastrite aiguë",
|
|
"Les nausées sont un symptôme courant de la gastrite, couvert par le DP."),
|
|
("R11.2", "Nausées avec vomissements, sans précision", "K56.6", "Occlusion intestinale, autre et sans précision",
|
|
"Les vomissements sont un signe cardinal de l'occlusion intestinale."),
|
|
("R00.0", "Tachycardie, sans précision", "A41.9", "Sepsis, sans précision",
|
|
"La tachycardie est un critère diagnostique du sepsis."),
|
|
("R00.0", "Tachycardie, sans précision", "I48.9", "Fibrillation auriculaire",
|
|
"La tachycardie est un symptôme de la FA, couverte par le DP."),
|
|
("R41.0", "Désorientation, sans précision", "F05.9", "Delirium, sans précision",
|
|
"La désorientation est un symptôme constitutif du delirium."),
|
|
("R40.0", "Somnolence", "S06.9", "Lésion traumatique intracrânienne, sans précision",
|
|
"La somnolence est un signe d'atteinte neurologique dans le trauma crânien."),
|
|
("R42", "Étourdissements et éblouissements", "H81.1", "Vertige paroxystique bénin",
|
|
"Les étourdissements sont le symptôme principal du VPPB."),
|
|
("R51", "Céphalée", "G43.9", "Migraine, sans précision",
|
|
"La céphalée est le symptôme principal de la migraine."),
|
|
("R63.0", "Anorexie", "C16.9", "Tumeur maligne de l'estomac",
|
|
"L'anorexie est un symptôme fréquent du cancer gastrique, couvert par le DP."),
|
|
("R53", "Malaise et fatigue", "D64.9", "Anémie, sans précision",
|
|
"La fatigue est un symptôme courant de l'anémie."),
|
|
("R31", "Hématurie, sans précision", "N20.0", "Calcul du rein",
|
|
"L'hématurie est un symptôme classique de la lithiase rénale."),
|
|
("R73.0", "Hyperglycémie SAI", "E11.9", "Diabète de type 2",
|
|
"L'hyperglycémie est un signe du diabète de type 2, couvert par le DP."),
|
|
("R60.0", "Oedème localisé", "I50.0", "Insuffisance cardiaque congestive",
|
|
"L'oedème est un signe de l'ICC, couvert par le DP."),
|
|
("R09.2", "Arrêt respiratoire", "J96.0", "Insuffisance respiratoire aiguë",
|
|
"L'arrêt respiratoire est la forme extrême de l'insuffisance respiratoire aiguë."),
|
|
("R57.0", "Choc cardiogénique", "I21.9", "IDM aigu",
|
|
"Le choc cardiogénique comme complication de l'IDM peut se coder comme DAS (mobilise des ressources), mais s'il est le tableau initial il est couvert par le DP."),
|
|
]
|
|
|
|
|
|
def generate_symptom_dp_examples(cim10_dict: dict, target: int = 500) -> list[dict]:
|
|
"""Générer des exemples de symptômes couverts par le DP."""
|
|
examples = []
|
|
|
|
# Templates de variation
|
|
user_templates = [
|
|
"Code ce diagnostic : {symptom_label}\nTYPE : DAS\nCONTEXTE : DP = {dp_code} ({dp_label})",
|
|
"Le patient est hospitalisé pour {dp_label} (DP : {dp_code}). Faut-il coder {symptom_label} ({symptom_code}) en DAS ?",
|
|
"DAS candidat : {symptom_code} ({symptom_label})\nDP du séjour : {dp_code} ({dp_label})\nCe DAS est-il pertinent ?",
|
|
]
|
|
|
|
assistant_template_null = json.dumps({
|
|
"code": None,
|
|
"confidence": "high",
|
|
"justification": "{explanation} Règle PMSI : ne pas coder un symptôme (R00-R99) si le diagnostic qui l'explique est déjà codé comme DP."
|
|
}, ensure_ascii=False)
|
|
|
|
# Générer depuis les paires prédéfinies (avec variations de templates)
|
|
for sym_code, sym_label, dp_code, dp_label, expl in SYMPTOM_DP_PAIRS:
|
|
for tmpl in user_templates:
|
|
user = tmpl.format(
|
|
symptom_code=sym_code, symptom_label=sym_label,
|
|
dp_code=dp_code, dp_label=dp_label
|
|
)
|
|
assistant = assistant_template_null.replace("{explanation}", expl)
|
|
examples.append(make_chatml(user, assistant))
|
|
|
|
# Générer des variations supplémentaires pour atteindre la cible
|
|
# Utiliser tous les codes R du dictionnaire CIM-10
|
|
r_codes = [(c, l) for c, l in cim10_dict.items() if c.startswith("R") and len(c) >= 4]
|
|
non_r_codes = [(c, l) for c, l in cim10_dict.items()
|
|
if not c.startswith("R") and not c.startswith("Z")
|
|
and c[0].isalpha() and len(c) >= 4 and len(l) > 5]
|
|
|
|
while len(examples) < target and r_codes and non_r_codes:
|
|
sym_code, sym_label = random.choice(r_codes)
|
|
dp_code, dp_label = random.choice(non_r_codes)
|
|
tmpl = random.choice(user_templates)
|
|
user = tmpl.format(
|
|
symptom_code=sym_code, symptom_label=sym_label,
|
|
dp_code=dp_code, dp_label=dp_label
|
|
)
|
|
expl = f"{sym_label} est un symptôme (code R) potentiellement couvert par le DP {dp_code} ({dp_label})."
|
|
assistant = json.dumps({
|
|
"code": None,
|
|
"confidence": "medium",
|
|
"justification": f"{expl} Règle PMSI : ne pas coder un symptôme en DAS s'il est expliqué par le DP. Vérifier si le symptôme a nécessité une prise en charge spécifique supplémentaire."
|
|
}, ensure_ascii=False)
|
|
examples.append(make_chatml(user, assistant))
|
|
|
|
random.shuffle(examples)
|
|
return examples[:target]
|
|
|
|
|
|
# ─── Type B : Redondances sémantiques ─────────────────────────────────────────
|
|
|
|
SEMANTIC_REDUNDANCIES = [
|
|
# (dominated_prefix, dominant_prefixes, explanation)
|
|
("I10", ["I11", "I12", "I13"],
|
|
"I10 (HTA essentielle) est redondant quand I11/I12/I13 est présent. Le code hypertensif spécifique inclut la composante HTA."),
|
|
("N30", ["N39"],
|
|
"N30 (cystite) est redondant quand N39.0 (infection urinaire) est présent. L'infection urinaire couvre la cystite."),
|
|
("J18", ["J15", "J16"],
|
|
"J18 (pneumonie SAI) est redondant quand J15/J16 (pneumonie spécifique) est présent. Le code spécifique prime."),
|
|
("E11.9", ["E11.0", "E11.1", "E11.2", "E11.3", "E11.4", "E11.5", "E11.6", "E11.7"],
|
|
"E11.9 (diabète type 2 SAI) est redondant si un sous-code E11.x spécifiant une complication est présent."),
|
|
("I25.9", ["I25.1", "I25.2", "I25.5"],
|
|
"I25.9 (cardiopathie ischémique chronique SAI) est redondant si un sous-code I25.x plus spécifique est présent."),
|
|
("N18.9", ["N18.1", "N18.2", "N18.3", "N18.4", "N18.5"],
|
|
"N18.9 (IRC SAI) est redondant si un stade N18.x spécifique est présent."),
|
|
("J44.9", ["J44.0", "J44.1"],
|
|
"J44.9 (BPCO SAI) est redondant si J44.0 (BPCO avec infection) ou J44.1 (BPCO avec exacerbation) est présent."),
|
|
("K21.9", ["K21.0"],
|
|
"K21.9 (RGO SAI) est redondant si K21.0 (RGO avec œsophagite) est présent."),
|
|
]
|
|
|
|
|
|
def generate_redundancy_examples(cim10_dict: dict, target: int = 200) -> list[dict]:
|
|
"""Générer des exemples de redondances sémantiques."""
|
|
examples = []
|
|
|
|
user_templates = [
|
|
"DAS candidats : {dominated} ({dom_label}), {dominant} ({sup_label})\nLesquels garder ?",
|
|
"Le codage inclut {dominated} et {dominant}. Y a-t-il une redondance ?",
|
|
"Vérification de codage :\n- DAS1 : {dominated} ({dom_label})\n- DAS2 : {dominant} ({sup_label})\nCes deux DAS sont-ils tous les deux pertinents ?",
|
|
]
|
|
|
|
for dominated_prefix, dominant_prefixes, explanation in SEMANTIC_REDUNDANCIES:
|
|
# Trouver des codes réels pour chaque préfixe
|
|
dom_codes = [(c, l) for c, l in cim10_dict.items() if c.startswith(dominated_prefix) and len(l) > 3]
|
|
sup_codes = [(c, l) for c, l in cim10_dict.items()
|
|
if any(c.startswith(dp) for dp in dominant_prefixes) and len(l) > 3]
|
|
|
|
if not dom_codes or not sup_codes:
|
|
continue
|
|
|
|
for _ in range(target // len(SEMANTIC_REDUNDANCIES) + 1):
|
|
dom_code, dom_label = random.choice(dom_codes)
|
|
sup_code, sup_label = random.choice(sup_codes)
|
|
tmpl = random.choice(user_templates)
|
|
|
|
user = tmpl.format(
|
|
dominated=dom_code, dom_label=dom_label,
|
|
dominant=sup_code, sup_label=sup_label
|
|
)
|
|
assistant = json.dumps({
|
|
"garder": [sup_code],
|
|
"retirer": [dom_code],
|
|
"justification": explanation
|
|
}, ensure_ascii=False)
|
|
examples.append(make_chatml(user, assistant))
|
|
|
|
random.shuffle(examples)
|
|
return examples[:target]
|
|
|
|
|
|
# ─── Type C : DAS non significatifs ───────────────────────────────────────────
|
|
|
|
# Diagnostics fréquemment mentionnés dans les antécédents mais sans ressources
|
|
NON_SIGNIFICANT_DAS = [
|
|
("J30.1", "Rhinite allergique due au pollen", "rhinite allergique mentionnée dans les antécédents"),
|
|
("J45.9", "Asthme, sans précision", "asthme stable mentionné dans les antécédents"),
|
|
("M54.5", "Lombalgie basse", "lombalgie chronique mentionnée dans les antécédents"),
|
|
("K21.0", "RGO avec œsophagite", "RGO mentionné dans les antécédents"),
|
|
("H52.1", "Myopie", "myopie mentionnée dans les antécédents"),
|
|
("E78.0", "Hypercholestérolémie pure", "hypercholestérolémie dans les antécédents, traitement habituel"),
|
|
("E03.9", "Hypothyroïdie, sans précision", "hypothyroïdie sous Lévothyrox dans les antécédents"),
|
|
("F32.0", "Épisode dépressif léger", "dépression traitée mentionnée dans les antécédents"),
|
|
("G47.3", "Apnée du sommeil", "SAOS appareillé mentionné dans les antécédents"),
|
|
("M81.9", "Ostéoporose sans fracture", "ostéoporose connue dans les antécédents"),
|
|
("H40.1", "Glaucome primaire à angle ouvert", "glaucome traité dans les antécédents"),
|
|
("I84.1", "Hémorroïdes internes avec complication", "hémorroïdes mentionnées dans les antécédents"),
|
|
("K58.9", "Syndrome du côlon irritable", "SCI mentionné dans les antécédents"),
|
|
("L40.0", "Psoriasis vulgaire", "psoriasis stable dans les antécédents"),
|
|
("N40", "Hyperplasie de la prostate", "HBP traitée dans les antécédents"),
|
|
("E66.9", "Obésité, sans précision", "obésité mentionnée, pas de prise en charge spécifique"),
|
|
("Z87.1", "Antécédents personnels de maladies de l'appareil digestif", "antécédent de chirurgie digestive ancienne"),
|
|
("Z86.7", "Antécédents personnels de maladies de l'appareil circulatoire", "antécédent d'AVC il y a 5 ans"),
|
|
("Z92.1", "Antécédents de traitement anticoagulant au long cours", "patient sous AVK au long cours"),
|
|
("F17.2", "Dépendance au tabac", "tabagisme actif mais pas de sevrage pendant le séjour"),
|
|
]
|
|
|
|
|
|
def generate_non_significant_examples(target: int = 300) -> list[dict]:
|
|
"""Générer des exemples de DAS non significatifs (antécédents sans ressources)."""
|
|
examples = []
|
|
|
|
user_templates = [
|
|
"Le patient a {context}. Faut-il le coder en DAS ?",
|
|
"Antécédent : {code} ({label}). Le patient est hospitalisé pour une autre raison. Ce diagnostic doit-il être codé comme DAS ?",
|
|
"Le dossier mentionne : {context}. Est-ce un DAS pertinent pour le séjour ?\nDP : {dp_code} ({dp_label})",
|
|
"Lors du codage du séjour (DP : {dp_code}), le dossier fait état de {context}. Coder {code} en DAS ?",
|
|
]
|
|
|
|
dp_examples = [
|
|
("K80.1", "Lithiase vésiculaire avec cholécystite"),
|
|
("S72.0", "Fracture du col du fémur"),
|
|
("I63.9", "Infarctus cérébral, sans précision"),
|
|
("J18.1", "Pneumonie lobaire, sans précision"),
|
|
("I21.9", "IDM aigu, sans précision"),
|
|
("C34.9", "Tumeur maligne des bronches ou du poumon"),
|
|
("K35.8", "Appendicite aiguë"),
|
|
("N20.0", "Calcul du rein"),
|
|
("G45.9", "AIT, sans précision"),
|
|
("A41.9", "Sepsis, sans précision"),
|
|
("C18.9", "Tumeur maligne du côlon"),
|
|
("I48.9", "Fibrillation auriculaire"),
|
|
]
|
|
|
|
# Générer en croisant chaque DAS avec plusieurs DPs
|
|
# On veut ~240 négatifs pour avoir assez de marge avec les positifs
|
|
combos_per_das = max(3, (target * 3 // 4) // len(NON_SIGNIFICANT_DAS) + 1)
|
|
for code, label, context in NON_SIGNIFICANT_DAS:
|
|
selected_dps = random.sample(dp_examples, min(combos_per_das, len(dp_examples)))
|
|
for dp_code, dp_label in selected_dps:
|
|
tmpl = random.choice(user_templates)
|
|
user = tmpl.format(
|
|
code=code, label=label, context=context,
|
|
dp_code=dp_code, dp_label=dp_label
|
|
)
|
|
assistant = json.dumps({
|
|
"coder": False,
|
|
"code": code,
|
|
"justification": f"Un DAS ne doit être codé que s'il a nécessité des ressources supplémentaires pendant le séjour (examens, traitements, surveillance spécifique). {label} mentionné uniquement dans les antécédents, sans prise en charge spécifique durant le séjour, ne justifie pas un DAS."
|
|
}, ensure_ascii=False)
|
|
examples.append(make_chatml(user, assistant))
|
|
|
|
# Ajouter des exemples positifs (contraste) — DAS qui DOIVENT être codés
|
|
positive_das = [
|
|
("E11.6", "Diabète de type 2 avec complications", "diabète déséquilibré ayant nécessité une adaptation thérapeutique",
|
|
"Le diabète a mobilisé des ressources supplémentaires (adaptation insuline, surveillance glycémique renforcée)."),
|
|
("I10", "HTA essentielle", "HTA sévère ayant nécessité un traitement IV pendant le séjour",
|
|
"L'HTA a nécessité un traitement intraveineux spécifique, justifiant le codage en DAS."),
|
|
("N18.4", "IRC stade 4", "IRC ayant nécessité une adaptation posologique de tous les médicaments",
|
|
"L'IRC a mobilisé des ressources (adaptation posologique, surveillance créatinine quotidienne)."),
|
|
("E87.1", "Hypo-osmolalité et hyponatrémie", "hyponatrémie sévère découverte pendant le séjour",
|
|
"L'hyponatrémie a nécessité un bilan étiologique et un traitement spécifique."),
|
|
("J96.0", "Insuffisance respiratoire aiguë", "détresse respiratoire ayant nécessité une oxygénothérapie",
|
|
"L'insuffisance respiratoire a mobilisé des ressources (oxygénothérapie, surveillance SpO2, GDS)."),
|
|
("N17.9", "Insuffisance rénale aiguë", "IRA survenue pendant le séjour ayant nécessité une surveillance biologique quotidienne",
|
|
"L'IRA a nécessité des bilans répétés et une adaptation thérapeutique, justifiant le DAS."),
|
|
("D62", "Anémie posthémorragique aiguë", "anémie aiguë ayant nécessité une transfusion de 2 CGR",
|
|
"L'anémie a mobilisé des ressources (transfusion, surveillance post-transfusionnelle)."),
|
|
("E87.6", "Hypokaliémie", "hypokaliémie sévère découverte en biologie nécessitant une supplémentation IV",
|
|
"L'hypokaliémie a nécessité un traitement spécifique IV, justifiant le DAS."),
|
|
]
|
|
|
|
combos_per_pos = max(3, (target // 4) // len(positive_das) + 1)
|
|
for code, label, context, justification in positive_das:
|
|
selected_dps = random.sample(dp_examples, min(combos_per_pos, len(dp_examples)))
|
|
for dp_code, dp_label in selected_dps:
|
|
user = f"Le patient est hospitalisé pour {dp_label} (DP : {dp_code}). Il présente aussi : {context}. Faut-il coder {code} ({label}) en DAS ?"
|
|
assistant = json.dumps({
|
|
"coder": True,
|
|
"code": code,
|
|
"justification": justification
|
|
}, ensure_ascii=False)
|
|
examples.append(make_chatml(user, assistant))
|
|
|
|
random.shuffle(examples)
|
|
return examples[:target]
|
|
|
|
|
|
def main():
|
|
print("=" * 60)
|
|
print("Génération d'exemples négatifs (quand NE PAS coder)")
|
|
print("=" * 60)
|
|
|
|
print("\nChargement du dictionnaire CIM-10...")
|
|
cim10_dict = load_cim10_dict()
|
|
print(f" {len(cim10_dict)} codes")
|
|
|
|
all_examples = []
|
|
|
|
# Type A : Symptômes couverts par le DP
|
|
print("\nType A : Symptômes couverts par le DP...")
|
|
symptom_examples = generate_symptom_dp_examples(cim10_dict, target=500)
|
|
print(f" → {len(symptom_examples)} exemples")
|
|
all_examples.extend(symptom_examples)
|
|
|
|
# Type B : Redondances sémantiques
|
|
print("\nType B : Redondances sémantiques...")
|
|
redundancy_examples = generate_redundancy_examples(cim10_dict, target=200)
|
|
print(f" → {len(redundancy_examples)} exemples")
|
|
all_examples.extend(redundancy_examples)
|
|
|
|
# Type C : DAS non significatifs
|
|
print("\nType C : DAS non significatifs...")
|
|
non_sig_examples = generate_non_significant_examples(target=300)
|
|
print(f" → {len(non_sig_examples)} exemples")
|
|
all_examples.extend(non_sig_examples)
|
|
|
|
# Mélanger et sauvegarder
|
|
random.shuffle(all_examples)
|
|
|
|
with open(OUTPUT, "w") as f:
|
|
for ex in all_examples:
|
|
f.write(json.dumps(ex, ensure_ascii=False) + "\n")
|
|
|
|
print(f"\n{'='*60}")
|
|
print(f"Total : {len(all_examples)} exemples → {OUTPUT}")
|
|
print(f" Type A (symptômes/DP) : {len(symptom_examples)}")
|
|
print(f" Type B (redondances) : {len(redundancy_examples)}")
|
|
print(f" Type C (non signif.) : {len(non_sig_examples)}")
|
|
print(f"Taille : {OUTPUT.stat().st_size / 1024:.0f} Ko")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|