Les embeddings, où comment synthétiser l’information textuelle

Pour pouvoir utiliser des données textuelles dans des algorithmes de machine learning, il faut les vectoriser, c’est à dire transformer le texte en données numériques. Dans ce TP, nous allons comparer différentes méthodes de vectorisation, à travers une tâche de prédiction : peut-on prédire un auteur littéraire à partir d’extraits de ses textes ? Parmi ces méthodes, on va notamment explorer le modèle Word2Vec, qui permet d’exploiter les structures latentes d’un texte en construisant des word embeddings (plongements de mots).

Auteur·rice

Lino Galiana

Date de publication

2024-12-23

Warning

Ce chapitre va évoluer prochainement.

Pour essayer les exemples présents dans ce tutoriel :
View on GitHub Onyxia Onyxia Open In Colab

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).

Sources d’inspiration

Ce chapitre s’inspire de plusieurs ressources disponibles en ligne:

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.

PyTorch sur le SSPCloud

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.

!pip install numpy pandas spacy transformers scikit-learn langchain_community

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

data_url = "https://github.com/GU4243-ADS/spring2018-project1-ginnyqg/raw/master/data/spooky.csv"
spooky_df = pd.read_csv(data_url)

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

nlp = spacy.load("en_core_web_sm")

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(
    texts: List[str],
    remove_stopwords: bool = False,
    n_process: int = 4,
    remove_punctuation: bool = True,
) -> 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
    docs = nlp.pipe(
        texts, n_process=n_process, disable=["parser", "ner", "lemmatizer", "textcat"]
    )

    # Pre-load stopwords for faster checking
    stopwords = set(nlp.Defaults.stop_words)

    # 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.

spooky_df["text_clean"] = clean_docs(spooky_df["text"])
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

y = spooky_df["author"]
X = spooky_df["text_clean"]
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, stratify=y, random_state=42
)

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_tfidf = Pipeline(
    [
        ("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.

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.

pipeline_tfidf.fit(spooky_df["text_clean"])
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.

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:

text = "He was afraid by Frankenstein monster"

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

Exercice 1: recherche de similarité avec TF-IDF
  1. Utiliser la méthode transform pour vectoriser tout notre corpus d’entraînement.

  2. En supposant que votre jeu d’entraînement vectorisé s’appelle X_train_tfidf, vous pouvez le transformer en DataFrame avec la commande suivante:

X_train_tfidf = pd.DataFrame(
    X_train_tfidf.todense(), columns=pipeline_tfidf.get_feature_names_out()
)
  1. Utiliser la méthode cosine_similarity de Scikit 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_similarities = cosine_similarity(
    X_train_tfidf, pipeline_tfidf.transform([text])
).flatten()

top_4_indices = np.argsort(cosine_similarities)[-4:][
    ::-1
]  # Sort and reverse for descending order
top_4_similarities = cosine_similarities[top_4_indices]
  1. 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

loader = DataFrameLoader(spooky_df, page_content_column="text_clean")

retriever = TFIDFRetriever.from_documents(loader.load())

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):
    documents += [{**best_echoes.metadata, **{"text_clean": best_echoes.page_content}}]

documents = pd.DataFrame(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
La métrique BM25

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.

Exercice 2: le classifieur naif de Bayes
  1. En repartant de l’exemple précédent, définir un pipeline qui vectorise chaque document (utiliser CountVectorizer plutôt que TFIDFVectorizer) et effectue une prédiction grâce à un modèle naif de Bayes.
  2. Entraîner ce modèle, faire une prédiction sur le jeu de test.
  3. Evaluer la performance de votre modèle
  4. 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 ?
  5. 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.

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
Comprendre la logique du classifieur naif de Bayes

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.

Table 3.1: Exemple fictif de probabilités de tirage
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

Illustration du principe de la représentation de Word2Vec (source: Jay Alammar)

Illustration du principe de la représentation de Word2Vec (source: Jay Alammar)

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} \]

Illustration du plongement lexical. Source : Post de blog Word Embedding : Basics

Illustration du plongement lexical. Source : Post de blog Word Embedding : Basics

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

Exemple de translation entre deux représentations vectorielles. Source: Meta

Exemple de translation entre deux représentations vectorielles. Source: Meta

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 :

  • Continuous bag of words (CBOW), où le modèle est entraîné à prédire un mot à partir de son contexte ;
  • Skip-gram, où le modèle tente de prédire le contexte à partir d’un seul mot.

Illustration de la différence entre les approches CBOW et Skip-gram

Illustration de la différence entre les approches CBOW et Skip-gram

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.

Illustration du modèle fastText

Illustration du modèle fastText

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.

Exercice 3
  1. Refaire un train/test split avec 500 lignes aléatoires
  2. Importer le modèle all-MiniLM-L6-v2 avec le package sentence transformers. Encoder X_train et X_test
  3. 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.
  4. Comprendre pourquoi les performances sont détériorées par rapport au classifieur naif de Bayes.

Réponse à la question 1:

random_rows = spooky_df.sample(500)
y = random_rows["author"]
X = random_rows["text"]

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, stratify=y, random_state=42
)

Réponse à la question 2:

from sentence_transformers import SentenceTransformer
from sklearn.svm import LinearSVC

model = SentenceTransformer("all-MiniLM-L6-v2", model_kwargs={"torch_dtype": "float16"})

X_train_vectors = model.encode(X_train.values)
X_test_vectors = model.encode(X_test.values)

Réponse à la question 3:

from sklearn.model_selection import cross_val_score

clf = LinearSVC(max_iter=10000, C=0.1, dual="auto")

scores = cross_val_score(
    clf, X_train_vectors, y_train, cv=4, scoring="f1_micro", n_jobs=4
)

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)
Retour au sommet

Les références

Face, Hugging. 2022. « The Hugging Face Course, 2022 ». https://huggingface.co/course.
Mikolov, Tomas. 2013. « Efficient estimation of word representations in vector space ». arXiv preprint arXiv:1301.3781 3781.
Vaswani, A. 2017. « Attention is all you need ». Advances in Neural Information Processing Systems.

Notes de bas de page

  1. 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.↩︎

  2. 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

BibTeX
@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}
}
Veuillez citer ce travail comme suit :
Galiana, Lino. 2023. Python pour la data science. https://doi.org/10.5281/zenodo.8229676.