Transformers documentation
Gabarits de discussion
Gabarits de discussion
Le guide bases du chat explique comment stocker les historiques de chat et générer du texte à partir de modèles de chat à l'aide de TextGenerationPipeline.
Ce guide s’adresse aux utilisateurs plus avancés et aborde les classes et méthodes sous-jacentes, ainsi que les concepts clés pour comprendre ce qui se passe réellement lorsque vous discutez avec un modèle.
L’insight critique à retenir pour comprendre les modèles de chat est le suivant : tous les LM causaux, qu’ils aient été entraînés pour le chat ou non, continuent une séquence de jetons. Lors de l’entraînement des LM causaux, celui-ci commence généralement par un « pre-training » sur un immense corpus de texte, ce qui crée un modèle « base ».
Ces modèles de base sont ensuite souvent « fine-tunés » pour le chat, ce qui signifie les entraîner sur des données formatées sous forme de séquence de messages. Le chat n’en reste pas moins une simple séquence de jetons ! La liste de dictionnaires role et content que vous transmettez
à un modèle de chat est convertie en séquence de jetons, souvent avec des jetons de contrôle comme <|user|> ou <|assistant|> ou <|end_of_message|>, qui permettent au modèle de percevoir la structure du chat.
Il existe de nombreux formats de chat possibles, et différents modèles peuvent utiliser des formats ou des jetons de contrôle distincts, même s’ils ont été fine-tunés à partir du même modèle de base !
Pas de panique, cependant - il n'est pas nécessaire de mémoriser tous les formats de chat possibles pour utiliser des modèles de chat. Les modèles de chat sont fournis avec des modèles de chat, qui indiquent comment ils s'attendent à ce que les conversations soient formatées.
Vous pouvez y accéder à l'aide de la méthode apply_chat_template. Voyons deux exemples. Ces deux modèles sont affinés à partir du même modèle de base Mistral-7B :
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-Instruct-v0.1")
chat = [
{"role": "user", "content": "Hello, how are you?"},
{"role": "assistant", "content": "I'm doing great. How can I help you today?"},
{"role": "user", "content": "I'd like to show off how chat templating works!"},
]
tokenizer.apply_chat_template(chat, tokenize=False)<s>[INST] Hello, how are you? [/INST]I'm doing great. How can I help you today?</s> [INST] I'd like to show off how chat templating works! [/INST]from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("HuggingFaceH4/zephyr-7b-beta")
chat = [
{"role": "user", "content": "Hello, how are you?"},
{"role": "assistant", "content": "I'm doing great. How can I help you today?"},
{"role": "user", "content": "I'd like to show off how chat templating works!"},
]
tokenizer.apply_chat_template(chat, tokenize=False)<|user|>\nHello, how are you?</s>\n<|assistant|>\nI'm doing great. How can I help you today?</s>\n<|user|>\nI'd like to show off how chat templating works!</s>\nMistral-7B-Instruct utilise les jetons [INST] et [/INST] pour indiquer le début et la fin des messages de l’utilisateur, tandis que Zephyr-7B utilise les jetons <|user|> et <|assistant|> pour indiquer les rôles des intervenants. C’est pourquoi les modèles de discussion sont importants : avec les mauvais jetons de contrôle, ces modèles auraient des performances nettement inférieures.
Utilisation de apply_chat_template
L'entrée de apply_chat_template doit être structurée sous forme d'une liste de dictionnaires avec les clés role et content. La clé role spécifie l'orateur, et la clé content contient le message. Les rôles courants sont :
userpour les messages de l'utilisateurassistantpour les messages du modèlesystempour les directives sur la manière dont le modèle doit se comporter (généralement placé au début de la conversation)
apply_chat_template prend cette liste et renvoie une séquence formatée. Définissez tokenize=True si vous souhaitez tokeniser la séquence.
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("HuggingFaceH4/zephyr-7b-beta")
model = AutoModelForCausalLM.from_pretrained("HuggingFaceH4/zephyr-7b-beta", device_map="auto", dtype=torch.bfloat16)
messages = [
{"role": "system", "content": "You are a friendly chatbot who always responds in the style of a pirate",},
{"role": "user", "content": "How many helicopters can a human eat in one sitting?"},
]
tokenized_chat = tokenizer.apply_chat_template(messages, tokenize=True, add_generation_prompt=True, return_tensors="pt").to(model.device)
print(tokenizer.decode(tokenized_chat["input_ids"][0]))<|system|>
You are a friendly chatbot who always responds in the style of a pirate</s>
<|user|>
How many helicopters can a human eat in one sitting?</s>
<|assistant|>Passez la conversation tokenisée à generate() pour générer une réponse.
outputs = model.generate(**tokenized_chat, max_new_tokens=128)
print(tokenizer.decode(outputs[0]))<|system|>
You are a friendly chatbot who always responds in the style of a pirate</s>
<|user|>
How many helicopters can a human eat in one sitting?</s>
<|assistant|>
Matey, I'm afraid I must inform ye that humans cannot eat helicopters. Helicopters are not food, they are flying machines. Food is meant to be eaten, like a hearty plate o' grog, a savory bowl o' stew, or a delicious loaf o' bread. But helicopters, they be for transportin' and movin' around, not for eatin'. So, I'd say none, me hearties. None at all.Certains tokenizers ajoutent des jetons spéciaux
<bos>et<eos>. Les modèles de dialogue devraient déjà inclure tous les jetons spéciaux nécessaires, et l’ajout de jetons spéciaux supplémentaires est souvent incorrect ou en double, ce qui nuit aux performances du modèle. Lorsque vous formatez du texte avecapply_chat_template(tokenize=False), assurez-vous de définiradd_special_tokens=Falsesi vous tokenisez ultérieurement, afin d’éviter de dupliquer ces jetons. Ce n’est pas un problème si vous utilisezapply_chat_template(tokenize=True), ce qui en fait généralement l’option la plus sûre !
add_generation_prompt
Vous avez peut-être remarqué l’argument add_generation_prompt dans les exemples ci-dessus.
Cet argument ajoute des jetons à la fin de la conversation pour indiquer le début d’une réponse de l’assistant. À retenir : sous toutes les abstractions de la conversation, les modèles de chat restent de simples modèles de langage qui continuent une séquence de jetons !
Si vous incluez des jetons indiquant qu’il est désormais dans une réponse de l’assistant, il rédigera correctement une réponse, mais si vous n’incluez pas ces jetons, le modèle peut se tromper et faire quelque chose d’étrange, comme continuer le message de l’utilisateur au lieu d’y répondre !
Voici un exemple pour comprendre ce que fait réellement add_generation_prompt. D’abord, formattions un chat sans add_generation_prompt :
tokenized_chat = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=False)
tokenized_chat<|im_start|>user
Hi there!<|im_end|>
<|im_start|>assistant
Nice to meet you!<|im_end|>
<|im_start|>user
Can I ask a question?<|im_end|>Maintenant, formatons la même conversation avec add_generation_prompt=True :
tokenized_chat = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
tokenized_chat<|im_start|>user
Hi there!<|im_end|>
<|im_start|>assistant
Nice to meet you!<|im_end|>
<|im_start|>user
Can I ask a question?<|im_end|>
<|im_start|>assistant
Lorsque add_generation_prompt=True, <|im_start|>assistant est ajouté à la fin pour indiquer le début d'un message assistant. Cela permet au modèle de savoir qu'une réponse assistant est attendue.
Tous les modèles n’ont pas besoin de prompts de génération, et certains modèles, comme Llama, ne disposent d’aucun jeton spécial avant la réponse assistant. Dans ces cas, add_generation_prompt n’a aucun effet.
continue_final_message
Le paramètre continue_final_message contrôle si le dernier message de la conversation doit être prolongé plutôt que de démarrer un nouveau message. Il supprime les jetons de fin de séquence afin que le modèle poursuive la génération à partir du dernier message.
Ceci est utile pour « préremplir » une réponse de modèle. Dans l’exemple ci-dessous, le modèle génère du texte qui prolonge la chaîne JSON plutôt que de démarrer un nouveau message. Cela peut s’avérer très utile pour améliorer la précision du suivi des instructions lorsque vous savez comment démarrer ses réponses.
chat = [
{"role": "user", "content": "Can you format the answer in JSON?"},
{"role": "assistant", "content": '{"name": "'},
]
formatted_chat = tokenizer.apply_chat_template(chat, tokenize=True, return_dict=True, continue_final_message=True)
model.generate(**formatted_chat)Vous ne devriez pas utiliser add_generation_prompt et continue_final_message ensemble. Le premier ajoute des jetons qui démarrent un nouveau message, tandis que le second supprime les jetons de fin de séquence. Les utiliser ensemble renvoie une erreur.
Passez un nom de champ sous forme de chaîne de caractères pour préremplir ce champ au lieu de content. Les modèles de raisonnement lisent leur raisonnement depuis un champ distinct, reasoning_content. Préremplir content ferme le bloc de raisonnement avant le début de la génération, ce qui empêche le modèle de continuer à l’intérieur. Préremplir directement reasoning_content laisse le bloc ouvert.
chat = [
{"role": "user", "content": "Explain 1+1"},
{"role": "assistant", "reasoning_content": "The user wants a simple addition. ", "content": ""},
]
formatted_chat = tokenizer.apply_chat_template(chat, tokenize=False, continue_final_message="reasoning_content")Le champ nommé doit exister sur le message final et doit être référencé par le modèle de chat. Une erreur est levée si l'une de ces vérifications échoue.
TextGenerationPipeline définit add_generation_prompt sur True par défaut pour démarrer un nouveau message. Toutefois, si le dernier message de la conversation a le rôle assistant, il considère que le message est un préremplissage et passe à continue_final_message=True. Cela est dû au fait que la plupart des modèles ne prennent pas en charge plusieurs messages consécutifs de l’assistant. Pour outrepasser ce comportement, passez explicitement l’argument continue_final_message au pipeline.
Entraînement du modèle
Entraîner un modèle avec un modèle de chat est une bonne façon de s’assurer que le modèle de chat correspond aux jetons sur lesquels le modèle a été entraîné. Appliquez le modèle de chat comme étape de prétraitement à votre jeu de données. Définissez add_generation_prompt=False car les jetons supplémentaires destinés à déclencher une réponse de l’assistant ne sont pas utiles pendant l’entraînement.
Un exemple de prétraitement d'un jeu de données avec un modèle de chat est présenté ci-dessous.
from transformers import AutoTokenizer
from datasets import Dataset
tokenizer = AutoTokenizer.from_pretrained("HuggingFaceH4/zephyr-7b-beta")
chat1 = [
{"role": "user", "content": "Which is bigger, the moon or the sun?"},
{"role": "assistant", "content": "The sun."}
]
chat2 = [
{"role": "user", "content": "Which is bigger, a virus or a bacterium?"},
{"role": "assistant", "content": "A bacterium."}
]
dataset = Dataset.from_dict({"chat": [chat1, chat2]})
dataset = dataset.map(lambda x: {"formatted_chat": tokenizer.apply_chat_template(x["chat"], tokenize=False, add_generation_prompt=False)})
print(dataset['formatted_chat'][0])<|user|>
Which is bigger, the moon or the sun?</s>
<|assistant|>
The sun.</s>Après cette étape, vous pouvez continuer à suivre la recette d’entraînement pour les modèles de langage causaux en utilisant la colonne formatted_chat.