!pip install scipy==1.12 gensim sentence_transformers pandas matplotlib seaborn
Ce chapitre va évoluer prochainement.
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 :
- 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.
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:
- Un premier notebook sur
Kaggle
et un deuxième ; - Un dépôt
Github
;
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.
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
:
= "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()
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
= 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 :
def clean_docs(texts, remove_stopwords=False, n_process=4):
= nlp.pipe(
docs =n_process, disable=["parser", "ner", "lemmatizer", "textcat"]
texts, n_process
)= nlp.Defaults.stop_words
stopwords
= []
docs_cleaned for doc in docs:
= [tok.text.lower().strip() for tok in doc if not tok.is_punct]
tokens if remove_stopwords:
= [tok for tok in tokens if tok not in stopwords]
tokens = " ".join(tokens)
doc_clean
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
.
"text_clean"] = clean_docs(spooky_df["text"]) spooky_df[
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.
= LabelEncoder()
le "author_encoded"] = le.fit_transform(spooky_df["author"]) spooky_df[
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.
= spooky_df["author_encoded"]
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 )
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
:
0] X_train[
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 :
= pd.Series(le.inverse_transform(y_train)).value_counts().plot(kind="bar")
fig 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
= clean_docs(X_train, remove_stopwords=True)
X_train_no_sw = np.array(X_train_no_sw) 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
= X_train_no_sw[le.inverse_transform(y_train) == initials]
texts = " ".join(texts).split()
all_tokens = Counter(all_tokens)
counts = [word[0] for word in counts.most_common(n_words)]
top_words = [word[1] for word in counts.most_common(n_words)]
top_words_counts
# Représentation sous forme de barplot
= sns.barplot(ax=ax, x=top_words, y=top_words_counts)
ax f"Most Common Words used by {initials_to_author[initials]}") ax.set_title(
= {
initials_to_author "EAP": "Edgar Allen Poe",
"HPL": "H.P. Lovecraft",
"MWS": "Mary Wollstonecraft Shelley",
}
= plt.subplots(3, 1, figsize=(12, 12))
fig, axs
"EAP", ax=axs[0])
plot_top_words("HPL", ax=axs[1])
plot_top_words("MWS", ax=axs[2]) plot_top_words(
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).
= LinearSVC(max_iter=10000, C=0.1, dual="auto") clf
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érente 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),
}
= GridSearchCV(
grid_search ="f1_micro", cv=4, n_jobs=4, verbose=1
pipeline, parameters, scoring
)
grid_search.fit(X_train, y_train)
= grid_search.best_estimator_.get_params()
best_parameters 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.",
]
= CountVectorizer()
vectorizer 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.
= vectorizer.transform(corpus)
X print(X.toarray())
Quel score F1
obtient-on finalement avec cette méthode de vectorisation sur notre problème de classification d’auteurs ?
= fit_vectorizers(CountVectorizer) cv_bow
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.",
]
= TfidfVectorizer()
vectorizer 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.
= vectorizer.transform(corpus)
X 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 ?
= fit_vectorizers(TfidfVectorizer) cv_tfidf
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.
= [text.split() for text in X_train]
X_train_tokens = Word2Vec(X_train_tokens, vector_size=200, window=5, min_count=1, workers=4) w2v_model
"mother") w2v_model.wv.most_similar(
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):
= [word for word in words if word in w2v_vectors]
words if words:
= np.mean(w2v_vectors[words], axis=0)
avg_vector else:
= np.zeros_like(w2v_vectors["hi"])
avg_vector return avg_vector
def fit_w2v_avg(w2v_vectors):
= np.array(
X_train_vectors for words in X_train_tokens]
[get_mean_vector(w2v_vectors, words)
)
= 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)}")
return scores
= fit_w2v_avg(w2v_model.wv) cv_w2vec
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 :
= gensim.downloader.load("glove-wiki-gigaword-200") glove_model
Par exemple, la représentation vectorielle de roi est l’objet multidimensionnel suivant :
"king"] glove_model[
Comme elle est peu intelligible, on va plutôt rechercher les termes les plus similaires. Par exemple,
"mother") glove_model.most_similar(
On peut retrouver notre formule précédente
\[\text{king} - \text{man} + \text{woman} ≈ \text{queen}\] dans ce plongement de mots:
=["king", "woman"], negative=["man"]) glove_model.most_similar(positive
Vous pouvez vous référer à ce tutoriel
pour en découvrir plus sur Word2Vec
.
Faisons notre apprentissage par transfert :
= fit_w2v_avg(glove_model) cv_w2vec_transfert
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 :
= pd.DataFrame(
perfs
["mean_test_score"]),
np.mean(cv_bow.cv_results_["mean_test_score"]),
np.mean(cv_tfidf.cv_results_[
np.mean(cv_w2vec),
np.mean(cv_w2vec_transfert),
],=[
index"Bag-of-Words",
"TF-IDF",
"Word2Vec non pré-entraîné",
"Word2Vec pré-entraîné",
],=["Mean F1 score"],
columns"Mean F1 score", ascending=False)
).sort_values( 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.).
TODO: approfondir le sujet
= SentenceTransformer("all-mpnet-base-v2") model
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.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 |
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.8 |
cytoolz | 1.0.0 |
dask | 2024.9.1 |
dask-expr | 1.1.15 |
databricks-sdk | 0.33.0 |
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.20.0 |
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 |
funcy | 2.0 |
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 |
h2 | 4.1.0 |
hpack | 4.0.0 |
htmltools | 0.6.0 |
hyperframe | 6.0.1 |
idna | 3.10 |
imageio | 2.36.0 |
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 |
langcodes | 3.5.0 |
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 |
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.10 |
mypy-extensions | 1.0.0 |
narwhals | 1.14.1 |
nbclient | 0.10.0 |
nbformat | 5.10.4 |
nest_asyncio | 1.6.0 |
networkx | 3.3 |
nltk | 3.9.1 |
numexpr | 2.10.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 |
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.0.7 |
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.9.2 |
pydantic_core | 2.23.4 |
Pygments | 2.18.0 |
pyLDAvis | 3.4.1 |
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.2 |
referencing | 0.35.1 |
regex | 2024.9.11 |
requests | 2.32.3 |
requests-cache | 1.2.1 |
retrying | 1.3.4 |
rich | 13.9.4 |
rpds-py | 0.21.0 |
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.0.5 |
smmap | 5.0.0 |
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.4.8 |
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.9.20 |
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.13.1 |
typing_extensions | 4.12.2 |
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 |
---|---|---|---|
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) |
Notes de bas de page
Jeffrey Pennington, Richard Socher, and Christopher D. Manning. 2014. GloVe: Global Vectors for Word Representation.↩︎
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}
}