Views
No views yet
emilyalsentzer/Bio_ClinicalBERT to detect substance involvement in death-certificate cause-of-death text, for overdose surveillance research (ROSLA pipeline).Methamphetamine, Heroin, Cocaine, Fentanyl, Alcohol, Prescription.opioids, Any Opioids, Benzodiazepines, Others, Any DrugsAny Opioids and Any Drugs are aggregate flags (true if any opioid / any substance is implicated, even when the specific drug isn't independently confirmed elsewhere).best_thresholds.json).| Label | F1 | Precision | Recall | Support |
|---|---|---|---|---|
| Methamphetamine | 0.964 | 0.930 | 1.000 | 93 |
| Heroin | 0.987 | 0.975 | 1.000 | 78 |
| Cocaine | 0.995 | 0.990 | 1.000 | 381 |
| Fentanyl | 0.999 | 0.998 | 1.000 | 615 |
| Alcohol | 0.992 | 1.000 | 0.983 | 357 |
| Prescription.opioids | 0.992 | 0.992 | 0.992 | 129 |
| Any Opioids | 0.998 | 0.997 | 1.000 | 665 |
| Benzodiazepines | 0.995 | 0.991 | 1.000 | 105 |
| Others | 0.900 | 0.960 | 0.848 | 309 |
| Any Drugs | 0.988 | 0.997 | 0.979 | 967 |
1import re
2
3def clean_bert_text(value) -> str:
4 if value is None or (isinstance(value, float) and value != value): # NaN
5 return ""
6 s = str(value)
7 s = s.replace("\t", " ").replace("\n", " ").replace("\r", " ")
8 s = re.sub(r"\bNULL\b", " ", s, flags=re.IGNORECASE)
9 s = s.replace(", ", " ")
10 s = s.upper()
11 return re.sub(r"\s+", " ", s).strip()"NULL" placeholder tokens, tabs/newlines, a "comma after every word" tokenization artifact present in some upstream data sources, and forces uppercase (the training data has no lowercase examples, so casing is not a signal the model can use — normalizing avoids an artificial train/inference mismatch).1import json
2import torch
3from transformers import AutoModelForSequenceClassification, AutoTokenizer
4
5MODEL_ID = "fabriceyhc/Bio_ClinicalBERT-DrugDetector"
6
7tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
8model = AutoModelForSequenceClassification.from_pretrained(MODEL_ID)
9model.eval()
10
11# Per-label decision thresholds -- do NOT use the default 0.5, thresholds
12# were individually tuned per label on a held-out validation split.
13import huggingface_hub
14thresholds_path = huggingface_hub.hf_hub_download(MODEL_ID, "best_thresholds.json")
15thresholds = json.load(open(thresholds_path))
16
17text = clean_bert_text("Acute intoxication due to the combined effects of fentanyl and cocaine")
18inputs = tokenizer(text, return_tensors="pt", padding="max_length", truncation=True)
19with torch.no_grad():
20 probs = torch.sigmoid(model(**inputs).logits)[0]
21
22for i, label in model.config.id2label.items():
23 prob = probs[int(i)].item()
24 pred = prob >= thresholds[label]
25 print(f"{label:<25} prob={prob:.3f} pred={pred}")"COMPLICATIONS OF FENTANYL AND COCAINE TOXICITY"), label-corrected via a consistency audit that checked for term/label agreement, logical-invariant violations between specific and aggregate columns, duplicate-text label conflicts, and negation. No names, addresses, or other directly identifying fields are present in the training text.emilyalsentzer/Bio_ClinicalBERT (BERT-base architecture, 12 layers, 768 hidden, 12 heads)problem_type="multi_label_classification"), sigmoid + independent per-label thresholds rather than softmaxOthers (F1 0.900) is the weakest label — it's a catch-all category covering a long tail of less-common substances and has the least consistent training signal.Any Opioids/Any Drugs flags, not to a specific-drug column, matching how the underlying data was labeled.