Clinical Natural Language Processing
Module: Healthcare AI | Difficulty: Advanced
Token-level Loss
Clinical BERT Embedding
Named Entity Recognition CRF
Clinical NLP Tasks
| Task | Model | F1 Score | Dataset | |------|-------|----------|---------| | NER (diseases) | BioClinicalBERT | 0.89 | MIMIC-III | | NER (medications) | PubMedBERT | 0.92 | MIMIC-III | | Relation Extraction | PL-Marker | 0.85 | i2b2 | | Assertion Status | BioBERT | 0.93 | i2b2 | | Negation Detection | NegEx | 0.94 | Clinical notes |
import torch
import torch.nn as nn
from transformers import AutoTokenizer, AutoModel
class ClinicalNERModel(nn.Module):
def __init__(self, model_name="emilyalsentzer/Bio_ClinicalBERT", num_labels=9):
super().__init__()
self.bert = AutoModel.from_pretrained(model_name)
self.classifier = nn.Sequential(
nn.Dropout(0.3),
nn.Linear(self.bert.config.hidden_size, 256),
nn.ReLU(),
nn.Dropout(0.1),
nn.Linear(256, num_labels)
)
def forward(self, input_ids, attention_mask):
outputs = self.bert(input_ids=input_ids, attention_mask=attention_mask)
sequence_output = outputs.last_hidden_state
logits = self.classifier(sequence_output)
return logits
def extract_medical_entities(text, model, tokenizer, label_map):
inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=512)
with torch.no_grad():
logits = model(**inputs)
predictions = torch.argmax(logits, dim=-1)[0]
entities = []
tokens = tokenizer.convert_ids_to_tokens(inputs['input_ids'][0])
current_entity = []
current_label = None
for token, pred in zip(tokens, predictions):
label = label_map.get(pred.item(), 'O')
if label.startswith('B-'):
if current_entity:
entities.append((''.join(current_entity).replace('##', ''), current_label))
current_entity = [token]
current_label = label[2:]
elif label.startswith('I-') and current_label == label[2:]:
current_entity.append(token)
else:
if current_entity:
entities.append((''.join(current_entity).replace('##', ''), current_label))
current_entity = []
current_label = None
return entities
label_map = {0: 'O', 1: 'B-DISEASE', 2: 'I-DISEASE', 3: 'B-MEDICATION',
4: 'I-MEDICATION', 5: 'B-PROCEDURE', 6: 'I-PROCEDURE',
7: 'B-LAB', 8: 'I-LAB'}
tokenizer = AutoTokenizer.from_pretrained("emilyalsentzer/Bio_ClinicalBERT")
print(f'Tokenizer vocabulary size: {tokenizer.vocab_size}')
Research Insight: Domain-adapted language models (ClinicalBERT, PubMedBERT) significantly outperform general-purpose models on clinical NLP tasks. The key challenge remains data annotation: clinical NER requires expert annotators, and inter-annotator agreement for clinical entities is typically 0.80-0.85 kappa, setting an upper bound on achievable performance.