Les données sont une liste d’exemples
Le fine-tuning (ou ajustement fin) apprend à un modèle à partir d’exemples que vous fournissez. Chaque exemple est une paire : ce que reçoit le modèle, et ce que vous voulez en retour. Ce que font vos paires, le modèle le copie.
Une graphie négligée, répétée quarante fois, fait donc quarante leçons du mauvais genre.
À la fin de cette leçon
Remarque
Je peux écrire des exemples d’entraînement dont un modèle peut apprendre.
Mots clés
| Mot | Ce que ça veut dire |
|---|---|
| Fine-tuning | entraîner un modèle plus loin sur vos exemples |
| Prompt | la moitié entrée d’une paire d’entraînement |
| Completion | la moitié réponse d’une paire d’entraînement |
| JSONL | un fichier avec un objet JSON par ligne |
| Étiquette | un mot fixe qui nomme une classe |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Une paire sur chaque ligne
Un fichier JSONL tient un exemple par ligne. Chaque ligne est un petit objet JSON avec un prompt et une completion.
Une ligne se contrôle seule. Si la ligne 90 est cassée, les lignes 1 à 89 marchent toujours. C’est pourquoi cette forme sert si souvent aux fichiers d’entraînement.
Exemple : trois lignes
| Ligne | Ce qu’elle contient | Verdict |
|---|---|---|
| 1 | prompt « J’ai été débité deux fois », completion « facturation » | Bonne |
| 2 | prompt « Facture fausse » et aucune completion | Completion manquante |
| 3 | prompt « Super boutique », completion « Autre » | Étiquette inconnue : la casse est fausse |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Garder toujours la même forme
Un modèle apprend le motif de vos paires, défauts compris. Si « facturation » s’écrit aussi « Facturation » et « factures », il apprend trois réponses.
Choisissez une liste fixe d’étiquettes, une graphie pour chacune et une seule mise en page pour tous les prompts. Puis contrôlez le fichier avec un programme, pas à l’œil.
Une erreur courante
Attention
Beaucoup pensent que plus d’exemples aident toujours. Cent paires propres apprennent plus que mille paires brouillonnes, car un modèle copie aussi les défauts. Corrigez d’abord le fichier, puis ajoutez.
Écrire l’assistant du modèle, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' '// Les jetons d’une réponse, ou null si elle n’en donne pas.' 'function compter(donnees, nom) {' ' return donnees.usage && typeof donnees.usage[nom] === \"number\" ? donnees.usage[nom] : null;' '}' > model.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire l’assistant du modèle, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function demander(messages, echantillon, max) {' ' try {' ' const res = await fetch(base + \"/v1/chat/completions\", {' ' method: \"POST\",' ' headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' ' body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 100 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"statut \" + res.status);' ' const d = await res.json();' ' return { texte: d.choices[0].message.content, entree: compter(d, \"prompt_tokens\"), sortie: compter(d, \"completion_tokens\"), enregistre: false };' ' } catch (erreur) {' ' console.log(\"Échantillon enregistré : aucun modèle n’a répondu (\" + erreur.message + \").\");' ' return { texte: echantillon, entree: null, sortie: null, enregistre: true };' ' }' '}' 'module.exports = { demander };' >> model.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire le contrôleur de lignes, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { demander } = require(\"./model\");' 'const ETIQUETTES = [\"facturation\", \"livraison\", \"autre\"];' 'const lignes = [' ' JSON.stringify({ prompt: \"J’ai été débité deux fois\", completion: \"facturation\" }),' ' JSON.stringify({ prompt: \"Où est mon colis\", completion: \"livraison\" }),' ' JSON.stringify({ prompt: \"Super boutique\", completion: \"Autre\" }),' ' JSON.stringify({ prompt: \"Facture fausse\" }),' ' JSON.stringify({ prompt: \"\", completion: \"autre\" }),' ' \"ceci n’est pas du json\",' ' JSON.stringify({ prompt: \"Carton écrasé à l’arrivée\", completion: \"livraison\" }),' '];' > adapt2.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire le contrôleur de lignes, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function estTexte(x) {' ' return typeof x === \"string\";' '}' 'function estObjet(x) {' ' return x === null ? false : typeof x === \"object\" && Array.isArray(x) === false;' '}' 'function controler(ligne) {' ' let rang;' ' try { rang = JSON.parse(ligne); } catch (e) { return \"pas du json\"; }' ' if (estObjet(rang) === false) return \"pas un objet\";' ' if (estTexte(rang.prompt) === false) return \"prompt manquant\";' ' if (estTexte(rang.completion) === false) return \"completion manquante\";' ' if (rang.prompt.trim() === \"\") return \"prompt vide\";' ' if (ETIQUETTES.includes(rang.completion) === false) return \"étiquette inconnue\";' ' return \"\";' '}' >> adapt2.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire le contrôleur de lignes, partie 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' let bonnes = 0;' ' lignes.forEach(function (ligne, i) {' ' const raison = controler(ligne);' ' if (raison === \"\") bonnes = bonnes + 1;' ' else console.log(\"ligne \" + (i + 1) + \" : \" + raison);' ' });' ' console.log(bonnes + \" lignes bonnes sur \" + lignes.length);' ' const q = \"En une phrase, pourquoi chaque exemple doit-il écrire une étiquette de la même façon ?\";' ' const echantillon = \"Le modèle apprend chaque graphie comme une réponse différente : des graphies mêlées éparpillent ce qu’il apprend.\";' ' const reponse = await demander([{ role: \"user\", content: q }], echantillon, 60);' ' console.log(\"le modèle dit : \" + reponse.texte);' '}' 'main();' >> adapt2.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Lancer le contrôleur de lignes
docker exec -w /home/node/project agent node adapt2.jsAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Ce que le contrôleur a trouvé
Les sept lignes sont inventées. Le contrôleur lit chacune seule et dit le premier défaut trouvé : on corrige donc un défaut à la fois.
La dernière ligne est un court appel au modèle. Sans modèle, elle affiche un Échantillon enregistré.