Tester le tokenizer et le modèle de dialogue MLX sur Mac cloud

Tester le tokenizer et le modèle de dialogue MLX sur Mac cloud

Tester le tokenizer et le modèle de dialogue MLX sur Mac cloud

Si des balises de rôle apparaissent soudain dans les réponses de MLX après avoir transféré une même conversation sur un Mac cloud, les poids ne sont pas forcément en cause. La version du tokenizer, le modèle de dialogue ou l’ajout répété de tokens spéciaux peuvent modifier l’entrée réellement transmise. Il faut d’abord figer la conversion des messages en tokens, puis comparer les résultats générés, pour distinguer une dérive des entrées des variations aléatoires de l’échantillonnage.

Définir le contrat d’entrée à valider

Cette procédure vérifie uniquement le prétraitement avant inférence. Elle ne charge pas les poids et ne mesure pas les tokens/s. Préparez un environnement virtuel Python et un répertoire de modèle local préalablement vérifié. Ce répertoire doit contenir la configuration du tokenizer, le vocabulaire et le modèle de dialogue nécessaires au modèle concerné. Ne récupérez pas ces fichiers dans un autre modèle de taille comparable.

Chaque validation doit consigner trois éléments : le tableau de messages d’origine, le texte obtenu par application du modèle de dialogue et la séquence complète des identifiants de tokens. Comparer uniquement le nombre de tokens ne suffit pas : deux séquences de même longueur peuvent représenter des contenus différents. De même, un vocabulaire courant identique ne garantit pas des délimitations de rôles et des marqueurs de fin identiques.

Le modèle de dialogue fait partie du protocole d’entrée du modèle ; ce n’est pas un simple format d’affichage. Une tokenisation réussie indique seulement que la conversion a abouti, pas que le modèle a reçu la bonne structure de rôles.

Enregistrez le script ci-dessous dans check_prompt.py, puis exécutez-le dans un environnement où transformers et les dépendances requises par le tokenizer sont installés. Commencez par lancer python -m pip freeze > requirements.lock.txt pour conserver une trace de l’environnement. Cette liste consigne les versions ; elle ne garantit pas un résultat d’installation strictement identique d’une plateforme à l’autre.

Figer les fichiers locaux et les messages de test

Le script désactive explicitement les téléchargements réseau et l’exécution de code distant personnalisé. Si le modèle dépend d’une implémentation personnalisée, examinez d’abord cette implémentation et définissez une procédure d’exécution distincte. N’autorisez pas temporairement son exécution pour simplement contourner une erreur.

Les exemples comprennent du chinois, des sauts de ligne, des chevrons et une conversation à plusieurs tours. Chaque cas est testé avec et sans préfixe de génération. Ils suivent l’alternance habituelle entre user et assistant. N’ajoutez des exemples avec le rôle system ou des appels d’outils qu’après avoir confirmé leur prise en charge explicite par le modèle.

import argparse
import json
from pathlib import Path
from transformers import AutoTokenizer

parser = argparse.ArgumentParser()
parser.add_argument("model_dir")
parser.add_argument("baseline")
parser.add_argument("--record", action="store_true")
args = parser.parse_args()

tokenizer = AutoTokenizer.from_pretrained(
    args.model_dir,
    local_files_only=True,
    trust_remote_code=False,
)
cases = [
    [{"role": "user", "content": "只回答数字:2+3\n<note>保留原文</note>"}],
    [
        {"role": "user", "content": "记住代号:实验甲"},
        {"role": "assistant", "content": "已记录。"},
        {"role": "user", "content": "代号是什么?"},
    ],
]
rows = []
for messages in cases:
    for generation in (False, True):
        rendered = tokenizer.apply_chat_template(
            messages,
            tokenize=False,
            add_generation_prompt=generation,
        )
        ids = tokenizer.apply_chat_template(
            messages,
            tokenize=True,
            add_generation_prompt=generation,
        )
        encoded = tokenizer.encode(
            rendered, add_special_tokens=False
        )
        if ids != encoded:
            raise SystemExit("Tokenization paths differ")
        rows.append({
            "messages": messages,
            "generation": generation,
            "rendered": rendered,
            "ids": ids,
        })

path = Path(args.baseline)
if args.record:
    with path.open("x", encoding="utf-8") as stream:
        json.dump(rows, stream, ensure_ascii=False, indent=2)
else:
    expected = json.loads(path.read_text(encoding="utf-8"))
    if rows != expected:
        raise SystemExit("Prompt contract changed")

Créer une référence sans l’approuver automatiquement

Au premier lancement, exécutez python check_prompt.py ./model ./prompt-baseline.json --record. L’enregistrement utilise le mode de création exclusive : si le fichier existe déjà, une erreur est déclenchée. Cela évite d’écraser les éléments de preuve pendant l’analyse d’un incident. Pour les exécutions suivantes, retirez --record. Le contrôle n’est réussi que si la commande ne produit aucune sortie et renvoie un code de sortie nul.

Examinez le JSON point par point : le contenu utilisateur est-il intégralement conservé ? Les séparateurs de rôles respectent-ils la documentation du modèle ? Les réponses précédentes de l’assistant sont-elles suivies d’une délimitation appropriée ? Le mode génération prépare-t-il correctement l’emplacement de la prochaine réponse ? Certains modèles de dialogue ignorent l’option de préfixe de génération : il ne faut donc pas exiger que les deux résultats soient nécessairement différents.

Placez sous gestion de versions la référence, le script, la liste des dépendances et les sommes de contrôle des fichiers du tokenizer utilisés. Les exemples doivent contenir uniquement des données synthétiques. N’intégrez ni conversations réelles, ni jetons d’accès, ni documents internes dans une référence destinée à être enregistrée dans le dépôt.

Repérer la dérive dans la chaîne d’appels MLX réelle

Même si le script réussit, vérifiez que le point d’entrée de l’application réutilise le même modèle de dialogue et les mêmes paramètres. Certaines couches d’appel acceptent un tableau de messages, d’autres une chaîne déjà mise en forme. Transmettre cette chaîne à un wrapper de dialogue peut appliquer le modèle de dialogue une seconde fois. Juste avant l’appel à la fonction de génération, capturez les identifiants de tokens finaux des exemples synthétiques et comparez-les un à un à la référence.

Éviter l’ajout répété de tokens spéciaux

Le modèle de dialogue insère souvent déjà des marqueurs de début ou de fin. C’est pourquoi le script utilise add_special_tokens=False lors de l’encodage du texte mis en forme. N’ajoutez pas manuellement un BOS supplémentaire et ne validez pas le résultat au seul motif que le texte décodé « semble correct » : les options de décodage qui masquent les tokens spéciaux peuvent dissimuler leur duplication dans l’entrée.

En cas d’écart, comparez d’abord rendered, puis ids. Si le texte diffère, vérifiez en priorité le modèle de dialogue et le prétraitement des messages. Si le texte est identique mais que les identifiants diffèrent, examinez le vocabulaire, les tokens ajoutés et l’implémentation du tokenizer. Ne poursuivez l’analyse des paramètres d’échantillonnage et de la version des poids qu’après avoir confirmé que les entrées réelles sont identiques.

Transformer les échecs en contrôles vérifiables

Lorsque vous ajoutez des exemples contenant un contenu vide, des espaces en fin de texte, des émojis ou de longs textes chinois, précisez ce que chacun doit vérifier. Ne vous contentez pas d’accumuler des chaînes aléatoires. Pour les entrées longues, vérifiez aussi le budget de contexte au niveau de l’application : ce script ne réserve pas d’espace de sortie pour la génération et ne valide pas la stratégie de troncature.

Avant la livraison, vérifiez quatre points : aucun fichier n’est téléchargé pendant l’exécution hors ligne ; un contrôle ordinaire ne réécrit pas la référence ; tout écart au niveau du modèle de dialogue ou des tokens bloque la tâche ; toute mise à jour des dépendances exige une revue humaine. Pour mettre à jour la référence, enregistrez un fichier candidat distinct et soumettez les différences à la revue. Ne relancez pas automatiquement l’enregistrement dans la branche de traitement d’un échec.

Pendant la location, conservez un ensemble de preuves sur les entrées dont les tests peuvent être réexécutés, plutôt qu’une capture d’écran d’une réponse particulière. Lors du passage à un autre Mac cloud, vous pourrez ainsi démontrer d’abord que le modèle reçoit toujours le même contenu, puis examiner pourquoi ses réponses diffèrent.

Questions fréquentes

Faut-il charger les poids du modèle pour ces tests ?

Non. Les fichiers locaux du tokenizer suffisent pour vérifier le rendu et les tokens. Le test ne mesure pas la qualité des réponses.

Peut-on remplacer la référence après une mise à jour ?

Pas automatiquement. Examinez les fichiers du tokenizer, le modèle de dialogue et les versions des dépendances avant de valider une nouvelle référence.

Louez un Mac pour la durée de votre expérience

Mac mini M4 dédié, avec 16 Go de mémoire unifiée et un SSD de 256 Go. Paiement à l’avance par jour, semaine, mois ou trimestre ; la machine s’arrête à la fin de la période si vous ne renouvelez pas. Préparez vous-même les modèles et l’environnement d’exécution. L’état réel des ressources est indiqué en temps réel dans la console.

Louer un Mac à la journée

Vérifiez la configuration avant de commencer

Vérifiez les poids des modèles, les pics de charge et la durée de location avant de choisir une période. Pour les étapes de connexion, consultez d’abord le centre d’aide.