Views
No views yet
@article{campillosetal2025,
title = {{Benchmarking Transformer Models for Relation Extraction and Concept Normalization in a Clinical Trials Corpus}},
author = {Campillos-Llanos, Leonardo and Valverde-Mateos, Ana and Capllonch-Carri{\'o}n, Adri{\'a}n and Zakhir-Puig, Sof{\'i}a and Heras-Vicente, J{\'o}nathan},
journal = {(Under review)},
year={2025}
}@article{campillosetal-midm2021,
title = {A clinical trials corpus annotated with UMLS© entities to enhance the access to Evidence-Based Medicine},
author = {Campillos-Llanos, Leonardo and Valverde-Mateos, Ana and Capllonch-Carri{\'o}n, Adri{\'a}n and Moreno-Sandoval, Antonio},
journal = {BMC Medical Informatics and Decision Making},
volume={21},
number={1},
pages={1--19},
year={2021},
publisher={BioMed Central}
}| Precision | Recall | F1 | Accuracy |
|---|---|---|---|
| 0.886 (±0.003) | 0.857 (±0.007) | 0.869 (±0.005) | 0.911 (±0.003) |
| Class | Precision | Recall | F1 | Support |
|---|---|---|---|---|
| Experiences | 0.96 | 0.97 | 0.97 | 2003 |
| Has_Age | 0.93 | 0.84 | 0.88 | 152 |
| Has_Dose_or_Strength | 0.84 | 0.81 | 0.83 | 189 |
| Has_Drug_Form | 0.90 | 0.73 | 0.81 | 64 |
| Has_Duration_or_Interval | 0.83 | 0.84 | 0.84 | 365 |
| Has_Frequency | 0.79 | 0.86 | 0.82 | 84 |
| Has_Quantifier_or_Qualifier | 0.91 | 0.89 | 0.90 | 1040 |
| Has_Result_or_Value | 0.92 | 0.87 | 0.89 | 384 |
| Has_Route_or_Mode | 0.91 | 0.87 | 0.89 | 221 |
| Has_Time_Data | 0.83 | 0.91 | 0.86 | 589 |
| Location_of | 0.96 | 0.96 | 0.96 | 1119 |
| Used_for | 0.89 | 0.88 | 0.89 | 731 |
pip install datasets1from transformers import (
2 DebertaV2Model, PreTrainedModel,
3 DataCollatorWithPadding,AutoTokenizer
4)
5from transformers.modeling_outputs import SequenceClassifierOutput
6import torch
7import torch.nn as nn
8from datasets import Dataset
9from torch.utils.data import DataLoader
10
11
12class DebertaV2ForRelationExtraction(PreTrainedModel):
13 def __init__(self, config, num_labels):
14 super(DebertaV2ForRelationExtraction, self).__init__(config)
15 self.num_labels = num_labels
16 # body
17 self.deberta = DebertaV2Model(config)
18 # head
19 self.dropout = nn.Dropout(config.hidden_dropout_prob)
20 self.layer_norm = nn.LayerNorm(config.hidden_size * 2)
21 self.linear = nn.Linear(config.hidden_size * 2, self.num_labels)
22 self.init_weights()
23
24 def forward(self, input_ids, token_type_ids, attention_mask,
25 span_idxs, labels=None):
26 outputs = (
27 self.deberta(input_ids, token_type_ids=token_type_ids,
28 attention_mask=attention_mask,
29 output_hidden_states=False)
30 .last_hidden_state)
31
32 sub_maxpool, obj_maxpool = [], []
33 for bid in range(outputs.size(0)):
34 # span includes entity markers, maxpool across span
35 sub_span = torch.max(outputs[bid, span_idxs[bid, 0]:span_idxs[bid, 1]+1, :],
36 dim=0, keepdim=True).values
37 obj_span = torch.max(outputs[bid, span_idxs[bid, 2]:span_idxs[bid, 3]+1, :],
38 dim=0, keepdim=True).values
39 sub_maxpool.append(sub_span)
40 obj_maxpool.append(obj_span)
41
42 sub_emb = torch.cat(sub_maxpool, dim=0)
43 obj_emb = torch.cat(obj_maxpool, dim=0)
44 rel_input = torch.cat((sub_emb, obj_emb), dim=-1)
45
46 rel_input = self.layer_norm(rel_input)
47 rel_input = self.dropout(rel_input)
48 logits = self.linear(rel_input)
49
50 if labels is not None:
51 loss_fn = nn.CrossEntropyLoss()
52 loss = loss_fn(logits.view(-1, self.num_labels), labels.view(-1))
53 return SequenceClassifierOutput(loss, logits)
54 else:
55 return SequenceClassifierOutput(None, logits)
56
57id2label = {0: 'Experiences',
58 1: 'Has_Age',
59 2: 'Has_Dose_or_Strength',
60 3: 'Has_Duration_or_Interval',
61 4: 'Has_Frequency',
62 5: 'Has_Route_or_Mode',
63 6: 'Location_of',
64 7: 'Used_for'}
65
66def encode_data_inference(token_list,tokenizer):
67 tokenized_inputs = tokenizer(token_list,
68 is_split_into_words=True,
69 truncation=True)
70 span_idxs = []
71 for input_id in tokenized_inputs.input_ids:
72 tokens = tokenizer.convert_ids_to_tokens(input_id)
73 span_idxs.append([
74 [idx for idx, token in enumerate(tokens) if token.startswith("<S:")][0],
75 [idx for idx, token in enumerate(tokens) if token.startswith("</S:")][0],
76 [idx for idx, token in enumerate(tokens) if token.startswith("<O:")][0],
77 [idx for idx, token in enumerate(tokens) if token.startswith("</O:")][0]
78 ])
79 tokenized_inputs["span_idxs"] = span_idxs
80 # tokenized_inputs["labels"] = [label2id[label] for label in examples["label"]]
81 return tokenized_inputs
82
83def predict_example(example,model,tokenizer):
84 device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
85 model.to(device)
86 collate_fn = DataCollatorWithPadding(tokenizer, padding="longest", return_tensors="pt")
87
88
89 encoded_data = encode_data_inference(example,tokenizer)
90
91 inferenceds = Dataset.from_dict(encoded_data)
92
93 inference_dl = DataLoader(inferenceds,
94 shuffle=False,
95 # sampler=SubsetRandomSampler(np.random.randint(0, encoded_nyt_dataset["test"].num_rows, 100).tolist()),
96 batch_size=1,
97 collate_fn=collate_fn)
98 for batch in inference_dl:
99 batch = {k: v.to(device) for k, v in batch.items()}
100 with torch.no_grad():
101 outputs = model(**batch)
102 predictions = torch.argmax(outputs.logits, dim=-1).cpu().numpy()
103 return [id2label[p] for p in predictions]
104 1example = [['Título',
2 'público:',
3 'Estudio',
4 'multicéntrico,',
5 'aleatorizado,',
6 'doble',
7 'ciego,',
8 'controlado',
9 'con',
10 'placebo',
11 'del',
12 'anticuerpo',
13 'monoclonal',
14 'humano',
15 'anti-TNF',
16 '<O:CHE>',
17 'Adalimumab',
18 '</O:CHE>',
19 'en',
20 '<S:LIV>',
21 'sujetos',
22 'pediátricos',
23 '</S:LIV>',
24 'con',
25 'colitis',
26 'ulcerosa',
27 'moderada',
28 'o',
29 'grave']]
30
31model = DebertaV2ForRelationExtraction.from_pretrained("medspaner/mdeberta-v3-base-re-ct-v2",8)
32tokenizer = AutoTokenizer.from_pretrained("medspaner/mdeberta-v3-base-re-ct-v2")
33predict_example(example,model,tokenizer)