Feature Extraction
Transformers
Safetensors
Italian
radgraph_it
radiology
information-extraction
named-entity-recognition
relation-extraction
medical
radgraph
custom_code
Instructions to use radgraphIT/Radgraph-IT with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use radgraphIT/Radgraph-IT with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("feature-extraction", model="radgraphIT/Radgraph-IT", trust_remote_code=True)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("radgraphIT/Radgraph-IT", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Add transformers-compatible wrapper (AutoModel/AutoTokenizer via trust_remote_code)
0c48771 verified | """NER head: score every candidate span, cross-entropy against the gold label (or null). | |
| Port of `radgraph.dygie.models.ner.NERTagger`. Simplified for batch_size=1 (dataset.py): no | |
| span mask, no per-sentence looping -- one document's worth of spans at a time. | |
| `transformer_params` (medbit_transformer arm, see notes/v2_plan.md item 1) swaps the plain | |
| FeedForward scorer for a TransformerBlock: every candidate span attends to every other | |
| candidate span in the document before classification, instead of each span being scored in | |
| isolation. NER scores every candidate span unpruned, so this is full O(n^2) self-attention | |
| over the whole doc's span pool (accepted cost, not capped -- see v2_plan.md item 1). | |
| """ | |
| from typing import Dict, List | |
| import torch | |
| from torch import nn | |
| from .document import PredictedNER, Sentence | |
| from .feedforward import FeedForward | |
| from .metrics import NERMetrics | |
| from .transformer_block import TransformerBlock | |
| from .vocab import Vocabulary | |
| class NERHead(nn.Module): | |
| def __init__(self, vocab: Vocabulary, span_emb_dim: int, feedforward_params: dict, | |
| transformer_params: dict = None): | |
| super().__init__() | |
| self.vocab = vocab | |
| self.namespaces = [ns for ns in vocab.get_namespaces() if ns.endswith("ner_labels")] | |
| self.n_labels = {ns: vocab.get_vocab_size(ns) for ns in self.namespaces} | |
| self.use_transformer = transformer_params is not None | |
| self.scorers = nn.ModuleDict() | |
| self.classifiers = nn.ModuleDict() | |
| self.metrics: Dict[str, NERMetrics] = {} | |
| for ns in self.namespaces: | |
| if self.use_transformer: | |
| body = TransformerBlock(span_emb_dim, **transformer_params) | |
| else: | |
| body = FeedForward(span_emb_dim, feedforward_params["hidden_dims"], | |
| feedforward_params["dropout"]) | |
| self.scorers[ns] = body | |
| self.classifiers[ns] = nn.Linear(body.output_dim, self.n_labels[ns] - 1) | |
| self.metrics[ns] = NERMetrics(self.n_labels[ns]) | |
| self._loss = nn.CrossEntropyLoss(reduction="sum") | |
| def forward(self, dataset: str, spans: List[tuple], span_embeddings: torch.Tensor, | |
| sentence: Sentence, ner_label_ids: torch.Tensor = None) -> dict: | |
| ns = f"{dataset}__ner_labels" | |
| if self.use_transformer: | |
| starts = torch.tensor([s[0] for s in spans], dtype=torch.long, | |
| device=span_embeddings.device) | |
| hidden = self.scorers[ns](span_embeddings, starts) | |
| else: | |
| hidden = self.scorers[ns](span_embeddings) | |
| scores = self.classifiers[ns](hidden) # (num_spans, n_labels - 1) | |
| dummy = scores.new_zeros(scores.size(0), 1) # null-label score, fixed at 0 | |
| scores = torch.cat([dummy, scores], dim=-1) # (num_spans, n_labels) | |
| predicted = scores.argmax(dim=-1) | |
| predictions = self.decode(ns, scores.detach(), spans, sentence) | |
| output = {"namespace": ns, "scores": scores, "predicted": predicted, "predictions": predictions} | |
| if ner_label_ids is not None: | |
| self.metrics[ns](predicted, ner_label_ids) | |
| output["loss"] = self._loss(scores, ner_label_ids) | |
| return output | |
| def decode(self, namespace: str, scores: torch.Tensor, spans: List[tuple], | |
| sentence: Sentence) -> List[PredictedNER]: | |
| softmax_scores = torch.softmax(scores, dim=-1) | |
| raw, predicted = scores.max(dim=-1) | |
| soft, _ = softmax_scores.max(dim=-1) | |
| predictions = [] | |
| for i in (predicted != 0).nonzero(as_tuple=True)[0].tolist(): | |
| label = self.vocab.get_token_from_index(int(predicted[i]), namespace) | |
| start, end = spans[i] | |
| entry = [start, end, label, float(raw[i]), float(soft[i])] | |
| predictions.append(PredictedNER(entry, sentence, sentence_offsets=True)) | |
| return predictions | |
| def get_metrics(self, reset: bool = False) -> Dict[str, float]: | |
| res = {} | |
| for ns, metric in self.metrics.items(): | |
| prefix = ns.replace("_labels", "") | |
| per_class = metric.get_per_class() # {class idx -> F1}, read BEFORE the reset below | |
| for idx, class_f1 in per_class.items(): | |
| res[f"ner_perclass/{self.vocab.get_token_from_index(idx, ns)}"] = class_f1 | |
| res["ner_macro_f1"] = sum(per_class.values()) / len(per_class) if per_class else 0.0 | |
| precision, recall, f1 = metric.get_metric(reset) | |
| res[f"{prefix}_precision"] = precision | |
| res[f"{prefix}_recall"] = recall | |
| res[f"{prefix}_f1"] = f1 | |
| for name in ("precision", "recall", "f1"): | |
| values = [v for k, v in res.items() | |
| if k.endswith(f"_{name}") and "perclass/" not in k and "_macro_" not in k] | |
| res[f"MEAN__ner_{name}"] = sum(values) / len(values) if values else 0.0 | |
| return res | |