"* On utilise la variable `content` qui contient le texte chargé précédemment à partir du fichier"
"* On utilise la variable `arques` qui contient le texte chargé précédemment à partir du fichier"
]
},
{
...
...
@@ -907,7 +908,7 @@
}
],
"source": [
"print(content)"
"print(arques)"
]
},
{
...
...
@@ -923,14 +924,25 @@
"metadata": {},
"outputs": [],
"source": [
"doc = stanza_parser(content)"
"arques_stanza = stanza_parser(arques)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* Afficher la liste des entités nommées repérées :"
"* Afficher la liste des entités nommées repérées. Avec Stanza, le résultat de l'analyse est un itérateur:"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {},
"outputs": [],
"source": [
"def show_ents(stanza_output):\n",
" for ent in stanza_output.ents:\n",
" print(ent.text, ent.type)"
]
},
{
...
...
@@ -951,8 +963,7 @@
}
],
"source": [
"for ent in doc.ents:\n",
" print(ent.text, ent.type)"
"show_ents(arques_stanza)"
]
},
{
...
...
@@ -1029,14 +1040,14 @@
"metadata": {},
"outputs": [],
"source": [
"doc = spacy_parser(content)"
"arques_spacy = spacy_parser(arques)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* Afficher la liste des entités nommées repérées:"
"* Afficher la liste des entités nommées repérées. Les sorties de SpaCy sont dans un format similaire à celui de Stanza mais les étiquettes sont portées par l'attribut `label_` et pas `type`:"
]
},
{
...
...
@@ -1060,7 +1071,7 @@
}
],
"source": [
"for ent in doc.ents:\n",
"for ent in arques_spacy.ents:\n",
" print(ent.text, ent.label_)"
]
},
...
...
@@ -1068,7 +1079,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"* Afficher de manière graphique les entités nommées avec `displaCy` :"
"* Mais SpaCy fournit également une fonction pour effectuer un rendu plus graphique des annotations avec `displaCy` :"
"La largeur de ligne du texte brut, due à la largeur de la colonne dans l'œuvre originale, semble avoir été conservée. Essayons de «lisser» ces caractéristiques pour voir s'il est possible d'améliorer la reconnaissance."
"Cette fois l'entité étendue incluant le nom commun «rivière» a été reconnu par SpaCy, qui a pu ainsi corriger le type de l'entité nommée et se rendre compte que l'Oron était un endroit et pas une personne.\n",
"\n",
"Essayons maintenant avec Stanza."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* Stanza"
"- Stanza"
]
},
{
...
...
@@ -2139,258 +2161,686 @@
"output_type": "stream",
"text": [
"Beaufort LOC\n",
"Géog LOC\n",
"Savoie LOC\n",
"Oron LOC\n"
]
}
],
"source": [
"doc = stanza_parser(beaufort_article)\n",
"for ent in doc.ents:\n",
" print(ent.text, ent.type)"
"beaufort_stanza = stanza_parser(beaufort)\n",
"show_ents(beaufort_stanza)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 6. Geoparsing / Geocoding\n",
"Stanza a directement repéré que l'Oron était un lieu mais veut, comme SpaCy, annoter «Géog» qui ne devrait pas l'être.\n",
"\n",
"En complément de la tâche de reconnaissance des entités nommées la librairie `Perdido` propose également celle de résolution des toponymes, on parle alors de *Geoparsing*. Cette tâche consiste a associer à un nom de lieu des coordonnées géographiques non ambigus. De manière classique elle s'appuie sur le repérage des entités spatiales identifées lors de la reconnaissance des entités nommées et fait appel à des ressources externes de type *gazetier* (ou dictionnaires topographique) pour localiser les lieux."
"Regardons maintenant ce que l'on dit sur la même ville de Beaufort un peu plus d'un siècle plus tard, fin XIXème siecle, dans [La Grande Encyclopédie](https://www.collexpersee.eu/projet/disco-lge/) (LGE)."
]
},
{
"cell_type": "markdown",
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"BEAUFORT ou Beaufort-sur-Doron ou Saint-Maxime-\n",
"de-Bf.aufort. Ch.-l. de cant. du dép. de la Savoie, arr.\n",
"d’Albertville, au débouché de trois vallées dont les tor¬\n",
"rents forment le Doron de Beaufort ; 2,393 hab. Les\n",
"superbes pâturages de la vallée nourrissent de nombreux\n",
"troupeaux ; il se fait à Beaufort un commerce important de\n",
"fromages et de bestiaux. Un assez grand nombre d’habi¬\n",
"tants quittent le pays pendant l’hiver. De l’ancien château\n",
"de la Salle qu’Henri IV habita à deux reprises pendant la\n",
"guerre qu’il soutint contre le duc de Savoie, il subsiste\n",
"trois tours. Ancienne chapelle, reconstruite en 1841,\n",
"qui est le but d’un pèlerinage très fréquenté.\n",
"\n"
]
}
],
"source": [
"### 6.1 Perdido Geoparser"
"lge_beaufort = load('data/beaufort.txt')\n",
"print(lge_beaufort)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* On re-execute Perdido sur l'exemple de l'article `ARQUES`"
"Cette fois l'article est un peu plus long et comporte des césures de lignes importantes, définissons donc une fonction pour recoller les morceaux:"
]
},
{
"cell_type": "code",
"execution_count": 40,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"content = \"* ARQUES, (Géog.) petite ville de France, en Normandie, au pays de Caux, sur la petite riviere d'Arques. Long. 18. 50. lat. 49. 54.\"\n",
"doc = geoparser(content)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* En plus de pouvoir afficher la liste des entités nommées comme nous l'avons fait précédemmment, nous pouvons directement afficher la carte des lieux localisés"
"L'analyse prend plus de temps avec Stanza mais les résultats ont l'air un peu plus précis sur cet exemple. Il y a également une meilleure couverture: Henri IV et 1841 sont annotés, comme avec Perdido, jusqu'à Saint-Maxime-de-Bf.aufort qui a été identifié malgré l'erreur d'OCR, bien que mal classé."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 6. Geoparsing / Geocoding\n",
"\n",
"En complément de la tâche de reconnaissance des entités nommées la librairie `Perdido` propose également celle de résolution des toponymes, on parle alors de *Geoparsing*. Cette tâche consiste a associer à un nom de lieu des coordonnées géographiques non ambigus. De manière classique elle s'appuie sur le repérage des entités spatiales identifées lors de la reconnaissance des entités nommées et fait appel à des ressources externes de type *gazetier* (ou dictionnaires topographique) pour localiser les lieux."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 6.1 Perdido Geoparser"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* Revenons à l'article `ARQUES`"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"* ARQUES, (Géog.) petite ville de France, en Normandie, au pays de Caux, sur la petite riviere d'Arques. Long. 18. 50. lat. 49. 54.\n"
"* En plus de pouvoir afficher la liste des entités nommées comme nous l'avons fait précédemmment, nous pouvons directement afficher la carte des lieux localisés"
Supports pour l'atelier [Librairies Python et Services Web pour la reconnaissance d’entités nommées et la résolution de toponymes](https://anf-tdm-2022.sciencesconf.org/resource/page/id/11) de la formation CNRS [ANF TDM 2022](https://anf-tdm-2022.sciencesconf.org).
**Animateurs**: [Ludovic Moncla](https://ludovicmoncla.github.io)(INSA Lyon) et [Alice Brenon](https://perso.liris.cnrs.fr/abrenon/)(CNRS / INSA Lyon)
## 1. En bref
Dans ce tutoriel, nous allons apprendre plusieurs choses :
- Charger des jeux de données :
- à partir de fichiers txt importés depuis le disque dur ;
- à partir de la librairie Python [Perdido](https://github.com/ludovicmoncla/perdido) dans un [Pandas dataframe](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html)(articles encyclopédiques et descriptions de randonnées).
- Manipuler et interroger un dataframe
- Utiliser les librairies [Stanza](https://stanfordnlp.github.io/stanza/index.html), [spaCy](https://spacy.io) et [Perdido](https://github.com/ludovicmoncla/perdido) pour la reconnaissance d'entités nommées
- afficher les entités nommées annotées ;
- comparer les résultats de `Stanza`, `spaCy` et `Perdido` ;
- discuter les limites des 3 outils pour la tâche de NER.
- Utiliser la librarie `Perdido` pour le geoparsing :
- cartographier les lieux geocodés ;
- illustrer la problématique de désambiguïsation des toponymes.
%% Cell type:markdown id: tags:
## 2. Introduction
%% Cell type:markdown id: tags:
## 3. Configurer l'environnement
### 3.1 Installer les librairies Python
* Si vous avez configuré votre environnement Conda en utilisant le fichier `requirements.txt`, vous pouvez sauter cette étape et aller à la section `3.2 Importer les librairies`.
* Si vous avez configuré votre environnement Conda en utilisant le fichier `environment.yml` ou si vous utilisez un environnement Google Colab / Binder, vous devez installer `perdido` en utilisant `pip` :
%% Cell type:code id: tags:
``` python
!pipinstallperdido
```
%% Cell type:markdown id: tags:
* Si vous avez déjà configuré votre environnement conda, soit avec conda, soit avec pip (voir le fichier readme), vous pouvez ignorer la cellule suivante.
* Si vous exécutez ce notebook depuis Google Colab / Binder, vous devez exécuter la cellule suivante :
%% Cell type:code id: tags:
``` python
!pipinstallstanza
```
%% Cell type:markdown id: tags:
### 3.2 Importer les librairies
Tout d'abord, nous allons charger certaines bibliothèques spécifiques de `Perdido` que nous utiliserons dans ce notebook. Ensuite, nous importons quelques outils qui nous aideront à analyser et à visualiser le texte.
### 4.1 Chargement d'un document texte à partir d'un fichier
%% Cell type:code id: tags:
``` python
filepath='data/volume01-4083.txt'
defload(filepath):
withopen(filepath)asf:
returnf.read()
```
%% Cell type:code id: tags:
``` python
withopen(filepath)asf:
content=f.read()
arques=load('data/volume01-4083.txt')
```
%% Cell type:markdown id: tags:
* Afficher le contenu du fichier
%% Cell type:code id: tags:
``` python
print(content)
print(arques)
```
%% Output
* ARQUES, (Géog.) petite ville de France, en Normandie, au pays de Caux, sur la petite riviere d'Arques. Long. 18. 50. lat. 49. 54.
%% Cell type:markdown id: tags:
### 4.2 Chargement d'un jeu de données à partir de la librairie Perdido
Perdido embarque deux jeux de données :
1. articles encyclopédiques (volume 7 de l'Encyclopédie de Diderot et d'Alembert (1751-1772)), fournit par l'[ARTFL](https://encyclopedie.uchicago.edu) dans le cadre du projet [GEODE](https://geode-project.github.io) ;
2. descriptions de randonnées (chaque description est associée à sa trace GPS. Elles proviennent du site [www.visorando.fr](https://www.visorando.com) et ont été collectées dans le cadre du projet [ANR CHOUCAS](http://choucas.ign.fr).
Dans un premier temps nous allons nous intéresser au jeu de données des articles encyclopédiques. Ce jeu de données est présent dans la librairie en deux versions, une version "brute" (articles fournis par l'ARTFL) au format dataframe et une version déjà annotée par Perdido (format PerdidoCollection). Nous allons charger la version brute et voir comment manipuler un dataframe.
%% Cell type:markdown id: tags:
* Charger le jeu de données :
%% Cell type:code id: tags:
``` python
dataset_artfl=load_edda_artfl()
data_artfl=dataset_artfl['data']
```
%% Cell type:markdown id: tags:
* Afficher les informations sur le jeu de données :
%% Cell type:code id: tags:
``` python
data_artfl.info()
```
%% Output
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3385 entries, 0 to 3384
Data columns (total 7 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 filename 3385 non-null object
1 volume 3385 non-null int64
2 number 3385 non-null int64
3 head 3384 non-null object
4 normClass 3384 non-null object
5 author 3384 non-null object
6 text 3385 non-null object
dtypes: int64(2), object(5)
memory usage: 185.2+ KB
%% Cell type:markdown id: tags:
On remarque que certaines colonnes ont une donnée manquante (3384 lignes non nulles contre 3385 lignes au total). Pour la suite des opérations que nous allons réaliser il est nécessaire de supprimer les lignes incomplètes.
0 unsigned ENCYCLOPÉDIE, ou DICTIONNAIRE RAISONNÉ DES SCI...
1 Bellin FOESNE ou FOUANE, sub. s. (Marine & Pêche.) c'...
2 Bellin Fond de la hune ; ce sont les planches qu on p...
3 Diderot * Fronteau, terme de Sellier-Bourrelier ; c'es...
4 Diderot * FRONTIERE, s. f. (Géog.) se dit des limites,...
%% Cell type:markdown id: tags:
### 4.3 Manipulation d'un dataframe
%% Cell type:markdown id: tags:
Nous avons maintenant accès à tous les attributs et méthodes de l'objet [dataframe](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.html). Par exemple, nous pouvons facilement connaître le nombre de lignes dans notre dataframe qui correspond au nombre d'articles dans notre corpus :
%% Cell type:code id: tags:
``` python
n=data_artfl.shape[0]
print('Il y a '+str(n)+' articles dans le jeu de données.')
```
%% Output
Il y a 3384 articles dans le jeu de données.
%% Cell type:markdown id: tags:
#### 4.3.1 Recherche par métadonnées
Maintenant que les données sont chargées dans un dataframe, nous pouvons sélectionner des groupes d'articles sur la base de leurs métadonnées.
Pour cela on utilise la méthode [loc()](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.loc.html).
%% Cell type:markdown id: tags:
* Selectionner la ligne du dataframe qui correspond à l'article 'FRONTIGNAN' :
5 FRONTIGNAN, (Géog.) petite ville de France. au...
%% Cell type:markdown id: tags:
* Récupérer les valeurs des attributs (colonnes) :
%% Cell type:code id: tags:
``` python
print('filename :',frontignan.filename.item())# similaire à frontignan['normClass'].item()
print('text :',frontignan.text.item())
```
%% Output
filename : volume07-1002.tei
text : FRONTIGNAN, (Géog.) petite ville de France. au Bas-Languedoc, connue par ses excellens vins muscats, & ses raisins de caisse qu'on appelle passerilles. Quelques savans croyent, sans en donner de preuves, que cette ville est le forum Domitii des Romains. Elle est située sur l'étang de Maguelone, à six lieues N. E. d'Agde, & cinq S. O. de Montpellier. Long. 15d. 24'. lat. 43d. 28'. (D. J.)
%% Cell type:markdown id: tags:
Nous pouvons également filtrer les données sur la base de l'auteur.
On peut également regrouper les données selon un ou plusieurs attributs (colonnes) et compter le nombre de données de chaque groupe avec les méthodes [groupby()](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.groupby.html) et [count()](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.count.html).
* Afficher le nombre d'articles classés en Géographie par auteur :
%% Cell type:code id: tags:
``` python
d_geo.groupby(['author'])["filename"].count()
```
%% Output
author
Desmarest 1
Diderot 1
Jaucourt 476
La Condamine 1
Mallet 1
Robert de Vaugondy 2
Robert de Vaugondy & d'Alembert 1
unsigned 13
Name: filename, dtype: int64
%% Cell type:markdown id: tags:
Dans cette partie nous avons vu brievement comment manipuler un dataframe pour selectionner certaines données en filtrant selon certaines métadonnées ou par une recheche par mot clés. Ces opérations sont utiles mais un peu limitées, nous allons voir dans la suite de ce notebook comment enrichir les métadonnées et en particulier comment annoter les entités nommées présents dans les textes.
%% Cell type:markdown id: tags:
## 5. Reconnaissance d'Entités Nommées (NER)
La reconnaissance d'entités nommées, *Named Entity Recognition* (NER) en anglais, est une tâche très importante et incontournable en traitement automatique des langues (TAL) et en compréhension du langage naturel (NLU en anglais).
Cette tâche consiste à rechercher des objets textuels (un mot, ou un groupe de mots, souvent associés aux noms propres) catégorisables dans des classes telles que noms de personnes, noms d'organisations ou d'entreprises, noms de lieux, quantités, distances, valeurs, dates, etc.
Dans cet atelier nous allons expérimenter et comparer trois outils de NER.
`Stanza` est une librairie Python de traitement du langage naturel. Elle contient des outils, qui peuvent être utilisés dans une chaîne de traitement, pour convertir du texte en listes de phrases et de mots, pour générer les formes de base de ces mots, leurs parties du discours et leurs caractéristiques morphologiques, pour produire une analyse syntaxique de dépendance, et pour reconnaître les entités nommées.
`Stanza` se base sur des modèles entrainés par des réseaux de neurones à partir de la bibliothèque [PyTorch](https://pytorch.org) et permet de traiter plus de 70 langues.
Dans cette partie nous allons voir comment utiliser `Stanza` pour la reconnaissance d'entités nommées à partir de textes en français.
%% Cell type:markdown id: tags:
* Importer la librairie `Stanza` et télécharger le modèle pré-entrainé pour le français :
%% Cell type:code id: tags:
``` python
importstanza
stanza.download('fr')
```
%% Output
2022-09-22 15:58:36 INFO: Downloading default packages for language: fr (French)...
* On utilise la variable `content` qui contient le texte chargé précédemment à partir du fichier
* On utilise la variable `arques` qui contient le texte chargé précédemment à partir du fichier
%% Cell type:code id: tags:
``` python
print(content)
print(arques)
```
%% Output
* ARQUES, (Géog.) petite ville de France, en Normandie, au pays de Caux, sur la petite riviere d'Arques. Long. 18. 50. lat. 49. 54.
%% Cell type:markdown id: tags:
* Executer la reconnaissance d'entités nommées :
%% Cell type:code id: tags:
``` python
doc=stanza_parser(content)
arques_stanza=stanza_parser(arques)
```
%% Cell type:markdown id: tags:
* Afficher la liste des entités nommées repérées :
* Afficher la liste des entités nommées repérées. Avec Stanza, le résultat de l'analyse est un itérateur:
%% Cell type:code id: tags:
``` python
defshow_ents(stanza_output):
forentinstanza_output.ents:
print(ent.text,ent.type)
```
%% Cell type:code id: tags:
``` python
forentindoc.ents:
print(ent.text,ent.type)
show_ents(arques_stanza)
```
%% Output
ARQUES LOC
France LOC
Normandie LOC
pays de Caux LOC
Arques LOC
%% Cell type:markdown id: tags:
### 5.2 SpaCy NER
`spaCy` est également une librairie Python de traitement du langage naturel.
Elle se compose de modèles pré-entrainés et supporte actuellement la tokenisation et l'entrainement pour plus de 60 langues. Elle est doté de modèles de réseaux neuronaux pour le balisage, l'analyse syntaxique, la reconnaissance d'entités nommées, la classification de textes, l'apprentissage multi-tâches avec des transformateurs pré-entraînés comme BERT, ainsi qu'un système d'entraînement prêt pour la production et un déploiement simple des modèles. `spaCy` est un logiciel commercial, publié en open-source sous la licence MIT.
Dans cette partie nous allons voir comment utiliser `spaCy` pour la reconnaissance d'entités nommées toujours à partir de notre exemple en français.
%% Cell type:markdown id: tags:
* Installer le modèle français pré-entrainé de `spaCy` :
%% Cell type:code id: tags:
``` python
!python-mspacydownloadfr_core_news_sm
```
%% Cell type:markdown id: tags:
* Importer la librarie `spaCy` :
%% Cell type:code id: tags:
``` python
importspacy
```
%% Cell type:markdown id: tags:
* Charger le modèle français pré-entrainé de `spaCy`
%% Cell type:code id: tags:
``` python
spacy_parser=spacy.load('fr_core_news_sm')
```
%% Cell type:markdown id: tags:
* Executer la reconnaissance d'entités nommées :
%% Cell type:code id: tags:
``` python
doc=spacy_parser(content)
arques_spacy=spacy_parser(arques)
```
%% Cell type:markdown id: tags:
* Afficher la liste des entités nommées repérées:
* Afficher la liste des entités nommées repérées. Les sorties de SpaCy sont dans un format similaire à celui de Stanza mais les étiquettes sont portées par l'attribut `label_` et pas `type`:
%% Cell type:code id: tags:
``` python
forentindoc.ents:
forentinarques_spacy.ents:
print(ent.text,ent.label_)
```
%% Output
ARQUES LOC
Géog LOC
de France LOC
Normandie LOC
pays de Caux LOC
Arques LOC
Long LOC
lat LOC
%% Cell type:markdown id: tags:
*Afficher de manière graphique les entités nommées avec `displaCy` :
*Mais SpaCy fournit également une fonction pour effectuer un rendu plus graphique des annotations avec `displaCy` :
La largeur de ligne du texte brut, due à la largeur de la colonne dans l'œuvre originale, semble avoir été conservée. Essayons de «lisser» ces caractéristiques pour voir s'il est possible d'améliorer la reconnaissance.
Cette fois l'entité étendue incluant le nom commun «rivière» a été reconnu par SpaCy, qui a pu ainsi corriger le type de l'entité nommée et se rendre compte que l'Oron était un endroit et pas une personne.
Essayons maintenant avec Stanza.
%% Cell type:markdown id: tags:
- Stanza
%% Cell type:code id: tags:
``` python
doc=stanza_parser(beaufort_article)
forentindoc.ents:
print(ent.text,ent.type)
beaufort_stanza=stanza_parser(beaufort)
show_ents(beaufort_stanza)
```
%% Output
Beaufort LOC
Géog LOC
Savoie LOC
Oron LOC
%% Cell type:markdown id: tags:
Stanza a directement repéré que l'Oron était un lieu mais veut, comme SpaCy, annoter «Géog» qui ne devrait pas l'être.
Regardons maintenant ce que l'on dit sur la même ville de Beaufort un peu plus d'un siècle plus tard, fin XIXème siecle, dans [La Grande Encyclopédie](https://www.collexpersee.eu/projet/disco-lge/)(LGE).
%% Cell type:code id: tags:
``` python
lge_beaufort=load('data/beaufort.txt')
print(lge_beaufort)
```
%% Output
BEAUFORT ou Beaufort-sur-Doron ou Saint-Maxime-
de-Bf.aufort. Ch.-l. de cant. du dép. de la Savoie, arr.
d’Albertville, au débouché de trois vallées dont les tor¬
rents forment le Doron de Beaufort ; 2,393 hab. Les
superbes pâturages de la vallée nourrissent de nombreux
troupeaux ; il se fait à Beaufort un commerce important de
fromages et de bestiaux. Un assez grand nombre d’habi¬
tants quittent le pays pendant l’hiver. De l’ancien château
de la Salle qu’Henri IV habita à deux reprises pendant la
guerre qu’il soutint contre le duc de Savoie, il subsiste
trois tours. Ancienne chapelle, reconstruite en 1841,
qui est le but d’un pèlerinage très fréquenté.
%% Cell type:markdown id: tags:
Cette fois l'article est un peu plus long et comporte des césures de lignes importantes, définissons donc une fonction pour recoller les morceaux:
L'analyse prend plus de temps avec Stanza mais les résultats ont l'air un peu plus précis sur cet exemple. Il y a également une meilleure couverture: Henri IV et 1841 sont annotés, comme avec Perdido, jusqu'à Saint-Maxime-de-Bf.aufort qui a été identifié malgré l'erreur d'OCR, bien que mal classé.
%% Cell type:markdown id: tags:
## 6. Geoparsing / Geocoding
En complément de la tâche de reconnaissance des entités nommées la librairie `Perdido` propose également celle de résolution des toponymes, on parle alors de *Geoparsing*. Cette tâche consiste a associer à un nom de lieu des coordonnées géographiques non ambigus. De manière classique elle s'appuie sur le repérage des entités spatiales identifées lors de la reconnaissance des entités nommées et fait appel à des ressources externes de type *gazetier* (ou dictionnaires topographique) pour localiser les lieux.
%% Cell type:markdown id: tags:
### 6.1 Perdido Geoparser
%% Cell type:markdown id: tags:
*On re-execute Perdido sur l'exemple de l'article `ARQUES`
*Revenons à l'article `ARQUES`
%% Cell type:code id: tags:
``` python
content="* ARQUES, (Géog.) petite ville de France, en Normandie, au pays de Caux, sur la petite riviere d'Arques. Long. 18. 50. lat. 49. 54."
* ARQUES, (Géog.) petite ville de France, en Normandie, au pays de Caux, sur la petite riviere d'Arques. Long. 18. 50. lat. 49. 54.
%% Cell type:markdown id: tags:
* En plus de pouvoir afficher la liste des entités nommées comme nous l'avons fait précédemmment, nous pouvons directement afficher la carte des lieux localisés
Par défaut, lors de l'instanciation du `Geoparser()`, seul [OpenStreetMap](https://www.openstreetmap.org/) est utilisé pour le geocoding et au maximum un résultat est retourné pour chaque lieu (nous verrons dans la suite comment paramétrer le geocoding).
On a déjà ici un aperçu de la difficulté de la tâche de résolution des toponymes. En effet, un grand nombre d'ambiguïtés existent tels que plusieurs lieux ayant le même nom, plusieurs noms pour un même lieu ou encore le fait qu'un lieu ne soit pas référencé dans les ressources que l'on interroge.
%% Cell type:markdown id: tags:
### 6.2 Perdido Geocoder
En complément du `Geoparser` qui prend en paramètre un texte et qui fait la reconnaissance d'entités nommées en amont de l'étape de geocoding, `Perdido`propose également une fonction de geocoding disctincte prenant en paramètre directement un nom de lieu (ou une liste de noms de lieux).
### 6.2 Résolution de toponymes / désambiguïsation
#### 6.2.1 Exemple : Arques
* Cherchons à localiser la ville `Arques`
%% Cell type:code id: tags:
``` python
geocoder=Geocoder()
doc=geocoder('Arques')
doc.get_folium_map()
```
%% Output
<folium.folium.Map at 0x139498370>
%% Cell type:markdown id: tags:
On remarque que par défaut, la localisation retournée pour le nom de lieu `Arques` n'est pas celle que l'on recherche. En effet, le texte indique qu'il s'agit d'une ville de Normandie hors ici la localisation proposée est située dans le Pas-de-Calais !
Changeons les paramètres du `Geocoder` (ces paramètres sont similaires pour le `Geoparser`) pour essayer de retrouver la bonne localisation.
* Augmenter le nombre de résultats retournés par les gazetiers interrogés
%% Cell type:code id: tags:
``` python
geocoder=Geocoder(max_rows=10)
doc=geocoder('Arques')
doc.get_folium_map()
```
%% Output
<folium.folium.Map at 0x13960ae50>
%% Cell type:markdown id: tags:
On observe parmi les 10 localisations retournées par OpenStreetMap (gazetier par défaut) qu'aucune ne se situe en Normandie.
* Remplacer OpenStreetMap par l'IGN
%% Cell type:code id: tags:
``` python
geocoder=Geocoder(sources=['ign'])
doc=geocoder('Arques')
doc.get_folium_map()
```
%% Output
<folium.folium.Map at 0x1393e1a60>
%% Cell type:markdown id: tags:
On observe que le premier résultat retourné par l'IGN ne se situe ni en Normandie (comme attendu), ni dans le Pas-de-Calais comme le premier résultat retourné par OpenStreetMap.
* Augmenter le nombre de résultats retournés par l'IGN
%% Cell type:code id: tags:
``` python
geocoder=Geocoder(sources=['ign'],max_rows=10)
doc=geocoder('Arques')
doc.get_folium_map()
```
%% Output
<folium.folium.Map at 0x1393e1e80>
%% Cell type:markdown id: tags:
Cette fois-ci on retrouve bien une localisation en Normandie au sud de Dieppe avec pour nom `Arques-la-Bataille'. On peut faire l'hypotèse que le nom a évolué car cette localisation se situe bien dans le Pays de Caux (voir illustration ci-dessous, source [Wikipedia](https://fr.wikipedia.org/wiki/Pays_de_Caux)) comme l'indique le texte de l'article.

%% Cell type:markdown id: tags:
Il reste néanmoins le problème de retrouver cette localisation de manière automatique.
Plusieurs approches existent dans la littérature mais ne sont pas encore implémentées dans `Perdido`.
Cet exemple illustre bien la difficulté de la problématique de désambiguïsation des toponymes avec notamment la gestion des natures de lieux différentes (pays, régions, communes, lieux-dits, lac, rivières, etc.) associés à un même nom, l'homonymie, la non exaustivité des ressources, l'évolution des noms au cours du temps ou encore les erreurs d'orthographe.
%% Cell type:markdown id: tags:
* Afficher la carte obtenue après le geoparsing avec l'IGN et 10 résultats max par nom de lieu
Prenons maintenant l'exemple du geoparsing de descriptions de randonnées. Certaines solutions de désambiguisation ont pu être développées et intégrées au sein de la librairie `Perdido` (d'autres sont en cours d'intégration). Les solutions décrites dans la suite de cette partie ont été développées dans le cadre des projets [Perdido](http://erig.univ-pau.fr/PERDIDO/) (2012-2015) et [ANR CHOUCAS](http://choucas.ign.fr) (2017-2022).
> Ludovic Moncla, Walter Renteria-Agualimpia, Javier Nogueras-Iso and Mauro Gaio (2014). "Geocoding for texts with fine-grain toponyms: an experiment on a geoparsed hiking descriptions corpus". In Proceedings of the 22nd ACM SIGSPATIAL International Conference on Advances in Geographic Information Systems, pp 183-192.
> Mauro Gaio and Ludovic Moncla (2019). “Geoparsing and geocoding places in a dynamic space context.“ In The Semantics of Dynamic Space in French: Descriptive, experimental and formal studies on motion expression, 66, 353.
Nous avons choisi un exemple pour illustrer les différentes phases du processus que nous avons mis en place dans le cadre du geoparsing de descriptions de randonnées :
1. filtrer les résultats en fonction du pays
2. filtrer les résultats en fonction d'une zone géographique définie
3. regrouper les résultats en utilisant un algorithme de clustering spatial (DBSCAN, *density-based spatial clustering of applications with noise*)
4. selectionner le cluster qui contient le plus d'entités distinctes
La librairie Perdido utilise la méthode DBSCAN implémentée dans la librairie [Scikit-Learn](https://scikit-learn.org/stable/modules/generated/sklearn.cluster.DBSCAN.html).
Cette stratégie est adaptée pour une description d'itinéraire où les différents lieux cités sont supposés être localisés à proximité les uns des autres.
%% Cell type:markdown id: tags:
* Charger le jeu de données CHOUCAS de descriptions de randonnées fourni par `Perdido`
%% Cell type:code id: tags:
``` python
dataset_choucas = load_choucas_perdido()
data_choucas = dataset_choucas['data']
data_choucas.to_dataframe().head()
```
%% Output
name \
0 Chalets de la Fullie
1 Traversée cabane de Pravouta à la Plagne
2 Refuge Entre Le Lac - Refuge de la Leisse
3 Le lac du Retour
4 Traversée Alpette - Dent de Crolles
text \
0 \n\nBoucle des chalets de la Fullie au départ ...
1 \n\nPartir de la cabane de Pravouta juste de l...
2 \n\nDépart du refuge d'Entre le Lac près du la...
3 \n\nDu parking de Pierre Giret, suivre la rout...
"\n\nDépart du refuge d'Entre le Lac près du lac de la Plagne.\nDu refuge Entre le Lac, un sentier remonte les pentes herbeuses et permet de rejoindre le GR5 un peu avant le chalet de la Grassaz (chalet du berger 2335m). Toujours en direction du sud, on remonte le vallon en longeant le ruisseau. On parvient ainsi à l'extrémité ouest du lac de Grattaleu; un peu plus haut, on atteint le refuge du col du Palet (2550m). On admire la beauté de la vallée et le sommet de Bellecote recouvert de glaciers. Le GR descend vers l'Est; le sentier serpente entre des entonnoirs créés dans le gypse par dissolution. Le GR passe sous un 1er télésiège, celui de Grattaleu, et près de l'arrivée d'un second, le Tichot. Au chalet de Lognan (croix) prendre à droite un sentier qui descend à Val Claret (2107m) (station de ski). Poursuivre jusqu'au chalet de la Leisse. Le GR55 s'élève vers le vallon du paquis. On passe en contrebas du chalet du Prariond; un peu plus loin on arrive à la bifurcation du col de Fresse (2531m). Laisser à gauche le sentier montant au col de Fresse (2576m), le GR55 continue sud sud ouest dans un décor sauvage entre le massif de la Grande motte et la pointe du Grand Pré; il grimpe parmi des rochers: les balises peuvent être des cairns; bien être attentif pour bien les suivre jusqu'au col de la Leisse (2758 m). Le GR55 descend dans une zone d'éboulis, bien suivre les cairns, par une piste peu visible. Ensuite, il longe le lac des Nettes dans un univers très minéral sur sa rive et pour en atteindre l'extrémité sud. On retrouve dans les alpages le sentier bien tracé qui parcourt le plan des Nettes sur la rive droite du torrent. Peu après un petit barrage qui est en cours de destruction, on arrive au refuge de la Leisse (2487m).\n\n\n\n"
On observe ici le résultat déjà pré-traité par `Perdido`. Nous allons maintenant illustrer le processus de désambiguïsation.
%% Cell type:markdown id: tags:
On recommence le processus de geoparsing en entier à partir du texte de la randonnées choisie.
%% Cell type:code id: tags:
``` python
geoparser=Geoparser()
doc_geoparsed=geoparser(doc.text)
```
%% Cell type:code id: tags:
``` python
doc_geoparsed.get_folium_map()
```
%% Output
<folium.folium.Map at 0x13f038520>
%% Cell type:markdown id: tags:
On voit clairement la différence par rapport au résultat précédent. Nous allons alors essayer de retrouver le même résultat en déroulant les différentes étapes pour désambiguïser avec `Perdido`.
Pour gagner un peu de temps lors des prochaines executions nous allons faire directement appel à la fonction de geocoding à partir de la liste des noms de lieux.
* Récuperer la liste des noms de lieux (sans doublon)
bbox=[5.62216508714297,45.051683489057,7.18563279407213,45.9384576816403]# zone d'intervention du PGHM Isère
# instancier le geocoder avec le code pays et une bounding box
geocoder=Geocoder(country_code='fr',bbox=bbox)
doc_geocoded=geocoder(places_list)
# ajouter la trace GPS
doc_geocoded.geometry_layer=doc.geometry_layer
# affiche la carte
doc_geocoded.get_folium_map()
```
%% Output
<folium.folium.Map at 0x13e6491f0>
%% Cell type:markdown id: tags:
#### 6.3.3 Clustering par densité spatiale
%% Cell type:code id: tags:
``` python
# appliquer la désambiguïsation
doc_geocoded.cluster_disambiguation()
doc_geocoded.get_folium_map()
```
%% Output
<folium.folium.Map at 0x13f0388b0>
%% Cell type:markdown id: tags:
Utilisation du contexte (autres entités nommées repérées dans le texte, relations spatiales, etc...). Développées dans le cadre du projet [Perdido]()(add ref 2014 et 2016) mais pas encore intégré à la librairie Python Perdido. Cette librairie est toujours en cours de développement et d'amélioration. Vos remarques et retours seront les bienvenues.