-
Notifications
You must be signed in to change notification settings - Fork 45
/
index.qmd
336 lines (250 loc) · 11.4 KB
/
index.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
---
title: "Quelques éléments pour comprendre les enjeux"
date: 2020-10-15T13:00:00Z
draft: false
weight: 10
slug: nlpintro
type: book
tags:
- NLP
- nltk
- Littérature
- preprocessing
- Tutoriel
categories:
- NLP
- Tutoriel
summary: |
Les corpus textuels étant des objets de très grande dimension
où le ratio signal/bruit est faible, il est nécessaire de mettre
en oeuvre une série d'étapes de nettoyage de texte. Ce chapitre va
explorer quelques méthodes classiques de nettoyage en s'appuyant
sur le _Comte de Monte Cristo_ d'Alexandre Dumas.
---
::: {.cell .markdown}
```{python}
#| echo: false
#| output: 'asis'
#| include: true
#| eval: true
import sys
sys.path.insert(1, '../../../../') #insert the utils module
from utils import print_badges
#print_badges(__file__)
print_badges("content/course/NLP/01_intro.qmd")
```
:::
{{% box status="warning" title="Warning" icon="fa fa-exclamation-triangle" %}}
Le NLP est un domaine immense de recherche. Cette page est une introduction
fort incomplète à la question. Il s'agit de montrer la logique, quelques exemples
avec `Python` <i class="fab fa-python"></i>
et s'amuser avec comme base d'exemple un livre formidable :books: :
*Le Comte de Monte Cristo*
{{% /box %}}
## Base d'exemple
La base d'exemple est le *Comte de Monte Cristo* d'Alexandre Dumas.
Il est disponible
gratuitement sur le site
[Project Gutemberg](http://www.gutenberg.org/ebooks/author/492) comme des milliers
d'autres livres du domaine public. La manière la plus simple de le récupérer
est de télécharger avec le module `urllib` le fichier texte et le retravailler
légèrement pour ne conserver que le corpus du livre :
```{python}
from urllib import request
url = "https://www.gutenberg.org/files/17989/17989-0.txt"
response = request.urlopen(url)
raw = response.read().decode('utf8')
dumas = raw.split("*** START OF THE PROJECT GUTENBERG EBOOK LE COMTE DE MONTE-CRISTO, TOME I ***")[1].split("*** END OF THE PROJECT GUTENBERG EBOOK LE COMTE DE MONTE-CRISTO, TOME I ***")[0]
import re
def clean_text(text):
text = text.lower() # mettre les mots en minuscule
text = " ".join(text.split())
return text
dumas = clean_text(dumas)
dumas[10000:10500]
```
## La particularité des données textuelles
### Objectif
Le *natural language processing* (NLP) ou
*traitement automatisé de la langue* (TAL) en Français, vise à extraire de l'information de textes à partir d'une analyse statistique du contenu.
Cette définition permet d'inclure de nombreux champs d'applications au sein
du NLP (traduction, analyse de sentiment, recommandation, surveillance, etc. ) ainsi que de méthodes.
Cette approche implique de transformer un texte, qui est une information compréhensible par un humain, en un nombre, information appropriée pour un ordinateur et une approche statistique ou algorithmique.
Transformer une information textuelle en valeurs numériques propres à une analyse statistique n'est pas une tâche évidente. Les données textuelles sont **non structurées** puisque l'information cherchée, qui est propre à chaque analyse, est perdue au milieu d'une grande masse d'informations qui doit, de plus, être interprétée dans un certain contexte (un même mot ou une phrase n'ayant pas la même signification selon le contexte).
Si cette tâche n'était pas assez difficile comme ça, on peut ajouter d'autres difficultés propres à l'analyse textuelle car ces données sont :
* bruitées : ortographe, fautes de frappe...
* changeantes : la langue évolue avec de nouveaux mots, sens...
* complexes : structures variables, accords...
* ambigues : synonymie, polysémie, sens caché...
* propres à chaque langue : il n'existe pas de règle de passage unique entre deux langues
* grande dimension : des combinaisons infinies de séquences de mots
### Méthode
L’unité textuelle peut être le mot ou encore une séquence de *n*
mots (un *n-gramme*) ou encore une chaîne de caractères (e.g. la
ponctuation peut être signifiante). On parle de **token**. L’analyse textuelle vise à transformer le texte en données
numériques manipulables.
On peut ensuite utiliser diverses techniques (clustering,
classification supervisée) suivant l’objectif poursuivi pour exploiter
l’information transformée. Mais les étapes de nettoyage de texte sont indispensables car sinon un algorithme sera incapable de détecter une information pertinente dans l'infini des possibles.
## Nettoyer un texte
Les *wordclouds* sont des représentations graphiques assez pratiques pour visualiser
les mots les plus fréquents. Elles sont très simples à implémenter en `Python`
avec le module `wordcloud` qui permet même d'ajuster la forme du nuage à
une image :
```{python}
#| include: false
#| echo: true
import wordcloud
import numpy as np
import io
import requests
import PIL
import matplotlib.pyplot as plt
img = "https://raw.githubusercontent.com/linogaliana/python-datascientist/master/content/course/NLP/book.png"
book_mask = np.array(PIL.Image.open(io.BytesIO(requests.get(img).content)))
fig = plt.figure()
def make_wordcloud(corpus):
wc = wordcloud.WordCloud(background_color="white", max_words=2000, mask=book_mask, contour_width=3, contour_color='steelblue')
wc.generate(corpus)
return wc
plt.imshow(make_wordcloud(dumas), interpolation='bilinear')
plt.axis("off")
#plt.show()
#plt.savefig('word.png', bbox_inches='tight')
```
Cela montre clairement qu'il est nécessaire de nettoyer notre texte. Le nom
du personnage principal, Dantès, est ainsi masqué par un certain nombre
d'articles ou mots de liaison qui perturbent l'analyse. Ces mots sont des
*stop-words*. La librairie `NLTK` (*Natural Language ToolKit*), librairie
de référence dans le domaine du NLP, permet de facilement retirer ces
stopwords (cela pourrait également être fait avec
la librairie plus récente, `spaCy`). Avant cela, il est nécessaire
de transformer notre texte en le découpant par unités fondamentales (les tokens)
### Tokenisation
La tokenisation consiste à découper un texte en morceaux. Ces morceaux
pourraient être des phrases, des chapitres, des n-grammes ou des mots. C'est
cette dernière option que l'on va choisir, plus simple pour retirer les
*stopwords* :
```{python}
import nltk
nltk.download('punkt')
words = nltk.word_tokenize(dumas, language='french')
words[1030:1050]
```
On remarque que les mots avec apostrophes sont liés en un seul, ce qui est
peut-être faux sur le plan de la grammaire mais peu avoir un sens pour une
analyse statistique. Il reste des signes de ponctuation qu'on peut éliminer
avec la méthode `isalpha`:
```{python}
words = [word for word in words if word.isalpha()]
words[1030:1050]
```
{{% box status="hint" title="Hint" icon="fa fa-lightbulb" %}}
Lors de la première utilisation de `NLTK`, il est nécessaire de télécharger
quelques éléments nécessaires à la tokenisation, notamment la ponctuation.
Pour cela,
~~~python
import nltk
nltk.download('punkt')
~~~
{{% /box %}}
### Retirer les stopwords
Le jeu de données est maintenant propre. On peut désormais retirer les
*stop words*.
{{% box status="hint" title="Hint" icon="fa fa-lightbulb" %}}
Lors de la première utilisation de `NLTK`, il est nécessaire de télécharger
les stopwords.
```{python}
import nltk
nltk.download('stopwords')
```
{{% /box %}}
```{python}
from nltk.corpus import stopwords
print(stopwords.words("french"))
stop_words = set(stopwords.words('french'))
words = [w for w in words if not w in stop_words]
print(words[1030:1050])
```
Ces retraitements commencent à porter leurs fruits puisque des mots ayant plus
de sens commencent à se dégager, notamment les noms des personnages
(Fernand, Mercédès, Villefort, etc.)
<!-- KA : ne s'affichait pas sur le site sans que je comprenne pourquoi. J'ai ajouté echo=TRUE... -->
```{python}
#| echo: true
wc = make_wordcloud(' '.join(words))
fig = plt.figure()
plt.imshow(wc, interpolation='bilinear')
plt.axis("off")
```
```{python}
#| echo: false
plt.savefig("featured.png")
```
### *Stemming*
Pour réduire la complexité d'un texte, on peut tirer partie de
"classes d'équivalence" : on peut
considérer que différentes formes d’un même mot (pluriel,
singulier, conjugaison) sont équivalentes et les remplacer par une
même forme dite canonique. Il existe deux approches dans le domaine :
* la **lemmatisation** qui requiert la connaissance des statuts
grammaticaux (exemple : chevaux devient cheval)
* la **racinisation** (*stemming*) plus fruste mais plus rapide, notamment
en présence de fautes d’orthographes. Dans ce cas, chevaux peut devenir chev
mais être ainsi confondu avec chevet ou cheveux
La racinisation est plus simple à mettre en oeuvre car elle peut s'appuyer sur
des règles simples pour extraire la racine d'un mot.
Pour réduire un mot dans sa forme "racine", c'est-à-dire en s'abstrayant des
conjugaisons ou variations comme les pluriels, on applique une méthode de
*stemming*. Le but du *stemming* est de regrouper de
nombreuses variantes d’un mot comme un seul et même mot.
Par exemple, une fois que l’on applique un stemming, "chats" et "chat"
deviennent un même mot.
Cette approche a l'avantage de réduire la taille du vocabulaire à maîtriser
pour l'ordinateur et le modélisateur. Il existe plusieurs algorithmes de
*stemming*, notamment le *Porter Stemming Algorithm* ou le
*Snowball Stemming Algorithm*. Nous pouvons utiliser ce dernier en Français :
```{python}
from nltk.stem.snowball import SnowballStemmer
stemmer = SnowballStemmer(language='french')
stemmed = [stemmer.stem(word) for word in words]
print(stemmed[1030:1050])
```
A ce niveau, les mots commencent à être moins intelligibles par un humain.
La machine prendra le relais, on lui a préparé le travail
{{% box status="note" title="Note" icon="fa fa-comment" %}}
Il existe aussi le stemmer suivant :
~~~python
from nltk.stem.snowball import FrenchStemmer
stemmer = FrenchStemmer()
~~~
{{% /box %}}
### Reconnaissance des entités nommées
Cette étape n'est pas une étape de préparation mais illustre la capacité
des librairies `Python` a extraire du sens d'un texte. La librairie
`spaCy` permet de faire de la reconnaissance d'entités nommées, ce qui peut
être pratique pour extraire rapidement certains personnages de notre oeuvre
~~~python
#!pip install deplacy
#!python -m spacy download fr_core_news_sm
import pkg_resources,imp
imp.reload(pkg_resources)
import spacy
nlp=spacy.load("fr_core_news_sm")
doc = nlp(dumas)
displacy.render(doc, style="ent", jupyter=True)
~~~
## Représentation d'un texte sous forme vectorielle
Une fois nettoyé, le texte est plus propice à une représentation vectorielle.
En fait, implicitement, on a depuis le début adopté une démarche *bag of words*.
Il s'agit d'une représentation, sans souci de contexte (ordre, utilisation),
où chaque *token* représente un élément dans un vocabulaire de taille $|V|$.
On peut ainsi avoir une représentation matricielle les occurrences de
chaque *token* dans plusieurs documents (par exemple plusieurs livres,
chapitres, etc.) pour, par exemple, en déduire une forme de similarité.
Afin de réduire la dimension de la matrice *bag of words*,
on peut s'appuyer sur des pondérations.
On élimine ainsi certains mots très fréquents ou au contraire très rares.
La pondération la plus simple est basée sur la fréquence des mots dans le document.
C'est l'objet de la métrique **tf-idf** (term frequency - inverse document frequency)
abordée dans un prochain chapitre.