transformers safetensors bert es base_model:dccuchile/bert-base-spanish-wwm-uncased base_model:finetune:dccuchile/bert-base-spanish-wwm-uncased license:cc-by-nc-4.0 endpoints_compatible region:us
---
base_model:
- dccuchile/bert-base-spanish-wwm-uncased
language:
- es
library_name: transformers
license: cc-by-nc-4.0
metrics:
- accuracy
- precision
- recall
- f1
---

# Model Card for dccuchile-bert-base-spanish-wwm-uncased-re-ct

This relation extraction model extracts intervention-associated relationships, temporal relations, negation/speculation and others relevant
for clinical trials. 

The model achieves the following results on the test set (when trained with the training and development set; results are averaged over 5 evaluation rounds):
- Precision: 0.868 (±0.009)
- Recall: 0.857 (±0.006)
- F1: 0.862 (±0.006)
- Accuracy: 0.907 (±0.003)


## Model description

This model adapts the pre-trained model [bert-base-spanish-wwm-uncased](https://huggingface.co/dccuchile/bert-base-spanish-wwm-uncased). 
It is fine-tuned to conduct relation extraction on Spanish texts about clinical trials. 
The model is fine-tuned on the [Clinical Trials for Evidence-Based-Medicine in Spanish corpus](http://www.lllf.uam.es/ESP/nlpdata/wp2/).

If you use this model, please, cite as follows:

```
@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}
}
```

## Intended uses & limitations

**Disclosure**: *This model is under development and needs to be improved. It should not be used for medical decision making without human assistance and supervision*

This model is intended for a generalist purpose, and may have bias and/or any other undesirable distortions.

Third parties who deploy or provide systems and/or services using any of these models (or using systems based on these models) should note that it is their responsibility to mitigate the risks arising from their use. Third parties, in any event, need to comply with applicable regulations, including regulations concerning the use of artificial intelligence.

The owner or creator of the models will in no event be liable for any results arising from the use made by third parties of these models.

**Descargo de responsabilidad**: *Esta herramienta se encuentra en desarrollo y no debe ser empleada para la toma de decisiones médicas*

La finalidad de este modelo es generalista, y se advierte que puede tener sesgos y/u otro tipo de distorsiones indeseables.

Terceras partes que desplieguen o proporcionen sistemas y/o servicios usando alguno de estos modelos (o utilizando sistemas basados en estos modelos) han tener presente que es su responsabilidad abordar y minimizar los riesgos derivados de su uso. Las terceras partes, en cualquier circunstancia, deben cumplir con la normativa aplicable, incluyendo la normativa que concierne al uso de la inteligencia artificial.

El propietario o creador de los modelos de ningún modo será responsable de los resultados derivados del uso que las terceras partes hagan de estos modelos.


## Training and evaluation data

The data used for fine-tuning are the [Clinical Trials for Evidence-Based-Medicine in Spanish corpus](http://www.lllf.uam.es/ESP/nlpdata/wp2/) version 3 (annotated with semantic relationships).
It is a collection of 1200 texts about clinical trials studies and clinical trials announcements:
- 500 abstracts from journals published under a Creative Commons license, e.g. available in PubMed or the Scientific Electronic Library Online (SciELO)
- 700 clinical trials announcements published in the European Clinical Trials Register and Repositorio Español de Estudios Clínicos

The CT-EBM-ES resource (version 1) can be cited as follows:

```
@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}
}
```



## Training procedure

### Training hyperparameters

The following hyperparameters were used during training:
- learning_rate: 5e-05
- train_batch_size: 16
- eval_batch_size: 16
- seed: we used different seeds for 5 evaluation rounds, and uploaded the model with the best results
- optimizer: AdamW
- weight decay: 1e-2
- lr_scheduler_type: linear
- num_epochs: 5 epochs.


### Training results (test set; average and standard deviation of 5 rounds with different seeds)

|   Precision    |     Recall     |       F1       |    Accuracy    |
|:--------------:|:--------------:|:--------------:|:--------------:|
| 0.877 (±0.009) | 0.857 (±0.006) | 0.862 (±0.006) | 0.907 (±0.003) |


**Results per class (test set; best model)**  

|      Class      |   Precision    |     Recall     |       F1       |  Support  |
|:---------------:|:--------------:|:--------------:|:--------------:|:---------:|
| Experiences | 0.96     |   0.98    |    0.97     |   2003 |
| Has_Age     |    0.93   |     0.82     |   0.87    |     152 |
| Has_Dose_or_Strength    |     0.79     |   0.83     |   0.81   |      189 |
| Has_Drug_Form       |  0.91    |    0.80    |    0.85    |      64 |
| Has_Duration_or_Interval  |       0.79   |     0.82   |     0.81     |    365 |
| Has_Frequency     |    0.84    |    0.75   |     0.79    |      84 |
| Has_Quantifier_or_Qualifier |        0.89   |     0.89   |     0.89   |     1040 |
| Has_Result_or_Value      |   0.91   |     0.91   |     0.91     |    384 |
| Has_Route_or_Mode    |     0.89    |    0.83    |    0.86     |    221 |
| Has_Time_Data     |    0.89     |   0.83   |     0.86      |   589 |
| Location_of     |    0.94     |   0.97    |    0.96     |   1119 |
| Used_for     |    0.86   |     0.88     |   0.87      |   731 |

### Usage


To use this model you need to install the datasets library. 

```shell
pip install datasets
```

Then you can define the necessary functions and classes to load the model. 

```python
from transformers import (
    BertTokenizerFast, BertModel, BertForPreTraining, BertConfig, BertPreTrainedModel,
    DataCollatorWithPadding,AutoTokenizer
)
from transformers.modeling_outputs import SequenceClassifierOutput
import torch
import torch.nn as nn
from datasets import Dataset
from torch.utils.data import DataLoader


class BertForRelationExtraction(BertPreTrainedModel):
  def __init__(self, config, num_labels):
    super(BertForRelationExtraction, self).__init__(config)
    self.num_labels = num_labels
    # body
    self.bert = BertModel(config)
    # head
    self.dropout = nn.Dropout(config.hidden_dropout_prob)
    self.layer_norm = nn.LayerNorm(config.hidden_size * 2)
    self.linear = nn.Linear(config.hidden_size * 2, self.num_labels)
    self.init_weights()

  def forward(self, input_ids, token_type_ids, attention_mask,
              span_idxs, labels=None):
    outputs = (
        self.bert(input_ids, token_type_ids=token_type_ids,
                  attention_mask=attention_mask,
                  output_hidden_states=False)
            .last_hidden_state)
            
    sub_maxpool, obj_maxpool = [], []
    for bid in range(outputs.size(0)):
      # span includes entity markers, maxpool across span
      sub_span = torch.max(outputs[bid, span_idxs[bid, 0]:span_idxs[bid, 1]+1, :], 
                           dim=0, keepdim=True).values
      obj_span = torch.max(outputs[bid, span_idxs[bid, 2]:span_idxs[bid, 3]+1, :],
                           dim=0, keepdim=True).values
      sub_maxpool.append(sub_span)
      obj_maxpool.append(obj_span)

    sub_emb = torch.cat(sub_maxpool, dim=0)
    obj_emb = torch.cat(obj_maxpool, dim=0)
    rel_input = torch.cat((sub_emb, obj_emb), dim=-1)

    rel_input = self.layer_norm(rel_input)
    rel_input = self.dropout(rel_input)
    logits = self.linear(rel_input)

    if labels is not None:
      loss_fn = nn.CrossEntropyLoss()
      loss = loss_fn(logits.view(-1, self.num_labels), labels.view(-1))
      return SequenceClassifierOutput(loss, logits)
    else:
      return SequenceClassifierOutput(None, logits)

id2label = {0: 'Experiences',
 1: 'Has_Age',
 2: 'Has_Dose_or_Strength',
 3: 'Has_Duration_or_Interval',
 4: 'Has_Frequency',
 5: 'Has_Route_or_Mode',
 6: 'Location_of',
 7: 'Used_for'}

def encode_data_inference(token_list,tokenizer):
  tokenized_inputs = tokenizer(token_list,
                               is_split_into_words=True,
                               truncation=True)
  span_idxs = []
  for input_id in tokenized_inputs.input_ids:
    tokens = tokenizer.convert_ids_to_tokens(input_id)
    span_idxs.append([
      [idx for idx, token in enumerate(tokens) if token.startswith("<S:")][0],
      [idx for idx, token in enumerate(tokens) if token.startswith("</S:")][0],
      [idx for idx, token in enumerate(tokens) if token.startswith("<O:")][0],
      [idx for idx, token in enumerate(tokens) if token.startswith("</O:")][0]
    ])
  tokenized_inputs["span_idxs"] = span_idxs
  # tokenized_inputs["labels"] = [label2id[label] for label in examples["label"]]
  return tokenized_inputs

def predict_example(example,model,tokenizer):
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    model.to(device)
    collate_fn = DataCollatorWithPadding(tokenizer, padding="longest", return_tensors="pt")


    encoded_data = encode_data_inference(example,tokenizer)

    inferenceds = Dataset.from_dict(encoded_data)

    inference_dl = DataLoader(inferenceds, 
                         shuffle=False,
                        #  sampler=SubsetRandomSampler(np.random.randint(0, encoded_nyt_dataset["test"].num_rows, 100).tolist()),
                         batch_size=1, 
                         collate_fn=collate_fn)
    for batch in inference_dl:
        batch = {k: v.to(device) for k, v in batch.items()}
        with torch.no_grad():
          outputs = model(**batch)
          predictions = torch.argmax(outputs.logits, dim=-1).cpu().numpy()
    return [id2label[p] for p in predictions]
        
```

Finally, you can use it to make predictions:

```python
example = [['Título',
  'público:',
  'Estudio',
  'multicéntrico,',
  'aleatorizado,',
  'doble',
  'ciego,',
  'controlado',
  'con',
  'placebo',
  'del',
  'anticuerpo',
  'monoclonal',
  'humano',
  'anti-TNF',
  '<O:CHE>',
  'Adalimumab',
  '</O:CHE>',
  'en',
  '<S:LIV>',
  'sujetos',
  'pediátricos',
  '</S:LIV>',
  'con',
  'colitis',
  'ulcerosa',
  'moderada',
  'o',
  'grave']]

model = BertForRelationExtraction.from_pretrained("medspaner/dccuchile-bert-base-spanish-wwm-uncased-re-ct-v2",8)  
tokenizer = AutoTokenizer.from_pretrained("medspaner/dccuchile-bert-base-spanish-wwm-uncased-re-ct-v2")
predict_example(example,model,tokenizer)
```


### Framework versions

- Transformers 4.42.4
- Pytorch 2.0.1+cu117
- Datasets 2.15.0
- Tokenizers 0.19.1