Instructions to use rntc/qwen3.5-2b-lymphome-ecrf-lora with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use rntc/qwen3.5-2b-lymphome-ecrf-lora with PEFT:
from peft import PeftModel from transformers import AutoModelForCausalLM base_model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3.5-2B") model = PeftModel.from_pretrained(base_model, "rntc/qwen3.5-2b-lymphome-ecrf-lora") - Notebooks
- Google Colab
- Kaggle
qwen3.5-2b-lymphome-ecrf-lora — LoRA 2B pour extraction eCRF lymphome (FR)
Adaptateur LoRA (PEFT) finetuné de Qwen/Qwen3.5-2B pour l'extraction de champs de registre clinique (eCRF lymphome, ~89 champs sur 10 sections) à partir de comptes-rendus français synthétiques. Entraîné 3 epochs.
Inférence section par section : on demande au modèle de remplir en JSON les champs d'une section (recopie verbatim, [] si absent).
Données & split (reproductible)
Dataset : rntc/lymphome-synth-v4 (split train, ~2050 dossiers). Split déterministe : seed 0, test = 20%.
from datasets import load_dataset
import random
d = load_dataset("rntc/lymphome-synth-v4")["train"]
idx = list(range(len(d))); random.Random(0).shuffle(idx)
n_test = int(len(idx) * 0.2)
test_ids = set(idx[:n_test]) # 20% APRÈS shuffle = TEST (~410) ; le reste = TRAIN (~1640)
id de doc = lym_{sample_index} ; gold = {champ: [[start, end, valeur]]} (spans de caractères).
Inférence — code complet autonome (transformers + PEFT)
import json, re, unicodedata
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
BASE = "Qwen/Qwen3.5-2B"
tok = AutoTokenizer.from_pretrained(BASE)
model = AutoModelForCausalLM.from_pretrained(BASE, torch_dtype="bfloat16", device_map="auto")
model = PeftModel.from_pretrained(model, "rntc/qwen3.5-2b-lymphome-ecrf-lora").eval() # applique le LoRA
SYS = ("Tu es un assistant d'extraction d'information clinique. À partir du compte-rendu, "
"extrais les valeurs des champs demandés. Réponds UNIQUEMENT en JSON. Recopie la "
"valeur EXACTEMENT depuis le texte (verbatim), n'invente rien, ne normalise pas. "
"Si un champ est absent, renvoie une liste vide [].")
def extract_section(text, section, fields, max_new_tokens=512):
champs = chr(10).join('- "%s"' % f for f in fields)
user = ("COMPTE-RENDU:\n%s\n\nCHAMPS À EXTRAIRE (section « %s ») :\n%s\n\n"
"Réponds en JSON : clés = labels ci-dessus, valeurs = listes d'extraits verbatim ([] si absent)."
% (text, section, champs))
msgs = [{"role": "system", "content": SYS}, {"role": "user", "content": user}]
prompt = tok.apply_chat_template(msgs, tokenize=False, add_generation_prompt=True)
ids = tok(prompt, return_tensors="pt").to(model.device)
gen = model.generate(**ids, max_new_tokens=max_new_tokens, do_sample=False)
out = tok.decode(gen[0][ids.input_ids.shape[1]:], skip_special_tokens=True)
try:
return json.loads(out)
except Exception:
m = re.search(r"\{.*\}", out, re.S) # tolère du texte autour du JSON
return json.loads(m.group(0)) if m else {}
# --- exemple ---
text = ("Compte-rendu d'hématologie. Patiente née le 12/05/1941, sexe féminin. "
"Diagnostic le 03/2023 : lymphome B diffus à grandes cellules.")
out = extract_section(text, "demographie",
["date de naissance", "sexe", "diagnostic du lymphome"])
print(out) # {'date de naissance': ['... 12/05/1941'], 'sexe': ['féminin'], ...}
# --- localisation des spans (value -> offsets caractères) ---
def locate(text, value):
i = text.find(value)
return (i, i + len(value)) if i >= 0 else (-1, -1)
for field, vals in out.items():
for v in vals:
print(field, repr(v), locate(text, v))
Boucler extract_section sur les 10 sections, fusionner les sorties. Les 89 champs et leur regroupement en sections sont dans le dataset.
JSON strict (optionnel, vLLM) : servir avec vLLM (enable_lora=True + LoRARequest) et StructuredOutputsParams(json=schema), où schema = {"type":"object","properties":{label: {"type":"array","items":{"type":"string"}} ...}}.
Scoring (value-µF1)
Un champ prédit est correct si sa valeur normalisée (minuscules, sans accents/ponctuation, en tokens) a un Jaccard ≥ 0.5 avec une valeur gold du même champ :
def norm(s):
s = unicodedata.normalize("NFKD", str(s)).encode("ascii", "ignore").decode().lower()
return set(re.sub(r"[^a-z0-9 ]", " ", s).split())
def value_match(pred, gold):
a, b = norm(pred), norm(gold)
return len(a & b) / max(1, len(a | b)) >= 0.5
Variante span-overlap : le span localisé (locate) chevauche un span gold du même champ.
Licence
MIT. Recherche. Pas un dispositif médical.
- Downloads last month
- 4
from peft import PeftModel from transformers import AutoModelForCausalLM base_model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3.5-2B") model = PeftModel.from_pretrained(base_model, "rntc/qwen3.5-2b-lymphome-ecrf-lora")