Méthodes de vectorisation : comptages et word embeddings

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

Tutoriel
NLP
Author

Lino Galiana

Published

2024-05-08

Download nbviewer Onyxia Onyxia
Open In Colab githubdev

Cette page approfondit certains aspects présentés dans la partie introductive. Après avoir travaillé sur le Comte de Monte Cristo, on va continuer notre exploration de la littérature avec cette fois des auteurs anglophones :

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.

Le but va être dans un premier temps de regarder dans le détail les termes les plus fréquents utilisés par les auteurs et de les représenter graphiquement, puis on va ensuite essayer de prédire quel texte correspond à quel auteur à partir de différents modèles de vectorisation, notamment les word embeddings.

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

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

!pip install spacy gensim sentence_transformers pandas matplotlib seaborn

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
from collections import Counter

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import gensim

from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import LinearSVC
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
from sklearn.model_selection import GridSearchCV, cross_val_score

from gensim.models.word2vec import Word2Vec
import gensim.downloader
from sentence_transformers import SentenceTransformer

2 Nettoyage des données

Nous allons ainsi à nouveau utiliser le jeu de données spooky :

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

2.1 Preprocessing

En NLP, la première étape est souvent celle du preprocessing, qui inclut notamment les étapes de tokenization et de nettoyage du texte. Comme celles-ci ont été vues en détail dans le précédent chapitre, 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).

Jusqu’à présent, nous avons utilisé principalement nltk pour le preprocessing de données textuelles. Cette fois, nous proposons d’utiliser la librairie spaCy qui permet de mieux automatiser sous forme de pipelines de preprocessing.

Pour initialiser le processus de nettoyage, on va utiliser le corpus en_core_web_sm (voir plus haut pour l’installation de ce corpus):

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 :

def clean_docs(texts, remove_stopwords=False, n_process=4):

    docs = nlp.pipe(
        texts, n_process=n_process, disable=["parser", "ner", "lemmatizer", "textcat"]
    )
    stopwords = nlp.Defaults.stop_words

    docs_cleaned = []
    for doc in docs:
        tokens = [tok.text.lower().strip() for tok in doc if not tok.is_punct]
        if remove_stopwords:
            tokens = [tok for tok in tokens if tok not in stopwords]
        doc_clean = " ".join(tokens)
        docs_cleaned.append(doc_clean)

    return 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()

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.

le = LabelEncoder()
spooky_df["author_encoded"] = le.fit_transform(spooky_df["author"])

On peut vérifier les classes de notre LabelEncoder :

le.classes_

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.

Aperçu du premier élément de X_train :

X_train[0]

On peut aussi vérifier qu’on est capable de retrouver la correspondance entre nos auteurs initiaux avec la méthode inverse_transform :

print(y_train[0], le.inverse_transform([y_train[0]])[0])

3 Statistiques exploratoires

3.1 Répartition des labels

Refaisons un graphique que nous avons déjà produit précédemment pour voir la répartition de notre corpus entre auteurs :

fig = pd.Series(le.inverse_transform(y_train)).value_counts().plot(kind="bar")
fig

On observe une petite asymétrie : les passages des livres d’Edgar Allen Poe sont plus nombreux que ceux des autres auteurs dans notre corpus d’entraînement, ce qui peut être problématique dans le cadre d’une tâche de classification. L’écart n’est pas dramatique, mais on essaiera d’en tenir compte dans l’analyse en choisissant une métrique d’évaluation pertinente.

3.2 Mots les plus fréquemment utilisés par chaque auteur

On va supprimer les stop words pour réduire le bruit dans notre jeu de données.

# Suppression des stop words
X_train_no_sw = clean_docs(X_train, remove_stopwords=True)
X_train_no_sw = np.array(X_train_no_sw)

Pour visualiser rapidement nos corpus, on peut utiliser la technique des nuages de mots déjà vue à plusieurs reprises.

Vous pouvez essayer de faire vous-même les nuages ci-dessous ou cliquer sur la ligne ci-dessous pour afficher le code ayant généré les figures :

Cliquer pour afficher le code 👇
def plot_top_words(initials, ax, n_words=20):
    # Calcul des mots les plus fréquemment utilisés par l'auteur
    texts = X_train_no_sw[le.inverse_transform(y_train) == initials]
    all_tokens = " ".join(texts).split()
    counts = Counter(all_tokens)
    top_words = [word[0] for word in counts.most_common(n_words)]
    top_words_counts = [word[1] for word in counts.most_common(n_words)]

    # Représentation sous forme de barplot
    ax = sns.barplot(ax=ax, x=top_words, y=top_words_counts)
    ax.set_title(f"Most Common Words used by {initials_to_author[initials]}")
initials_to_author = {
    "EAP": "Edgar Allen Poe",
    "HPL": "H.P. Lovecraft",
    "MWS": "Mary Wollstonecraft Shelley",
}

fig, axs = plt.subplots(3, 1, figsize=(12, 12))

plot_top_words("EAP", ax=axs[0])
plot_top_words("HPL", ax=axs[1])
plot_top_words("MWS", ax=axs[2])

Beaucoup de mots se retrouvent très utilisés par les trois auteurs. Il y a cependant des différences notables : le mot “life” est le plus employé par MWS, alors qu’il n’apparaît pas dans les deux autres tops. De même, le mot “old” est le plus utilisé par HPL là où les deux autres ne l’utilisent pas de manière surreprésentée.

Il semble donc qu’il y ait des particularités propres à chacun des auteurs en termes de vocabulaire, ce qui laisse penser qu’il est envisageable de prédire les auteurs à partir de leurs textes dans une certaine mesure.

4 Prédiction sur le set d’entraînement

Nous allons à présent vérifier cette conjecture en comparant plusieurs modèles de vectorisation, i.e. de transformation du texte en objets numériques pour que l’information contenue soit exploitable dans un modèle de classification.

4.1 Démarche

Comme nous nous intéressons plus à l’effet de la vectorisation qu’à la tâche de classification en elle-même, nous allons utiliser un algorithme de classification simple (un SVM linéaire), avec des paramètres non fine-tunés (c’est-à-dire des paramètres pas nécessairement choisis pour être les meilleurs de tous).

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

Ce modèle est connu pour être très performant sur les tâches de classification de texte, et nous fournira donc un bon modèle de référence (baseline). Cela nous permettra également de comparer de manière objective l’impact des méthodes de vectorisation sur la performance finale.

Pour les deux premières méthodes de vectorisation (basées sur des fréquences et fréquences relatives des mots), on va simplement normaliser les données d’entrée, ce qui va permettre au SVM de converger plus rapidement, ces modèles étant sensibles aux différences d’échelle dans les données.

On va également fine-tuner via grid-search certains hyperparamètres liés à ces méthodes de vectorisation :

  • on teste différents ranges de n-grams (unigrammes et unigrammes + bigrammes)
  • on teste avec et sans stop-words

Afin d’éviter le surapprentissage, on va évaluer les différents modèles via validation croisée, calculée sur 4 blocs.

On récupère à la fin le meilleur modèle selon une métrique spécifiée. On choisit le score F1, moyenne harmonique de la précision et du rappel, qui donne un poids équilibré aux deux métriques, tout en pénalisant fortement le cas où l’une des deux est faible. Précisément, on retient le score F1 *micro-averaged* : les contributions des différentes classes à prédire sont agrégées, puis on calcule le score F1 sur ces données agrégées. L’avantage de ce choix est qu’il permet de tenir compte des différences de fréquences des différentes classes.

4.2 Pipeline de prédiction

On va utiliser un pipeline scikit ce qui va nous permettre d’avoir un code très concis pour effectuer cet ensemble de tâches cohérentes. De plus, cela va nous assurer de gérer de manière cohérentes nos différentes transformations (cf. partie sur les pipelines)

Pour se faciliter la vie, on définit une fonction fit_vectorizers qui intègre dans un pipeline générique une méthode d’estimation scikit et fait de la validation croisée en cherchant le meilleur modèle (en excluant/incluant les stop words et avec unigrammes/bigrammes)

def fit_vectorizers(vectorizer):
    pipeline = Pipeline(
        [
            ("vect", vectorizer()),
            ("scaling", StandardScaler(with_mean=False)),
            ("clf", clf),
        ]
    )

    parameters = {
        "vect__ngram_range": ((1, 1), (1, 2)),  # unigrams or bigrams
        "vect__stop_words": ("english", None),
    }

    grid_search = GridSearchCV(
        pipeline, parameters, scoring="f1_micro", cv=4, n_jobs=4, verbose=1
    )
    grid_search.fit(X_train, y_train)

    best_parameters = grid_search.best_estimator_.get_params()
    for param_name in sorted(parameters.keys()):
        print("\t%s: %r" % (param_name, best_parameters[param_name]))

    print(f"CV scores {grid_search.cv_results_['mean_test_score']}")
    print(f"Mean F1 {np.mean(grid_search.cv_results_['mean_test_score'])}")

    return grid_search

5 Approche bag-of-words

On commence par une approche “bag-of-words”, i.e. qui revient simplement à représenter chaque document par un vecteur qui compte le nombre d’apparitions de chaque mot du vocabulaire dans le document.

Illustrons d’abord le principe à l’aide d’un exemple simple.

corpus = [
    "Un premier document à propos des chats.",
    "Un second document qui parle des chiens.",
]

vectorizer = CountVectorizer()
vectorizer.fit(corpus)

L’objet vectorizer a été “entraîné” (fit) sur notre corpus d’exemple contenant deux documents. Il a notamment appris le vocabulaire complet du corpus, dont on peut afficher l’ordre.

vectorizer.get_feature_names_out()

L’objet vectorizer entraîné peut maintenant vectoriser le corpus initial, selon l’ordre du vocabulaire affiché ci-dessus.

X = vectorizer.transform(corpus)
print(X.toarray())

Quel score F1 obtient-on finalement avec cette méthode de vectorisation sur notre problème de classification d’auteurs ?

cv_bow = fit_vectorizers(CountVectorizer)

6 TF-IDF

On s’intéresse ensuite à l’approche TF-IDF, qui permet de tenir compte des fréquences relatives des mots.

Ainsi, pour un mot donné, on va multiplier la fréquence d’apparition du mot dans le document (calculé comme dans la méthode précédente) par un terme qui pénalise une fréquence élevée du mot dans le corpus. L’image ci-dessous, empruntée à Chris Albon, illustre cette mesure:

Source: Towards Data Science

La vectorisation TF-IDF permet donc de limiter l’influence des stop-words et donc de donner plus de poids aux mots les plus salients d’un document. Illustrons cela à nouveau avec notre corpus d’exemple de deux documents.

corpus = [
    "Un premier document à propos des chats.",
    "Un second document qui parle des chiens.",
]

vectorizer = TfidfVectorizer()
vectorizer.fit(corpus)

Là encore, le vectoriseur a “appris” le vocabulaire du corpus.

vectorizer.get_feature_names_out()

Et peut être utilisé pour calculer les scores TF-IDF de chacun des termes des documents.

X = vectorizer.transform(corpus)
print(X.toarray())

On remarque que “chats” et “chiens” possèdent les scores les plus élevés, ce sont bien les termes les plus distinctifs. A l’inverse, les termes qui reviennent dans les deux documents (“un”, “document”, “des”) ont un score inférieur, car ils sont beaucoup présents dans le corpus relativement.

Quel score F1 obtient-on avec cette méthode de vectorisation sur notre problème de classification d’auteurs ?

cv_tfidf = fit_vectorizers(TfidfVectorizer)

On observe clairement que la performance de classification est bien supérieure, ce qui montre la pertinence de cette technique.

7 Word2vec avec averaging

On va maintenant explorer les techniques de vectorisation basées sur les embeddings de mots, et notamment la plus populaire : Word2Vec.

L’idée derrière est simple, mais a révolutionné le NLP : au lieu de représenter les documents par des vecteurs sparse de très grande dimension (la taille du vocabulaire) comme on l’a fait jusqu’à présent, on va les représenter par des vecteurs dense (continus) de dimension réduite (en général, autour de 100-300).

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.

Source: Medium

Pourquoi est-ce intéressant ? Pour de nombreuses raisons, mais pour résumer : cela permet de beaucoup mieux capturer la similarité sémantique entre les documents.

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.

Pourtant, une vectorisation de type comptage ou TF-IDF ne permet pas de saisir cette similarité : le calcul d’une mesure de similarité (norme euclidienne ou similarité cosinus) entre les deux vecteurs ne prendra en compte la similarité des deux concepts, puisque les mots utilisés sont différents.

A l’inverse, 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}\]

Comment ces modèles sont-ils entraînés ? Via une tâche de prédiction résolue par un réseau de neurones simple.

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.

X_train_tokens = [text.split() for text in X_train]
w2v_model = Word2Vec(X_train_tokens, vector_size=200, window=5, min_count=1, workers=4)
w2v_model.wv.most_similar("mother")

On voit que les mots les plus similaires à “mother” sont souvent des mots liés à la famille, mais pas toujours.

C’est lié à la taille très restreinte du corpus sur lequel on entraîne le modèle, qui ne permet pas de réaliser des associations toujours pertinentes.

L’embedding (la représentation vectorielle) de chaque document correspond à la moyenne des word-embeddings des mots qui le composent :

def get_mean_vector(w2v_vectors, words):
    words = [word for word in words if word in w2v_vectors]
    if words:
        avg_vector = np.mean(w2v_vectors[words], axis=0)
    else:
        avg_vector = np.zeros_like(w2v_vectors["hi"])
    return avg_vector


def fit_w2v_avg(w2v_vectors):
    X_train_vectors = np.array(
        [get_mean_vector(w2v_vectors, words) for words in X_train_tokens]
    )

    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)}")
    return scores
cv_w2vec = fit_w2v_avg(w2v_model.wv)

La performance chute fortement ; la faute à la taille très restreinte du corpus, comme annoncé précédemment.

8 Word2vec pré-entraîné + averaging

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

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.

L’un des modèles les plus connus pour démarrer est le glove_model de Gensim (Glove pour Global Vectors for Word Representation)1:

GloVe is an unsupervised learning algorithm for obtaining vector representations for words. Training is performed on aggregated global word-word co-occurrence statistics from a corpus, and the resulting representations showcase interesting linear substructures of the word vector space.

Source : https://nlp.stanford.edu/projects/glove/

On peut le charger directement grâce à l’instruction suivante :

glove_model = gensim.downloader.load("glove-wiki-gigaword-200")

Par exemple, la représentation vectorielle de roi est l’objet multidimensionnel suivant :

glove_model["king"]

Comme elle est peu intelligible, on va plutôt rechercher les termes les plus similaires. Par exemple,

glove_model.most_similar("mother")

On peut retrouver notre formule précédente

\[\text{king} - \text{man} + \text{woman} ≈ \text{queen}\] dans ce plongement de mots:

glove_model.most_similar(positive=["king", "woman"], negative=["man"])

Vous pouvez vous référer à ce tutoriel pour en découvrir plus sur Word2Vec.

Faisons notre apprentissage par transfert :

cv_w2vec_transfert = fit_w2v_avg(glove_model)

La performance remonte substantiellement. Cela étant, on ne parvient pas à faire mieux que les approches basiques, on arrive à peine aux performances de la vectorisation par comptage.

En effet, pour rappel, les performances sont les suivantes :

perfs = pd.DataFrame(
    [
        np.mean(cv_bow.cv_results_["mean_test_score"]),
        np.mean(cv_tfidf.cv_results_["mean_test_score"]),
        np.mean(cv_w2vec),
        np.mean(cv_w2vec_transfert),
    ],
    index=[
        "Bag-of-Words",
        "TF-IDF",
        "Word2Vec non pré-entraîné",
        "Word2Vec pré-entraîné",
    ],
    columns=["Mean F1 score"],
).sort_values("Mean F1 score", ascending=False)
perfs

Les performences limitées du modèle Word2Vec sont cette fois certainement dues à la manière dont les word-embeddings sont exploités : ils sont moyennés pour décrire chaque document.

Cela a plusieurs limites :

  • on ne tient pas compte de l’ordre et donc du contexte des mots
  • lorsque les documents sont longs, la moyennisation peut créer des représentation bruitées.

9 Contextual embeddings

Les embeddings contextuels visent à pallier les limites des embeddings traditionnels évoquées précédemment.

Cette fois, les mots n’ont plus de représentation vectorielle fixe, celle-ci est calculée dynamiquement en fonction des mots du voisinage, et ainsi de suite. Cela permet de tenir compte de la structure des phrases et de tenir compte du fait que le sens d’un mot est fortement dépendant des mots qui l’entourent. Par exemple, dans les expressions “le président Macron” et “le camembert Président” le mot président n’a pas du tout le même rôle.

Ces embeddings sont produits par des architectures très complexes, de type Transformer (BERT, etc.).

model = SentenceTransformer("all-mpnet-base-v2")
X_train_vectors = model.encode(X_train)
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)}")
perfs = pd.concat(
    [
        perfs,
        pd.DataFrame(
            [np.mean(scores)], index=["Contextual Embedding"], columns=["Mean F1 score"]
        ),
    ]
).sort_values("Mean F1 score", ascending=False)
perfs

Verdict : on fait très légèrement mieux que la vectorisation TF-IDF. On voit donc l’importance de tenir compte du contexte.

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.

10 Aller plus loin

  • Nous avons entraîné différents modèles sur l’échantillon d’entraînement par validation croisée, mais nous n’avons toujours pas utilisé l’échantillon test que nous avons mis de côté au début. Réaliser la prédiction sur les données de test, et vérifier si l’on obtient le même classement des méthodes de vectorisation.
  • Faire un vrai split train/test : faire l’entraînement avec des textes de certains auteurs, et faire la prédiction avec des textes d’auteurs différents. Cela permettrait de neutraliser la présence de noms de lieux, de personnages, etc.
  • Comparer avec d’autres algorithmes de classification qu’un SVM
  • (Avancé) : fine-tuner le modèle d’embeddings contextuels sur la tâche de classification

Informations additionnelles

environment files have been tested on.

Python version used:

Package Version
affine 2.4.0
aiobotocore 2.12.2
aiohttp 3.9.3
aioitertools 0.11.0
aiosignal 1.3.1
alembic 1.13.1
aniso8601 9.0.1
annotated-types 0.6.0
appdirs 1.4.4
archspec 0.2.3
astroid 3.1.0
asttokens 2.4.1
attrs 23.2.0
Babel 2.15.0
bcrypt 4.1.2
beautifulsoup4 4.12.3
black 24.4.2
blinker 1.7.0
blis 0.7.11
bokeh 3.4.0
boltons 23.1.1
boto3 1.34.51
botocore 1.34.51
branca 0.7.1
Brotli 1.1.0
cachetools 5.3.3
cartiflette 0.0.2
Cartopy 0.23.0
catalogue 2.0.10
cattrs 23.2.3
certifi 2024.2.2
cffi 1.16.0
charset-normalizer 3.3.2
chromedriver-autoinstaller 0.6.4
click 8.1.7
click-plugins 1.1.1
cligj 0.7.2
cloudpathlib 0.16.0
cloudpickle 3.0.0
colorama 0.4.6
comm 0.2.2
commonmark 0.9.1
conda 24.3.0
conda-libmamba-solver 24.1.0
conda-package-handling 2.2.0
conda_package_streaming 0.9.0
confection 0.1.4
contextily 1.6.0
contourpy 1.2.1
cryptography 42.0.5
cycler 0.12.1
cymem 2.0.8
cytoolz 0.12.3
dask 2024.4.1
dask-expr 1.0.10
debugpy 1.8.1
decorator 5.1.1
dill 0.3.8
distributed 2024.4.1
distro 1.9.0
docker 7.0.0
duckdb 0.10.1
en-core-web-sm 3.7.1
entrypoints 0.4
et-xmlfile 1.1.0
exceptiongroup 1.2.0
executing 2.0.1
fastjsonschema 2.19.1
fiona 1.9.6
flake8 7.0.0
Flask 3.0.2
folium 0.16.0
fontawesomefree 6.5.1
fonttools 4.51.0
fr-core-news-sm 3.7.0
frozenlist 1.4.1
fsspec 2023.12.2
GDAL 3.8.4
gensim 4.3.2
geographiclib 2.0
geopandas 0.12.2
geoplot 0.5.1
geopy 2.4.1
gitdb 4.0.11
GitPython 3.1.43
google-auth 2.29.0
graphene 3.3
graphql-core 3.2.3
graphql-relay 3.2.0
graphviz 0.20.3
great-tables 0.5.1
greenlet 3.0.3
gunicorn 21.2.0
h11 0.14.0
htmltools 0.5.1
hvac 2.1.0
idna 3.6
imageio 2.34.1
importlib_metadata 7.1.0
importlib_resources 6.4.0
inflate64 1.0.0
ipykernel 6.29.3
ipython 8.22.2
ipywidgets 8.1.2
isort 5.13.2
itsdangerous 2.1.2
jedi 0.19.1
Jinja2 3.1.3
jmespath 1.0.1
joblib 1.3.2
jsonpatch 1.33
jsonpointer 2.4
jsonschema 4.21.1
jsonschema-specifications 2023.12.1
jupyter-cache 1.0.0
jupyter_client 8.6.1
jupyter_core 5.7.2
jupyterlab_widgets 3.0.10
kaleido 0.2.1
kiwisolver 1.4.5
kubernetes 29.0.0
langcodes 3.4.0
language_data 1.2.0
lazy_loader 0.4
libmambapy 1.5.7
llvmlite 0.42.0
locket 1.0.0
lxml 5.2.1
lz4 4.3.3
Mako 1.3.2
mamba 1.5.7
mapclassify 2.6.1
marisa-trie 1.1.1
Markdown 3.6
MarkupSafe 2.1.5
matplotlib 3.8.3
matplotlib-inline 0.1.6
mccabe 0.7.0
menuinst 2.0.2
mercantile 1.2.1
mizani 0.11.2
mlflow 2.11.3
mlflow-skinny 2.11.3
msgpack 1.0.7
multidict 6.0.5
multivolumefile 0.2.3
munkres 1.1.4
murmurhash 1.0.10
mypy 1.9.0
mypy-extensions 1.0.0
nbclient 0.10.0
nbformat 5.10.4
nest_asyncio 1.6.0
networkx 3.3
nltk 3.8.1
numba 0.59.1
numpy 1.26.4
oauthlib 3.2.2
opencv-python-headless 4.9.0.80
openpyxl 3.1.2
outcome 1.3.0.post0
OWSLib 0.28.1
packaging 23.2
pandas 2.2.1
paramiko 3.4.0
parso 0.8.4
partd 1.4.1
pathspec 0.12.1
patsy 0.5.6
Pebble 5.0.7
pexpect 4.9.0
pickleshare 0.7.5
pillow 10.3.0
pip 24.0
pkgutil_resolve_name 1.3.10
platformdirs 4.2.0
plotly 5.19.0
plotnine 0.13.5
pluggy 1.4.0
polars 0.20.18
preshed 3.0.9
prometheus_client 0.20.0
prometheus-flask-exporter 0.23.0
prompt-toolkit 3.0.42
protobuf 4.25.3
psutil 5.9.8
ptyprocess 0.7.0
pure-eval 0.2.2
py7zr 0.20.8
pyarrow 15.0.0
pyarrow-hotfix 0.6
pyasn1 0.5.1
pyasn1-modules 0.3.0
pybcj 1.0.2
pycodestyle 2.11.1
pycosat 0.6.6
pycparser 2.21
pycryptodomex 3.20.0
pydantic 2.7.1
pydantic_core 2.18.2
pyflakes 3.2.0
Pygments 2.17.2
PyJWT 2.8.0
pylint 3.1.0
PyNaCl 1.5.0
pynsee 0.1.7
pyOpenSSL 24.0.0
pyparsing 3.1.2
pyppmd 1.1.0
pyproj 3.6.1
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.0
PyYAML 6.0.1
pyzmq 25.1.2
pyzstd 0.15.10
QtPy 2.4.1
querystring-parser 1.2.4
rasterio 1.3.10
referencing 0.34.0
regex 2023.12.25
requests 2.31.0
requests-cache 1.2.0
requests-oauthlib 2.0.0
rpds-py 0.18.0
rsa 4.9
Rtree 1.2.0
ruamel.yaml 0.18.6
ruamel.yaml.clib 0.2.8
s3fs 2023.12.2
s3transfer 0.10.1
scikit-image 0.23.2
scikit-learn 1.4.1.post1
scipy 1.13.0
seaborn 0.13.2
selenium 4.20.0
setuptools 69.2.0
shapely 2.0.3
six 1.16.0
smart-open 6.4.0
smmap 5.0.0
sniffio 1.3.1
snuggs 1.4.7
sortedcontainers 2.4.0
soupsieve 2.5
spacy 3.7.4
spacy-legacy 3.0.12
spacy-loggers 1.0.5
SQLAlchemy 2.0.29
sqlparse 0.4.4
srsly 2.4.8
stack-data 0.6.2
statsmodels 0.14.1
tabulate 0.9.0
tblib 3.0.0
tenacity 8.2.3
texttable 1.7.0
thinc 8.2.3
threadpoolctl 3.4.0
tifffile 2024.5.3
tomli 2.0.1
tomlkit 0.12.4
toolz 0.12.1
topojson 1.8
tornado 6.4
tqdm 4.66.2
traitlets 5.14.2
trio 0.25.0
trio-websocket 0.11.1
truststore 0.8.0
typer 0.9.4
typing_extensions 4.11.0
tzdata 2024.1
Unidecode 1.3.8
url-normalize 1.4.3
urllib3 1.26.18
wasabi 1.1.2
wcwidth 0.2.13
weasel 0.3.4
webcolors 1.13
webdriver-manager 4.0.1
websocket-client 1.7.0
Werkzeug 3.0.2
wheel 0.43.0
widgetsnbextension 4.0.10
wordcloud 1.9.3
wrapt 1.16.0
wsproto 1.2.0
xgboost 2.0.3
xlrd 2.0.1
xyzservices 2024.4.0
yarl 1.9.4
yellowbrick 1.5
zict 3.0.0
zipp 3.17.0
zstandard 0.22.0

View file history

SHA Date Author Description
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)
Back to top

Footnotes

  1. Jeffrey Pennington, Richard Socher, and Christopher D. Manning. 2014. GloVe: Global Vectors for Word Representation.↩︎

Citation

BibTeX 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 = {en}
}
For attribution, please cite this work as:
Galiana, Lino. 2023. Python Pour La Data Science. https://doi.org/10.5281/zenodo.8229676.