Les grands modèles de langage (LLMs) ont fait sensation dans le monde entier, démontrant une capacité étonnante à comprendre et générer le langage humain. Cependant, bien qu'ils excellent à saisir les schémas linguistiques généraux, atteindre une spécialisation dans des domaines spécifiques nécessite une formation supplémentaire. C'est là que le fine-tuning intervient. Le fine-tuning des LLMs exploite la vaste connaissance acquise par les LLMs et l'adapte aux tâches spécialisées.
Optimisation fine des LLM en utilisant PEFT
Imaginez un LLM pré-entraîné sur un corpus massif de texte. Il peut rédiger différents types de contenus créatifs, traduire des langues et répondre de manière informative aux questions. Mais peut-il diagnostiquer une maladie, rédiger des contrats légaux ou composer de la musique dans un style spécifique ? Pas sans un certain guidage ciblé. Le réglage fin comble cette lacune, transformant le LLM d'un touche-à-tout en un expert spécifique à un domaine. Par exemple, examinons les complétions de davinci (modèle de base GPT-3) et text-davinci-003 (modèle finement réglé selon des instructions).
Figure 1. Comparaison de l'achèvement entre un modèle de base GPT-3 et son modèle fine-tuned correspondant.
Remarquez la différence ? Alors que le modèle de base passe en mode recherche, la version affinée donne une réponse plus utile et informative. C'est là la puissance de l'affinage. En formant un modèle sur des objectifs et des valeurs spécifiques, nous pouvons libérer son véritable potentiel. Dans cet article, nous fournirons un bref aperçu des techniques populaires d'affinage. Ensuite, nous nous concentrerons sur la compréhension de LoRA (Low-Rank Adaptation) en détail. Ensuite, nous fournirons un guide détaillé, en parcourant le processus étape par étape de l'affinage d'un grand modèle de langage (LLM) pour une tâche de résumé en utilisant LoRA.
Pourquoi affiner les LLMs ?
Pourquoi se donner la peine de peaufiner alors que les LLM semblent déjà assez impressionnants ? La réponse réside dans la puissance de la spécialisation. Voici quelques avantages clés de la peaufinage des LLM :
- Précision et performance améliorées : Le peaufinage permet aux LLM d'explorer plus en profondeur les subtilités d'un domaine spécifique, conduisant à des résultats plus précis et pertinents. Imaginez un LLM médical formé sur des revues médicales et des données de patients ; ses diagnostics pourraient être bien plus précis qu'un LLM se basant uniquement sur du texte général.
- Temps de formation et ressources réduits : Commencer avec un LLM pré-entraîné permet d'économiser énormément de temps et de ressources computationnelles par rapport à une formation à partir de zéro. Pensez-y comme construire sur une base existante plutôt que d'en poser une nouvelle.
- Amélioration de la généralisabilité : Bien que peaufiné pour une tâche spécifique, le LLM conserve sa capacité à s'adapter et à apprendre dans ce domaine. Cette flexibilité accrue lui permet de gérer les données et les situations inconnues de manière plus efficace.
- Débloque le plein potentiel des LLM : Le peaufinage ouvre la voie à une variété d'applications que les LLM sont capables de réaliser. De l'éducation personnalisée à la recherche juridique automatisée, les possibilités sont illimitées.
L'optimisation fine non seulement améliore les performances d'un modèle de base, mais un modèle plus petit (optimisé) peut souvent surpasser des modèles plus grands (plus coûteux) sur l'ensemble des tâches sur lesquelles il a été entraîné. OpenAI a démontré cela avec leurs modèles de première génération "InstructGPTˮ, où les complétions du modèle InstructGPT de 1,3 milliard de paramètres étaient préférées au modèle de base GPT-3 de 175 milliards de paramètres malgré sa taille 100 fois plus petite. Cela montre la puissance qu'offre l'optimisation fine.
Dans cet article, nous nous concentrerons sur les techniques de fine-tuning à faible nombre de paramètres (PEFT). Pour explorer le fine-tuning complet, vous pouvez consulter notre article précédent sur Fine Tuning T5.
Techniques PEFT
Les LLM peuvent être assez volumineux, les plus grands nécessitant des centaines de gigaoctets de stockage. Lors de l'exécution d'un réglage fin complet, les besoins en mémoire GPU sont également énormes. Vous devez stocker non seulement tous les poids du modèle, mais aussi les gradients, les états de l'optimiseur, les activations avant, et les états temporaires tout au long du processus d'entraînement. Cela peut nécessiter une quantité importante de puissance de calcul, ce qui peut être très coûteux et difficile à obtenir.
Contrairement à un réglage fin complet, où chaque poids du modèle est mis à jour pendant le processus d'apprentissage supervisé, les méthodes de réglage fin à efficacité de paramètres (PEFT) ne mettent à jour qu'un petit sous-ensemble de poids. Cela peut être réalisé en affinant des couches ou des composants sélectionnés du LLM, ou en gelant tous les poids du LLM, en ajoutant un petit nombre de nouveaux paramètres ou de nouvelles couches, et en mettant à jour uniquement les nouveaux composants. En général, seuls 15 à 20 % du nombre total de paramètres du LLM sont mis à jour, et souvent, le PEFT peut être effectué en utilisant un seul GPU.
Étant donné que la plupart des poids LLM sont seulement légèrement modifiés ou laissés inchangés, le risque d'oubli catastrophique est également considérablement réduit.
Les 3 principales classes de méthodes PEFT sont les suivantes :
1. Méthodes sélectives : Ces méthodes se spécialisent dans l'ajustement fin d'une partie seulement des paramètres initiaux du LLM. Vous avez diverses options pour décider quels paramètres modifier. Vous pouvez choisir de former des composants spécifiques du modèle, des couches spécifiques, ou même des types de paramètres spécifiques.
2. Méthodes de reparamétrisation : Ces méthodes réduisent le nombre de paramètres à entraîner en créant de nouvelles transformations de bas rang des poids du réseau d'origine. Une technique couramment utilisée de ce type est LoRA, que nous explorerons en détail dans les sections suivantes.
3. Méthodes additives : Ces méthodes effectuent un ajustement fin en gardant les poids originaux du LLM inchangés et en incorporant de nouveaux composants entraînables. Il existe deux approches principales dans ce contexte. Les méthodes d'adaptateur introduisent de nouvelles couches entraînables dans l'architecture du modèle, généralement au sein des composants de l'encodeur ou du décodeur, suivant les couches d'attention ou feed-forward. En revanche, les méthodes de prompt doux maintiennent l'architecture du modèle fixe et figée tout en se concentrant sur la manipulation de l'entrée pour améliorer les performances. Cela peut être réalisé en ajoutant des paramètres entraînables aux embeddings de prompt ou en gardant l'entrée inchangée et en réentraînant les poids des embeddings.
Adaptateurs
Les méthodes basées sur les adaptateurs ajoutent des paramètres entraînables supplémentaires après les couches d'attention et entièrement connectées d'un modèle pré-entraîné figé pour réduire l'utilisation de la mémoire et accélérer l'entraînement. La méthode varie en fonction de l'adaptateur, il pourrait simplement s'agir d'une couche supplémentaire ajoutée ou il pourrait exprimer les mises à jour de poids ∆W sous forme d'une décomposition de rang faible de la matrice de poids. De toute façon, les adaptateurs sont généralement petits mais démontrent des performances comparables à un modèle entièrement affiné, permettant d'entraîner des modèles plus grands avec moins de ressources.
Il existe plusieurs façons d'exprimer la matrice de poids sous forme de décomposition de rang faible, mais l'Adaptation de Rang Faible (LoRA) est la méthode la plus courante. Il existe plusieurs autres variantes de LoRA, telles que le Produit Hadamard de Rang Faible (LoHa), le Produit de Kronecker de Rang Faible (LoKr), et l'Adaptation de Rang Faible Adaptative (AdaLoRA). Dans cet article, nous nous concentrerons sur la compréhension de comment LoRA fonctionne et comment l'utiliser pour affiner les LLM selon vos besoins.
Suggestions douces
La formation de grands modèles de langage pré-entraînés (LLM) représente un défi important en termes d'investissement en ressources temporelles et computationnelles. À mesure que leur taille continue de croître, les chercheurs se tournent de plus en plus vers des méthodes d'entraînement plus efficaces telles que le prompting. Le prompting exploite un modèle pré-entraîné et figé pour des tâches spécifiques en introduisant un prompt textuel qui décrit la tâche ou propose un exemple illustratif. Cette approche élimine le besoin de former entièrement un modèle séparé pour chaque tâche spécifique, permettant à un même modèle pré-entraîné figé d'être utilisé.
Cette simplification présente deux principaux avantages : le modèle peut être facilement adapté pour gérer diverses tâches, et l'entraînement et le stockage d'un ensemble plus restreint de paramètres d'invite se révèlent considérablement plus efficaces que l'entraînement de l'ensemble des paramètres du modèle.
Les méthodologies de sollicitation peuvent être largement classées en deux groupes distincts :
- Indications dures : Il s'agit d'indications textuelles créées manuellement composées de jetons d'entrée discrets. Bien que cette approche offre l'avantage de l'interprétabilité humaine, elle nécessite des efforts importants pour construire une indication efficace. 2. Indications douces : Celles-ci utilisent des tenseurs apprenables concaténés avec les plongements d'entrée, permettant une optimisation basée sur un ensemble de données spécifique. L'inconvénient est qu'elles ne sont pas lisibles par les humains car vous ne faites pas correspondre ces "jetons virtuels" aux plongements d'un mot réel
Un bref aperçu des méthodes de saisie douce est donné comme suit :
Réglage de l'instruction : C'est comme donner à votre modèle des instructions spécifiques au début. Cela signifie lui demander de générer l'étiquette de classe pour la classification de texte. Les instructions, appelées prompts, sont ajoutées à l'entrée sous forme d'une série de jetons. L'idée clé derrière le réglage de l'instruction est que les jetons d'instruction ont leurs paramètres qui sont mis à jour indépendamment. Cela signifie que vous pouvez garder les paramètres du modèle pré-entraîné figés et mettre à jour uniquement les gradients des plongements de jetons d'instruction. Les résultats sont comparables à la méthode traditionnelle de formation de l'ensemble du modèle, et les performances du réglage de l'instruction augmentent à mesure que la taille du modèle augmente.
Réglage du préfixe : Il a été conçu pour les tâches de génération de langage naturel (NLG) sur les modèles GPT. Il est très similaire au réglage de la requête ; le réglage du préfixe ajoute également une séquence de vecteurs spécifiques à la tâche à l'entrée qui peuvent être entraînés et mis à jour tout en maintenant les autres paramètres du modèle pré-entraîné figés.
La principale différence est que les paramètres de préfixe sont insérés dans toutes les couches du modèle, tandis que l'ajustement de l'amorçage n'ajoute que les paramètres d'amorçage aux plongements d'entrée du modèle. Les paramètres de préfixe sont également optimisés par un réseau feed-forward (FFN) séparé au lieu d'être entraînés directement sur les amorces douces car cela provoque de l'instabilité et dégrade les performances. Le FFN est abandonné après la mise à jour des amorces douces.
En conséquence, les auteurs ont constaté que l'accordage de préfixes démontre des performances comparables à celles d'un réglage fin complet d'un modèle, malgré le fait qu'il a 1000 fois moins de paramètres, et il fonctionne encore mieux dans des contextes de faibles données.
P-tuning : Il est conçu pour la compréhension du langage naturel (NLU) et toutes les tâches des modèles de langage. C'est une autre variation d'une méthode de prompt souple ; P-tuning ajoute également un tenseur d'incorporation entraînable qui peut être optimisé pour trouver de meilleurs prompts, et il utilise un encodeur de prompt (un réseau LSTM bidirectionnel à mémoire court-terme ou longue) pour optimiser les paramètres du prompt. Contrairement au réglage de préfixe, cependant :
- Les jetons d'invite peuvent être insérés n'importe où dans la séquence d'entrée, et cela n'est pas limité uniquement au début
- Les jetons d'invite ne sont ajoutés qu'à l'entrée au lieu de les ajouter à chaque couche du modèle
- L'introduction de jetons d'ancre peut améliorer les performances car ils indiquent les caractéristiques d'un composant dans la séquence d'entrée
Les résultats suggèrent que le P-tuning est plus efficace que la création manuelle de prompts, et il permet aux modèles de type GPT de rivaliser avec les modèles de type BERT sur les tâches de compréhension automatique du langage naturel.
LoRA : Un Aperçu
Low-rank Adaptation, ou LoRA pour faire court, est une technique de fine-tuning efficace en termes de paramètres qui relève de la catégorie de re-paramétrisation. Pour rappel rapide, voici le diagramme de l'architecture du transformateur que nous avons vu précédemment dans Décryptage des LLM : Des Transformers à la Quantification
Figure 5. Architecture du Transformer de ce document.
La saisie de l'invite passe par un processus de tokenisation, la divisant en jetons. Ces jetons sont ensuite transformés en vecteurs d'incorporation, qui sont ensuite introduits dans les sections de l'encodeur et/ou du décodeur du transformateur. À l'intérieur de ces composants, deux types de réseaux neuronaux opèrent : les réseaux d'auto-attention et les réseaux à propagation avant. Les poids de ces réseaux sont acquis lors de la pré-formation.
Une fois que les vecteurs d'incorporation sont générés, ils sont dirigés vers les couches d'auto-attention. Ici, un ensemble de poids est appliqué pour calculer les scores d'attention. Dans le processus complet de fine-tuning, chaque paramètre de ces couches subit des mises à jour pour affiner le modèle
Figure 6. Adaptateurs à faible rang de ce document.
Figure 7. Matrices de faible rang.
LoRA est une stratégie qui réduit le nombre de paramètres à entraîner lors du fine-tuning en gelant tous les paramètres du modèle d'origine, puis en injectant une paire de matrices de décomposition de rang aux côtés des poids d'origine. Les dimensions des matrices plus petites sont définies de sorte que leur produit soit une matrice avec les mêmes dimensions que les poids qu'elles modifient. Ensuite, vous gardez les poids d'origine du LLM gelés et entraînez les matrices plus petites avec l'apprentissage supervisé. Pour l'inférence, les deux matrices de faible rang sont multipliées pour créer une matrice avec les mêmes dimensions que les poids gelés. Vous ajoutez ensuite cela aux poids d'origine et les remplacez par ces valeurs mises à jour dans le modèle.
Les chercheurs ont découvert que l'application de LoRA aux seules couches d'auto-attention du modèle est souvent suffisante pour affiner une tâche et obtenir des gains de performance.
Prenons un exemple pratique en utilisant l'architecture du transformateur décrite dans l'article 'Attention is All You Need'. Selon l'article, les poids du transformateur ont des dimensions de 512 par 64, ce qui donne 32 768 paramètres entraînables pour chaque matrice de poids.
Si vous utilisez LoRA comme méthode de peaufinage avec un rang égal à seize, vous entraînerez deux petites matrices de décomposition de rang avec une petite dimension de huit. La matrice A sera de 64 par 16, totalisant 1024 paramètres au total. Pendant ce temps, la matrice B sera de 16 par 512, contribuant à 8 192 paramètres entraînables. En mettant à jour les poids de ces nouvelles matrices de bas rang au lieu des poids originaux, vous réduisez efficacement les paramètres d'entraînement de 32 768 à 9 216 – une réduction substantielle de 72 %.
QLoRA
S'appuyant sur le succès de LoRA (Low-Rank Adaptation), QLoRA (Quantized Low-Rank Adaptation) introduit deux innovations clés pour maximiser davantage l'efficacité lors du fine-tuning des grands modèles de langage (LLMs) :
1. Quantification : Alors que LoRA réduit le nombre de paramètres entraînables en représentant les mises à jour avec des matrices de faible rang, QLoRA ajoute une autre couche d'optimisation. Il utilise la quantification NormalFloat 4 bits (NF4), un type de données de quantification optimal sur le plan informationnel pour les données distribuées normalement qui donne de meilleurs résultats empiriques que les entiers 4 bits et les flottants 4 bits. Un processus de quantification typique consiste à convertir un type de données avec plus de bits en un type de données contenant moins de bits. Pour garantir que l'ensemble de la plage du type de données à faible nombre de bits est utilisé, le type de données d'entrée est communément rééchelonné dans la plage du type de données cible par normalisation par le maximum absolu des éléments d'entrée.
où c est la constante de quantification ou échelle de quantification.
Le problème avec cette approche est que si une valeur de grande magnitude (c'est-à-dire, un outlier) se produit dans le tenseur d'entrée, alors le bin de quantification certaines combinaisons de bits ne sont pas bien utilisées avec peu ou pas de nombres quantifiés dans certains bins. Pour résoudre ce problème, le type de données NormalFloat (NF) s'appuie sur la Quantification des Quantiles qui est un type de données optimal d'un point de vue informationnel qui garantit que chaque bin de quantification a un nombre égal de valeurs assignées à partir du tenseur d'entrée. La quantification des quantiles fonctionne en estimant le quantile du tenseur d'entrée à travers la fonction de distribution cumulative empirique. En termes simples, la différence entre la quantification standard et la quantification de flottants normaux est que la représentation ici est de taille égale plutôt que d'être également espacée.
Cela offre plusieurs avantages significatifs tels qu'une réduction significative (de 32 bits à 4 bits par paramètre) des besoins en mémoire, une réduction des calculs et une formation et une inférence plus rapides.
2. Double Quantification : QLoRA va plus loin dans la quantification en utilisant la double quantification. Cela signifie que non seulement les poids LLM d'origine sont quantifiés, mais aussi les constantes de quantification. Plus précisément, la Double Quantification traite les constantes de quantification cFP32 de la première quantification comme des entrées pour une deuxième quantification. Cette deuxième étape produit les constantes de quantification quantifiées cFP8. Cela réduit l'empreinte mémoire de 0,373 bits par paramètre.
En mots de l'auteur :
QLoRA, est une approche d'optimisation efficace qui réduit suffisamment l'utilisation de la mémoire pour affiner un modèle de 65 milliards de paramètres sur un seul GPU de 48 Go tout en préservant les performances complètes de la tâche d'affinage sur 16 bits.
À partir de QLoRA: Optimisation Efficace des LLM Quantifiés
C'est incroyable. Pour effectuer un réglage fin complet sur un modèle de 65 milliards de paramètres de précision 16 bits, il faudrait environ 800 Go de mémoire GPU, et obtenir des performances similaires en utilisant simplement un seul GPU de 48 Go est tout simplement incroyable. Cela montre à quel point QLoRA est puissant.
Optimisation fine de LLM en utilisant LoRA
Dans cette section, nous allons peaufiner un modèle de langage pour une tâche de résumé et voir comment cela améliore les performances de notre modèle. Ici, nous allons peaufiner le modèle google/flan-t5-base avec 248 millions de paramètres en utilisant LoRA sur l'ensemble de données [samsum](https://huggingface.co/datasets/samsum
Télécharger le code Pour suivre facilement ce tutoriel, veuillez télécharger le code en cliquant sur le bouton ci-dessous. C'est GRATUIT !
Importations
Nous commencerons par importer des bibliothèques et des modules importants. Beaucoup des bibliothèques utilisées ici proviennent de Hugging Face (HF)
de jeux de données importation charger_jeu_de_données à partir de transformers importation AutoModèlePourSeq2SeqLM, AutoTokenizer, ConfigurationGénération, ArgumentsEntraînement, Entraîneur importation torch importation temps importation évaluer importation pandas comme pd importation numpy comme np
Chargement du modèle de base
Maintenant, nous allons charger notre modèle de base. Vous pouvez charger n'importe quel modèle de votre choix ici. Vous pouvez trouver la liste des modèles disponibles ici. À la deuxième ligne, nous chargeons la version pré-entraînée du modèle google/flan-t5-base. Le paramètre torch_dtype spécifie le type de données avec lequel vous souhaitez charger les poids du modèle. Le type de données par défaut est float32, mais nous chargerons les poids en utilisant le type de données bfloat16, un choix populaire pour la quantification afin de réduire nos besoins computationnels. Ensuite, nous initialiserons le tokenizer qui a été initialement utilisé pour pré-entraîner le modèle google/flan-t5-base.
model_name='google/flan-t5-base'
original_model = AutoModèlePourSeq2SeqLM.from_pretrained(model_name, torch_dtype=torch.bfloat16) tokenizer = AutoTokenizer.from_pretrained(model_name)
Prétraitement des données
Pour appliquer le tokenizer à l'ensemble de données, nous utilisons la méthode map(). Cela prend en entrée une fonction personnalisée qui spécifie comment le texte doit être prétraité. Dans ce cas, cette fonction s'appelle tokenize_function(). Nous supprimerons les colonnes dont nous n'avons pas besoin à l'étape suivante.
def tokenize_function(example): start_prompt = 'Résumez la conversation suivante.
' end_prompt = '
Résumé : ' prompt = [start_prompt + dialogue + end_prompt for dialogue in example["dialogue"]] example['input_ids'] = tokenizer(prompt, padding="max_length", truncation=True, return_tensors="pt").input_ids example['labels'] = tokenizer(example["summary"], padding="max_length", truncation=True, return_tensors="pt").input_ids
retourner exemple
L'ensemble de données contient 3 divisions différentes. La fonction de tokenisation gère toutes ces divisions
jeux_de_donnees_tokenizes = dataset.map(fonction_tokeniser, batched=True) jeux_de_donnees_tokenizes = jeux_de_donnees_tokenizes.remove_columns(['id', 'dialogue', 'summary',]) jeux_de_donnees_tokenizes = jeux_de_donnees_tokenizes.filter(lambda exemple, index: index % 12 == 0, with_indices=True)
Nous utiliserons un sous-ensemble des données originales pour le peaufinage.
Optimisation fine avec LoRA
Tout d'abord, nous allons définir les paramètres de configuration pour LoRA. Plus de détails sont fournis dans les commentaires. Ensuite, nous initialiserons le modèle PEFT en utilisant le modèle original et la configuration LoRA. Pour obtenir les noms des paramètres pour les target_modules, vous pouvez imprimer l'architecture du modèle.
from peft import LoraConfig, get_peft_model, TaskType