Analyser du HTML avec des LLM : Une quête arthurienne
Note: Cet article a été traduit avec Claude Code le 31 janvier 2026. La version originale est disponible ici.
Il y a quelques semaines, j’ai regardé la vidéo d’Andrej Karpathy, Let’s Build GPT: From Scratch, in Code, Spelled Out, où il construit un modèle de type GPT et utilise un dataset appelé Tiny Shakespeare, qu’il a créé lui-même, je pense. La vidéo est vraiment intéressante, mais en la regardant, je pensais “Ouais, tout ce qui est Shakespeare n’est généralement pas ma tasse de thé” peut-être que ce sont mes gènes français, qui sait…
Alors j’ai commencé à me demander : Pourrais-je construire un petit dataset personnel qui aurait plus de sens pour moi ?
Il y a quelques semaines, j’ai essayé et j’ai réussi à créer quelque chose de plus personnel. Au début, je prévoyais seulement de partager le dataset, mais j’ai pensé que ce parcours pourrait valoir la peine d’être partagé, car j’ai eu des découvertes intéressantes avec les LLM en cours de route. Alors commençons par la source de mon dataset : Kaamelott
Kaamelott quoi ?
Kaamelott est une série TV comique française inspirée de la légende arthurienne. Elle a été diffusée de 2005 à 2009 et compte 6 saisons. Les 4 premières saisons ont un format court, avec des épisodes d’environ 3 minutes, totalisant 400 épisodes. Les 2 dernières saisons ont un format plus long, avec des épisodes durant entre 50 et 60 minutes (selon le montage). Ça va au-delà de la blague classique ‘six saisons et un film’ empruntée à la série TV Community — avec un film sorti et deux films en préparation (il y a aussi plusieurs bandes dessinées dans l’univers Kaamelott).

[L’image ne rend pas justice à quel point la série est drôle !]
La série est très populaire en France et si vous êtes curieux de voir l’émission TV, il y a un lien vers une chaîne YouTube officielle avec tous les épisodes.
J’ai toujours été surpris qu’il n’y ait pas de dataset pour cette série (du moins pas complet), alors j’ai décidé d’en construire un. Je voulais garder le même format qu’un ancien projet que j’avais sur Doctor Who, qui était une collection de scripts pour chaque saison de la série (et ajouter un dataset de type ‘tiny’)
Pour faire cela pour Kaamelott, j’avais deux options : scraper les scripts officiels qui peuvent être achetés (en format Kindle, par exemple) ou utiliser un site web communautaire appelé kaamelott.hypnoweb.net avec une communauté qui a transcrit tous les épisodes (chapeau à eux pour le travail incroyable). J’ai choisi la seconde : utiliser le site communautaire et scraper le site web en utilisant la patience et Selenium pour extraire tout le contenu HTML brut pour le traitement. Je voulais collecter les scripts dans un format spécifique avec une ligne par locuteur, incluant :
- Le personnage qui a parlé
- Le dialogue prononcé
- La didascalie : tout le contexte sur le personnage et le dialogue (c’est-à-dire la tonalité du dialogue, etc.)
Pour traiter le HTML brut, j’ai commencé à utiliser diverses regex et techniques de nettoyage de données, mais le résultat était moyen au mieux. Par exemple, certains dialogues entre personnages n’étaient pas divisés correctement, et pour extraire correctement les didascalies, je devais créer des règles très complexes pour obtenir un résultat qui était encore loin d’être parfait. Alors après quelques jours, j’ai pensé, essayons d’utiliser l’approche du Large Language Model (LLM) et voyons comment ça se passe.
PS : Je suis sûr que si j’avais continué à travailler avec mon approche classique, j’aurais réussi à obtenir quelque chose de décent—j’ai juste trouvé l’approche LLM plus amusante

[Désolé à l’avance—je vais utiliser beaucoup de GIF de Kaamelott dans cet article]
Du prompting zero-shot au few-shot
Je ne vais pas expliquer ce que sont les LLM ; il y a de bonnes ressources/séries en ligne qui l’expliquent, comme 3Blue1Brown, Andrej Karpathy, ou ScienceEtonnante si vous préférez une explication en français.
Pour ce projet, je devais choisir quelle famille de LLM utiliser. Il y avait plusieurs options comme DeepSeek, LLaMA, ou GPT, mais j’ai décidé de me concentrer sur les modèles cloud car les exécuter sur ma machine locale n’était pas très efficace. Pour vous donner une idée, ma première tentative d’analyse avec un modèle local était avec DeepSeek-7B via llama.cpp a pris environ 10 minutes pour une ligne (et il y a plus de 30 000 lignes à traiter).
J’ai choisi de me concentrer sur OpenAI et Mistral. OpenAI était un choix évident, mais j’ai choisi Mistral également car le dataset était basé sur une émission TV française, et je ne pouvais pas ne pas avoir un fournisseur français. J’étais également curieux d’explorer leurs outils comme le Chat, la plateforme, et leurs modèles xxxstral.

Mes premières tentatives étaient un peu force brute, en utilisant leurs interfaces de chat, ChatGPT et le Chat mais les résultats étaient bons, et les modèles ont bien performé avec un prompt très court. Alors j’ai décidé d’utiliser leurs API pour automatiser le processus, car je n’allais pas analyser manuellement plus de 400 scripts via l’interface. L’objectif était d’utiliser un workflow de prompt plus structuré.
Voici le prompt typique que j’ai utilisé pour l’analyse.
Cette approche est appelée prompting zero-shot, où vous demandez au modèle de produire une sortie basée sur une description de tâche avec un prompt qui explique quoi faire. Rien de sophistiqué—juste une instruction simple montrant ce qui était attendu.
Les résultats étaient solides, les modèles fonctionnaient bien même avec des prompts courts, mais il y avait quelques problèmes :
- Prix : J’ai fait un calcul rapide. Lors des tests avec GPT-4, cela coûterait entre 50 centimes et 1 dollar pour traiter un épisode de la première saison. Avec plus de 400 épisodes, le coût total devient élevé. Il y avait des options pour
- Grouper les appels pour réduire le coût d’inférence de moitié, mais c’est toujours trop cher.
- Appliquer des modèles plus petits qui sont moins chers, mais les résultats n’étaient pas si bons
- Contrôle de sortie : Dans certains épisodes, des parties de la sortie étaient manquantes—certaines lignes étaient sautées et pas traitées du tout.
Une première amélioration était de modifier le prompt envoyé au modèle, et passer de zero à un prompting few-shot. Cette technique aide à enseigner au modèle comment résoudre une tâche en fournissant quelques exemples avant de faire le prompt de traitement, ce qui aide à guider la sortie. Il y a une illustration des exemples utilisés pour le prompting few-shot.
J’ai utilisé le format basé sur les messages en créant une séquence de paires de messages utilisateur/assistant avec tous les exemples.
Cette technique est puissante, mais l’un des principaux inconvénients est la taille de l’entrée que vous fournissez, car vous payez pour les tokens d’entrée et de sortie lorsque vous générez du texte. Utiliser de nombreux exemples few-shot peut rendre la requête coûteuse (OpenAI semble offrir un mécanisme automatique de mise en cache de prompt pour le rendre moins cher).
Pour réduire les coûts, j’ai décidé d’explorer une autre méthode appelée fine-tuning, qui adapte un modèle plus petit à une tâche spécifique en utilisant des données personnalisées.
Fine-tuning
Le fine-tuning est une technique inspirée du développement de modèles d’apprentissage profond, où un modèle pré-entraîné est adapté à une nouvelle tâche, ce qui peut être appelé dans la littérature apprentissage par transfert.

(Merci à Chris Albon et ses flashcards IA sur le fine-tuning)
Il y a plein d’applications qui utilisent cette méthode, mais les principales sont en vision par ordinateur, où vous réutilisez un classificateur d’images pour une autre tâche. Par exemple, dans cet article, ils ont réutilisé un modèle entraîné pour classifier les classes ImageNet pour détecter la pneumonie dans les rayons X (données qui ne font pas partie du dataset ImageNet).
Les fournisseurs de modèles comme OpenAI ou Mistral offrent des fonctionnalités de fine-tuning supervisé directement sur leurs plateformes, avec seulement quelques clics.
Le processus est simple et comprend ces étapes :
- Choisir le modèle à fine-tuner : Dans mon cas, j’ai utilisé de petits modèles comme Mistral NeMo et GPT-4 Nano d’OpenAI.
- Définir les critères de fine-tuning : Vous devez spécifier le type de tâche, le nombre d’époques et le taux d’apprentissage.
- Fournir des ensembles d’entraînement et de validation : Ces datasets doivent suivre un format spécifique requis par le fournisseur de modèle.
- Surveiller le processus de fine-tuning : Suivre la perte pour optimiser l’entraînement. Habituellement, c’est une perte d’entropie croisée pour la complétion de texte, qui compare la probabilité assignée par le modèle au prochain token correct avec le token de vérité terrain réel dans la séquence.
OpenAI offre du fine-tuning qui va au-delà des méthodes supervisées, avec :
- optimisation de préférence directe, qui peut aligner un modèle avec les préférences humaines en utilisant des données de classement (comme des complétions choisies vs rejetées).
- approche de fine-tuning par renforcement liée directement au développement d’agents IA, avec des récompenses etc.

Pour construire mon dataset, j’ai suivi les directives de Mistral et sélectionné mes échantillons de toutes les saisons, en choisissant des lignes qui incluaient parfois plus d’un dialogue à extraire. J’ai collecté 500 échantillons pour l’ensemble d’entraînement et 50 pour l’ensemble de validation. Les étiquettes ont été générées en utilisant le prompting few-shot et vérifiées manuellement (rapidement). Les datasets sont sauvegardés en .jsonl, ce qui impose une structure de dictionnaire JSON propre pour chaque ligne—quelque chose requis pour le fine-tuning.
OpenAI et Mistral offrent deux façons proches de fine-tuner les modèles, voici quelques choses que j’ai apprises de mes tests :
- Le fine-tuning était rapide sur les deux plateformes, mais Mistral était plus rapide (moins de 5 minutes).
- OpenAI et Mistral offrent quelques paramètres à ajuster pendant le fine-tuning : nombre d’époques, taux d’apprentissage et taille de batch (la taille de batch n’est que pour OpenAI). OpenAI a également une sélection automatique de paramètres, que j’ai utilisée dans mon cas.
- Mistral peut fine-tuner pour la complétion de texte et la classification
- OpenAI fournit également un meilleur logging et un suivi en temps réel de la précision et de la perte
- J’ai trouvé Mistral plus transparent sur le coût de l’opération et du déploiement du modèle qu’OpenAI.
- Mistral inclut également une métrique supplémentaire pour la génération de texte appelée validation mean token accuracy (le nombre de tokens corrects prédits), que j’ai trouvée plus informative que la perte seule.
- OpenAI semble avoir une vérification légale à la fin du fine-tuning pour respecter leurs conditions d’utilisation
Au final, le meilleur modèle fine-tuné était d’OpenAI, avec une perte de 0.098, tandis que Mistral NeMo était autour de 0.67 (j’ai choisi le modèle fine-tuné de mistral avec la meilleure performance). Basé sur ce benchmark, j’ai décidé de rester avec le GPT Nano fine-tuné.

La touche finale
Alors finalement, pour traiter mes données, j’ai décidé d’utiliser le prompting few-shot en plus du modèle GPT Nano fine-tuné. Pourquoi ? Après quelques calculs rapides, j’ai vu que traiter toutes les six saisons me coûterait entre 15$ et 20$, prenant environ 1 heure par saison sans grouper les appels donc bien dans mon budget.
Pendant l’analyse des données, j’ai ajouté une couche de validation avec un mécanisme de réessai. L’objectif est de valider le contenu (en termes de format et de champs) dans la sortie et d’éviter de casser le post-traitement.
OpenAI, par exemple, offre la possibilité d’imposer la structure de sortie, mais je pense toujours qu’il est bon d’ajouter une validation plus profonde de la structure pour éviter les surprises dans le processus.
Alors, maintenant avec toutes les données traitées dans le LLM, tout est parfait ? Pas vraiment.

Spécifiquement, il y avait quelques problèmes avec le champ personnage. Certaines didascalies n’étaient pas traitées correctement. J’ai dû ajouter une couche de mapping pour environ 400 personnages qui étaient traités trop grossièrement, pour extraire le nom et la didascalie. J’ai construit un grand mapping qui normalise également les noms de personnages à travers les saisons (comme “Dame Seli,” “Seli,” etc.).
J’ai utilisé quelques fonctions de nettoyage de données que j’ai initialement développées pour le projet. Mais pour être honnête, mon fichier de traitement original sans LLM avait plus de 1 200 noms de personnages qui n’étaient pas traités correctement, donc le LLM a quand même fait un bon travail.
Vous pouvez trouver le dataset final sur Kaggle ou Github. J’ai également pris la liberté d’ajouter l’URL de l’épisode associé sur YouTube, que j’ai récupérée de l’API YouTube et associée aux informations sur l’épisode de l’extrait du site web Hypnoweb.
Notes de clôture
Comme vous pouvez vous y attendre, les prochaines étapes seront de travailler avec le dataset et de faire quelques projets sur le NLP et l’IA générative. Je travaille actuellement sur l’embedding de texte et le RAG principalement pour la découverte d’épisodes. J’attendrai également des retours pour améliorer le dataset, car je suis sûr que la transcription n’est pas parfaite.

Plus généralement, travailler avec les LLM pour ce projet a souligné quelques choses :
- Un benchmarking approprié est essentiel : Je n’ai fait qu’un basique ici, et je pense qu’il est important de pouvoir changer rapidement de fournisseur de modèles pour tester la qualité des prédictions. Ne négligez pas la possibilité d’exécuter des modèles localement lorsque c’est faisable.
- Tout se résume au coût, à la précision et au temps : Pour ce type de projet, vous pouvez garder les coûts bas si vous êtes intelligent (et rappelez-vous de ne pas utiliser la fonction de recharge automatique du solde de votre compte).
- Avoir un bon pipeline d’annotation compte vraiment : Dans mon cas, c’était simple, mais être capable de créer rapidement des échantillons pour le prompting few-shot ou des datasets pour le fine-tuning est la clé d’une itération rapide. Le fine-tuning n’est maintenant qu’à quelques clics et appels API, donc vous devez être efficace dans la création de données.
Au-delà de cet article, j’aime discuter de données, d’IA et de conception de systèmes—comment les projets sont construits, où ils réussissent et où ils peinent. Si vous voulez échanger des idées, remettre en question des hypothèses, ou parler de vos propres projets, n’hésitez pas à me contacter. Je suis toujours ouvert à une bonne conversation.