1 points par GN⁺ 2024-09-16 | 1 commentaires | Partager sur WhatsApp
  • WordLlama est une boîte à outils NLP rapide et légère qui réutilise les embeddings de tokens d’un LLM pour effectuer du dédoublonnage flou, du calcul de similarité, du ranking, du clustering et du découpage sémantique de texte
  • L’inférence repose principalement sur la consultation de tokens et le pooling moyen, avec un pipeline léger exécutable uniquement avec NumPy et optimisé pour le CPU
  • Le modèle par défaut fait 256 dimensions pour 16 Mo ; il est possible de réduire la dimension avec une représentation Matryoshka, et les embeddings binaires permettent des calculs plus rapides via la similarité de Hamming
  • Dans le tableau MTEB, les modèles WL64 à WL1024 obtiennent de meilleurs scores que GloVe 300d et Komninos sur plusieurs métriques, mais restent globalement en dessous de all-MiniLM-L6-v2
  • Après pip install wordllama, on peut l’utiliser avec WordLlama.load(), et .key(query) renvoie un callable pouvant être passé à des fonctions de la bibliothèque standard comme sorted, min ou max

Ce que fait WordLlama

  • WordLlama est une boîte à outils légère pour des tâches utilitaires NLP comme le dédoublonnage flou, le calcul de similarité, le ranking, le clustering et le découpage sémantique de texte
  • Il extrait le codebook d’embeddings de tokens de LLM récents comme LLaMA 2 ou LLaMA 3 70B afin de produire des représentations compactes de mots, proches de GloVe, Word2Vec ou FastText
  • À l’inférence, il a peu de dépendances et est optimisé pour le matériel CPU, ce qui le rend adapté aux déploiements dans des environnements aux ressources limitées
  • Grâce à sa rapidité et à sa petite taille, il peut servir à des usages utilitaires comme l’analyse exploratoire, les évaluateurs de sorties de LLM, ou la préparation de workflows multi-hop ou agentiques

Installation et usage de base

  • L’installation se fait avec pip
pip install wordllama
  • Le modèle 256 dimensions par défaut se charge avec WordLlama.load()
from wordllama import WordLlama

wl = WordLlama.load()
  • .key(query) renvoie un Callable[[str], float] permettant de trier des chaînes candidates selon leur similarité avec la requête, ou de choisir la meilleure
query = "Machine learning methods"
candidates = [
    "Foundations of neural science",
    "Introduction to neural networks",
    "Cooking delicious pasta at home",
    "Introduction to philosophy: logic",
]

sim_key = wl.key(query)

sorted_candidates = sorted(candidates, key=sim_key, reverse=True)
best_candidate = max(candidates, key=sim_key)
  • Dans l’exemple, "Introduction to neural networks" est la meilleure candidate avec un score de 0.3414

Fonctionnalités principales

  • Génération d’embeddings : création rapide d’embeddings de texte via simple consultation de tokens et pooling moyen
  • Calcul de similarité : calcule la similarité cosinus entre deux textes
  • Ranking de documents : classe les documents candidats selon leur similarité avec une requête
  • Dédoublonnage flou : supprime les textes dupliqués selon un seuil de similarité
  • Clustering : regroupe des documents avec KMeans
  • Filtrage : ne conserve que les documents dont la similarité avec la requête dépasse un seuil
  • Recherche Top-K : renvoie les K documents les plus proches de la requête
  • Découpage sémantique de texte : divise un texte en segments sémantiquement cohérents
  • Embeddings binaires : permettent des calculs plus rapides avec la similarité de Hamming
  • Représentation Matryoshka : permet d’ajuster la taille du modèle et les performances en tronquant la dimension des embeddings selon les besoins

Architecture du modèle et performances

  • WordLlama entraîne un petit modèle sans contexte dans un framework général d’embeddings
  • Le modèle par défaut a une taille de 16 Mo en 256 dimensions
  • Le tableau MTEB du README compare WL64, WL128, WL256, WL512 et WL1024 à GloVe 300d, Komninos et all-MiniLM-L6-v2
    • WL256 obtient 33.25 en Clustering, 52.03 en Reranking, 58.21 en Classification, 78.22 en Pair Classification, 67.91 en STS, 24.12 en CQA DupStack et 30.99 en SummEval
    • GloVe 300d obtient respectivement 27.73, 43.29, 57.29, 70.92, 61.85, 15.47, 28.87
    • all-MiniLM-L6-v2 obtient 42.35 en Clustering, 58.04 en Reranking, 63.05 en Classification, 82.37 en Pair Classification, 78.90 en STS, 41.32 en CQA DupStack et 30.81 en SummEval
  • l2_supercat est un modèle basé sur le vocabulaire de LLaMA 2
    • Il est entraîné après suppression des tokens spéciaux additionnels, puis concaténation des codebooks de plusieurs modèles comme LLaMA 2 70B et phi 3 medium
    • Il peut être entraîné en concaténant les codebooks de plusieurs modèles utilisant le tokenizer LLaMA 2
    • Il offre des performances proches d’un entraînement sur le codebook de LLaMA 3 70B, avec un vocabulaire 4 fois plus petit, soit 32k contre 128k
  • Un modèle basé sur LLaMA 3, l3_supercat, est également proposé
  • Des résultats supplémentaires sont disponibles dans Results

Découpage sémantique de texte

  • .split() découpe un long texte en segments sémantiques
long_text = "Your very long text goes here... " * 100
chunks = wl.split(long_text, target_size=1536)

print(list(map(len, chunks)))

# Output: [1055, 1055, 1187]
  • target_size est à la fois la taille visée et la taille maximale
  • Le processus de découpage essaie de préserver l’ordre du texte, la structure des phrases et, si possible, celle des paragraphes
  • Il utilise les embeddings de WordLlama pour trouver des indices de découpage plus naturels
  • La taille des segments produits peut varier tant qu’elle reste inférieure ou égale à target_size
  • La taille recommandée pour target_size est de 512 à 2048 caractères, avec une valeur par défaut de 1536
  • Si des segments plus grands sont nécessaires, il est recommandé de regrouper plusieurs semantic chunks en lot après le découpage
  • Plus de détails sont disponibles dans le technical overview

Model2Vec et inférence directe

  • La mise à jour du 2025-01-04 ajoute la prise en charge des embeddings statiques Model2Vec
  • Les modèles Model2Vec peuvent être chargés avec WordLlama.load_m2v()
wl = WordLlama.list_configs()

wl = WordLlama.load_m2v("potion_base_8m")  # 256-dim model
wl = WordLlama.load_m2v("m2v_multilingual")  # multilingual model
  • Model2Vec est une autre méthode de création d’embeddings statiques, fondée sur la PCA
  • Le projet Model2Vec propose un modèle multilingue et des modèles basés sur GloVe, avec de bons résultats sur les tâches de similarité lexicale
  • Ils sont disponibles sur la page Hugging Face de minishlab
  • WordLlamaInference peut aussi être utilisé directement en fournissant un tableau d’embeddings statiques de forme (n_vocab, dim) et un tokenizer, sans passer par le loader
from wordllama import WordLlamaInference
from tokenizers import Tokenizer

tokenizer = Tokenizer.from_pretrained(...)
wl = WordLlamaInference(np_embeddings_ar, tokenizer)

Entraînement et extraction des embeddings

  • Les modèles d’embeddings binaires montrent des gains plus nets à haute dimension, et 512 ou 1024 dimensions sont recommandées pour ce type d’embedding
  • Le modèle L2 Supercat a été entraîné pendant 12 heures sur un seul GPU A100 avec une batch size de 512
  • Pour extraire les embeddings de tokens d’un modèle LLaMA, il faut accepter le contrat d’utilisation et se connecter au CLI Hugging Face
from wordllama.extract.extract_safetensors import extract_safetensors

extract_safetensors("llama3_70B", "path/to/saved/model-0001-of-00XX.safetensors")
  • Les embeddings se trouvent généralement dans le premier fichier safetensors, mais pas toujours
    • il peut y avoir un manifest
    • il peut être nécessaire d’inspecter les fichiers manuellement pour les trouver
  • L’entraînement utilise les scripts du dépôt ; il faut ajouter un fichier de configuration en copiant ou modifiant une configuration existante
pip install wordllama[train]
python train.py train --config your_new_config
python train.py save --config your_new_config --checkpoint ... --outdir /path/to/weights/
  • L’étape de sauvegarde enregistre les modèles un par un pour chaque dimension Matryoshka

Mises à jour, feuille de route et licence

  • La mise à jour du 2025-02-01 ajoute la prise en charge de callables utilisables avec des fonctions de la bibliothèque standard comme sorted, min et max
  • La mise à jour du 2024-10-04 ajoute l’algorithme d’inférence pour le découpage sémantique
  • La feuille de route prévoit l’ajout d’un évaluateur DSPy et de notebooks d’exemple pour des pipelines de Retrieval-Augmented Generation, c’est-à-dire RAG
  • Parmi les projets communautaires figurent Gradio Demo HF Space et CPU-ish RAG
  • Le projet est sous licence MIT License

1 commentaires

 
GN⁺ 2024-09-16
Avis sur Hacker News
  • J’aime beaucoup sa petite taille. Il a déjà des avantages même par rapport au plus petit modèle de SBERT.
    Cela dit, techniquement, l’approche semble assez ancienne, et je comprends que ce soit un compromis avec les performances. Je me demande tout de même s’il serait possible de proposer un changement de type de similarité, par exemple similarité sémantique, inférence en langage naturel (NLI), ou abstraction nominale.
    Par exemple, si l’on regroupe des articles de presse dans une catégorie comme « événements environnementaux extrêmes », on veut que « Freezing » et « Burning » ressortent comme très proches. C’est le comportement de MTEB/Sentence-Similarity ou des classiques Word2Vec/GloVe. Mais dans un article de chimie, les deux devraient presque apparaître comme opposés, et l’on peut aussi vouloir utiliser des embeddings d’inférence en langage naturel pour examiner les relations causales entre deux choses.
    Les deux derniers types d’embeddings sont des approches relativement récentes, postérieures à 2019, donc j’y vois davantage d’opportunités techniques. Les anciennes familles MTEB/similarité sémantique suffisaient déjà à de nombreux usages depuis 2014, et se sont nettement améliorées en 2019 avec mini-lm-v2 et autres.
    Ces trois types d’embeddings sont aussi possibles avec SBERT, mais les dimensions sont grandes et les modèles volumineux ; charger plusieurs modèles selon le type devient coûteux en ressources. Les modèles d’embeddings génératifs, E5 ou les modèles d’inférence en langage naturel sont gros et nécessitent souvent autour de 6 Go.

    • Bonne idée. Je vais faire quelques expériences et vérifier si c’est faisable.
      J’aimerais voir quelles sont les performances lorsqu’on l’entraîne sur un seul type de similarité. Je ne suis pas sûr qu’il existe une autre manière de gérer cela sans calcul contextuel. Il faudra peut-être changer de modèle, mais ce n’est pas un gros problème en soi.
    • C’est un modèle de 17 Mo et, dans les benchmarks, il est évidemment en dessous de MiniLM v2, donc de SBERT. De mon côté, je fais tourner V3 en ONNX avec un modèle de 23 Mo sur presque toutes les plateformes.
      Je ne dis pas cela pour le dénigrer ; il est important de replacer ce genre de travail dans son contexte. Ici, le contexte est qu’en approfondissant sa compréhension des LLM, on découvre qu’ils ont aussi des embeddings, et sous cet angle, il est plus naturel de bricoler avec ces embeddings et d’avancer d’un pas, plutôt que de repasser en revue tout l’état de l’art du domaine des embeddings.
    • Si « embeddings ChatGPT » désigne les modèles d’embeddings d’OpenAI, alors « burning » et « freezing » ne sont pas du tout opposés. En utilisant text-embedding-large-3 en 1024 dimensions, leur similarité cosinus est d’environ 0,46. Si les embeddings étaient totalement opposés, la similarité devrait être de -1.
      Croire que des mots de sens opposé ont des embeddings opposés est une erreur courante. En réalité, des mots de sens opposé ont aussi beaucoup de points communs. « burning » et « freezing » sont tous deux liés à la température et à la physique, ce sont des mots anglais, ils peuvent être utilisés comme verbes, noms ou adjectifs, et leur orthographe est correcte. Toutes ces caractéristiques se retrouvent dans les embeddings.
  • Les embeddings contiennent beaucoup d’information sémantique selon les données d’entraînement et la fonction objectif, et peuvent être utilisés indépendamment pour de nombreuses tâches utiles.
    J’ai déjà utilisé les embeddings de l’encodeur texte d’un modèle CLIP pour enrichir des prompts afin qu’ils correspondent mieux aux images associées. Par exemple, lorsqu’un prompt contenait « building », je cherchais dans la matrice d’embeddings ses plus proches voisins comme « concrete » ou « underground », puis je les remplaçais ou les ajoutais après ce mot. Dans des expériences limitées, cela a amélioré le rappel pour la plupart des requêtes.

    • Exact. On peut entraîner un modèle d’embeddings à capturer ce type de relations contextuelles propres au domaine.
      https://www.marqo.ai/blog/generalized-contrastive-learning-f...
    • C’est une très bonne idée. Elle semble aussi applicable à cette implémentation, donc je vais y réfléchir davantage.
      Dans wordllama, la taille des embeddings de tokens pourrait aussi aider à identifier les tokens importants à enrichir. Cela dit, l’approche fonctionnerait probablement beaucoup mieux avec un entraînement sur des données sélectionnées pour cette tâche.
  • Je me demande s’il y a aussi des projets pour des langues autres que l’anglais. Cela me semble être un outil parfait pour le français.

    • C’est tout à fait possible. Il faudrait constituer un corpus d’entraînement, mais je ne connais pas bien les ressources disponibles en français.
      J’ai déjà fait un peu d’entraînement avec des modèles de la famille Mistral, donc j’essaierais probablement d’abord cette piste pour un corpus français.
      Si vous ouvrez une issue, je m’y mettrai quand j’aurai du temps.
  • Pour de grands corpus, par exemple plus de 10 000 phrases où chaque phrase est considérée comme un document, on peut obtenir des résultats similaires en faisant du clustering k-means sur des vecteurs de matrice creuse TF-IDF.
    Cela dit, cet outil semble proposer pas mal d’utilitaires pour accélérer la partie k-means, par exemple via la binarisation. Je compte faire des benchmarks dans les prochaines semaines.

  • Il y a quelques années, j’ai créé une collection de jeux de langage utilisant des fonctions similaires : https://github.com/Hellisotherpeople/Language-games

  • Je me demande si quelqu’un a déjà pensé à utiliser des embeddings pour résoudre Little Alchemy. #sample-use

  • Ça a l’air sympa. Je me demande s’il y a des avantages par rapport aux modèles mini-lm. Sur la plupart des tâches MTEB, mini-lm semble meilleur ; je me demande donc s’il y a des points où celui-ci fait mieux, comme la vitesse d’inférence.

    • Mini-lm est un meilleur modèle d’embeddings. Ce modèle-ci ne fait pas de calcul d’attention et, après l’entraînement, n’utilise même pas de framework de deep learning. Il ne bénéficie donc pas des avantages contextuels des modèles Transformer.
      Il ne vise pas non plus les meilleures performances de l’état de l’art. C’est un modèle conçu avec des contraintes assez fortes afin de réduire les dépendances, la taille et les exigences matérielles, tout en augmentant la vitesse.
      Même comme modèle d’embeddings de mots, il est plutôt léger. En général, ces modèles disposent d’un vocabulaire beaucoup plus grand et pèsent souvent plusieurs Go.
    • Cela semble venir de la différence de taille du modèle lui-même. Il est plus léger et plus rapide. mini-lm fait 80 Mo, tandis que le plus petit modèle ici fait 16 Mo.
  • Ça a l’air très utile pour créer des jeux.

  • Cela montre bien la quantité de contenu sémantique présente dans les tokens eux-mêmes.

  • Pourrait-on en faire une extension PostgreSQL ?