Transformers for Biomedical NLP
Overview
HuggingFace Transformers provides a unified API to load, run, and fine-tune 500+ biomedical language models. The key biomedical models — BioBERT (trained on PubMed abstracts + PMC full text), PubMedBERT (trained from scratch on PubMed), BioGPT (generative, trained on PubMed), and BioMedLM — significantly outperform general-purpose BERT on biomedical NER, relation extraction, and question answering. The pipeline() abstraction handles tokenization, inference, and postprocessing in one call. Fine-tuning on task-specific labeled data (e.g., BC5CDR for chemical/disease NER) takes under an hour on a single GPU. The datasets library provides direct access to standard biomedical benchmarks.
When to Use
- Extracting gene names, disease mentions, drug names, or chemical entities from biomedical abstracts (NER)
- Classifying abstracts by topic, sentiment of clinical outcomes, or PICO elements for systematic reviews
- Answering specific questions from biomedical literature using extractive QA (BioASQ format)
- Generating hypotheses or summaries from biomedical text using BioGPT or BioMedLM
- Fine-tuning a pre-trained biomedical model on a custom labeled dataset (e.g., your lab's annotations)
- Embedding biomedical sentences for semantic similarity search across literature
- Use spaCy + en_core_sci_lg for fast rule-augmented NER; use Stanza for dependency parsing
Prerequisites
- Python packages:
transformers,torch,datasets,accelerate,sentencepiece - GPU: Strongly recommended for fine-tuning; inference on CPU is viable for single texts
- Data requirements: plain text biomedical strings; for fine-tuning, annotated data in BIO/IOB format
pip install transformers torch datasets accelerate sentencepiece
# For GPU (CUDA 11.8)
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118
Quick Start
from transformers import pipeline
# Named entity recognition with BioBERT
ner = pipeline("ner", model="allenai/scibert_scivocab_cased",
aggregation_strategy="simple")
text = "BRCA1 mutations are associated with increased risk of breast cancer and ovarian cancer."
entities = ner(text)
for ent in entities:
print(f" {ent['word']:20s} {ent['entity_group']:10s} score={ent['score']:.3f}")
Core API
Module 1: Named Entity Recognition (NER)
Extract biomedical entities using pre-trained NER models.
from transformers import pipeline, AutoTokenizer, AutoModelForTokenClassification
# BioBERT fine-tuned for NER (genes, diseases, chemicals)
# Common choices:
# "allenai/scibert_scivocab_cased" — scientific NER
# "d4data/biomedical-ner-all" — multi-entity biomedical NER
# "pruas/BENT-PubMedBERT-NER-Gene" — gene-specific NER
ner_pipe = pipeline(
"ner",
model="d4data/biomedical-ner-all",
aggregation_strategy="simple", # merge subword tokens into words
device=-1 # -1=CPU, 0=GPU
)
abstracts = [
"Imatinib inhibits the BCR-ABL1 tyrosine kinase and is first-line treatment for CML.",
"EGFR mutations in non-small cell lung cancer predict response to erlotinib.",
]
for text in abstracts:
entities = ner_pipe(text)
print(f"\nText: {text[:60]}...")
for e in entities:
print(f" [{e['entity_group']}] '{e['word']}' (score={e['score']:.2f})")
# Manual tokenization + inference for batch processing
from transformers import AutoTokenizer, AutoModelForTokenClassification
import torch
model_name = "allenai/scibert_scivocab_cased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForTokenClassification.from_pretrained(model_name)
model.eval()
text = "Metformin activates AMPK and reduces hepatic glucose production in type 2 diabetes."
inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=512)
with torch.no_grad():
outputs = model(**inputs)
logits = outputs.logits # shape: (1, seq_len, n_labels)
predictions = logits.argmax(dim=-1)[0]
tokens = tokenizer.convert_ids_to_tokens(inputs["input_ids"][0])
labels = [model.config.id2label[p.item()] for p in predictions]
for token, label in zip(tokens[1:-1], labels[1:-1]): # skip [CLS] and [SEP]
if label != "O":
print(f" {token:20s} {label}")
Module 2: Text Classification
Classify biomedical abstracts or sentences.
from transformers import pipeline
# Zero-shot classification — no fine-tuning needed
zs_clf = pipeline("zero-shot-classification",
model="facebook/bart-large-mnli",
device=-1)
abstract = """
This randomized controlled trial evaluated the efficacy of pembrolizumab versus
chemotherapy in patients with advanced non-small-cell lung cancer. Overall survival
was significantly improved in the pembrolizumab arm (HR=0.60, 95% CI 0.41-0.89).
"""
candidate_labels = ["clinical trial", "basic research", "meta-analysis", "review"]
result = zs_clf(abstract, candidate_labels)
print("Zero-shot classification:")
for label, score in zip(result["labels"], result["scores"]):
print(f" {label:20s}: {score:.3f}")
# Fine-tuned sentiment/outcome classification
from transformers import pipeline
# Example: classify clinical outcome sentiment
clf = pipeline("text-classification",
model="pruas/BENT-PubMedBERT-NER-Gene", # use appropriate task-specific model
device=-1)
sentences = [
"Treatment significantly improved overall survival (p<0.001).",
"No statistically significant difference was observed between groups.",
]
results = clf(sentences)
for sent, result in zip(sentences, results):
print(f" [{result['label']} | {result['score']:.2f}] {sent[:50]}...")
Module 3: Biomedical Question Answering
Extract answers from biomedical text passages.
from transformers import pipeline
# Extractive QA: find answer span within context
qa_pipe = pipeline(
"question-answering",
model="sultan/BioM-ELECTRA-Large-SQuAD2", # biomedical QA model
device=-1
)
context = """
BRCA1 is a tumor suppressor gene located on chromosome 17q21. Pathogenic variants
in BRCA1 confer a lifetime breast cancer risk of 50-72% and ovarian cancer risk
of 44-46%. BRCA1 protein functions in DNA double-strand break repair via
homologous recombination.
"""
questions = [
"What chromosome is BRCA1 located on?",
"What is the lifetime breast cancer risk from BRCA1 variants?",
"What DNA repair pathway does BRCA1 participate in?",
]
for q in questions:
result = qa_pipe(question=q, context=context)
print(f"Q: {q}")
print(f"A: {result['answer']} (score={result['score']:.3f})\n")
Module 4: Text Generation with BioGPT
Generate biomedical text, hypotheses, and summaries.
from transformers import AutoTokenizer, BioGptForCausalLM
import torch
model_name = "microsoft/biogpt"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = BioGptForCausalLM.from_pretrained(model_name)
model.eval()
prompt = "The role of VEGF in tumor angiogenesis"
inputs = tokenizer(prompt, return_tensors="pt")
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=100,
num_beams=5,
early_stopping=True,
no_repeat_ngram_size=3,
pad_token_id=tokenizer.eos_token_id,
)
generated = tokenizer.decode(outputs[0], skip_special_tokens=True)
print(f"Generated:\n{generated}")
Module 5: Sentence Embeddings for Semantic Search
Embed biomedical text for similarity search and clustering.
from transformers import AutoTokenizer, AutoModel
import torch
import numpy as np
def mean_pooling(model_output, attention_mask):
"""Mean pooling across token embeddings."""
token_embeddings = model_output.last_hidden_state
input_mask_expanded = attention_mask.unsqueeze(-1).expand(token_embeddings.size()).float()
return (token_embeddings * input_mask_expanded).sum(1) /