Transformers
Safetensors
mbart
text2text-generation
v1.0.0
entity-disambiguation
named-entity-linking
entity-linking
Instructions to use emanuelaboros/globalise-entity-linker with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use emanuelaboros/globalise-entity-linker with Transformers:
# Load model directly from transformers import AutoTokenizer, AutoModelForSeq2SeqLM tokenizer = AutoTokenizer.from_pretrained("emanuelaboros/globalise-entity-linker") model = AutoModelForSeq2SeqLM.from_pretrained("emanuelaboros/globalise-entity-linker", device_map="auto") - Notebooks
- Google Colab
- Kaggle
Upload folder using huggingface_hub
Browse files- .gitattributes +1 -0
- README.md +259 -0
- config.json +44 -0
- generation_config.json +8 -0
- generic_nel.py +590 -0
- model.safetensors +3 -0
- requirements.txt +5 -0
- scheduler.pt +3 -0
- sentencepiece.bpe.model +3 -0
- special_tokens_map.json +51 -0
- tokenizer_config.json +55 -0
- trainer_state.json +207 -0
- utils.py +62 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
tokenizer.json filter=lfs diff=lfs merge=lfs -text
|
README.md
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
library_name: transformers
|
| 3 |
+
language:
|
| 4 |
+
- multilingual
|
| 5 |
+
- af
|
| 6 |
+
- am
|
| 7 |
+
- ar
|
| 8 |
+
- as
|
| 9 |
+
- az
|
| 10 |
+
- be
|
| 11 |
+
- bg
|
| 12 |
+
- bm
|
| 13 |
+
- bn
|
| 14 |
+
- br
|
| 15 |
+
- bs
|
| 16 |
+
- ca
|
| 17 |
+
- cs
|
| 18 |
+
- cy
|
| 19 |
+
- da
|
| 20 |
+
- de
|
| 21 |
+
- el
|
| 22 |
+
- en
|
| 23 |
+
- eo
|
| 24 |
+
- es
|
| 25 |
+
- et
|
| 26 |
+
- eu
|
| 27 |
+
- fa
|
| 28 |
+
- ff
|
| 29 |
+
- fi
|
| 30 |
+
- fr
|
| 31 |
+
- fy
|
| 32 |
+
- ga
|
| 33 |
+
- gd
|
| 34 |
+
- gl
|
| 35 |
+
- gn
|
| 36 |
+
- gu
|
| 37 |
+
- ha
|
| 38 |
+
- he
|
| 39 |
+
- hi
|
| 40 |
+
- hr
|
| 41 |
+
- ht
|
| 42 |
+
- hu
|
| 43 |
+
- hy
|
| 44 |
+
- id
|
| 45 |
+
- ig
|
| 46 |
+
- is
|
| 47 |
+
- it
|
| 48 |
+
- ja
|
| 49 |
+
- jv
|
| 50 |
+
- ka
|
| 51 |
+
- kg
|
| 52 |
+
- kk
|
| 53 |
+
- km
|
| 54 |
+
- kn
|
| 55 |
+
- ko
|
| 56 |
+
- ku
|
| 57 |
+
- ky
|
| 58 |
+
- la
|
| 59 |
+
- lg
|
| 60 |
+
- ln
|
| 61 |
+
- lo
|
| 62 |
+
- lt
|
| 63 |
+
- lv
|
| 64 |
+
- mg
|
| 65 |
+
- mk
|
| 66 |
+
- ml
|
| 67 |
+
- mn
|
| 68 |
+
- mr
|
| 69 |
+
- ms
|
| 70 |
+
- my
|
| 71 |
+
- ne
|
| 72 |
+
- nl
|
| 73 |
+
- no
|
| 74 |
+
- om
|
| 75 |
+
- or
|
| 76 |
+
- pa
|
| 77 |
+
- pl
|
| 78 |
+
- ps
|
| 79 |
+
- pt
|
| 80 |
+
- qu
|
| 81 |
+
- ro
|
| 82 |
+
- ru
|
| 83 |
+
- sa
|
| 84 |
+
- sd
|
| 85 |
+
- si
|
| 86 |
+
- sk
|
| 87 |
+
- sl
|
| 88 |
+
- so
|
| 89 |
+
- sq
|
| 90 |
+
- sr
|
| 91 |
+
- ss
|
| 92 |
+
- su
|
| 93 |
+
- sv
|
| 94 |
+
- sw
|
| 95 |
+
- ta
|
| 96 |
+
- te
|
| 97 |
+
- th
|
| 98 |
+
- ti
|
| 99 |
+
- tl
|
| 100 |
+
- tn
|
| 101 |
+
- tr
|
| 102 |
+
- uk
|
| 103 |
+
- ur
|
| 104 |
+
- uz
|
| 105 |
+
- vi
|
| 106 |
+
- wo
|
| 107 |
+
- xh
|
| 108 |
+
- yo
|
| 109 |
+
- zh
|
| 110 |
+
|
| 111 |
+
license: agpl-3.0
|
| 112 |
+
tags:
|
| 113 |
+
- v1.0.0
|
| 114 |
+
- entity-disambiguation
|
| 115 |
+
- named-entity-linking
|
| 116 |
+
- entity-linking
|
| 117 |
+
- text2text-generation
|
| 118 |
+
---
|
| 119 |
+
|
| 120 |
+
# Model Card for `impresso-project/nel-mgenre-multilingual`
|
| 121 |
+
|
| 122 |
+
The **Impresso multilingual named entity linking (NEL)** model is based on **mGENRE** (multilingual Generative ENtity
|
| 123 |
+
REtrieval) proposed by [De Cao et al](https://arxiv.org/abs/2103.12528), a sequence-to-sequence architecture for entity
|
| 124 |
+
disambiguation based on [mBART](https://arxiv.org/abs/2001.08210). It uses **constrained generation** to output entity
|
| 125 |
+
names mapped to Wikidata/QIDs.
|
| 126 |
+
|
| 127 |
+
This model was adapted for historical texts and fine-tuned on
|
| 128 |
+
the [HIPE-2022 dataset](https://github.com/hipe-eval/HIPE-2022-data), which includes a variety of historical document
|
| 129 |
+
types and languages.
|
| 130 |
+
|
| 131 |
+
## Model Details
|
| 132 |
+
|
| 133 |
+
### Model Description
|
| 134 |
+
|
| 135 |
+
- **Developed by:** EPFL from the [Impresso team](https://impresso-project.ch). The project is an interdisciplinary
|
| 136 |
+
project focused on historical media analysis across languages, time, and modalities. Funded by the Swiss National
|
| 137 |
+
Science
|
| 138 |
+
Foundation ([CRSII5_173719](http://p3.snf.ch/project-173719), [CRSII5_213585](https://data.snf.ch/grants/grant/213585))
|
| 139 |
+
and the Luxembourg National Research Fund (grant No. 17498891).
|
| 140 |
+
- **Model type:** mBART-based sequence-to-sequence model with constrained beam search for named entity linking
|
| 141 |
+
- **Languages:** Multilingual (100+ languages, optimized for French, German, and English)
|
| 142 |
+
- **License:** [AGPL v3+](https://github.com/impresso/impresso-pyindexation/blob/master/LICENSE)
|
| 143 |
+
- **Finetuned from:** [`facebook/mgenre-wiki`](https://huggingface.co/facebook/mgenre-wiki)
|
| 144 |
+
|
| 145 |
+
### Model Architecture
|
| 146 |
+
|
| 147 |
+
- **Architecture:** mBART-based seq2seq with constrained beam search
|
| 148 |
+
|
| 149 |
+
## Training Details
|
| 150 |
+
|
| 151 |
+
### Training Data
|
| 152 |
+
|
| 153 |
+
The model was trained on the following datasets:
|
| 154 |
+
|
| 155 |
+
| Dataset alias | README | Document type | Languages | Suitable for | Project | License |
|
| 156 |
+
|---------------|--------------------------------------------|------------------------|----------------|----------------------------|-------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
|
| 157 |
+
| ajmc | [link](documentation/README-ajmc.md) | classical commentaries | de, fr, en | NERC-Coarse, NERC-Fine, EL | [AjMC](https://mromanello.github.io/ajax-multi-commentary/) | [](https://creativecommons.org/licenses/by/4.0/) |
|
| 158 |
+
| hipe2020 | [link](documentation/README-hipe2020.md) | historical newspapers | de, fr, en | NERC-Coarse, NERC-Fine, EL | [CLEF-HIPE-2020](https://impresso.github.io/CLEF-HIPE-2020) | [](https://creativecommons.org/licenses/by-nc-sa/4.0/) |
|
| 159 |
+
| topres19th | [link](documentation/README-topres19th.md) | historical newspapers | en | NERC-Coarse, EL | [Living with Machines](https://livingwithmachines.ac.uk/) | [](https://creativecommons.org/licenses/by-nc-sa/4.0/) |
|
| 160 |
+
| newseye | [link](documentation/README-newseye.md) | historical newspapers | de, fi, fr, sv | NERC-Coarse, NERC-Fine, EL | [NewsEye](https://www.newseye.eu/) | [](https://creativecommons.org/licenses/by/4.0/) |
|
| 161 |
+
| sonar | [link](documentation/README-sonar.md) | historical newspapers | de | NERC-Coarse, EL | [SoNAR](https://sonar.fh-potsdam.de/) | [](https://creativecommons.org/licenses/by/4.0/) |
|
| 162 |
+
|
| 163 |
+
## How to Use
|
| 164 |
+
|
| 165 |
+
```python
|
| 166 |
+
from transformers import AutoTokenizer, pipeline
|
| 167 |
+
|
| 168 |
+
NEL_MODEL_NAME = "impresso-project/nel-mgenre-multilingual"
|
| 169 |
+
nel_tokenizer = AutoTokenizer.from_pretrained(NEL_MODEL_NAME)
|
| 170 |
+
|
| 171 |
+
nel_pipeline = pipeline("generic-nel", model=NEL_MODEL_NAME,
|
| 172 |
+
tokenizer=nel_tokenizer,
|
| 173 |
+
trust_remote_code=True,
|
| 174 |
+
device='cpu')
|
| 175 |
+
|
| 176 |
+
sentence = "Le 0ctobre 1894, [START] Dreyfvs [END] est arrêté à Paris, accusé d'espionnage pour l'Allemagne — un événement qui déch1ra la société fr4nçaise pendant des années."
|
| 177 |
+
print(nel_pipeline(sentence))
|
| 178 |
+
```
|
| 179 |
+
|
| 180 |
+
### Output Format
|
| 181 |
+
|
| 182 |
+
```python
|
| 183 |
+
[
|
| 184 |
+
{
|
| 185 |
+
'surface': 'Dreyfvs',
|
| 186 |
+
'wkd_id': 'Q171826',
|
| 187 |
+
'wkpedia_pagename': 'Alfred Dreyfus',
|
| 188 |
+
'wkpedia_url': 'https://fr.wikipedia.org/wiki/Alfred_Dreyfus',
|
| 189 |
+
'type': 'UNK',
|
| 190 |
+
'confidence_nel': 99.98,
|
| 191 |
+
'lOffset': 24,
|
| 192 |
+
'rOffset': 33}]
|
| 193 |
+
```
|
| 194 |
+
|
| 195 |
+
----
|
| 196 |
+
|
| 197 |
+
### Batched predictions
|
| 198 |
+
|
| 199 |
+
```python
|
| 200 |
+
from transformers import AutoTokenizer, pipeline
|
| 201 |
+
|
| 202 |
+
NEL_MODEL_NAME = "impresso-project/nel-mgenre-multilingual"
|
| 203 |
+
|
| 204 |
+
nel_tokenizer = AutoTokenizer.from_pretrained(NEL_MODEL_NAME)
|
| 205 |
+
|
| 206 |
+
nel_pipeline = pipeline(
|
| 207 |
+
"generic-nel",
|
| 208 |
+
model=NEL_MODEL_NAME,
|
| 209 |
+
tokenizer=nel_tokenizer,
|
| 210 |
+
trust_remote_code=True,
|
| 211 |
+
device="cpu",
|
| 212 |
+
)
|
| 213 |
+
|
| 214 |
+
sentences = [
|
| 215 |
+
"Le 0ctobre 1894, [START] Dreyfvs [END] est arrêté à Paris.",
|
| 216 |
+
"En 1912, [START] Giollitti [END] était encore au pouvoir en Italie.",
|
| 217 |
+
"La ville de [START] Lpzbourg [END] est importante dans la région."
|
| 218 |
+
]
|
| 219 |
+
|
| 220 |
+
results = nel_pipeline(sentences)
|
| 221 |
+
|
| 222 |
+
from pprint import pprint
|
| 223 |
+
pprint(results)
|
| 224 |
+
```
|
| 225 |
+
|
| 226 |
+
The type of the entity is `UNK` because the model was not trained on the entity type. The `confidence_nel` score
|
| 227 |
+
indicates the model's confidence in the prediction.
|
| 228 |
+
|
| 229 |
+
## Use Cases
|
| 230 |
+
|
| 231 |
+
- Entity disambiguation in noisy OCR settings
|
| 232 |
+
- Linking historical names to modern Wikidata entities
|
| 233 |
+
- Assisting downstream event extraction and biography generation from historical archives
|
| 234 |
+
|
| 235 |
+
> 🐱**Easter Egg**:
|
| 236 |
+
> While the pipeline’s main goal is to link surface forms to QIDs and Wikipedia pages,
|
| 237 |
+
> it sometimes slips in an extra surprise: a coarse Wikidata type label (`pers`, `org`, or `loc`).
|
| 238 |
+
> Not strictly required — but a handy little bonus when it shows up!
|
| 239 |
+
|
| 240 |
+
## Limitations
|
| 241 |
+
|
| 242 |
+
- Sensitive to tokenisation and malformed spans
|
| 243 |
+
- Accuracy degrades on non-Wikidata entities or in highly ambiguous contexts
|
| 244 |
+
- Focused on historical entity mentions — performance may vary on modern texts
|
| 245 |
+
|
| 246 |
+
## Environmental Impact
|
| 247 |
+
|
| 248 |
+
- **Hardware:** 1x A100 (80GB) for finetuning
|
| 249 |
+
- **Training time:** ~12 hours
|
| 250 |
+
- **Estimated CO₂ Emissions:** ~2.3 kg CO₂eq
|
| 251 |
+
|
| 252 |
+
## Contact
|
| 253 |
+
|
| 254 |
+
- Website: [https://impresso-project.ch](https://impresso-project.ch)
|
| 255 |
+
|
| 256 |
+
<p align="center">
|
| 257 |
+
<img src="https://github.com/impresso/impresso.github.io/blob/master/assets/images/3x1--Yellow-Impresso-Black-on-White--transparent.png?raw=true" width="300" alt="Impresso Logo"/>
|
| 258 |
+
</p>
|
| 259 |
+
|
config.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_name_or_path": "facebook/mgenre-wiki",
|
| 3 |
+
"activation_dropout": 0.0,
|
| 4 |
+
"activation_function": "gelu",
|
| 5 |
+
"architectures": [
|
| 6 |
+
"MBartForConditionalGeneration"
|
| 7 |
+
],
|
| 8 |
+
"custom_pipelines": {
|
| 9 |
+
"generic-nel": {
|
| 10 |
+
"impl": "generic_nel.NelPipeline",
|
| 11 |
+
"pt": [
|
| 12 |
+
"MBartForConditionalGeneration"
|
| 13 |
+
],
|
| 14 |
+
"tf": []
|
| 15 |
+
}
|
| 16 |
+
},
|
| 17 |
+
"attention_dropout": 0.0,
|
| 18 |
+
"bos_token_id": 0,
|
| 19 |
+
"classifier_dropout": 0.0,
|
| 20 |
+
"d_model": 1024,
|
| 21 |
+
"decoder_attention_heads": 16,
|
| 22 |
+
"decoder_ffn_dim": 4096,
|
| 23 |
+
"decoder_layerdrop": 0.0,
|
| 24 |
+
"decoder_layers": 12,
|
| 25 |
+
"decoder_start_token_id": 2,
|
| 26 |
+
"dropout": 0.1,
|
| 27 |
+
"encoder_attention_heads": 16,
|
| 28 |
+
"encoder_ffn_dim": 4096,
|
| 29 |
+
"encoder_layerdrop": 0.0,
|
| 30 |
+
"encoder_layers": 12,
|
| 31 |
+
"eos_token_id": 2,
|
| 32 |
+
"forced_eos_token_id": 2,
|
| 33 |
+
"init_std": 0.02,
|
| 34 |
+
"is_encoder_decoder": true,
|
| 35 |
+
"max_position_embeddings": 1024,
|
| 36 |
+
"model_type": "mbart",
|
| 37 |
+
"num_hidden_layers": 12,
|
| 38 |
+
"pad_token_id": 1,
|
| 39 |
+
"scale_embedding": true,
|
| 40 |
+
"torch_dtype": "float32",
|
| 41 |
+
"transformers_version": "4.31.0",
|
| 42 |
+
"use_cache": true,
|
| 43 |
+
"vocab_size": 256001
|
| 44 |
+
}
|
generation_config.json
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"bos_token_id": 0,
|
| 3 |
+
"decoder_start_token_id": 2,
|
| 4 |
+
"eos_token_id": 2,
|
| 5 |
+
"forced_eos_token_id": 2,
|
| 6 |
+
"pad_token_id": 1,
|
| 7 |
+
"transformers_version": "4.46.0.dev0"
|
| 8 |
+
}
|
generic_nel.py
ADDED
|
@@ -0,0 +1,590 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from transformers import Pipeline
|
| 2 |
+
import requests
|
| 3 |
+
import torch
|
| 4 |
+
import time
|
| 5 |
+
import os
|
| 6 |
+
import random
|
| 7 |
+
import logging
|
| 8 |
+
from filelock import FileLock
|
| 9 |
+
import requests_cache
|
| 10 |
+
from requests.adapters import HTTPAdapter
|
| 11 |
+
from urllib3.util.retry import Retry
|
| 12 |
+
|
| 13 |
+
logger = logging.getLogger("nel_wikidata")
|
| 14 |
+
|
| 15 |
+
NEL_MODEL = "nel-mgenre-multilingual"
|
| 16 |
+
|
| 17 |
+
# Required by Wikimedia: identify your app + contact
|
| 18 |
+
HEADERS = {"User-Agent": "NEL-HF-Space/0.1 (contact: emanuela.boros@epfl.ch)"}
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def safe_get(url, params, retries=3, backoff=2):
|
| 22 |
+
"""
|
| 23 |
+
Perform a safe HTTP GET request with retry logic.
|
| 24 |
+
|
| 25 |
+
This function wraps `requests.get` and automatically retries the request if it fails
|
| 26 |
+
due to common transient errors such as HTTP 403 (forbidden, often due to missing/blocked
|
| 27 |
+
User-Agent headers) or HTTP 429 (rate-limited). Retries use exponential backoff.
|
| 28 |
+
|
| 29 |
+
Args:
|
| 30 |
+
url (str): The target URL for the request.
|
| 31 |
+
params (dict): Query parameters to send with the GET request.
|
| 32 |
+
retries (int, optional): Maximum number of attempts before giving up. Default is 3.
|
| 33 |
+
backoff (int, optional): Base for exponential backoff in seconds. Default is 2.
|
| 34 |
+
- Example: with retries=3 and backoff=2, wait times will be 1s, 2s, 4s.
|
| 35 |
+
|
| 36 |
+
Returns:
|
| 37 |
+
requests.Response or None:
|
| 38 |
+
- A `Response` object if the request eventually succeeds (status=200).
|
| 39 |
+
- `None` if all retries fail or an unrecoverable error occurs.
|
| 40 |
+
|
| 41 |
+
Notes:
|
| 42 |
+
- Adds `HEADERS` to all requests, which should include a descriptive User-Agent
|
| 43 |
+
as required by Wikimedia APIs.
|
| 44 |
+
- Raises no exceptions; instead prints errors and returns None if the request
|
| 45 |
+
cannot be completed successfully.
|
| 46 |
+
"""
|
| 47 |
+
for attempt in range(retries):
|
| 48 |
+
try:
|
| 49 |
+
resp = requests.get(url, params=params, headers=HEADERS, timeout=10)
|
| 50 |
+
if resp.status_code == 200:
|
| 51 |
+
return resp
|
| 52 |
+
elif resp.status_code in (403, 429): # forbidden or rate limited
|
| 53 |
+
wait = backoff**attempt
|
| 54 |
+
print(f"Retrying {url} in {wait}s (status={resp.status_code})")
|
| 55 |
+
time.sleep(wait)
|
| 56 |
+
continue
|
| 57 |
+
else:
|
| 58 |
+
resp.raise_for_status()
|
| 59 |
+
except Exception as e:
|
| 60 |
+
print(f"Request error: {e}")
|
| 61 |
+
time.sleep(backoff**attempt)
|
| 62 |
+
return None
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
# ---- Persistent cache (shared on disk across processes) ----
|
| 66 |
+
# 30 days is fine for QID/title/type lookups
|
| 67 |
+
requests_cache.install_cache(
|
| 68 |
+
cache_name=os.getenv("WIKIMEDIA_CACHE", "wikimedia_cache"),
|
| 69 |
+
backend="sqlite",
|
| 70 |
+
expire_after=int(os.getenv("WIKIMEDIA_CACHE_TTL", str(60 * 60 * 24 * 30))),
|
| 71 |
+
allowable_methods=("GET",),
|
| 72 |
+
)
|
| 73 |
+
|
| 74 |
+
# ---- One Session per process (connection pooling + retries) ----
|
| 75 |
+
_session = requests.Session()
|
| 76 |
+
_session.headers.update(HEADERS)
|
| 77 |
+
|
| 78 |
+
_retry = Retry(
|
| 79 |
+
total=int(os.getenv("WIKIMEDIA_RETRIES", "5")),
|
| 80 |
+
connect=int(os.getenv("WIKIMEDIA_RETRIES", "5")),
|
| 81 |
+
read=int(os.getenv("WIKIMEDIA_RETRIES", "5")),
|
| 82 |
+
backoff_factor=float(os.getenv("WIKIMEDIA_BACKOFF", "1.5")),
|
| 83 |
+
status_forcelist=(429, 500, 502, 503, 504),
|
| 84 |
+
allowed_methods=frozenset(["GET"]),
|
| 85 |
+
respect_retry_after_header=False, # <-- IMPORTANT
|
| 86 |
+
raise_on_status=False,
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
_adapter = HTTPAdapter(max_retries=_retry, pool_connections=50, pool_maxsize=50)
|
| 90 |
+
_session.mount("https://", _adapter)
|
| 91 |
+
_session.mount("http://", _adapter)
|
| 92 |
+
|
| 93 |
+
# ---- Cross-process rate limit (global QPS across all workers on this machine) ----
|
| 94 |
+
# Choose a conservative QPS; Wikimedia is happier with low QPS.
|
| 95 |
+
WIKIMEDIA_QPS = float(os.getenv("WIKIMEDIA_QPS", "3.0")) # total across ALL processes
|
| 96 |
+
_MIN_INTERVAL = 1.0 / max(WIKIMEDIA_QPS, 0.1)
|
| 97 |
+
|
| 98 |
+
_LOCK = FileLock(os.getenv("WIKIMEDIA_RATELOCK", "/tmp/wikimedia_rate.lock"))
|
| 99 |
+
_TS_FILE = os.getenv("WIKIMEDIA_RATETS", "/tmp/wikimedia_rate.ts")
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def _throttle_global():
|
| 103 |
+
"""Global rate limiter shared across processes via file lock + timestamp."""
|
| 104 |
+
with _LOCK:
|
| 105 |
+
now = time.time()
|
| 106 |
+
last = 0.0
|
| 107 |
+
try:
|
| 108 |
+
with open(_TS_FILE, "r") as f:
|
| 109 |
+
last = float(f.read().strip() or "0")
|
| 110 |
+
except FileNotFoundError:
|
| 111 |
+
last = 0.0
|
| 112 |
+
|
| 113 |
+
wait = (last + _MIN_INTERVAL) - now
|
| 114 |
+
if wait > 0:
|
| 115 |
+
time.sleep(wait)
|
| 116 |
+
|
| 117 |
+
with open(_TS_FILE, "w") as f:
|
| 118 |
+
f.write(str(time.time()))
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
def safe_get_old(url, params, timeout=10):
|
| 122 |
+
"""
|
| 123 |
+
Cached + globally rate-limited + retrying GET.
|
| 124 |
+
Returns Response or None.
|
| 125 |
+
"""
|
| 126 |
+
_throttle_global()
|
| 127 |
+
try:
|
| 128 |
+
resp = _session.get(url, params=params, timeout=timeout)
|
| 129 |
+
|
| 130 |
+
# requests-cache sets this attribute; useful to log
|
| 131 |
+
from_cache = getattr(resp, "from_cache", False)
|
| 132 |
+
|
| 133 |
+
if resp.status_code == 200:
|
| 134 |
+
if from_cache:
|
| 135 |
+
logger.debug(f"[WIKI] 200 (cache) {url} params={_short(params)}")
|
| 136 |
+
return resp
|
| 137 |
+
|
| 138 |
+
if resp.status_code == 429:
|
| 139 |
+
# Even with Retry-After handling, sometimes you still get here.
|
| 140 |
+
ra = resp.headers.get("Retry-After")
|
| 141 |
+
sleep = float(ra) if (ra and ra.isdigit()) else (1.0 + random.random())
|
| 142 |
+
logger.warning(
|
| 143 |
+
f"[WIKI] 429 rate limited. retry-after={ra} sleeping={sleep:.2f}s url={url}"
|
| 144 |
+
)
|
| 145 |
+
time.sleep(sleep)
|
| 146 |
+
return resp # caller may treat as failure; Retry already happened.
|
| 147 |
+
|
| 148 |
+
logger.warning(
|
| 149 |
+
f"[WIKI] HTTP {resp.status_code} url={url} params={_short(params)}"
|
| 150 |
+
)
|
| 151 |
+
return resp
|
| 152 |
+
except Exception as e:
|
| 153 |
+
logger.exception(f"[WIKI] Request error: {e} url={url}")
|
| 154 |
+
return None
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
def _short(params: dict, maxlen: int = 120) -> str:
|
| 158 |
+
s = str(params)
|
| 159 |
+
return s if len(s) <= maxlen else s[:maxlen] + "..."
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
def get_wikipedia_page_props(input_str: str):
|
| 163 |
+
"""
|
| 164 |
+
Retrieve the Wikidata QID for a given Wikipedia page name.
|
| 165 |
+
|
| 166 |
+
This function attempts to resolve a text prediction (e.g. `"Paris >> fr"`)
|
| 167 |
+
into a Wikidata identifier (`Q90`) and its language code.
|
| 168 |
+
|
| 169 |
+
Resolution happens in two steps:
|
| 170 |
+
1. Query the specified language Wikipedia API (`prop=pageprops`) to check if
|
| 171 |
+
the page is associated with a `wikibase_item` (QID).
|
| 172 |
+
2. If that fails, fall back to the Wikidata `wbsearchentities` API, which
|
| 173 |
+
searches for entities matching the page name in the given language.
|
| 174 |
+
|
| 175 |
+
Args:
|
| 176 |
+
input_str (str): A string in the form `"page_name >> language"`.
|
| 177 |
+
- Example: `"Paris >> fr"` → page_name="Paris", language="fr".
|
| 178 |
+
- If no `>>` separator is found, defaults to `"en"` as the language.
|
| 179 |
+
|
| 180 |
+
Returns:
|
| 181 |
+
tuple(str, str):
|
| 182 |
+
- QID of the page (e.g., `"Q90"`) if found, otherwise `"NIL"`.
|
| 183 |
+
- Language code (e.g., `"fr"`, `"en"`).
|
| 184 |
+
|
| 185 |
+
Notes:
|
| 186 |
+
- Uses `safe_get()` internally for API calls, with retry logic and
|
| 187 |
+
a proper User-Agent header (required by Wikimedia).
|
| 188 |
+
- Falls back gracefully to `"NIL"` if both Wikipedia and Wikidata lookups fail.
|
| 189 |
+
- This function does not fetch human-readable titles or URLs;
|
| 190 |
+
use `get_wikipedia_title()` for that.
|
| 191 |
+
"""
|
| 192 |
+
|
| 193 |
+
if ">>" in input_str:
|
| 194 |
+
page_name, language = [x.strip() for x in input_str.split(">>", 1)]
|
| 195 |
+
else:
|
| 196 |
+
page_name, language = input_str.strip(), "en"
|
| 197 |
+
|
| 198 |
+
qid = "NIL"
|
| 199 |
+
|
| 200 |
+
# --- Step 1: Try Wikipedia API ---
|
| 201 |
+
wikipedia_url = f"https://{language}.wikipedia.org/w/api.php"
|
| 202 |
+
wikipedia_params = {
|
| 203 |
+
"action": "query",
|
| 204 |
+
"prop": "pageprops",
|
| 205 |
+
"format": "json",
|
| 206 |
+
"titles": page_name,
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
resp = safe_get(wikipedia_url, wikipedia_params)
|
| 210 |
+
if resp:
|
| 211 |
+
data = resp.json()
|
| 212 |
+
if "pages" in data.get("query", {}):
|
| 213 |
+
page_id = list(data["query"]["pages"].keys())[0]
|
| 214 |
+
page_props = data["query"]["pages"][page_id].get("pageprops", {})
|
| 215 |
+
if "wikibase_item" in page_props:
|
| 216 |
+
return page_props["wikibase_item"], language
|
| 217 |
+
|
| 218 |
+
# --- Step 2: Fallback to Wikidata search ---
|
| 219 |
+
wd_url = "https://www.wikidata.org/w/api.php"
|
| 220 |
+
wd_params = {
|
| 221 |
+
"action": "wbsearchentities",
|
| 222 |
+
"search": page_name,
|
| 223 |
+
"language": language,
|
| 224 |
+
"format": "json",
|
| 225 |
+
}
|
| 226 |
+
|
| 227 |
+
resp = safe_get(wd_url, wd_params)
|
| 228 |
+
if resp:
|
| 229 |
+
data = resp.json()
|
| 230 |
+
if data.get("search"):
|
| 231 |
+
first_hit = data["search"][0]
|
| 232 |
+
return first_hit["id"], language
|
| 233 |
+
|
| 234 |
+
return qid, language
|
| 235 |
+
|
| 236 |
+
|
| 237 |
+
def resolve_type_from_wikidata(p31_qids, visited=None, depth=0, max_depth=3):
|
| 238 |
+
"""
|
| 239 |
+
Resolve a Wikidata entity's high-level type ("pers", "org", "loc")
|
| 240 |
+
by checking its 'instance of' (P31) values and, if necessary,
|
| 241 |
+
recursively climbing 'subclass of' (P279) relations.
|
| 242 |
+
|
| 243 |
+
Args:
|
| 244 |
+
p31_qids (list[str]): List of QIDs from 'instance of' (P31) claims.
|
| 245 |
+
visited (set, optional): Set of QIDs already checked (to avoid cycles).
|
| 246 |
+
depth (int, optional): Current recursion depth (default=0).
|
| 247 |
+
max_depth (int, optional): Maximum recursion depth for P279 traversal.
|
| 248 |
+
|
| 249 |
+
Returns:
|
| 250 |
+
str: One of {"pers", "org", "loc", "UNK"}.
|
| 251 |
+
"UNK" is returned if no mapping is found within max_depth.
|
| 252 |
+
|
| 253 |
+
Notes:
|
| 254 |
+
- Anchors:
|
| 255 |
+
Q5 → person
|
| 256 |
+
Any in `qid_to_organization_label_keys` or TYPE_MAP=organization → org
|
| 257 |
+
Any in `qid_to_location_label_keys` or TYPE_MAP=location → loc
|
| 258 |
+
- Subclass traversal:
|
| 259 |
+
If P31 is not directly mapped, checks P279 (subclass of)
|
| 260 |
+
recursively up to `max_depth`.
|
| 261 |
+
"""
|
| 262 |
+
|
| 263 |
+
if visited is None:
|
| 264 |
+
visited = set()
|
| 265 |
+
|
| 266 |
+
for q in p31_qids:
|
| 267 |
+
if q in visited:
|
| 268 |
+
continue
|
| 269 |
+
visited.add(q)
|
| 270 |
+
|
| 271 |
+
# direct anchors
|
| 272 |
+
if q == "Q5":
|
| 273 |
+
return "pers"
|
| 274 |
+
if (
|
| 275 |
+
q in qid_to_organization_label_keys
|
| 276 |
+
or q in TYPE_MAP
|
| 277 |
+
and TYPE_MAP[q] == "organization"
|
| 278 |
+
):
|
| 279 |
+
return "org"
|
| 280 |
+
if (
|
| 281 |
+
q in qid_to_location_label_keys
|
| 282 |
+
or q in TYPE_MAP
|
| 283 |
+
and TYPE_MAP[q] == "location"
|
| 284 |
+
):
|
| 285 |
+
return "loc"
|
| 286 |
+
|
| 287 |
+
# subclass traversal
|
| 288 |
+
if depth < max_depth:
|
| 289 |
+
wd_url = "https://www.wikidata.org/w/api.php"
|
| 290 |
+
params = {
|
| 291 |
+
"action": "wbgetentities",
|
| 292 |
+
"format": "json",
|
| 293 |
+
"ids": q,
|
| 294 |
+
"props": "claims",
|
| 295 |
+
}
|
| 296 |
+
resp = safe_get(wd_url, params)
|
| 297 |
+
if resp:
|
| 298 |
+
data = resp.json()
|
| 299 |
+
entity = data["entities"].get(q, {})
|
| 300 |
+
claims = entity.get("claims", {})
|
| 301 |
+
if "P279" in claims: # subclass of
|
| 302 |
+
parents = [
|
| 303 |
+
c["mainsnak"]["datavalue"]["value"]["id"]
|
| 304 |
+
for c in claims["P279"]
|
| 305 |
+
if "datavalue" in c["mainsnak"]
|
| 306 |
+
]
|
| 307 |
+
resolved = resolve_type_from_wikidata(
|
| 308 |
+
parents, visited, depth + 1, max_depth
|
| 309 |
+
)
|
| 310 |
+
if resolved != "UNK":
|
| 311 |
+
return resolved
|
| 312 |
+
|
| 313 |
+
return "UNK"
|
| 314 |
+
|
| 315 |
+
|
| 316 |
+
def get_wikipedia_title(qid, language="en"):
|
| 317 |
+
"""
|
| 318 |
+
Retrieve a Wikipedia title, URL, and a mapped coarse entity type from a Wikidata QID.
|
| 319 |
+
|
| 320 |
+
This function queries Wikidata to fetch:
|
| 321 |
+
- The title of the corresponding Wikipedia article in the given language.
|
| 322 |
+
- The article URL.
|
| 323 |
+
- A coarse-grained entity type label ("pers", "org", "loc", or "UNK"),
|
| 324 |
+
derived from the entity's "instance of" (P31) and, if necessary,
|
| 325 |
+
recursively from its "subclass of" (P279) hierarchy.
|
| 326 |
+
|
| 327 |
+
Args:
|
| 328 |
+
qid (str): The Wikidata identifier (e.g., "Q90" for Paris).
|
| 329 |
+
If "NIL" or empty, returns ("NIL", "None", "UNK").
|
| 330 |
+
language (str, optional): ISO language code for the Wikipedia site
|
| 331 |
+
(default is "en"). Example: "fr" for French Wikipedia.
|
| 332 |
+
|
| 333 |
+
Returns:
|
| 334 |
+
tuple:
|
| 335 |
+
- title (str): The Wikipedia page title if available, otherwise "NIL".
|
| 336 |
+
- wiki_url (str): The Wikipedia page URL if available, otherwise "None".
|
| 337 |
+
- type_label (str): A mapped entity type:
|
| 338 |
+
* "pers" → person (Q5, or subclass thereof)
|
| 339 |
+
* "org" → organization (Q43229, Q95074, etc. or subclass thereof)
|
| 340 |
+
* "loc" → location (Q515, Q6256, Q484170, etc. or subclass thereof)
|
| 341 |
+
* "UNK" → unknown if not resolved within recursion depth
|
| 342 |
+
|
| 343 |
+
Notes:
|
| 344 |
+
- Uses `safe_get()` internally for robust requests with retries and
|
| 345 |
+
a proper User-Agent header (required by Wikimedia).
|
| 346 |
+
- Calls `resolve_type_from_wikidata()` to map P31/P279 values into
|
| 347 |
+
coarse-grained types.
|
| 348 |
+
- Limits subclass-of (P279) traversal to `max_depth` defined in
|
| 349 |
+
`resolve_type_from_wikidata` (default=3).
|
| 350 |
+
- Returns gracefully with ("NIL", "None", "UNK") if the QID is invalid
|
| 351 |
+
or Wikidata returns no useful data.
|
| 352 |
+
"""
|
| 353 |
+
|
| 354 |
+
if not qid or qid == "NIL":
|
| 355 |
+
return "NIL", "None", "UNK"
|
| 356 |
+
|
| 357 |
+
url = "https://www.wikidata.org/w/api.php"
|
| 358 |
+
params = {
|
| 359 |
+
"action": "wbgetentities",
|
| 360 |
+
"format": "json",
|
| 361 |
+
"ids": qid,
|
| 362 |
+
"props": "sitelinks/urls|claims",
|
| 363 |
+
"sitefilter": f"{language}wiki",
|
| 364 |
+
}
|
| 365 |
+
|
| 366 |
+
resp = safe_get(url, params)
|
| 367 |
+
if not resp:
|
| 368 |
+
return "NIL", "None", "UNK"
|
| 369 |
+
|
| 370 |
+
try:
|
| 371 |
+
data = resp.json()
|
| 372 |
+
entity = data["entities"][qid]
|
| 373 |
+
|
| 374 |
+
# --- title + url ---
|
| 375 |
+
sitelinks = entity.get("sitelinks", {})
|
| 376 |
+
if f"{language}wiki" in sitelinks:
|
| 377 |
+
title = sitelinks[f"{language}wiki"]["title"]
|
| 378 |
+
wiki_url = sitelinks[f"{language}wiki"]["url"]
|
| 379 |
+
else:
|
| 380 |
+
title, wiki_url = "NIL", "None"
|
| 381 |
+
|
| 382 |
+
# --- detect type from P31 ---
|
| 383 |
+
claims = entity.get("claims", {})
|
| 384 |
+
|
| 385 |
+
if "P31" in claims:
|
| 386 |
+
p31_qids = [
|
| 387 |
+
c["mainsnak"]["datavalue"]["value"]["id"]
|
| 388 |
+
for c in claims["P31"]
|
| 389 |
+
if "datavalue" in c["mainsnak"]
|
| 390 |
+
]
|
| 391 |
+
type_label = resolve_type_from_wikidata(p31_qids)
|
| 392 |
+
else:
|
| 393 |
+
type_label = "UNK"
|
| 394 |
+
|
| 395 |
+
return title, wiki_url, type_label
|
| 396 |
+
|
| 397 |
+
except Exception as e:
|
| 398 |
+
print(f"Error parsing Wikidata for {qid}: {e}")
|
| 399 |
+
return "NIL", "None", "UNK"
|
| 400 |
+
|
| 401 |
+
|
| 402 |
+
class NelPipeline(Pipeline):
|
| 403 |
+
"""
|
| 404 |
+
Hugging Face Pipeline for NEL that supports:
|
| 405 |
+
- Single input: text with [START] ... [END]
|
| 406 |
+
- Batch input: list of texts, each with one mention
|
| 407 |
+
"""
|
| 408 |
+
|
| 409 |
+
def _sanitize_parameters(self, **kwargs):
|
| 410 |
+
preprocess_kwargs = {}
|
| 411 |
+
if "text" in kwargs:
|
| 412 |
+
preprocess_kwargs["text"] = kwargs["text"]
|
| 413 |
+
return preprocess_kwargs, {}, {}
|
| 414 |
+
|
| 415 |
+
def preprocess(self, text, **kwargs):
|
| 416 |
+
"""
|
| 417 |
+
Accepts:
|
| 418 |
+
text → string with [START]...[END]
|
| 419 |
+
text → list[str] (batched)
|
| 420 |
+
Returns:
|
| 421 |
+
list of dict items (one per mention)
|
| 422 |
+
"""
|
| 423 |
+
if isinstance(text, str):
|
| 424 |
+
text_list = [text]
|
| 425 |
+
else:
|
| 426 |
+
text_list = text
|
| 427 |
+
|
| 428 |
+
batch = []
|
| 429 |
+
for t in text_list:
|
| 430 |
+
start_token = "[START]"
|
| 431 |
+
end_token = "[END]"
|
| 432 |
+
|
| 433 |
+
if start_token in t and end_token in t:
|
| 434 |
+
start_idx = t.index(start_token) + len(start_token)
|
| 435 |
+
end_idx = t.index(end_token)
|
| 436 |
+
enclosed = t[start_idx:end_idx].strip()
|
| 437 |
+
lOffset = start_idx
|
| 438 |
+
rOffset = end_idx
|
| 439 |
+
else:
|
| 440 |
+
enclosed, lOffset, rOffset = None, None, None
|
| 441 |
+
|
| 442 |
+
batch.append(
|
| 443 |
+
{"text": t, "surface": enclosed, "lOffset": lOffset, "rOffset": rOffset}
|
| 444 |
+
)
|
| 445 |
+
|
| 446 |
+
return batch
|
| 447 |
+
|
| 448 |
+
def _forward(self, batch):
|
| 449 |
+
"""
|
| 450 |
+
batch is a list of entries from preprocess()
|
| 451 |
+
We batch all texts into one generate() call.
|
| 452 |
+
"""
|
| 453 |
+
texts = [b["text"] for b in batch]
|
| 454 |
+
|
| 455 |
+
enc = self.tokenizer(
|
| 456 |
+
texts, return_tensors="pt", padding=True, truncation=True
|
| 457 |
+
).to(self.device)
|
| 458 |
+
|
| 459 |
+
outputs = self.model.generate(
|
| 460 |
+
**enc,
|
| 461 |
+
num_beams=1,
|
| 462 |
+
num_return_sequences=1,
|
| 463 |
+
max_new_tokens=30,
|
| 464 |
+
return_dict_in_generate=True,
|
| 465 |
+
output_scores=True,
|
| 466 |
+
)
|
| 467 |
+
|
| 468 |
+
# decode predictions
|
| 469 |
+
decoded = self.tokenizer.batch_decode(
|
| 470 |
+
outputs.sequences, skip_special_tokens=True
|
| 471 |
+
)
|
| 472 |
+
|
| 473 |
+
# compute per-sequence confidence
|
| 474 |
+
transition_scores = self.model.compute_transition_scores(
|
| 475 |
+
outputs.sequences, outputs.scores, normalize_logits=True
|
| 476 |
+
)
|
| 477 |
+
|
| 478 |
+
confidences = []
|
| 479 |
+
for i in range(len(decoded)):
|
| 480 |
+
log_prob = sum(transition_scores[i])
|
| 481 |
+
prob = torch.exp(log_prob).cpu().numpy()
|
| 482 |
+
confidences.append(float(prob))
|
| 483 |
+
|
| 484 |
+
# attach outputs to batch
|
| 485 |
+
for i, item in enumerate(batch):
|
| 486 |
+
item["wikipedia_prediction"] = decoded[i]
|
| 487 |
+
item["confidence"] = confidences[i]
|
| 488 |
+
|
| 489 |
+
return batch
|
| 490 |
+
|
| 491 |
+
def postprocess(self, batch, **kwargs):
|
| 492 |
+
"""
|
| 493 |
+
N items → N linked entities
|
| 494 |
+
"""
|
| 495 |
+
|
| 496 |
+
results = []
|
| 497 |
+
for item in batch:
|
| 498 |
+
pred = item["wikipedia_prediction"]
|
| 499 |
+
surface = item["surface"]
|
| 500 |
+
lOffset = item["lOffset"]
|
| 501 |
+
rOffset = item["rOffset"]
|
| 502 |
+
|
| 503 |
+
# Wikidata resolution
|
| 504 |
+
qid, lang = get_wikipedia_page_props(pred)
|
| 505 |
+
title, url, ent_type = get_wikipedia_title(qid, language=lang)
|
| 506 |
+
|
| 507 |
+
# normalize confidence into [0,1]
|
| 508 |
+
conf = item["confidence"]
|
| 509 |
+
if conf > 1:
|
| 510 |
+
conf = conf / 100
|
| 511 |
+
conf = round(conf, 4)
|
| 512 |
+
|
| 513 |
+
results.append(
|
| 514 |
+
{
|
| 515 |
+
"surface": surface,
|
| 516 |
+
"wkd_id": qid,
|
| 517 |
+
"wkpedia_pagename": title,
|
| 518 |
+
"wkpedia_url": url,
|
| 519 |
+
"type": ent_type,
|
| 520 |
+
"confidence_nel": conf,
|
| 521 |
+
# "lOffset": lOffset,
|
| 522 |
+
# "rOffset": rOffset,
|
| 523 |
+
}
|
| 524 |
+
)
|
| 525 |
+
|
| 526 |
+
return results
|
| 527 |
+
|
| 528 |
+
|
| 529 |
+
qid_to_location_label = {
|
| 530 |
+
"Q82794": "geographical region",
|
| 531 |
+
"Q6256": "country",
|
| 532 |
+
"Q3624078": "sovereign state",
|
| 533 |
+
"Q35657": "U.S. state",
|
| 534 |
+
"Q515": "city",
|
| 535 |
+
"Q532": "village",
|
| 536 |
+
"Q3957": "town",
|
| 537 |
+
"Q2983893": "municipality",
|
| 538 |
+
"Q486972": "human settlement",
|
| 539 |
+
"Q23442": "island",
|
| 540 |
+
"Q8502": "mountain",
|
| 541 |
+
"Q23397": "lake",
|
| 542 |
+
"Q4022": "river",
|
| 543 |
+
"Q123705": "peninsula",
|
| 544 |
+
"Q13221722": "bay",
|
| 545 |
+
"Q165": "sea",
|
| 546 |
+
"Q124714": "ocean",
|
| 547 |
+
"Q42124": "continent",
|
| 548 |
+
"Q23413": "territory",
|
| 549 |
+
"Q1048835": "province",
|
| 550 |
+
"Q107390": "county",
|
| 551 |
+
"Q13220204": "district",
|
| 552 |
+
"Q13218630": "subdistrict",
|
| 553 |
+
"Q618123": "location", # geographical object
|
| 554 |
+
"Q2221906": "location", # geographical location
|
| 555 |
+
"Q17334923": "location", # geographical region
|
| 556 |
+
}
|
| 557 |
+
TYPE_MAP = {
|
| 558 |
+
"Q5": "person", # human
|
| 559 |
+
"Q43229": "organization", # organization
|
| 560 |
+
"Q95074": "organization", # company
|
| 561 |
+
"Q783794": "organization", # non-profit
|
| 562 |
+
"Q618123": "location", # geographical object
|
| 563 |
+
"Q2221906": "location", # geographical location
|
| 564 |
+
"Q17334923": "location", # geographical region
|
| 565 |
+
"Q6256": "location", # country
|
| 566 |
+
"Q515": "location", # city
|
| 567 |
+
}
|
| 568 |
+
|
| 569 |
+
qid_to_location_label_keys = set(qid_to_location_label.keys())
|
| 570 |
+
qid_to_organization_label = {
|
| 571 |
+
"Q43229": "organization", # organization
|
| 572 |
+
"Q95074": "company", # company
|
| 573 |
+
"Q783794": "non-profit", # non-profit organization
|
| 574 |
+
"Q891723": "international organization",
|
| 575 |
+
"Q163740": "political party",
|
| 576 |
+
"Q178706": "trade union",
|
| 577 |
+
"Q189004": "government agency",
|
| 578 |
+
"Q327333": "educational institution",
|
| 579 |
+
"Q31855": "university",
|
| 580 |
+
"Q219885": "research institute",
|
| 581 |
+
"Q12973014": "NGO", # non-governmental organization
|
| 582 |
+
"Q7210356": "television network",
|
| 583 |
+
"Q33231": "military unit",
|
| 584 |
+
"Q15911314": "sports club",
|
| 585 |
+
"Q57660343": "media organization",
|
| 586 |
+
"Q167037": "religious organization",
|
| 587 |
+
"Q4438121": "publishing company",
|
| 588 |
+
"Q2088357": "academic publisher",
|
| 589 |
+
}
|
| 590 |
+
qid_to_organization_label_keys = set(qid_to_organization_label.keys())
|
model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f8cfb5bf9aa521336b586ae37eecac31ed7e86327a1be1802d32551472988633
|
| 3 |
+
size 2468961388
|
requirements.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
torch
|
| 2 |
+
transformers
|
| 3 |
+
requests
|
| 4 |
+
requests-cache
|
| 5 |
+
filelock
|
scheduler.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:bc00424e1006d8552c992d3bde1acf8d4282909093c4e18a2112a6e6b087b217
|
| 3 |
+
size 1064
|
sentencepiece.bpe.model
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6ee4dc054a17c18fe81f76c0b1cda00e9fc1cfd9e0f1a16cb6d77009e2076653
|
| 3 |
+
size 4870365
|
special_tokens_map.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"bos_token": {
|
| 3 |
+
"content": "<s>",
|
| 4 |
+
"lstrip": false,
|
| 5 |
+
"normalized": false,
|
| 6 |
+
"rstrip": false,
|
| 7 |
+
"single_word": false
|
| 8 |
+
},
|
| 9 |
+
"cls_token": {
|
| 10 |
+
"content": "<s>",
|
| 11 |
+
"lstrip": false,
|
| 12 |
+
"normalized": false,
|
| 13 |
+
"rstrip": false,
|
| 14 |
+
"single_word": false
|
| 15 |
+
},
|
| 16 |
+
"eos_token": {
|
| 17 |
+
"content": "</s>",
|
| 18 |
+
"lstrip": false,
|
| 19 |
+
"normalized": false,
|
| 20 |
+
"rstrip": false,
|
| 21 |
+
"single_word": false
|
| 22 |
+
},
|
| 23 |
+
"mask_token": {
|
| 24 |
+
"content": "<mask>",
|
| 25 |
+
"lstrip": true,
|
| 26 |
+
"normalized": true,
|
| 27 |
+
"rstrip": false,
|
| 28 |
+
"single_word": false
|
| 29 |
+
},
|
| 30 |
+
"pad_token": {
|
| 31 |
+
"content": "<pad>",
|
| 32 |
+
"lstrip": false,
|
| 33 |
+
"normalized": false,
|
| 34 |
+
"rstrip": false,
|
| 35 |
+
"single_word": false
|
| 36 |
+
},
|
| 37 |
+
"sep_token": {
|
| 38 |
+
"content": "</s>",
|
| 39 |
+
"lstrip": false,
|
| 40 |
+
"normalized": false,
|
| 41 |
+
"rstrip": false,
|
| 42 |
+
"single_word": false
|
| 43 |
+
},
|
| 44 |
+
"unk_token": {
|
| 45 |
+
"content": "<unk>",
|
| 46 |
+
"lstrip": false,
|
| 47 |
+
"normalized": false,
|
| 48 |
+
"rstrip": false,
|
| 49 |
+
"single_word": false
|
| 50 |
+
}
|
| 51 |
+
}
|
tokenizer_config.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"added_tokens_decoder": {
|
| 3 |
+
"0": {
|
| 4 |
+
"content": "<s>",
|
| 5 |
+
"lstrip": false,
|
| 6 |
+
"normalized": false,
|
| 7 |
+
"rstrip": false,
|
| 8 |
+
"single_word": false,
|
| 9 |
+
"special": true
|
| 10 |
+
},
|
| 11 |
+
"1": {
|
| 12 |
+
"content": "<pad>",
|
| 13 |
+
"lstrip": false,
|
| 14 |
+
"normalized": false,
|
| 15 |
+
"rstrip": false,
|
| 16 |
+
"single_word": false,
|
| 17 |
+
"special": true
|
| 18 |
+
},
|
| 19 |
+
"2": {
|
| 20 |
+
"content": "</s>",
|
| 21 |
+
"lstrip": false,
|
| 22 |
+
"normalized": false,
|
| 23 |
+
"rstrip": false,
|
| 24 |
+
"single_word": false,
|
| 25 |
+
"special": true
|
| 26 |
+
},
|
| 27 |
+
"3": {
|
| 28 |
+
"content": "<unk>",
|
| 29 |
+
"lstrip": false,
|
| 30 |
+
"normalized": false,
|
| 31 |
+
"rstrip": false,
|
| 32 |
+
"single_word": false,
|
| 33 |
+
"special": true
|
| 34 |
+
},
|
| 35 |
+
"256001": {
|
| 36 |
+
"content": "<mask>",
|
| 37 |
+
"lstrip": true,
|
| 38 |
+
"normalized": true,
|
| 39 |
+
"rstrip": false,
|
| 40 |
+
"single_word": false,
|
| 41 |
+
"special": true
|
| 42 |
+
}
|
| 43 |
+
},
|
| 44 |
+
"bos_token": "<s>",
|
| 45 |
+
"clean_up_tokenization_spaces": false,
|
| 46 |
+
"cls_token": "<s>",
|
| 47 |
+
"eos_token": "</s>",
|
| 48 |
+
"mask_token": "<mask>",
|
| 49 |
+
"model_max_length": 512,
|
| 50 |
+
"pad_token": "<pad>",
|
| 51 |
+
"sep_token": "</s>",
|
| 52 |
+
"sp_model_kwargs": {},
|
| 53 |
+
"tokenizer_class": "XLMRobertaTokenizer",
|
| 54 |
+
"unk_token": "<unk>"
|
| 55 |
+
}
|
trainer_state.json
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"best_metric": null,
|
| 3 |
+
"best_model_checkpoint": null,
|
| 4 |
+
"epoch": 10.0,
|
| 5 |
+
"eval_steps": 500,
|
| 6 |
+
"global_step": 6480,
|
| 7 |
+
"is_hyper_param_search": false,
|
| 8 |
+
"is_local_process_zero": true,
|
| 9 |
+
"is_world_process_zero": true,
|
| 10 |
+
"log_history": [
|
| 11 |
+
{
|
| 12 |
+
"epoch": 0.7716049382716049,
|
| 13 |
+
"grad_norm": 1.0553990602493286,
|
| 14 |
+
"learning_rate": 1.846913580246914e-05,
|
| 15 |
+
"loss": 0.9346,
|
| 16 |
+
"step": 500
|
| 17 |
+
},
|
| 18 |
+
{
|
| 19 |
+
"epoch": 1.0,
|
| 20 |
+
"eval_bleu": 0.0,
|
| 21 |
+
"eval_gen_len": 10.0959,
|
| 22 |
+
"eval_loss": 0.15309424698352814,
|
| 23 |
+
"eval_runtime": 7.8759,
|
| 24 |
+
"eval_samples_per_second": 154.903,
|
| 25 |
+
"eval_steps_per_second": 2.539,
|
| 26 |
+
"step": 648
|
| 27 |
+
},
|
| 28 |
+
{
|
| 29 |
+
"epoch": 1.5432098765432098,
|
| 30 |
+
"grad_norm": 0.7297214269638062,
|
| 31 |
+
"learning_rate": 1.6925925925925926e-05,
|
| 32 |
+
"loss": 0.0763,
|
| 33 |
+
"step": 1000
|
| 34 |
+
},
|
| 35 |
+
{
|
| 36 |
+
"epoch": 2.0,
|
| 37 |
+
"eval_bleu": 0.0,
|
| 38 |
+
"eval_gen_len": 10.159,
|
| 39 |
+
"eval_loss": 0.16104426980018616,
|
| 40 |
+
"eval_runtime": 7.6405,
|
| 41 |
+
"eval_samples_per_second": 159.674,
|
| 42 |
+
"eval_steps_per_second": 2.618,
|
| 43 |
+
"step": 1296
|
| 44 |
+
},
|
| 45 |
+
{
|
| 46 |
+
"epoch": 2.314814814814815,
|
| 47 |
+
"grad_norm": 0.44237253069877625,
|
| 48 |
+
"learning_rate": 1.5382716049382717e-05,
|
| 49 |
+
"loss": 0.0446,
|
| 50 |
+
"step": 1500
|
| 51 |
+
},
|
| 52 |
+
{
|
| 53 |
+
"epoch": 3.0,
|
| 54 |
+
"eval_bleu": 0.0,
|
| 55 |
+
"eval_gen_len": 10.0426,
|
| 56 |
+
"eval_loss": 0.17489495873451233,
|
| 57 |
+
"eval_runtime": 7.8385,
|
| 58 |
+
"eval_samples_per_second": 155.642,
|
| 59 |
+
"eval_steps_per_second": 2.552,
|
| 60 |
+
"step": 1944
|
| 61 |
+
},
|
| 62 |
+
{
|
| 63 |
+
"epoch": 3.0864197530864197,
|
| 64 |
+
"grad_norm": 0.3801327049732208,
|
| 65 |
+
"learning_rate": 1.3839506172839507e-05,
|
| 66 |
+
"loss": 0.0275,
|
| 67 |
+
"step": 2000
|
| 68 |
+
},
|
| 69 |
+
{
|
| 70 |
+
"epoch": 3.8580246913580245,
|
| 71 |
+
"grad_norm": 0.29495081305503845,
|
| 72 |
+
"learning_rate": 1.2296296296296298e-05,
|
| 73 |
+
"loss": 0.0162,
|
| 74 |
+
"step": 2500
|
| 75 |
+
},
|
| 76 |
+
{
|
| 77 |
+
"epoch": 4.0,
|
| 78 |
+
"eval_bleu": 0.0,
|
| 79 |
+
"eval_gen_len": 10.1139,
|
| 80 |
+
"eval_loss": 0.1843736320734024,
|
| 81 |
+
"eval_runtime": 7.7649,
|
| 82 |
+
"eval_samples_per_second": 157.118,
|
| 83 |
+
"eval_steps_per_second": 2.576,
|
| 84 |
+
"step": 2592
|
| 85 |
+
},
|
| 86 |
+
{
|
| 87 |
+
"epoch": 4.62962962962963,
|
| 88 |
+
"grad_norm": 0.29735738039016724,
|
| 89 |
+
"learning_rate": 1.0753086419753086e-05,
|
| 90 |
+
"loss": 0.0106,
|
| 91 |
+
"step": 3000
|
| 92 |
+
},
|
| 93 |
+
{
|
| 94 |
+
"epoch": 5.0,
|
| 95 |
+
"eval_bleu": 0.0,
|
| 96 |
+
"eval_gen_len": 9.9508,
|
| 97 |
+
"eval_loss": 0.19341909885406494,
|
| 98 |
+
"eval_runtime": 7.6995,
|
| 99 |
+
"eval_samples_per_second": 158.452,
|
| 100 |
+
"eval_steps_per_second": 2.598,
|
| 101 |
+
"step": 3240
|
| 102 |
+
},
|
| 103 |
+
{
|
| 104 |
+
"epoch": 5.401234567901234,
|
| 105 |
+
"grad_norm": 0.07027166336774826,
|
| 106 |
+
"learning_rate": 9.209876543209877e-06,
|
| 107 |
+
"loss": 0.0076,
|
| 108 |
+
"step": 3500
|
| 109 |
+
},
|
| 110 |
+
{
|
| 111 |
+
"epoch": 6.0,
|
| 112 |
+
"eval_bleu": 0.0,
|
| 113 |
+
"eval_gen_len": 9.9377,
|
| 114 |
+
"eval_loss": 0.20017552375793457,
|
| 115 |
+
"eval_runtime": 7.6996,
|
| 116 |
+
"eval_samples_per_second": 158.45,
|
| 117 |
+
"eval_steps_per_second": 2.598,
|
| 118 |
+
"step": 3888
|
| 119 |
+
},
|
| 120 |
+
{
|
| 121 |
+
"epoch": 6.172839506172839,
|
| 122 |
+
"grad_norm": 0.1504916250705719,
|
| 123 |
+
"learning_rate": 7.666666666666667e-06,
|
| 124 |
+
"loss": 0.0059,
|
| 125 |
+
"step": 4000
|
| 126 |
+
},
|
| 127 |
+
{
|
| 128 |
+
"epoch": 6.944444444444445,
|
| 129 |
+
"grad_norm": 0.24264627695083618,
|
| 130 |
+
"learning_rate": 6.123456790123458e-06,
|
| 131 |
+
"loss": 0.0043,
|
| 132 |
+
"step": 4500
|
| 133 |
+
},
|
| 134 |
+
{
|
| 135 |
+
"epoch": 7.0,
|
| 136 |
+
"eval_bleu": 0.0,
|
| 137 |
+
"eval_gen_len": 10.0279,
|
| 138 |
+
"eval_loss": 0.20386986434459686,
|
| 139 |
+
"eval_runtime": 7.7944,
|
| 140 |
+
"eval_samples_per_second": 156.523,
|
| 141 |
+
"eval_steps_per_second": 2.566,
|
| 142 |
+
"step": 4536
|
| 143 |
+
},
|
| 144 |
+
{
|
| 145 |
+
"epoch": 7.716049382716049,
|
| 146 |
+
"grad_norm": 0.08363181352615356,
|
| 147 |
+
"learning_rate": 4.580246913580247e-06,
|
| 148 |
+
"loss": 0.0035,
|
| 149 |
+
"step": 5000
|
| 150 |
+
},
|
| 151 |
+
{
|
| 152 |
+
"epoch": 8.0,
|
| 153 |
+
"eval_bleu": 0.0,
|
| 154 |
+
"eval_gen_len": 10.1566,
|
| 155 |
+
"eval_loss": 0.20531675219535828,
|
| 156 |
+
"eval_runtime": 7.6989,
|
| 157 |
+
"eval_samples_per_second": 158.465,
|
| 158 |
+
"eval_steps_per_second": 2.598,
|
| 159 |
+
"step": 5184
|
| 160 |
+
},
|
| 161 |
+
{
|
| 162 |
+
"epoch": 8.487654320987655,
|
| 163 |
+
"grad_norm": 0.13225023448467255,
|
| 164 |
+
"learning_rate": 3.0370370370370372e-06,
|
| 165 |
+
"loss": 0.0029,
|
| 166 |
+
"step": 5500
|
| 167 |
+
},
|
| 168 |
+
{
|
| 169 |
+
"epoch": 9.0,
|
| 170 |
+
"eval_bleu": 0.0,
|
| 171 |
+
"eval_gen_len": 10.0689,
|
| 172 |
+
"eval_loss": 0.20702147483825684,
|
| 173 |
+
"eval_runtime": 7.6619,
|
| 174 |
+
"eval_samples_per_second": 159.23,
|
| 175 |
+
"eval_steps_per_second": 2.61,
|
| 176 |
+
"step": 5832
|
| 177 |
+
},
|
| 178 |
+
{
|
| 179 |
+
"epoch": 9.25925925925926,
|
| 180 |
+
"grad_norm": 0.022540247067809105,
|
| 181 |
+
"learning_rate": 1.4938271604938272e-06,
|
| 182 |
+
"loss": 0.003,
|
| 183 |
+
"step": 6000
|
| 184 |
+
}
|
| 185 |
+
],
|
| 186 |
+
"logging_steps": 500,
|
| 187 |
+
"max_steps": 6480,
|
| 188 |
+
"num_input_tokens_seen": 0,
|
| 189 |
+
"num_train_epochs": 10,
|
| 190 |
+
"save_steps": 1000,
|
| 191 |
+
"stateful_callbacks": {
|
| 192 |
+
"TrainerControl": {
|
| 193 |
+
"args": {
|
| 194 |
+
"should_epoch_stop": false,
|
| 195 |
+
"should_evaluate": false,
|
| 196 |
+
"should_log": false,
|
| 197 |
+
"should_save": true,
|
| 198 |
+
"should_training_stop": true
|
| 199 |
+
},
|
| 200 |
+
"attributes": {}
|
| 201 |
+
}
|
| 202 |
+
},
|
| 203 |
+
"total_flos": 4.288315707563704e+17,
|
| 204 |
+
"train_batch_size": 64,
|
| 205 |
+
"trial_name": null,
|
| 206 |
+
"trial_params": null
|
| 207 |
+
}
|
utils.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
qid_to_location_label = {
|
| 2 |
+
"Q82794": "geographical region",
|
| 3 |
+
"Q6256": "country",
|
| 4 |
+
"Q3624078": "sovereign state",
|
| 5 |
+
"Q35657": "U.S. state",
|
| 6 |
+
"Q515": "city",
|
| 7 |
+
"Q532": "village",
|
| 8 |
+
"Q3957": "town",
|
| 9 |
+
"Q2983893": "municipality",
|
| 10 |
+
"Q486972": "human settlement",
|
| 11 |
+
"Q23442": "island",
|
| 12 |
+
"Q8502": "mountain",
|
| 13 |
+
"Q23397": "lake",
|
| 14 |
+
"Q4022": "river",
|
| 15 |
+
"Q123705": "peninsula",
|
| 16 |
+
"Q13221722": "bay",
|
| 17 |
+
"Q165": "sea",
|
| 18 |
+
"Q124714": "ocean",
|
| 19 |
+
"Q42124": "continent",
|
| 20 |
+
"Q23413": "territory",
|
| 21 |
+
"Q1048835": "province",
|
| 22 |
+
"Q107390": "county",
|
| 23 |
+
"Q13220204": "district",
|
| 24 |
+
"Q13218630": "subdistrict",
|
| 25 |
+
"Q618123": "location", # geographical object
|
| 26 |
+
"Q2221906": "location", # geographical location
|
| 27 |
+
"Q17334923": "location", # geographical region
|
| 28 |
+
}
|
| 29 |
+
TYPE_MAP = {
|
| 30 |
+
"Q5": "person", # human
|
| 31 |
+
"Q43229": "organization", # organization
|
| 32 |
+
"Q95074": "organization", # company
|
| 33 |
+
"Q783794": "organization", # non-profit
|
| 34 |
+
"Q618123": "location", # geographical object
|
| 35 |
+
"Q2221906": "location", # geographical location
|
| 36 |
+
"Q17334923": "location", # geographical region
|
| 37 |
+
"Q6256": "location", # country
|
| 38 |
+
"Q515": "location", # city
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
qid_to_location_label_keys = set(qid_to_location_label.keys())
|
| 42 |
+
qid_to_organization_label = {
|
| 43 |
+
"Q43229": "organization", # organization
|
| 44 |
+
"Q95074": "company", # company
|
| 45 |
+
"Q783794": "non-profit", # non-profit organization
|
| 46 |
+
"Q891723": "international organization",
|
| 47 |
+
"Q163740": "political party",
|
| 48 |
+
"Q178706": "trade union",
|
| 49 |
+
"Q189004": "government agency",
|
| 50 |
+
"Q327333": "educational institution",
|
| 51 |
+
"Q31855": "university",
|
| 52 |
+
"Q219885": "research institute",
|
| 53 |
+
"Q12973014": "NGO", # non-governmental organization
|
| 54 |
+
"Q7210356": "television network",
|
| 55 |
+
"Q33231": "military unit",
|
| 56 |
+
"Q15911314": "sports club",
|
| 57 |
+
"Q57660343": "media organization",
|
| 58 |
+
"Q167037": "religious organization",
|
| 59 |
+
"Q4438121": "publishing company",
|
| 60 |
+
"Q2088357": "academic publisher",
|
| 61 |
+
}
|
| 62 |
+
qid_to_organization_label_keys = set(qid_to_organization_label.keys())
|