TATECHATLAS
◎ Français
Intelligence artificielle / Guide

Modèles de chat comme partie du contrat du modèle : rôles, jetons spéciaux et double tokenisation

Un modèle de chat fixe la séquence exacte de jetons attendue, incluant les jetons de rôle et les séparateurs. En le traitant comme donnée contractuelle, on évite les pertes de qualité silencieuses dues à un formatage incorrect ou à une double insertion de jetons spéciaux.

Dans ce guide

Traitez le modèle de chat comme une partie déclarée du contrat du modèle : enregistrez les rôles pris en charge (system, user, assistant), les jetons de contrôle associés à chaque rôle, les jetons spéciaux du tokenizer et les drapeaux exacts utilisés lors du formatage. Construisez les invites avec tokenizer.apply_chat_template, en privilégiant tokenize=True afin que les seuls jetons spéciaux émis soient ceux du modèle. Si vous formatez d'abord en chaîne, passez add_special_tokens=False lors de la tokenisation ultérieure, car le modèle inclut déjà les jetons de délimitation nécessaires. Utilisez add_generation_prompt=True uniquement lors du démarrage d'une nouvelle réponse d'assistant, utilisez continue_final_message uniquement lors du préremplissage du dernier message, et ne passez jamais les deux ensemble. Vérifiez le contrat avec un court test de génération sur chaque variante du modèle, car les modèles diffèrent même entre modèles fine‑tuned à partir de la même base.

Pourquoi un modèle de chat fait partie du contrat du modèle

Un modèle de langage causal ne reçoit jamais une conversation telle quelle. Il reçoit une séquence plate de jetons et prédit la suite. Le modèle de chat est le composant qui convertit une liste de dictionnaires rôle‑contenu en la séquence précise rencontrée lors du fine‑tuning, incluant les jetons de contrôle comme <|user|>, <|assistant|> et les marqueurs de fin de message qui permettent au modèle de percevoir la structure de l’échange.

Comme deux modèles fine‑tuned à partir de la même base peuvent utiliser des formats totalement différents, le modèle de chat constitue une donnée contractuelle comportementale plutôt qu’une simple présentation. Un décalage ne lève généralement aucune exception ; il dégrade silencieusement la qualité des réponses, d’où la nécessité d’inclure explicitement le modèle et ses jetons dans le contrat.

Stockez la chaîne d'invite rendue avec la révision du modèle dans votre banc d'évaluation. Si la qualité diminue après une mise à jour du modèle ou de la bibliothèque, décodiez l'invite stockée et comparez‑la à une nouvelle génération ; cela isole la dérive du modèle de la dérive du formatage.

Rôles standards et leurs sémantiques

Trois rôles couvrent les cas courants. system porte les directives sur le comportement du modèle et apparaît généralement en premier. user contient la requête humaine. assistant contient la réponse du modèle. Le modèle de chat associe chaque rôle à des jetons de contrôle, et le mapping est propre à chaque modèle : Mistral‑7B‑Instruct encadre les tours utilisateur avec [INST] et [/INST], tandis que Zephyr‑7B utilise les marqueurs <|user|> et <|assistant|> avec des séparateurs de fin de séquence.

Le contrat doit donc préciser les rôles ainsi que leurs orthographes de jetons concrètes. Nommer uniquement les rôles est insuffisant, car le même nom de rôle se rend différemment selon les checkpoints ; un jeu de jetons erroné est exactement le mode d’échec que le modèle de chat vise à prévenir.

Définir les jetons spéciaux dans le tokenizer

La configuration du tokenizer expose les éléments consommés par le modèle de chat. Les attributs pertinents comprennent chat_template, une chaîne Jinja qui formate les listes de messages, ainsi que les jetons spéciaux tels que bos_token, eos_token, unk_token, sep_token, pad_token, cls_token et mask_token. Le modèle de chat lit ces attributs plutôt que de coder en dur leurs orthographes, de sorte que le tokenizer et le modèle doivent être chargés à partir de la même révision.

Prérequis : le tokenizer doit réellement posséder un attribut chat_template. S’il n’en a pas, apply_chat_template n’a rien à rendre et il faut fournir explicitement un modèle ou choisir un autre checkpoint. Il s’agit d’une contrainte d’environnement et de version, pas d’une garantie que le modèle possède un modèle de chat correspondant à son format d’entraînement.

Appliquer le modèle avec apply_chat_template

La séquence de travail est : construire une liste de dictionnaires avec les clés role et content, appeler apply_chat_template, et choisir tokenize=True quand on veut les IDs de jetons prêts pour generate(), ou tokenize=False lorsqu’on a besoin de la chaîne formatée pour inspection ou journalisation. Utilisez add_special_tokens=False uniquement si vous prévoyez d’ajouter vous‑même les jetons spéciaux plus tard.

L’exemple ci‑dessous charge un tokenizer avec un modèle de chat, formate un message système et un message utilisateur, et demande un prompt de génération. La ligne imprimée est illustrative : l’espacement exact dépend de la version du tokenizer et de la révision du modèle.

Sortie illustrative (forme, pas une exécution réelle) :

<|system|> You are a helpful assistant </s><|user|> What is 2+2? </s><|assistant|>

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained('HuggingFaceH4/zephyr-7b-beta')
messages = [
    {"role": "system", "content": "You are a helpful assistant"},
    {"role": "user", "content": "What is 2+2?"}
]
ids = tokenizer.apply_chat_template(
    messages,
    tokenize=True,
    add_generation_prompt=True,
    return_tensors='pt'
)
print(tokenizer.decode(ids['input_ids'][0]))

Éviter la double tokenisation

Les modèles de chat émettent déjà les jetons spéciaux dont le modèle a besoin. Si vous rendez la chaîne avec tokenize=False puis la passez à l’appel normal du tokenizer, le chemin par défaut add_special_tokens=True peut insérer une seconde fois les jetons bos ou eos. Le doublon ne génère pas d’erreur audible ; il modifie la séquence attendue par le modèle.

Le schéma sûr est apply_chat_template(tokenize=True), qui renvoie des IDs incluant les jetons de contrôle. Si une chaîne intermédiaire est inévitable, tokenisez‑la avec add_special_tokens=False. Cette distinction est cruciale dans les pipelines où le formatage et l’encodage se font dans des services séparés.

Prompts de génération et gestion du dernier message

add_generation_prompt=True ajoute les jetons qui annoncent le début d’une réponse d’assistant, de sorte que le modèle réponde plutôt que de continuer le texte de l’utilisateur. Il n’a aucun effet sur des modèles comme Llama qui n’ont pas de jeton spécial d’ouverture d’assistant, le contrat doit donc indiquer si le modèle cible en possède.

continue_final_message fait l’inverse : il retire les jetons de fin de séquence afin que la génération continue à l’intérieur du dernier message, utile pour préremplir un préfixe de réponse connu ou un champ de raisonnement. Les deux drapeaux sont mutuellement exclusifs, et les combiner lève une erreur. En entraînement, utilisez add_generation_prompt=False, car les jetons d’ouverture d’assistant ne sont pas utiles dans la séquence d’entraînement.

Tester le contrat sur les variantes du modèle

La syntaxe seule ne prouve pas la justesse. Pour chaque variante, rendez une conversation fixe à deux tours, décodifiez‑la et confirmez que les jetons de contrôle et de délimitation correspondent au format d’entraînement du modèle. Puis lancez une courte génération et vérifiez que le modèle répond en tant qu’assistant plutôt que de prolonger le texte de l’utilisateur.

Exécutez cette vérification chaque fois que la révision du modèle, la version de transformers ou la source du tokenizer changent. Des noms de rôle identiques n’impliquent pas des séquences de jetons identiques, et un modèle de chat fonctionnant pour un fine‑tune peut échouer silencieusement sur un autre dérivé de la même base.

Documenter le modèle dans un contrat

Un contrat reproductible enregistre les rôles, les jetons de contrôle requis, le comportement du prompt de génération et la chaîne exacte du modèle ou sa révision immuable. L’extrait ci‑dessous est un fragment de schéma pour votre système de documentation, pas une configuration exécutable : il vous faut remplir l’identifiant du modèle et le texte du modèle.

Enregistrez la source du modèle (attribut tokenizer ou chaîne explicite) plutôt que seulement sa sortie, car le rendu peut changer lorsque la version de la bibliothèque du tokenizer évolue.

model_contract:
  model_id: <hugging-face-model-id>
  tokenizer_revision: <revision-or-commit>
  roles:
    system: <role-token-or-pattern>
    user: <role-token-or-pattern>
    assistant: <role-token-or-pattern>
  special_tokens:
    bos: <token>
    eos: <token>
  add_generation_prompt_on_inference: true
  add_generation_prompt_on_training: false
  continue_final_message: prefill-only
  chat_template_source: tokenizer.chat_template
  chat_template_text: <exact-jinja-template>

Points à vérifier

  • Confirmer que le tokenizer expose un attribut chat_template non vide pour la révision exacte du modèle utilisé.
  • Décoder une invite rendue et vérifier que les jetons de contrôle de chaque rôle correspondent au format d’entraînement du modèle.
  • Vérifier qu’un seul jeton bos/eos apparaît par frontière de message après tokenisation.
  • Si le formatage se fait d’abord en chaîne, confirmer que l’appel tokenizer ultérieur utilise add_special_tokens=False.
  • S’assurer que add_generation_prompt et continue_final_message ne sont jamais passés ensemble.
  • Enregistrer si add_generation_prompt a un effet pour le modèle cible, certains modèles n’ayant pas de jeton d’ouverture d’assistant.
  • Exécuter un court test de génération par variante de modèle et vérifier les réponses du modèle plutôt que d’étendre le tour utilisateur.
  • Épingler la version de transformers et la révision du tokenizer avec le texte du modèle stocké.

Les modèles de chat sont spécifiques à chaque modèle : un contrat rédigé pour un checkpoint peut ne pas s’appliquer à un autre, même si les deux proviennent du même modèle de base. add_generation_prompt est inefficace pour les modèles comme Llama qui n’ont pas de jeton d’ouverture d’assistant, et les combiner avec continue_final_message provoque une erreur. Les exemples supposent un tokenizer Hugging Face avec un attribut chat_template et la bibliothèque transformers installée ; l’espacement et les IDs de jetons exacts dépendent de la version du tokenizer et de la bibliothèque, la chaîne décodée étant illustrative et non issue d’une exécution réelle. Cet article ne traite que du formatage d’invite et ne fait aucune affirmation sur la qualité, la latence ou la précision des tâches générées.

Sources

  1. Hugging Face: chat templates ↗
  2. Hugging Face: tokenizers ↗
Retour en haut ↑