Guide_Evaluation_LLM / pages /3_I.2._Tokénisation.py
bourdoiscatie's picture
Upload 44 files
1162aae verified
import streamlit as st
st.set_page_config(layout="wide")
from streamlit_extras.switch_page_button import switch_page
st.markdown(
"""
## Tokénisation
""", unsafe_allow_html=True)
st.markdown(""" """)
st.markdown(""" """)
st.markdown("""
### Pourquoi et comment tokéniser un texte ?
Étant donné que les grands modèles de langage sont en réalité de grandes fonctions mathématiques, ils traitent des nombres et non du texte.
Supposons que vous souhaitiez transformer une phrase en chiffres. Vous devez d'abord décider comment découper votre phrase en petits morceaux, puis faire correspondre chaque petit morceau à un nombre ; c'est ce qu'on appelle la *tokénisation*.
Dans le passé, les gens essayaient de faire correspondre chaque caractère d'un texte avec son index dans un alphabet (`a` -> 1, `b` -> 2, etc.), ce qui est appelé la *tokénisation basée sur les caractères*.
De l'autre côté du spectre, les gens essayaient aussi de faire correspondre chaque mot avec son index dans un dictionnaire (`a` -> 1, `aardvark` -> 2, `ab` -> 3, etc), ce qui est appelé la *tokénisation basée sur les mots* (vous séparez sur les espaces si votre langue en a, si ce n'est pas le cas c'est un peu plus difficile).
Ces deux méthodes ont une limite importante : elles suppriment des informations du texte d'entrée. Elles effacent les connexions sémantiques que vous pouvez voir dans la forme des mots (ex : `dis similar`, `similar`, `similar ity`, `similar ly`), des informations que nous voudrions que notre modèle conserve, afin qu'il relie les mots apparentés entre eux.<br>
De plus, que se passe-t-il si vous avez soudainement un mot complètement nouveau en entrée ? Il ne reçoit pas de numéro et votre modèle ne peut pas le traiter 😔.
Certains ont donc eu l'idée de découper les mots en sous-mots, et d'attribuer un index à ces sous-mots (`dis`, `similar`, `ity`, `ly`) !
À l'origine, on utilisait des règles morpho-syntaxiques (la « morpho-syntaxe » est comme la grammaire de la création des mots). Aujourd'hui, la plupart des gens utilisent l'encodage par paire d'octets (*byte pair encoding* ou BPE), une méthode statistique intelligente pour créer automatiquement les sous-mots en fonction de leur fréquence dans un texte de référence.
En résumé, la tokénisation est un moyen de mettre en correspondance de petites unités de texte (qui peuvent être un ou plusieurs caractères, jusqu'au niveau du mot) avec des nombres (similaires à un index). Lorsque vous souhaitez traiter du texte, votre texte d'entrée est divisé en ces *tokens* par un *tokenizer*. L'ensemble des *tokens* qu'un modèle ou un *tokenizer* peut analyser est appelé son *vocabulaire*.
""", unsafe_allow_html=True)
st.markdown(""" """)
st.markdown("""
##### Aller plus loin : Comprendre la tokénisation
Je conseille de lire l'un des 2 premiers liens en profondeur.
- ⭐ [Explication des différentes méthodes de tokénisation dans le cours de NLP d'Hugging Face🤗](https://huggingface.co/learn/nlp-course/fr/chapter2/4) (en français)
- ⭐ [Guide conceptuel sur la tokénisation dans la documentation de transformers 🤗](https://huggingface.co/docs/transformers/en/tokenizer_summary)
- [Cours de Jurafsky sur la tokénisation (et d'autres choses)](https://web.stanford.edu/~jurafsky/slp3/2.pdf), plus académique dans son approche, passez aux points 2.5 et 2.6 (le reste est également intéressant mais trop large).
""", unsafe_allow_html=True)
st.markdown(""" """)
st.markdown("""
##### Aller plus loin : *Byte Pair Encoding*
- ⭐ [Explication du BPE dans le cours de NLP d'Hugging Face🤗](https://huggingface.co/learn/nlp-course/fr/chapter6/5) (en français)
- [Papier introduisant le BPE au domaine du NLP](https://aclanthology.org/P16-1162/)
""", unsafe_allow_html=True)
st.markdown(""" """)
st.markdown(""" """)
st.markdown(""" """)
st.markdown("""
### Quelques-uns des nombreux problèmes de tokénisation
##### Choisir la bonne taille de vocabulaire
La taille du vocabulaire indique le nombre de *tokens* individuels (par exemple, les sous-mots) que le modèle devra apprendre.<br>
Un vocabulaire **trop grand** peut contenir des mots très rares comme *tokens* (par exemple : `aardvark`), ce qui peut conduire à 2 problèmes.<br>
Si un tel mot rare n'apparaît presque jamais dans les données d'entraînement, il peut être difficile de le relier à d'autres concepts, et le modèle peut être incapable de déduire de quoi il s'agit.<br>
D'un autre côté, s'il apparaît rarement et uniquement dans des contextes spécifiques, il peut être lié à d'autres mots très spécifiques : par exemple, si vous entraînez sur des données de forum, et que votre *tokenizer* associe un nom d'utilisateur à un seul *token* dans son vocabulaire, votre modèle peut alors associer ce *token* au contenu de l'utilisateur en question.
""", unsafe_allow_html=True)
st.markdown(""" """)
st.markdown("""
Un vocabulaire **trop petit** présentera deux autres problèmes : des capacités de représentation moindres et un coût accru lors de l'inférence.
Revenons à notre exemple ci-dessus, où nous avons tokénisé les mots dérivés de `similar`. L'utilisation d'une approche pseudo-BPE (grand vocabulaire) pour tokéniser `similarly` divise le mot en 2 *tokens* (`similaire`, `ly`). Si nous avions plutôt utilisé une tokénisation au niveau du caractère (donc avec un très petit vocabulaire, de la taille d'un alphabet), le même mot aurait été découpé en 9 *tokens* (`s`, `i`, `m`, `i`, `l`, `a`, `r`, `l`, `y`).<br>
Alors que la première méthode divise `similarly` en *tokens* qui ont une signification sémantique individuelle, ce n'est pas le cas dans la seconde méthode. Avec un vocabulaire trop petit, nous avons perdu une partie de la représentation sémantique. La différence de longueur des représentations signifie également qu'il est beaucoup plus coûteux de générer notre mot avec un vocabulaire plus petit (il faut 9 *tokens* au lieu de 2, donc 4,5 fois plus coûteux !)
""", unsafe_allow_html=True)
st.markdown(""" """)
st.markdown("""
Pour l'instant, la plupart des gens semblent utiliser une heuristique pour la taille du vocabulaire, qui semble corrélée au nombre de langues couvertes et à la taille du modèle. Il est donc probable que l'utilisation d'un nombre de *tokens* proche des modèles de référence d'une taille similaire puisse vous convenir.
""", unsafe_allow_html=True)
st.success("""
Pour entraîner un modèle de manière efficiente, il est primordial d'utiliser un multiple de 8 ou de 64 pour la taille du vocabulaire afin d'optimiser les capacités de votre GPU. Voir la documentation de NVIDIA [ici](https://docs.nvidia.com/deeplearning/performance/dl-performance-matrix-multiplication/index.html#requirements-tc) et [ici](https://developer.nvidia.com/blog/optimizing-gpu-performance-tensor-cores/) pour déterminer le bon multiple en fonction de la précision désirée.""")
st.markdown(""" """)
st.markdown(""" """)
st.markdown(""" """)
st.markdown("""
### Gestion de plusieurs langues
""", unsafe_allow_html=True)
st.markdown(""" """)
st.info("""Il est recommandé d'avoir lu une explication du BPE avant de se pencher sur cette section.""")
st.markdown("""
Lorsque vous construisez ou choisissez votre *tokenizer*, vous construisez votre vocabulaire à partir d'un texte de référence. Cela signifie que votre *tokenizer* connaîtra les mots de vocabulaire et les caractères de ce texte de référence. En général, cela signifie que vous utilisez des données en anglais, avec un script latin.
Si vous voulez ajouter une nouvelle langue, et que cette nouvelle langue utilise le même script et partage certaines racines, vous pouvez théoriquement espérer qu'une partie de la sémantique de votre langue d'origine soit transférée à la nouvelle langue.
Cependant, si vous voulez permettre à votre *tokenizer* de découper correctement du texte dans d'autres langues (en particulier des langues écrites dans d'autres scripts), vous feriez mieux d'inclure des données de ces langues lors de la construction dudit *tokenizer*. La plupart du temps, cependant, ces données contiendront une proportion déséquilibrée de la langue initiale (ex : anglais) par rapport à la nouvelle langue (ex : thaï ou birman), la langue initiale étant beaucoup plus présente. Étant donné que les méthodes de tokénisation les plus efficaces utilisées de nos jours (comme BPE) créent leurs *tokens* de vocabulaire complexe sur la base des mots les plus fréquents, la plupart des longs *tokens* seront des mots anglais - et la plupart des mots des langues moins fréquentes ne seront scindés qu'au niveau des caractères.
Cet effet entraîne une injustice dans la tokénisation multilingue : certaines langues (moins fréquentes ou **moins bien dotées en ressources**) nécessitent des ordres de grandeur de *tokens* supplémentaires pour générer une phrase de longueur équivalente à celle de l'anglais.
""", unsafe_allow_html=True)
st.markdown(""" """)
st.markdown("""
##### Aller plus loin : Langue et tokénisation
- ⭐ [Une belle analyse et démonstration de Yennie Jun sur les problèmes de tokénisation dans les différentes langues](https://www.artfish.ai/p/all-languages-are-not-created-tokenized)<br>La décomposition en elle-même est très claire, et cela vaut la peine de jouer avec la [démo](https://huggingface.co/spaces/yenniejun/tokenizers-languages).
- ⭐ [Une démonstration d'Aleksandar Petrov sur les injustices de la tokénisation](https://aleksandarpetrov.github.io/tokenization-fairness/)<br>Je recommande de regarder « Compare tokenization of sentences » pour avoir une idée des différences de coût d'inférence en fonction de la langue.
""", unsafe_allow_html=True)
st.markdown(""" """)
st.markdown(""" """)
st.markdown("""
### Qu'en est-il des nombres ?
Lorsque vous construisez votre *tokenizer*, vous devez décider de ce que vous allez faire des nombres. Faut-il indexer uniquement les chiffres de 0 à 9 et supposer que tous les autres nombres seront des compositions de chiffres, ou veut-on stocker les nombres jusqu'à, disons, un milliard, individuellement ? Les modèles actuels bien connus présentent une série d'approches à cet égard, mais on ne sait pas exactement ce qui fonctionne le mieux pour permettre un raisonnement mathématique. Peut-être que de nouvelles approches de tokénisation, telles que la tokénisation hiérarchique, pourraient être nécessaires pour cela.
""", unsafe_allow_html=True)
st.markdown(""" """)
st.markdown("""
##### Aller plus loin : Tokénisation des nombres
- ⭐ [Une belle démonstration visuelle par Yennie Jun sur la façon dont les *tokenizers* des modèles d'Anthropic, Meta, OpenAI, et Mistral divisent les nombres](https://www.artfish.ai/p/how-would-you-tokenize-or-break-down).
- [Petite histoire de l'évolution de la tokénisation des nombres au fil des années par Beren Millidge](https://www.beren.io/2024-05-11-Integer-tokenization-is-now-much-less-insane/).
""", unsafe_allow_html=True)
st.markdown(""" """)
st.markdown(""" """)
st.markdown(""" """)
col1, col2, col3= st.columns(3)
with col1:
if st.button('Section précédente', use_container_width=True):
switch_page("I.1._Inférence et évaluation des modèles")
with col2:
if st.button("Accueil", use_container_width=True):
switch_page("Home")
with col3:
if st.button("Chapitre suivant", use_container_width=True):
switch_page("II._JEUX_D'ÉVALUATION_AUTOMATISÉS")