!pip install numpy pandas spacy transformers scikit-learn langchain_community
Ce chapitre va évoluer prochainement.
1 Introduction
Cette page approfondit certains aspects présentés dans la partie introductive. Nous allons avancer dans notre compréhension des enjeux du NLP grâce à la modélisation du langage.
Nous partirons de la conclusion, évoquée à la fin du précédent chapitre, que les approches fréquentistes présentent plusieurs inconvénients : représentation du langage sur des régularités statistiques indépendantes des proximités entre des mots ou phrases, difficulté à prendre en compte le contexte.
L’objectif de ce chapitre est d’évoquer le premier point. Il s’agira d’une introduction au sujet des embeddings, ces représentations du langage qui sont au fondement des modèles de langage actuels utilisés par des outils entrés dans notre quotidien (DeepL
, ChatGPT
…).
1.1 Données utilisées
Nous allons continuer notre exploration de la littérature avec, à nouveau, les trois auteurs anglophones :
- Edgar Allan Poe, (EAP) ;
- HP Lovecraft (HPL) ;
- Mary Wollstonecraft Shelley (MWS).
Les données sont disponibles sur un CSV mis à disposition sur Github
. L’URL pour les récupérer directement est
https://github.com/GU4243-ADS/spring2018-project1-ginnyqg/raw/master/data/spooky.csv.
Pour rentrer dans le sujet des embeddings, nous allons faire de la modélisation du langage en essayant de prédire les auteurs ayant écrit tel ou tel texte. On parle de modèle de langage pour désigner la représentation d’un texte ou d’une langue sous la forme d’une distribution de probabilités de termes (généralement les mots).
Ce chapitre s’inspire de plusieurs ressources disponibles en ligne:
- Un premier notebook sur
Kaggle
et un deuxième ; - Un dépôt
Github
;
1.2 Packages à installer
Comme dans la partie précédente, il faut télécharger des librairies
spécialiséees pour le NLP, ainsi que certaines de leurs dépendances. Ce TD utilisera plusieurs librairies dont certaines dépendent de PyTorch
qui est une librairie volumineuse.
La prochaine remarque ne concerne que les utilisateurs.trices du SSPCloud
.
Les services Python
standards sur le SSPCloud
(les services vscode-python
et jupyter-python
) ne proposent pas PyTorch
préinstallé. Cette librairie est en effet assez volumineuse (de l’ordre de 600Mo) et nécessite un certain nombre de configurations ad hoc pour fonctionner de manière transparente quelle que soit la configuration logicielle derrière. Pour des raisons de frugalité écologique, cet environnement boosté n’est pas proposé par défaut. Néanmoins, si besoin, un tel environnement où Pytorch
est pré à l’emploi est disponible.
Pour cela, il suffit de démarrer un service vscode-pytorch
ou jupyter-pytorch
. Si vous avez utilisé l’un des boutons disponibles ci-dessus, c’est ce service standardisé qui a automatiquement été mis à disposition pour vous.
Ensuite, comme nous allons utiliser la librairie SpaCy
avec un corpus de textes
en Anglais, il convient de télécharger le modèle NLP pour l’Anglais. Pour cela,
on peut se référer à la documentation de SpaCy
,
extrêmement bien faite.
!python -m spacy download en_core_web_sm
2 Préparation des données
Nous allons à nouveau utiliser le jeu de données spooky
:
import pandas as pd
= "https://github.com/GU4243-ADS/spring2018-project1-ginnyqg/raw/master/data/spooky.csv"
data_url = pd.read_csv(data_url) spooky_df
Le jeu de données met ainsi en regard un auteur avec une phrase qu’il a écrite :
spooky_df.head()
id | text | author | |
---|---|---|---|
0 | id26305 | This process, however, afforded me no means of... | EAP |
1 | id17569 | It never once occurred to me that the fumbling... | HPL |
2 | id11008 | In his left hand was a gold snuff box, from wh... | EAP |
3 | id27763 | How lovely is spring As we looked from Windsor... | MWS |
4 | id12958 | Finding nothing else, not even gold, the Super... | HPL |
2.1 Preprocessing
Comme nous l’avons évoqué dans le chapitre précédent, la première étape de tout travail sur données textuelles est souvent celle du preprocessing, qui inclut notamment les étapes de tokenization et de nettoyage du texte.
On se contentera ici d’un preprocessing minimaliste : suppression de la ponctuation et des stop words (pour la visualisation et les méthodes de vectorisation basées sur des comptages).
Pour initialiser le processus de nettoyage,
on va utiliser le corpus en_core_web_sm
de Spacy
import spacy
= spacy.load("en_core_web_sm") nlp
On va utiliser un pipe
spacy
qui permet d’automatiser, et de paralléliser,
un certain nombre d’opérations. Les pipes sont l’équivalent, en NLP, de
nos pipelines scikit
ou des pipes pandas
. Il s’agit donc d’un outil
très approprié pour industrialiser un certain nombre d’opérations de
preprocessing :
Preprocessing de la base textuelle
from typing import List
import spacy
def clean_docs(
str],
texts: List[bool = False,
remove_stopwords: int = 4,
n_process: bool = True,
remove_punctuation: -> List[str]:
) """
Cleans a list of text documents by tokenizing, optionally removing stopwords, and optionally removing punctuation.
Parameters:
texts (List[str]): List of text documents to clean.
remove_stopwords (bool): Whether to remove stopwords. Default is False.
n_process (int): Number of processes to use for processing. Default is 4.
remove_punctuation (bool): Whether to remove punctuation. Default is True.
Returns:
List[str]: List of cleaned text documents.
"""
# Load spacy's nlp model
= nlp.pipe(
docs =n_process, disable=["parser", "ner", "lemmatizer", "textcat"]
texts, n_process
)
# Pre-load stopwords for faster checking
= set(nlp.Defaults.stop_words)
stopwords
# Process documents
= (
docs_cleaned " ".join(
tok.text.lower().strip()for tok in doc
if (not remove_punctuation or not tok.is_punct)
and (not remove_stopwords or tok.text.lower() not in stopwords)
)for doc in docs
)
return list(docs_cleaned)
On applique la fonction clean_docs
à notre colonne pandas
.
Les pandas.Series
étant itérables, elles se comportent comme des listes et
fonctionnent ainsi très bien avec notre pipe
spacy
.
"text_clean"] = clean_docs(spooky_df["text"]) spooky_df[
spooky_df.head()
id | text | author | text_clean | |
---|---|---|---|---|
0 | id26305 | This process, however, afforded me no means of... | EAP | this process however afforded me no means of a... |
1 | id17569 | It never once occurred to me that the fumbling... | HPL | it never once occurred to me that the fumbling... |
2 | id11008 | In his left hand was a gold snuff box, from wh... | EAP | in his left hand was a gold snuff box from whi... |
3 | id27763 | How lovely is spring As we looked from Windsor... | MWS | how lovely is spring as we looked from windsor... |
4 | id12958 | Finding nothing else, not even gold, the Super... | HPL | finding nothing else not even gold the superin... |
2.2 Encodage de la variable à prédire
On réalise un simple encodage de la variable à prédire :
il y a trois catégories (auteurs), représentées par des entiers 0, 1 et 2.
Pour cela, on utilise le LabelEncoder
de Scikit
déjà présenté
dans la partie modélisation. On va utiliser la méthode
fit_transform
qui permet, en un tour de main, d’appliquer à la fois
l’entraînement (fit
), à savoir la création d’une correspondance entre valeurs
numériques et labels, et l’appliquer (transform
) à la même colonne.
On peut vérifier les classes de notre LabelEncoder
:
array(['EAP', 'HPL', 'MWS'], dtype=object)
2.3 Construction des bases d’entraînement et de test
On met de côté un échantillon de test (20 %) avant toute analyse (même descriptive). Cela permettra d’évaluer nos différents modèles toute à la fin de manière très rigoureuse, puisque ces données n’auront jamais utilisées pendant l’entraînement.
Notre échantillon initial n’est pas équilibré (balanced) : on retrouve plus d’oeuvres de certains auteurs que d’autres. Afin d’obtenir un modèle qui soit évalué au mieux, nous allons donc stratifier notre échantillon de manière à obtenir une répartition similaire d’auteurs dans nos ensembles d’entraînement et de test.
from sklearn.model_selection import train_test_split
= spooky_df["author"]
y = spooky_df["text_clean"]
X = train_test_split(
X_train, X_test, y_train, y_test =0.2, stratify=y, random_state=42
X, y, test_size )
Aperçu du premier élément de X_train
:
'this process however afforded me no means of ascertaining the dimensions of my dungeon as i might make its circuit and return to the point whence i set out without being aware of the fact so perfectly uniform seemed the wall'
3 Vectorisation par l’approche bag of words
La représentation de nos textes sous forme de sac de mot permet de vectoriser notre corpus et ainsi d’avoir une représentation numérique de chaque texte. On peut à partir de là effectuer plusieurs types de tâches de modélisation.
Définissons notre représentation vectorielle par TF-IDF grâce à Scikit
:
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.pipeline import Pipeline
= Pipeline(
pipeline_tfidf
["tfidf", TfidfVectorizer(max_features=10000)),
(
]
) pipeline_tfidf
Pipeline(steps=[('tfidf', TfidfVectorizer(max_features=10000))])In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
Pipeline(steps=[('tfidf', TfidfVectorizer(max_features=10000))])
TfidfVectorizer(max_features=10000)
Entraînons d’ores et déjà notre modèle à vectoriser le texte à partir de la méthode TF-IDF. Pour le moment il n’est pas encore question de faire de l’évaluation, faisons donc un entraînement sur l’ensemble de notre base et pas seulement sur X_train
.
"text_clean"]) pipeline_tfidf.fit(spooky_df[
Pipeline(steps=[('tfidf', TfidfVectorizer(max_features=10000))])In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
Pipeline(steps=[('tfidf', TfidfVectorizer(max_features=10000))])
TfidfVectorizer(max_features=10000)
3.1 Trouver le texte le plus similaire
En premier lieu, on peut chercher le texte le plus proche, au sens de TF-IDF, d’une phrase donnée. Prenons cet exemple:
= "He was afraid by Frankenstein monster" text
Comment retrouver le texte le plus proche de celui-ci ? Il faudrait transformer notre texte dans cette même représentation vectorielle et rapprocher ensuite celui-ci des autres textes représentés sous cette même forme.
Cela revient à effectuer une tâche de recherche d’information, cas d’usage classique du NLP, mis en oeuvre par exemple par les moteurs de recherche. Le terme Frankenstein étant assez discrminant, nous devrions, grâce à TF-IDF, retrouver des similarités entre notre texte et d’autres textes écrits par Mary Shelley.
Une métrique régulièrement utilisée pour comparer des vecteurs est la similarité cosinus. Il s’agit d’ailleurs d’une mesure centrale dans l’approche moderne du NLP. Celle-ci a plus de sens avec des vecteurs dense, que nous verrons prochainement, qu’avec des vecteurs comportant beaucoup de 0 comme le sont les vecteurs sparse des mesures bag-of-words. Néanmoins c’est déjà un exercice intéressant pour comprendre la similarité entre deux vecteurs.
Si chaque dimension d’un vecteur correspond à une direction, l’idée derrière la similarité cosinus est de mesurer l’angle entre deux vecteurs. L’angle sera réduit si les vecteurs sont proches.
3.1.1 Avec Scikit-Learn
Utiliser la méthode
transform
pour vectoriser tout notre corpus d’entraînement.En supposant que votre jeu d’entraînement vectorisé s’appelle
X_train_tfidf
, vous pouvez le transformer en DataFrame avec la commande suivante:
= pd.DataFrame(
X_train_tfidf =pipeline_tfidf.get_feature_names_out()
X_train_tfidf.todense(), columns )
- Utiliser la méthode
cosine_similarity
deScikit
pour calculer la similarité cosinus entre notre texte vectorisé et l’ensemble du corpus d’entraînement grâce au code suivant:
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
= cosine_similarity(
cosine_similarities
X_train_tfidf, pipeline_tfidf.transform([text])
).flatten()
= np.argsort(cosine_similarities)[-4:][
top_4_indices -1
::# Sort and reverse for descending order
] = cosine_similarities[top_4_indices] top_4_similarities
- Retrouver les documents concernés. Êtes-vous satisfait du résultat ? Comprenez-vous ce qu’il s’est passé ?
A l’issue de l’exercice, les 4 textes les plus similaires sont:
text | author | score | |
---|---|---|---|
8181 | Listen to me, Frankenstein. | MWS | 0.402964 |
8606 | He was gazing at me gaspingly and fascinatedly... | HPL | 0.330177 |
14550 | The light is dimmer and the gods are afraid. . . | HPL | 0.314670 |
11366 | I screamed aloud that I was not afraid; that I... | HPL | 0.311235 |
3.1.2 Avec Langchain
Cette approche de calcul de similarité textuelle est assez laborieuse avec Scikit
. Avec le développement continu d’applications Python
utilisant des modèles de langage, un écosystème très complet s’est développé pour pouvoir faire ces tâches en quelques lignes de code avec Python
.
Parmi les outils les plus précieux, nous trouvons Langchain
un écosystème Python
haut-niveau permettant de construire des chaînes de production utilisant des données textuelles.
Nous allons ici procéder en 2 étapes:
- Créer un retriever, c’est-à-dire vectoriser avec TF-IDF notre corpus (les textes de nos trois auteurs) et les stocker sous forme de base de données vectorielle.
- Vectoriser à la volée notre texte de recherche (l’object
text
créé précédemment) et rechercher sa contrepartie la plus proche dans la base de données vectorielle.
La vectorisation de notre corpus se fait très simplement grâce à Langchain
puisque le TFIDFVectoriser de Scikit
est encapsulé dans un module ad hoc de Lanchain
from langchain_community.retrievers import TFIDFRetriever
from langchain_community.document_loaders import DataFrameLoader
= DataFrameLoader(spooky_df, page_content_column="text_clean")
loader
= TFIDFRetriever.from_documents(loader.load()) retriever
Cet objet retriever
est un point d’entrée sur notre corpus. Langchain
présente l’intérêt de fournir plusieurs points d’entrées standardisés, forts utiles dans les projets de NLP puisqu’il suffit de changer les vectoriseurs en entrée sans avoir à changer leur usage en fin de chaîne.
La méthode invoke
permet de rechercher les vecteurs les plus similaires à notre texte de recherche:
retriever.invoke(text)
[Document(metadata={'id': 'id12587', 'text': 'Listen to me, Frankenstein.', 'author': 'MWS', 'author_encoded': 2}, page_content='listen to me frankenstein'),
Document(metadata={'id': 'id09284', 'text': 'I screamed aloud that I was not afraid; that I never could be afraid; and others screamed with me for solace.', 'author': 'HPL', 'author_encoded': 1}, page_content='i screamed aloud that i was not afraid that i never could be afraid and others screamed with me for solace'),
Document(metadata={'id': 'id09797', 'text': 'It seemed to be a sort of monster, or symbol representing a monster, of a form which only a diseased fancy could conceive.', 'author': 'HPL', 'author_encoded': 1}, page_content='it seemed to be a sort of monster or symbol representing a monster of a form which only a diseased fancy could conceive'),
Document(metadata={'id': 'id10816', 'text': 'And, as I have implied, it was not of the dead man himself that I became afraid.', 'author': 'HPL', 'author_encoded': 1}, page_content='and as i have implied it was not of the dead man himself that i became afraid')]
La sortie est un objet Lanchain
, ce qui n’est pas pratique pour nous dans notre situation. On se ramène à un DataFrame:
= []
documents for best_echoes in retriever.invoke(text):
+= [{**best_echoes.metadata, **{"text_clean": best_echoes.page_content}}]
documents
= pd.DataFrame(documents) documents
On peut ajouter à ce DataFrame la colonne de score:
On retrouve bien les mêmes documents:
id | text | author | author_encoded | text_clean | score | |
---|---|---|---|---|---|---|
0 | id12587 | Listen to me, Frankenstein. | MWS | 2 | listen to me frankenstein | 0.402964 |
1 | id09284 | I screamed aloud that I was not afraid; that I... | HPL | 1 | i screamed aloud that i was not afraid that i ... | 0.311235 |
2 | id09797 | It seemed to be a sort of monster, or symbol r... | HPL | 1 | it seemed to be a sort of monster or symbol re... | 0.295587 |
3 | id10816 | And, as I have implied, it was not of the dead... | HPL | 1 | and as i have implied it was not of the dead m... | 0.261818 |
BM25 est un modèle de récupération d’informations basé sur la pertinence probabiliste, au même titre que TF-IDF. BM25 est souvent utilisée dans les moteurs de recherche pour classer les documents par rapport à une requête.
BM25 repose sur une combinaison de la fréquence des termes (TF), la fréquence inverse des documents (IDF), et une normalisation basée sur la longueur des documents. Autrement dit, il s’agit de tenir compte d’améliorer TF-IDF tout en normalisant les mesures en fonction de la taille des strings afin de ne pas surpondérer les grands documents.
BM25 est donc particulièrement performant dans des environnements où les documents varient en longueur et en contenu. C’est pour cette raison que des moteurs de recherche comme Elasticsearch
en ont fait une pierre angulaire du mécanisme de recherche.
Pourquoi ne sont-ils pas tous pertinents ? On peut anticiper plusieurs raisons à cela.
La première hypothèse vient du fait qu’on entraîne notre vectoriseur sur un corpus biaisé. Certes Frankestein est un terme rare mais il est beaucoup plus fréquent dans notre corpus que dans la langue anglaise. L’inverse document frequency est donc biaisée en défaveur de ce terme: son apparition devrait être un signe beaucoup plus fort que le texte recherché correspond à Mary Shelley. Si cela peut améliorer un peu la pertinence des résultats renvoyés, ce n’est néanmoins pas là que le bât blesse.
L’approche fréquentiste suppose que les termes sont aussi dissemblables les uns que les autres. Une phrase où apparaît le terme “créature” ne bénéficiera pas d’un score positif si on recherche “monstre”. De plus, là encore, nous avons pris notre corpus comme un sac où les mots sont indépendants: on n’a pas plus de chance de tirer “Frankenstein” après “docteur”. Ces limites vont nous amener vers le sujet des embeddings. Néanmoins, si l’approche fréquentiste est un peu old school, elle n’est néanmoins pas inutile et représente souvent une “tough to beat baseline”. Dans les domaines de l’extraction d’information avec des textes courts, où chaque terme est porteur d’un signal fort, cette approche est souvent judicieuse.
3.2 Trouver l’auteur le plus proche: une introduction au classifieur naif Bayes
Avant d’explorer les embeddings, nous pouvons essayer d’avoir un cas d’usage un petit peu différent dans notre cadre probabiliste. Supposons qu’on désire maintenant faire de la prédiction d’auteur. Si l’intuition précédente est vraie - certains mots sont plus probables dans les textes de certains auteurs - cela veut dire qu’on peut entraîner un algorithme de classification automatique à prédire un auteur à partir d’un texte.
La méthode la plus naturelle pour se lancer dans cette approche est d’utiliser le classifieur naif de Bayes. Ce dernier est parfaitement adapté à l’approche fréquentiste que nous avons adoptée jusqu’à présent puisqu’il exploite les probabilités d’occurrence de mots par auteur.
Le classifieur naif de Bayes consiste à appliquer une règle de décision, à savoir sélectionner la classe la plus probable sachant la structure observée du document, c’est-à-dire les mots apparaissant dans celui-ci.
Autrement dit, on sélectionne la classe \(\widehat{c}\) qui est la plus probable, sachant les termes dans le document \(d\).
\[ \widehat{c} = \arg \max_{c \in \mathcal{C}} \mathbb{P}(c|d) = \arg \max_{c \in \mathcal{C}} \frac{ \mathbb{P}(d|c)\mathbb{P}(c)}{\mathbb{P}(d)} \tag{3.1}\]
Comme ceci est classique en estimation bayésienne, on peut se passer de certains termes constants, à savoir \(\mathbb{P}(d)\). La définition de la classe estimée peut ainsi être reformulée de cette manière:
\[ \widehat{c} = \arg \max_{c \in \mathcal{C}} \mathbb{P}(d|c)\mathbb{P}(c) \tag{3.2}\]
L’hypothèse du sac de mot intervient à ce niveau. Un document \(d\) est une collection de mots \(w_i\) dont l’ordre n’a pas d’intérêt. Autrement dit, on peut se contenter de faire un modèle sur les mots, sans faire intervenir des probabilités conditionnelles sur l’ordre d’occurrence. La seconde hypothèse forte est l’hypothèse naive à laquelle la méthode doit son nom: la probabilité de tirer un mot ne dépend que de la catégorie \(c\) d’appartenance du document. Autrement dit, on peut considérer qu’un document est une suite de tirage indépendants de mots dont la probabilité ne dépend que de l’auteur.
Comme cela est expliqué dans la boite dédiée, en faisant ces hypothèses, on peut réécrire ce classifieur sous la forme
\[ \widehat{c} = \arg \max_{c \in \mathcal{C}} \mathbb{P}(c)\prod_{w \in \mathcal{W}}{\mathbb{P}(w|c)} \]
avec \(\mathcal{W}\) l’ensemble des mots dans le corpus (notre vocabulaire).
Empiriquement, nous sommes dans une tâche d’apprentissage supervisé où le label est la classe du document et les features sont nos mots vectorisés. Empiriquement, les probabilités sont estimées à partir du dénombrement des mots dans le corpus et des types de documents dans le corpus.
Il est bien-sûr possible de calculer toutes ces grandeurs à la main mais Scikit
permet d’implémenter un estimateur naif de Bayes après avoir vectorisé son corpus comme le montre le prochain exercice. Cela peut néanmoins poser un problème pratique: en principe, le corpus de test ne doit pas comporter de nouveaux mots car ces “nouvelles” dimensions n’étaient pas présentes dans le corpus d’entraînement. En pratique, la solution la plus simple est souvent celle choisie: ces mots sont ignorés.
- En repartant de l’exemple précédent, définir un pipeline qui vectorise chaque document (utiliser
CountVectorizer
plutôt queTFIDFVectorizer
) et effectue une prédiction grâce à un modèle naif de Bayes. - Entraîner ce modèle, faire une prédiction sur le jeu de test.
- Evaluer la performance de votre modèle
- Faire une prédiction sur la phrase que nous avons utilisée tout à l’heure dans la variable
text
. Obtenez-vous ce qui était attendu ? - Regarder les probabilités obtenues (méthode
predict_proba
).
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB
= Pipeline(
pipeline "vectorizer", CountVectorizer()), ("classifier", MultinomialNB())]
[(
)
# Train the pipeline on the training data
pipeline.fit(X_train, y_train)
Pipeline(steps=[('vectorizer', CountVectorizer()), ('classifier', MultinomialNB())])In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
Pipeline(steps=[('vectorizer', CountVectorizer()), ('classifier', MultinomialNB())])
CountVectorizer()
MultinomialNB()
On obtient une précision satisfaisante:
Précision: 0.8429519918283963
Les performances décomposées sont les suivantes:
precision recall f1-score support
EAP 0.85 0.85 0.85 1580
HPL 0.87 0.82 0.84 1127
MWS 0.81 0.86 0.83 1209
accuracy 0.84 3916
macro avg 0.84 0.84 0.84 3916
weighted avg 0.84 0.84 0.84 3916
Sans surprise, on obtient bien la prédiction de Mary Shelley:
'MWS'
Finalement, si on regarde les probabilités estimées (question 5), on se rend compte que la prédiction est très certaine:
author | proba | |
---|---|---|
0 | EAP | 0.001675 |
1 | HPL | 0.084844 |
2 | MWS | 0.913481 |
Supposons que nous sommes dans un problème de classification avec des classes $(c_1,…,c_K) (ensemble noté \(\mathcal{C}\)). Nous plaçant dans le cadre de pensée du sac de mot, nous pouvons ne pas nous préoccuper des positions des mots dans les documents, qui complexifieraient beaucoup l’écriture de nos équations.
L’équation Équation 3.2 peut être réécrite
\[ \widehat{c} = \arg \max_{c \in \mathcal{C}} \mathbb{P}(w_1, ..., w_n|c)\mathbb{P}(c) \]
Dans le monde bayésien, on nomme \(\mathbb{P}(w_1, ..., w_n|c)\) la vraisemblance (likelihood) et \(\mathbb{P}(c)\) l’a priori (prior).
L’hypothèse Bayes naive permet de traiter un document comme une suite de tirages aléatoires dont les probabilités ne dépendent que de la catégorie. Dans ce cas, le tirage d’une phrase est une suite de tirages de mots et la probabilité composée est donc
\[ \mathbb{P}(w_1, ..., w_n|c) = \prod_{i=1}^n \mathbb{P}(w_i|c) \]
Par exemple, en simplifiant en deux classes, si les probabilités sont celles du Table 3.1, la phrase “afraid by Doctor Frankenstein” aura un peu moins de 1% de chance (0.8%) d’être écrite si l’autrice est Mary Shelley mais sera encore moins vraisemblable chez Lovecraft (0.006%) car si “afraid” est très probable chez lui, Frankenstein est un événement rare qui rend peu vraisemblable cette composition de mots.
Mot (\(w_i\)) | Probabilité chez Mary Shelley | Probabilité chez Lovecraft |
---|---|---|
Afraid | 0.1 | 0.6 |
By | 0.2 | 0.2 |
Doctor | 0.2 | 0.05 |
Frankenstein | 0.2 | 0.01 |
En composant ces différentes équations, on obtient
\[ \widehat{c} = \arg \max_{c \in \mathcal{C}} \mathbb{P}(c)\prod_{w \in \mathcal{W}}{\mathbb{P}(w|c)} \]
La contrepartie empirique de \(\mathbb{P}(c)\) est assez évidente: la fréquence observée de chaque catégorie (les auteurs) dans notre corpus. Autrement dit,
\[ \widehat{\mathbb{P}(c)} = \frac{n_c}{n_{doc}} \]
Quelle est la contrepartie empirique de \(\mathbb{P}(w_i|c)\) ? C’est la fréquence d’apparition du mot en question chez l’auteur. Pour le calculer, il suffit de compter le nombre de fois qu’il apparaît chez l’auteur et de diviser par le nombre de mots de l’auteur.
4 Le modèle Word2Vec, une représentation plus synthétique
4.1 Vers une représentation plus synthétique du langage
La représentation vectorielle issue de l’approche bag of words n’est pas très synthétique ni stable et surtout est assez frustre.
Si on a un petit corpus, on va avoir des problèmes à extrapoler puisque de nouveaux textes ont toutes les chances d’apporter de nouveaux mots, qui sont de nouvelles dimensions de features qui n’étaient pas présentes dans le corpus d’entraînement, ce qui conceptuellement est un problème puisque les algorithmes de machine learning n’ont pas vocation à prédire sur des caractéristiques sur lesquelles ils n’ont pas été entraîné1.
A l’inverse, plus on a de texte dans un corpus, plus notre représentation vectorielle sera importante. Par exemple, si votre sac de mot a vu tout le vocabulaire français, soit 60 000 mots selon l’Académie Française (les estimations étant de 200 000 pour la langue anglaise), cela fait des vecteurs de taille conséquente. Cependant, la diversité des textes est, en pratique, bien moindre: l’usage courant du Français nécessite plutôt autour de 3000 mots et la plupart des textes, notamment s’ils sont courts, n’utilisent pas un vocabulaire si complet. Ceci implique donc des vecteurs très peu denses, avec beaucoup de 0.
La vectorisation selon cette approche est donc peu efficace; le signal est peu compressé. Des représentations denses, c’est-à-dire de dimension plus faible mais portant toutes une information, semblent plus adéquate pour pouvoir généraliser notre modélisation du langage.
L’algorithme qui a rendu célèbre cette approche est le modèle Word2Vec
, en quelques sortes le premier ancêtre commun des LLM modernes. La représentation vectorielle de Word2Vec
est assez synthétique: la dimension de ces embeddings est entre 100 et 300.
4.2 Des relations sémantique entre les termes
Cette représentation dense va représenter une solution à une limite de l’approche bag of words que nous avons évoquée à de multiples reprises. Chacune de ces dimensions va représenter un facteur latent, c’est à dire une variable inobservée, de la même manière que les composantes principales produites par une ACP. Ces dimensions latentes peuvent être interprétées comme des dimensions “fondamentales” du langage
Par exemple, un humain sait qu’un document contenant le mot “Roi”
et un autre document contenant le mot “Reine” ont beaucoup de chance
d’aborder des sujets semblables. Un modèle Word2Vec
bien entraîné va capter
qu’il existe un facteur latent de type “royauté”
et la similarité entre les vecteurs associés aux deux mots sera forte.
La magie va même plus loin : le modèle captera aussi qu’il existe un facteur latent de type “genre”, et va permettre de construire un espace sémantique dans lequel les relations arithmétiques entre vecteurs ont du sens. Par exemple,
\[ \text{king} - \text{man} + \text{woman} ≈ \text{queen} \]
ou, pour reprendre, l’exemple issu du papier originel Word2Vec
(Mikolov 2013),
\[ \text{Paris} - \text{France} + \text{Italy} ≈ \text{Rome} \]
Un autre “miracle” de cette approche est qu’on obtient une forme de transfert entre les langues. Les relations sémantiques pouvant être similaires entre les langues, pour de nombreux mots usuels, on peut voir translater certaines langues les unes avec les autres si elles ont un socle commun (par exemple les langues occidentales). Ce concept est le point de départ des traducteurs automatiques et des IA multilingues
4.4 Modèles liés
Plusieurs modèles ont une filiation directe avec le modèle Word2Vec
quoiqu’ils s’en distinguent par la nature de l’architecture utilisée.
C’est le cas, par exemple, du modèle modèle GloVe
, développé en 2014 à Stanford,
qui ne repose pas sur des réseaux de neurones mais sur la construction d’une grande matrice de co-occurrences de mots. Pour chaque mot, il s’agit de calculer les fréquences d’apparition des autres mots dans une fenêtre de taille fixe autour de lui. La matrice de co-occurrences obtenue est ensuite factorisée par une décomposition en valeurs singulières.
Le modèle FastText
, développé en 2016 par une équipe de Facebook
, fonctionne de façon similaire à Word2Vec
mais se distingue particulièrement sur deux points :
- En plus des mots eux-mêmes, le modèle apprend des représentations pour les n-grams de caractères (sous-séquences de caractères de taille \(n\), par exemple « tar », « art » et « rte » sont les trigrammes du mot « tarte »), ce qui le rend notamment robuste aux variations d’orthographe ;
- Le modèle a été optimisé pour que son entraînement soit particulièrement rapide.
Le modèle FastText
est particulièrement performant pour les problématiques de classification automatique. L’Insee l’utilise par exemple pour plusieurs modèles de classification de libellés textuels dans des nomenclatures.
Voici un exemple sur un projet de classification automatisée des professions dans la typologie
des nomenclatures d’activités (les PCS) à partir d’un modèle entraîné par la librairie Fasttext
:
Ces modèles sont héritiers de Word2Vec
dans le sens où ils reprennent une représentation vectorielle dense de faible dimension de documents textuels. Word2Vec
reste un modèle héritier de la logique sac de mot. La représentation d’une phrase ou d’un document est une forme de moyenne des représentations des mots qui les composent.
Depuis 2013, plusieurs révolutions ont amené à enrichir les modèles de langage pour aller au-delà d’une représentation par mot de ceux-ci. Des architectures beaucoup plus complexes pour représenter non seulement les mots sous forme d’embeddings mais aussi les phrases et les documents sont aujourd’hui à l’oeuvre et peuvent être reliées à la révolution des architectures transformers.
5 Les transformers: une représentation plus riche du langage
Si le modèle Word2Vec
est entraîné de manière contextuelle, sa vocation est de donner une représentation vectorielle d’un mot de manière absolue, indépendamment du contexte. Par exemple, le terme “banc” aura exactement la même représentation vectorielle qu’il se trouve dans la phrase “Elle court vers le banc de sable” ou “Il t’attend sur un banc au parc”_. C’est une limite majeure de ce type d’approche et on se doute bien de l’importance du contexte pour l’interprétation du langage.
L’objectif des architectures transformers est de permettre des représentations vectorielles contextuelles. Autrement dit, un mot aura plusieurs représentations vectorielles, selon son contexte d’occurrence. Ces modèles s’appuient sur le mécanisme d’attention (Vaswani 2017). Avant cette approche, lorsqu’un modèle apprenait à vectoriser un texte et qu’il arrivait au énième mot, la seule mémoire qu’il gardait était celle du mot précédent. Par récurrence, cela signifiait qu’il gardait une mémoire des mots précédents mais celle-ci tendait à se dissiper. Par conséquent, pour un mot arrivant loin dans la phrase, il était probable que le contexte de début de phrase était oublié. Autrement dit, dans la phrase “à la plage, il allait explorer le banc”, il était fort probable qu’arrivé au mot “banc”, le modèle ait oublié le début de phrase qui avait pourtant de l’importance pour l’interprétation.
L’objectif du mécanisme d’attention est de créer une mémoire interne au modèle permettant, pour tout mot d’un texte, de pouvoir garder trace des autres mots. Bien-sûr tous ne sont pas pertinents pour interpréter le texte mais cela évite d’oublier ceux qui sont importants. L’innovation principale des dernières années en NLP a été de parvenir à créer des mécanismes d’attention à grande échelle sans pour autant rendre intractables les modèles. Les fenêtres de contexte des modèles les plus performants deviennent immenses. Par exemple le modèle Llama 3.1 (rendu public par Meta en Juillet 2024) propose une fenêtre de contexte de 128 000 tokens, soit environ 96 000 mots, l’équivalent du Hobbit de Tolkien. Autrement dit, pour déduire la subtilité du sens d’un mot, ce modèle peut parcourir un contexte aussi long qu’un roman d’environ 300 pages.
Les deux modèles qui ont marqué leur époque dans le domaine sont les modèles BERT
développé en 2018 par Google (qui était déjà à l’origine de Word2Vec
) et la première version du bien-connu GPT
d’OpenAI
, qui, en 2017, était le premier modèle préentrainé basé sur l’architecture transformer. Ces deux familles de transformer diffèrent dans la manière dont ils intègrent le contexte pour faire une prédiction. GPT
est un modèle autorégressif, donc ne considère que les tokens avant celui dont on désire faire une prédiction. BERT
utilise les tokens à gauche et à droite pour inférer le contexte. Ces deux grands modèles de langage entraînés sont entraînés par auto-renforcement, principalement sur des tâches de prédiction du prochain token (Face 2022). Depuis le succès de ChatGPT
, les nouveaux modèles GPT (à partir de la version 3) ne sont plus open source. Pour les utiliser, il faut donc passer par les API d’OpenAI. Il existe néanmoins de nombreuses alternatives dont les poids sont ouverts, à défaut d’être open source2, qui permettent d’utiliser ces LLM par le biais de Python
, par le biais, notamment, de la librairie transformers
développée par Hugging Face.
Quand on travaille avec des corpus de taille restreinte, c’est généralement une mauvaise idée d’entraîner son propre modèle from scratch. Heureusement, des modèles pré-entraînés sur de très gros corpus sont disponibles. Ils permettent de réaliser du transfer learning, c’est-à-dire de bénéficier de la performance d’un modèle qui a été entraîné sur une autre tâche ou bien sur un autre corpus.
- Refaire un train/test split avec 500 lignes aléatoires
- Importer le modèle
all-MiniLM-L6-v2
avec le packagesentence transformers
. EncoderX_train
etX_test
- Faire une classification avec une méthode simple, par exemple des SVC, s’appuyant sur les embeddings produits à la question précédente. Comme le jeu d’entraînement est réduit, vous pouvez faire de la validation croisée.
- Comprendre pourquoi les performances sont détériorées par rapport au classifieur naif de Bayes.
Réponse à la question 1:
= spooky_df.sample(500)
random_rows = random_rows["author"]
y = random_rows["text"]
X
= train_test_split(
X_train, X_test, y_train, y_test =0.2, stratify=y, random_state=42
X, y, test_size )
Réponse à la question 2:
from sentence_transformers import SentenceTransformer
from sklearn.svm import LinearSVC
= SentenceTransformer("all-MiniLM-L6-v2", model_kwargs={"torch_dtype": "float16"})
model
= model.encode(X_train.values)
X_train_vectors = model.encode(X_test.values) X_test_vectors
Réponse à la question 3:
from sklearn.model_selection import cross_val_score
= LinearSVC(max_iter=10000, C=0.1, dual="auto")
clf
= cross_val_score(
scores =4, scoring="f1_micro", n_jobs=4
clf, X_train_vectors, y_train, cv
)
print(f"CV scores {scores}")
print(f"Mean F1 {np.mean(scores)}")
Mais pourquoi, avec une méthode très compliquée, ne parvenons-nous pas à battre une méthode toute simple ?
On peut avancer plusieurs raisons :
- le
TF-IDF
est un modèle simple, mais toujours très performant (on parle de “tough-to-beat baseline”). - la classification d’auteurs est une tâche très particulière et très ardue, qui ne fait pas justice aux embeddings. Comme on l’a dit précédemment, ces derniers se révèlent particulièrement pertinents lorsqu’il est question de similarité sémantique entre des textes (clustering, etc.).
Dans le cas de notre tâche de classification, il est probable que certains mots (noms de personnage, noms de lieux) soient suffisants pour classifier de manière pertinente, ce que ne permettent pas de capter les embeddings qui accordent à tous les mots la même importance.
Informations additionnelles
environment files have been tested on.
Latest built version: 2024-12-23
Python version used:
'3.12.6 | packaged by conda-forge | (main, Sep 30 2024, 18:08:52) [GCC 13.3.0]'
Package | Version |
---|---|
affine | 2.4.0 |
aiobotocore | 2.15.1 |
aiohappyeyeballs | 2.4.3 |
aiohttp | 3.10.8 |
aioitertools | 0.12.0 |
aiosignal | 1.3.1 |
alembic | 1.13.3 |
altair | 5.4.1 |
aniso8601 | 9.0.1 |
annotated-types | 0.7.0 |
anyio | 4.7.0 |
appdirs | 1.4.4 |
archspec | 0.2.3 |
asttokens | 2.4.1 |
attrs | 24.2.0 |
babel | 2.16.0 |
bcrypt | 4.2.0 |
beautifulsoup4 | 4.12.3 |
black | 24.8.0 |
blinker | 1.8.2 |
blis | 0.7.11 |
bokeh | 3.5.2 |
boltons | 24.0.0 |
boto3 | 1.35.23 |
botocore | 1.35.23 |
branca | 0.7.2 |
Brotli | 1.1.0 |
cachetools | 5.5.0 |
cartiflette | 0.0.2 |
Cartopy | 0.24.1 |
catalogue | 2.0.10 |
cattrs | 24.1.2 |
certifi | 2024.8.30 |
cffi | 1.17.1 |
charset-normalizer | 3.3.2 |
click | 8.1.7 |
click-plugins | 1.1.1 |
cligj | 0.7.2 |
cloudpathlib | 0.20.0 |
cloudpickle | 3.0.0 |
colorama | 0.4.6 |
comm | 0.2.2 |
commonmark | 0.9.1 |
conda | 24.9.1 |
conda-libmamba-solver | 24.7.0 |
conda-package-handling | 2.3.0 |
conda_package_streaming | 0.10.0 |
confection | 0.1.5 |
contextily | 1.6.2 |
contourpy | 1.3.0 |
cryptography | 43.0.1 |
cycler | 0.12.1 |
cymem | 2.0.10 |
cytoolz | 1.0.0 |
dask | 2024.9.1 |
dask-expr | 1.1.15 |
databricks-sdk | 0.33.0 |
dataclasses-json | 0.6.7 |
debugpy | 1.8.6 |
decorator | 5.1.1 |
Deprecated | 1.2.14 |
diskcache | 5.6.3 |
distributed | 2024.9.1 |
distro | 1.9.0 |
docker | 7.1.0 |
duckdb | 0.10.1 |
en-core-web-sm | 3.7.1 |
entrypoints | 0.4 |
et_xmlfile | 2.0.0 |
exceptiongroup | 1.2.2 |
executing | 2.1.0 |
fastexcel | 0.11.6 |
fastjsonschema | 2.21.1 |
fiona | 1.10.1 |
Flask | 3.0.3 |
folium | 0.17.0 |
fontawesomefree | 6.6.0 |
fonttools | 4.54.1 |
fr-core-news-sm | 3.7.0 |
frozendict | 2.4.4 |
frozenlist | 1.4.1 |
fsspec | 2023.12.2 |
gensim | 4.3.2 |
geographiclib | 2.0 |
geopandas | 1.0.1 |
geoplot | 0.5.1 |
geopy | 2.4.1 |
gitdb | 4.0.11 |
GitPython | 3.1.43 |
google-auth | 2.35.0 |
graphene | 3.3 |
graphql-core | 3.2.4 |
graphql-relay | 3.2.0 |
graphviz | 0.20.3 |
great-tables | 0.12.0 |
greenlet | 3.1.1 |
gunicorn | 22.0.0 |
h11 | 0.14.0 |
h2 | 4.1.0 |
hpack | 4.0.0 |
htmltools | 0.6.0 |
httpcore | 1.0.7 |
httpx | 0.28.1 |
httpx-sse | 0.4.0 |
hyperframe | 6.0.1 |
idna | 3.10 |
imageio | 2.36.1 |
importlib_metadata | 8.5.0 |
importlib_resources | 6.4.5 |
inflate64 | 1.0.0 |
ipykernel | 6.29.5 |
ipython | 8.28.0 |
itsdangerous | 2.2.0 |
jedi | 0.19.1 |
Jinja2 | 3.1.4 |
jmespath | 1.0.1 |
joblib | 1.4.2 |
jsonpatch | 1.33 |
jsonpointer | 3.0.0 |
jsonschema | 4.23.0 |
jsonschema-specifications | 2024.10.1 |
jupyter-cache | 1.0.0 |
jupyter_client | 8.6.3 |
jupyter_core | 5.7.2 |
kaleido | 0.2.1 |
kiwisolver | 1.4.7 |
langchain | 0.3.13 |
langchain-community | 0.3.9 |
langchain-core | 0.3.28 |
langchain-text-splitters | 0.3.4 |
langcodes | 3.5.0 |
langsmith | 0.1.147 |
language_data | 1.3.0 |
lazy_loader | 0.4 |
libmambapy | 1.5.9 |
locket | 1.0.0 |
lxml | 5.3.0 |
lz4 | 4.3.3 |
Mako | 1.3.5 |
mamba | 1.5.9 |
mapclassify | 2.8.1 |
marisa-trie | 1.2.1 |
Markdown | 3.6 |
markdown-it-py | 3.0.0 |
MarkupSafe | 2.1.5 |
marshmallow | 3.23.2 |
matplotlib | 3.9.2 |
matplotlib-inline | 0.1.7 |
mdurl | 0.1.2 |
menuinst | 2.1.2 |
mercantile | 1.2.1 |
mizani | 0.11.4 |
mlflow | 2.16.2 |
mlflow-skinny | 2.16.2 |
msgpack | 1.1.0 |
multidict | 6.1.0 |
multivolumefile | 0.2.3 |
munkres | 1.1.4 |
murmurhash | 1.0.11 |
mypy-extensions | 1.0.0 |
narwhals | 1.19.0 |
nbclient | 0.10.0 |
nbformat | 5.10.4 |
nest_asyncio | 1.6.0 |
networkx | 3.3 |
nltk | 3.9.1 |
numpy | 1.26.4 |
opencv-python-headless | 4.10.0.84 |
openpyxl | 3.1.5 |
opentelemetry-api | 1.16.0 |
opentelemetry-sdk | 1.16.0 |
opentelemetry-semantic-conventions | 0.37b0 |
orjson | 3.10.12 |
OWSLib | 0.28.1 |
packaging | 24.1 |
pandas | 2.2.3 |
paramiko | 3.5.0 |
parso | 0.8.4 |
partd | 1.4.2 |
pathspec | 0.12.1 |
patsy | 0.5.6 |
Pebble | 5.1.0 |
pexpect | 4.9.0 |
pickleshare | 0.7.5 |
pillow | 10.4.0 |
pip | 24.2 |
platformdirs | 4.3.6 |
plotly | 5.24.1 |
plotnine | 0.13.6 |
pluggy | 1.5.0 |
polars | 1.8.2 |
preshed | 3.0.9 |
prometheus_client | 0.21.0 |
prometheus_flask_exporter | 0.23.1 |
prompt_toolkit | 3.0.48 |
protobuf | 4.25.3 |
psutil | 6.0.0 |
ptyprocess | 0.7.0 |
pure_eval | 0.2.3 |
py7zr | 0.20.8 |
pyarrow | 17.0.0 |
pyarrow-hotfix | 0.6 |
pyasn1 | 0.6.1 |
pyasn1_modules | 0.4.1 |
pybcj | 1.0.2 |
pycosat | 0.6.6 |
pycparser | 2.22 |
pycryptodomex | 3.21.0 |
pydantic | 2.10.4 |
pydantic_core | 2.27.2 |
pydantic-settings | 2.7.0 |
Pygments | 2.18.0 |
PyNaCl | 1.5.0 |
pynsee | 0.1.8 |
pyogrio | 0.10.0 |
pyOpenSSL | 24.2.1 |
pyparsing | 3.1.4 |
pyppmd | 1.1.0 |
pyproj | 3.7.0 |
pyshp | 2.3.1 |
PySocks | 1.7.1 |
python-dateutil | 2.9.0 |
python-dotenv | 1.0.1 |
python-magic | 0.4.27 |
pytz | 2024.1 |
pyu2f | 0.1.5 |
pywaffle | 1.1.1 |
PyYAML | 6.0.2 |
pyzmq | 26.2.0 |
pyzstd | 0.16.2 |
querystring_parser | 1.2.4 |
rasterio | 1.4.3 |
referencing | 0.35.1 |
regex | 2024.9.11 |
requests | 2.32.3 |
requests-cache | 1.2.1 |
requests-toolbelt | 1.0.0 |
retrying | 1.3.4 |
rich | 13.9.4 |
rpds-py | 0.22.3 |
rsa | 4.9 |
Rtree | 1.3.0 |
ruamel.yaml | 0.18.6 |
ruamel.yaml.clib | 0.2.8 |
s3fs | 2023.12.2 |
s3transfer | 0.10.2 |
scikit-image | 0.24.0 |
scikit-learn | 1.5.2 |
scipy | 1.13.0 |
seaborn | 0.13.2 |
setuptools | 74.1.2 |
shapely | 2.0.6 |
shellingham | 1.5.4 |
six | 1.16.0 |
smart-open | 7.1.0 |
smmap | 5.0.0 |
sniffio | 1.3.1 |
sortedcontainers | 2.4.0 |
soupsieve | 2.5 |
spacy | 3.7.5 |
spacy-legacy | 3.0.12 |
spacy-loggers | 1.0.5 |
SQLAlchemy | 2.0.35 |
sqlparse | 0.5.1 |
srsly | 2.5.0 |
stack-data | 0.6.2 |
statsmodels | 0.14.4 |
tabulate | 0.9.0 |
tblib | 3.0.0 |
tenacity | 9.0.0 |
texttable | 1.7.0 |
thinc | 8.2.5 |
threadpoolctl | 3.5.0 |
tifffile | 2024.12.12 |
toolz | 1.0.0 |
topojson | 1.9 |
tornado | 6.4.1 |
tqdm | 4.66.5 |
traitlets | 5.14.3 |
truststore | 0.9.2 |
typer | 0.15.1 |
typing_extensions | 4.12.2 |
typing-inspect | 0.9.0 |
tzdata | 2024.2 |
Unidecode | 1.3.8 |
url-normalize | 1.4.3 |
urllib3 | 1.26.20 |
wasabi | 1.1.3 |
wcwidth | 0.2.13 |
weasel | 0.4.1 |
webdriver-manager | 4.0.2 |
websocket-client | 1.8.0 |
Werkzeug | 3.0.4 |
wheel | 0.44.0 |
wordcloud | 1.9.3 |
wrapt | 1.16.0 |
xgboost | 2.1.1 |
xlrd | 2.0.1 |
xyzservices | 2024.9.0 |
yarl | 1.13.1 |
yellowbrick | 1.5 |
zict | 3.0.0 |
zipp | 3.20.2 |
zstandard | 0.23.0 |
View file history
SHA | Date | Author | Description |
---|---|---|---|
24d4ff6 | 2024-12-09 15:22:19 | lgaliana | ensure non executable block |
3817cdc | 2024-12-09 13:34:56 | lgaliana | eval false pour le dernier exo |
8d09742 | 2024-12-09 13:34:16 | lgaliana | update embedding |
441da89 | 2024-12-08 20:28:21 | lgaliana | Utilise un service pytorch |
0ec1e15 | 2024-12-07 15:54:20 | lgaliana | Commence à décrire l’attention |
35443b7 | 2024-12-07 13:40:34 | lgaliana | Word2Vec |
89397cf | 2024-12-06 21:50:25 | lgaliana | Preprocessing |
4181dab | 2024-12-06 13:16:36 | lgaliana | Transition |
38b5152 | 2024-12-05 22:21:11 | lgaliana | détails sur l’approche proba |
5df69cc | 2024-12-05 17:56:47 | lgaliana | up |
1b7188a | 2024-12-05 13:21:11 | lgaliana | Embedding chapter |
c641de0 | 2024-08-22 11:37:13 | Lino Galiana | A series of fix for notebooks that were bugging (#545) |
c5a9fb7 | 2024-07-22 09:56:18 | Julien PRAMIL | Fix bug in LDA chapter (#525) |
c9f9f8a | 2024-04-24 15:09:35 | Lino Galiana | Dark mode and CSS improvements (#494) |
06d003a | 2024-04-23 10:09:22 | Lino Galiana | Continue la restructuration des sous-parties (#492) |
005d89b | 2023-12-20 17:23:04 | Lino Galiana | Finalise l’affichage des statistiques Git (#478) |
3437373 | 2023-12-16 20:11:06 | Lino Galiana | Améliore l’exercice sur le LASSO (#473) |
4cd44f3 | 2023-12-11 17:37:50 | Antoine Palazzolo | Relecture NLP (#474) |
deaafb6 | 2023-12-11 13:44:34 | Thomas Faria | Relecture Thomas partie NLP (#472) |
1f23de2 | 2023-12-01 17:25:36 | Lino Galiana | Stockage des images sur S3 (#466) |
6855667 | 2023-11-29 10:21:01 | Romain Avouac | Corrections tp vectorisation + improve badge creation (#465) |
69cf52b | 2023-11-21 16:12:37 | Antoine Palazzolo | [On-going] Suggestions chapitres modélisation (#452) |
652009d | 2023-10-09 13:56:34 | Lino Galiana | Finalise le cleaning (#430) |
a771183 | 2023-10-09 11:27:45 | Antoine Palazzolo | Relecture TD2 par Antoine (#418) |
154f09e | 2023-09-26 14:59:11 | Antoine Palazzolo | Des typos corrigées par Antoine (#411) |
9a4e226 | 2023-08-28 17:11:52 | Lino Galiana | Action to check URL still exist (#399) |
3bdf3b0 | 2023-08-25 11:23:02 | Lino Galiana | Simplification de la structure 🤓 (#393) |
78ea2cb | 2023-07-20 20:27:31 | Lino Galiana | Change titles levels (#381) |
29ff3f5 | 2023-07-07 14:17:53 | linogaliana | description everywhere |
f21a24d | 2023-07-02 10:58:15 | Lino Galiana | Pipeline Quarto & Pages 🚀 (#365) |
b395985 | 2023-02-13 17:29:36 | Lino Galiana | Retire shortcode spoiler (#352) |
f10815b | 2022-08-25 16:00:03 | Lino Galiana | Notebooks should now look more beautiful (#260) |
d201e3c | 2022-08-03 15:50:34 | Lino Galiana | Pimp la homepage ✨ (#249) |
12965ba | 2022-05-25 15:53:27 | Lino Galiana | :launch: Bascule vers quarto (#226) |
9c71d6e | 2022-03-08 10:34:26 | Lino Galiana | Plus d’éléments sur S3 (#218) |
7058752 | 2022-03-04 15:35:17 | Lino Galiana | Relecture Word2Vec (#216) |
ce1f2b5 | 2022-02-16 13:54:27 | Lino Galiana | spacy corpus pre-downloaded |
66e2837 | 2021-12-24 16:54:45 | Lino Galiana | Fix a few typos in the new pipeline tutorial (#208) |
8ab1956 | 2021-12-23 21:07:30 | Romain Avouac | TP vectorization prediction authors (#206) |
09b60a1 | 2021-12-21 19:58:58 | Lino Galiana | Relecture suite du NLP (#205) |
495599d | 2021-12-19 18:33:05 | Lino Galiana | Des éléments supplémentaires dans la partie NLP (#202) |
2a8809f | 2021-10-27 12:05:34 | Lino Galiana | Simplification des hooks pour gagner en flexibilité et clarté (#166) |
2e4d586 | 2021-09-02 12:03:39 | Lino Galiana | Simplify badges generation (#130) |
4cdb759 | 2021-05-12 10:37:23 | Lino Galiana | :sparkles: :star2: Nouveau thème hugo :snake: :fire: (#105) |
7f9f97b | 2021-04-30 21:44:04 | Lino Galiana | 🐳 + 🐍 New workflow (docker 🐳) and new dataset for modelization (2020 🇺🇸 elections) (#99) |
d164635 | 2020-12-08 16:22:00 | Lino Galiana | :books: Première partie NLP (#87) |
Les références
Notes de bas de page
Cette remarque peut apparaître étonnante alors que les IA génératives occupent une place importante dans nos usages. Néanmoins, il faut garder à l’esprit que certes vous posez de nouvelles questions à des IA mais vous les posez dans des termes qu’elles connaissent: du langage naturel dans une langue présente dans leur corpus d’entraînement, des images numériques qui sont donc interprétables par une machine, etc. Autrement dit, votre prompt n’est pas, en soi, inconnu pour l’IA, elle peut l’interpréter même si son contenu est nouveau et original.↩︎
Certaines organisations, comme Meta pour Llama, mettent à disposition les poids après entraînement de leur modèle sur la plateforme Hugging Face, permettant une réutilisation de ces modèles si la licence le permet. Néanmoins, il ne s’agit pas pour autant de modèles open source puisque le code utilisé pour entraîner les modèles et constituer les corpus d’apprentissage, issus de collectes massives de données par webscraping, et les éventuelles annotations supplémentaires pour en faire des versions spécialisées, ne sont pas partagés.↩︎
Citation
@book{galiana2023,
author = {Galiana, Lino},
title = {Python pour la data science},
date = {2023},
url = {https://pythonds.linogaliana.fr/},
doi = {10.5281/zenodo.8229676},
langid = {fr}
}
4.3 Comment ces modèles sont-ils entraînés ?
Ces modèles sont entraînés à partir d’une tâche de prédiction résolue par un réseau de neurones simple, généralement avec une approche par renforcement.
L’idée fondamentale est que la signification d’un mot se comprend en regardant les mots qui apparaissent fréquemment dans son voisinage. Pour un mot donné, on va donc essayer de prédire les mots qui apparaissent dans une fenêtre autour du mot cible.
En répétant cette tâche de nombreuses fois et sur un corpus suffisamment varié, on obtient finalement des embeddings pour chaque mot du vocabulaire, qui présentent les propriétés discutées précédemment. L’ensemble des articles
Wikipedia
est un des corpus de prédilection des personnes ayant construit des plongements lexicaux. Il comporte en effet des phrases complètes, contrairement à des informations issues de commentaires de réseaux sociaux, et propose des rapprochements intéressants entre des personnes, des lieux, etc.Le contexte d’un mot est défini par une fenêtre de taille fixe autour de ce mot. La taille de la fenêtre est un paramètre de la construction de l’embedding. Le corpus fournit un grand ensemble d’exemples mots-contexte, qui peuvent servir à entraîner un réseau de neurones.
Plus précisément, il existe deux approches, dont nous ne développerons pas les détails :