Entraîner un LLM sur un iGPU : nanochat (Karpathy) sur un simple GPU intégré
nanochat est le projet d’Andrej Karpathy qui montre, en un seul dépôt lisible, toutes les étapes d’un « ChatGPT » : tokenizer, pré-entraînement, fine-tuning (SFT) et interface de chat. Il est pensé pour 8 GPU NVIDIA H100, mais il propose aussi une configuration réduite pour CPU.
Nous l’avons fait tourner entièrement sur un iGPU : l’Intel UHD 770, le GPU intégré au processeur (un Core i5 de bureau), sans aucune carte graphique dédiée et sans CUDA. Le tout avec PyTorch XPU, le support natif des GPU Intel dans PyTorch, puis avec l’iGPU et le CPU qui calculent ensemble.
Résultats en bref
- Un simple iGPU suffit pour entraîner un LLM de A à Z : installation, données, tokenizer, pré-entraînement (33 min), SFT (environ 6 min) et premières réponses en moins d’une heure, sur le GPU intégré de la VM, sans carte graphique.
- 1,33 s par pas de pré-entraînement, soit environ 6 150 tokens/s, pour un petit modèle de 4 couches. Sur le même travail, le GPU va environ 1,3 fois plus vite que les 6 vCPU.
- iGPU et CPU ensemble : en confiant 4 lignes sur 16 de chaque lot au CPU, le pré-entraînement passe de 1,33 à 1,13 s par pas (−15 % de temps), pour une qualité presque identique (validation 1,55 contre 1,52). Donner plus de travail au CPU ralentit au contraire.
- Le modèle obtenu joue l’assistant et parle anglais, mais reste incohérent : c’est la taille du modèle (37 millions de paramètres, surtout des tables d’embeddings, 39 min d’entraînement), pas la plateforme.
- Il a fallu 4 adaptations pour le GPU Intel et 4 correctifs de nanochat pour une petite configuration. Tout est dans un script d’une commande, en bas de l’article.
Pourquoi un iGPU ?
Un iGPU (GPU intégré) est la partie graphique qui se trouve dans le processeur, sur la même puce que les cœurs CPU. On le réserve d’habitude à l’affichage ; ici, il sert au calcul.
- Il partage la RAM du serveur au lieu d’avoir sa propre mémoire vidéo. Pas de limite de VRAM à 8 ou 12 Go, et pas de copie coûteuse entre CPU et GPU : c’est ce qui rend l’entraînement iGPU + CPU efficace plus bas. Notre modèle n’a utilisé que 1,5 Go.
- PyTorch le gère nativement (build XPU),
torch.compilecompris : le même code qu’avec CUDA, à quelques lignes près. - Il coûte une fraction d’un GPU dédié : notre offre iGPU Pro est à 44,99 € HT par mois, quand un GPU dédié en cloud se loue souvent 300 à 500 € par mois.
- Il est beaucoup moins puissant (moins de 1 TFLOPS) : il ne remplace pas un GPU dédié pour entraîner un gros modèle. Il suffit pour apprendre, expérimenter et faire tourner de petits modèles.
Conditions du test
| Élément | Valeur |
|---|---|
| Machine virtuelle | offre Pro : 6 vCPU, 24 Go de RAM, Debian 13, noyau 7.3 |
| CPU / GPU | Intel Core i5-13500, GPU intégré Intel UHD 770 (partagé avec d’autres VM) |
| Logiciel | nanochat (commit 92d63d4), PyTorch 2.9.1 build XPU, torch.compile avec Triton XPU |
| Modèle | d4 : 4 couches, largeur 256, contexte 512 tokens, vocabulaire de 32 768 |
| Pré-entraînement | 1 500 pas de 8 192 tokens (12,3 millions de tokens) |
| SFT | 220 pas de 8 192 tokens, mélange de conversations, MMLU et GSM8K |
C’est la configuration « CPU » de nanochat, réduite pour tenir en moins d’une heure. Le vrai
nanochat (speedrun.sh, modèle de 20 couches) demande environ 4 h sur 8 H100 : c’est trois ordres
de grandeur de calcul en plus, hors de portée d’un GPU intégré.
1. Les chiffres
Calcul brut
| Produit de matrices 2048×2048 | TFLOPS |
|---|---|
| fp32 | 0,71 |
| bf16 | 0,73 |
Le GPU est partagé : quand une autre VM l’occupe, nous avons mesuré jusqu’à 0,27 TFLOPS. Ce GPU n’a pas d’unités matricielles dédiées, donc le bf16 ne va pas plus vite que le fp32, mais il prend deux fois moins de mémoire.
Entraînement et chat
| Étape | Durée | Détail |
|---|---|---|
| Installation (paquets, nanochat, PyTorch XPU) | ~1 min | |
| Données (8 fichiers) + tokenizer | 50 s | sur le CPU |
| 1ʳᵉ itération du pré-entraînement | 83 s | compilation des kernels GPU, une seule fois |
| Pré-entraînement, 1 500 pas | 33 min | 1,33 s/pas, ~6 150 tokens/s, 1,5 Go de mémoire GPU |
| SFT, 220 pas | ~6 min | ~1,5 s/pas |
| 1ʳᵉ réponse du chat | 3 min | compilation |
| Réponses suivantes | 13 à 15 s | réponses longues, jusqu’à la limite de tokens |
GPU contre CPU, même travail
| SFT, pas de 16 384 tokens | s/pas | tokens/s |
|---|---|---|
| GPU Intel UHD 770 | 2,64 | 6 200 |
| 6 vCPU (i5-13500) | 3,0 à 3,5 | 4 630 |
La courbe d’apprentissage
| Début | Fin | |
|---|---|---|
| Pré-entraînement, bits par octet en validation | 3,20 | 1,52 |
| SFT, bits par octet en validation | 1,35 | 1,10 |
Plus c’est bas, mieux le modèle prédit le texte. Les deux courbes descendent régulièrement, sans accident.
2. iGPU et CPU ensemble
Le GPU calcule pendant que les 6 vCPU attendent. Nous avons donc fait travailler les deux en même temps (correctif nanochat_hybrid_patch.py) :
- Chaque lot de 16 lignes est coupé en deux : une partie part sur le GPU, l’autre sur une copie du modèle en mémoire CPU. Les deux calculent en parallèle.
- Les gradients des deux parties sont additionnés, au prorata du nombre de lignes. C’est mathématiquement le même calcul qu’un lot entier sur un seul processeur.
- L’optimiseur met à jour le modèle sur le GPU, puis les poids sont recopiés vers la copie CPU. Sur un GPU intégré, GPU et CPU partagent la même mémoire : cette copie coûte environ 0,1 s par pas.
Tout tourne en fp32 : le CPU de ce serveur n’a pas d’instructions bf16 rapides, et sur ce GPU le fp32 va aussi vite que le bf16.
Combien de travail donner au CPU ?
| Lignes GPU + CPU (threads CPU) | s/pas | Par rapport au GPU seul en fp32 (1,40 s) |
|---|---|---|
| 16 + 0 (GPU seul) | 1,40 | référence |
| 12 + 4 (3 ou 4 threads) | 1,16 | −17 % |
| 10 + 6 (5 threads) | 1,32 | −6 % |
| 10 + 6 (4 threads) | 1,41 | aucun gain |
| 8 + 8 (4 threads) | 1,43 | plus lent |
Contre-intuitif : donner plus de lignes au CPU ralentit tout. Nos mesures montrent que le CPU finit toujours avant le GPU, mais que la partie GPU devient plus lente quand le CPU calcule : les cœurs qui calculent ne sont plus disponibles pour piloter le GPU (lancer ses kernels, gérer la différentiation automatique). Le meilleur réglage laisse donc de la marge : 4 lignes sur 16 et 3 threads de calcul sur 6 vCPU.
Le run complet, GPU seul contre GPU + CPU
| Pré-entraînement, 1 500 pas | GPU seul (bf16) | GPU + CPU (fp32) |
|---|---|---|
| Temps par pas | 1,33 s | 1,13 s |
| Tokens par seconde | ~6 150 | ~7 200 |
| Durée totale | 33 min | 28 min |
| Validation finale (bits par octet) | 1,52 | 1,55 |
| SFT puis chat | identique | identique |
La validation finale est un peu moins bonne en hybride (1,55 contre 1,52). Nous n’avons pas encore isolé la cause : le passage en fp32, ou un effet de la répartition entre GPU et CPU.
Deux limites à connaître : le gain dépend du nombre de vCPU (avec 2 vCPU, il n’y a rien à partager), et nous n’avons appliqué cette méthode qu’au pré-entraînement, l’étape la plus longue. Le SFT tourne sur le GPU seul.
3. Ce que répond le modèle
What is the capital of France? The city of France has a fundamental concept in Japan, which is a city of France’s Italian history and country. […] a fundamental concept in Paris and the way to showcase the world’s most fundamental and modern aspects of the history…
Hi! Who are you? I’m glad you’ve got a few minutes of time. We’re passionate about the concept of “A”…
Le modèle a appris la forme d’une réponse d’assistant et des associations de mots (France, Paris), mais pas à raisonner : avec 4 couches et 39 minutes de calcul, c’est le résultat attendu. Le modèle de base, avant le SFT, se contentait de prolonger le texte sans répondre.
4. Ce qu’il a fallu changer
Pour le GPU Intel (PyTorch XPU)
- Reconnaître le GPU : nanochat ne connaît que
cuda,mpsetcpu. Nous ajoutonsxpu(détection, synchronisation, mémoire). - Pas de calcul en double précision (fp64) sur ce GPU : une ligne du calcul des embeddings
rotatifs passait par du fp64 et plantait (
UR error: 45). Nous la réécrivons en fp32. - En-têtes Level Zero :
torch.compilecompile ses kernels GPU avec Triton XPU, qui a besoin du paquetlibze-dev. - Limite de 512 work-items par groupe : le tri des 32 768 tokens du vocabulaire (
topk) dépasse cette limite. Nous le faisons sur le CPU, le coût est négligeable.
Pour nanochat en petite configuration
Ces quatre problèmes apparaissent aussi sur CPU : ils viennent de la configuration réduite (contexte de 512 tokens), pas du GPU.
- Le chargeur du SFT se bloque : il ne prend que les conversations qui tiennent entières dans
513 tokens. Les plus longues restent dans sa file d’attente jusqu’à la remplir, puis il ne
produit plus que du remplissage : le modèle n’apprend plus rien, dérive et finit en
nan. Nous tronquons les conversations à 513 tokens. - Une loss
nanquand un lot ne contient aucun token de réponse (division 0/0). Nous divisons par au moins 1. - Un learning rate trop fort pour un si petit modèle : nous utilisons 20 % de la valeur par défaut pour le SFT, et nous nous arrêtons à 220 pas (au-delà, le modèle s’effondrait d’un coup, toujours au même pas).
- Le chat chargeait un ancien modèle :
chat_cliprend le checkpoint au plus grand numéro de pas, pas le plus récent. Nous lui passons-s 219.
5. Le faire vous-même
Sur une VM avec un GPU Intel (Debian 13, noyau récent), une seule commande lance tout et chronomètre chaque étape :
curl -fsSLO https://linuxgpu.eu/blog/files/nanochat-xpu/run.sh
sudo bash run.sh # GPU seul
sudo HYBRID_CPU_ROWS=4 bash run.sh # GPU + CPU
Le script installe les paquets, nanochat et PyTorch XPU, applique les correctifs
(nanochat_xpu_patch.py et
nanochat_hybrid_patch.py), puis enchaîne
données, tokenizer, pré-entraînement, SFT et une première question. Les durées de chaque étape sont
dans ~/nanochat-logs/timeline.txt. Pour discuter ensuite :
cd ~/nanochat && .venv/bin/python -m scripts.chat_cli --device-type=xpu -s 219
Ce qu’il faut en retenir
- Un iGPU entraîne vraiment un petit modèle de langage, sans carte graphique dédiée : PyTorch
natif,
torch.compile, pas de CUDA, et le CPU peut l’épauler (−15 % de temps). - C’est le bon terrain pour apprendre comment fonctionne un LLM de bout en bout, modifier le code et voir l’effet en quelques minutes, pas pour entraîner un modèle utile en production.
- Pour servir un modèle déjà entraîné, voir plutôt nos guides sur les modèles Hugging Face sur iGPU et l’inférence avec OpenVINO.
Essayer
Toutes nos VM ont un iGPU Intel UHD 770 utilisable par PyTorch XPU, OpenCL et Vulkan dès l’installation, à partir de 14,99 € HT par mois. Ce test a tourné sur l’offre iGPU Pro (6 vCPU, 24 Go de RAM, 240 Go de SSD, la plus grosse part de l’iGPU), à 44,99 € HT par mois : le bon choix pour entraîner, grâce aux vCPU qui épaulent l’iGPU. Voir les offres iGPU.