Avatar video only feels believable if the person stays consistent over time. When the face drifts, the teeth change, the lip sync slips, or the motion resets between clips, people notice immediately. This matters more for avatars than for many other video generation tasks because the viewer is watching a specific person speak, often at close range, for a long time.
In today's video generation world, duration is still one of the most visible limitations. Many models and products expose generation as a fixed-length clip — a few seconds, with few systems able to generate more than a few minutes. For avatar products, that limit shows up directly in customer workflows. Customers want longer consistent scenes/videos for training videos, sales demos, product walkthroughs, education, support, and agents that should keep talking until the task is done, and also they want fast preview to iterate on prompts, motion and script.
Chez HeyGen, cela s’est traduit par trois exigences concrètes :
- Long-scene consistency. The avatar needs to preserve identity, lip sync, expression, and motion continuity not just for one short clip, but across many chunks of generated video.
- Aucune limite de durée fixeUne génération peut durer dix secondes, dix minutes, ou prendre la forme d’une session en temps réel sans fin prédéfinie.
- Fast preview, realtime or faster-than-realtime generation. The system should start producing frames quickly and even allow streaming out the generated frames while inference is still ongoing.
Cet article présente en détail le framework d’inférence que nous avons conçu pour répondre à ces exigences.
The Underlying Model Architecture
The framework is built around HeyGen's avatar video generation models — the Avatar IV and Avatar V families. At a high level, the model takes a reference image/video, driving audio, and optional text or scene conditioning, then generates a video of that avatar speaking with the right identity, expression, and motion.
Le modèle de génération principal est un Diffusion Transformer, ou DiT, entraîné avec le flow matching. Au lieu de compresser la personne dans un petit vecteur d’identité, le modèle se base sur des jetons de référence riches afin de préserver les détails importants pour les avatars : forme du visage, dents, texture de la peau, mouvement de la bouche, style de gestuelle et rythme de parole.
Le chemin d’inférence en production comporte trois étapes principales :
- Génération audio-vers-vidéo. Un modèle DiT de base génère des latents vidéo basse résolution à partir de l’identité de référence, des caractéristiques audio et des signaux de conditionnement. Cette étape se concentre sur le mouvement, la synchronisation labiale et la cohérence temporelle.
- Identity-aware super-resolution. A second model refines those latents into high-resolution output, with extra attention on regions where people are most sensitive to artifacts, especially the face and mouth.
- Décodage VAE en streaming. Un décodeur VAE convertit les latents haute résolution en images RGB morceau par morceau, afin que les images puissent être émises avant que la vidéo complète ne soit générée.
To generate long videos, the system processes data in chunks. While the first chunk relies entirely on the static reference, subsequent chunks use boundary data from preceding segments. This allows the avatar to continue speaking naturally without resetting its posture or identity from scratch.
Le cadre de diffusion en continu et la boucle de pipeline
Pour prendre en charge une exécution par blocs, le framework d’inférence utilise une architecture modulaire à trois niveaux qui fonctionne sur des fenêtres temporelles localisées, libérant immédiatement les ressources après le traitement de chaque bloc.
- Module: A wrapper around a specific model and its checkpoint (e.g., A2V DiT, Super-Resolution DiT, VAE components, text/audio encoders).
- Étape: une unité d’exécution typée qui coordonne un ou plusieurs modules (par exemple, génération de contexte, super-résolution).
- Pipeline: Le graphe d’exécution qui relie les étapes entre elles, gère l’état partagé et coordonne les modes d’exécution en flux continu ou par lots.
La phase d’initialisation encode l’identité de référence en latents une fois par requête. Le pipeline exécute ensuite une boucle continue à travers les étapes restantes jusqu’à ce que le flux audio d’entrée soit épuisé :

- Génération de contexte: Convertit les segments audio entrants en caractéristiques, les combine avec un conditionnement textuel ou de scène et prépare les tenseurs de bruit cibles.
- Audio-vers-vidéo: Exécute un passage de diffusion en plusieurs étapes pour produire des latents en basse résolution. Cette étape conditionne le segment actuel sur les images de bord du segment précédent afin de maintenir la continuité du mouvement.
- Super-résolution: Met à l’échelle les latents de mouvement en pleine résolution en une seule étape, en donnant la priorité aux détails spatiaux du visage.
- VAE Decode-and-Publish: Décode les latents haute résolution en images RGB et les envoie directement à l’encodeur de sortie (H.264 / AAC) pour un stockage immédiat ou une lecture en direct.
Continuité des frontières et cohérence des segments
La génération de vidéo en segments distincts peut entraîner des discontinuités aux frontières. Le framework atténue ce problème en utilisant deux types de segments distincts :
- Blocs N: segments qui génèrent la chronologie principale de l’avatar.
- I Chunks (Interpolation) : segments conçus pour fluidifier les transitions entre des N Chunks successifs.
La séquence d’exécution est structurée comme suit :
N0 -> N1 -> I0 -> N2 -> I1 -> N3 -> I2 -> ...
Un bloc I n’est généré qu’une fois que les blocs N qui le précèdent et le suivent sont terminés. Il utilise la dernière image du bloc N précédent et une image précoce du bloc N courant comme images d’ancrage pour calculer le mouvement de transition. Après la génération, les prédictions d’ancrage redondantes sont supprimées, ne laissant que la transition interpolée de manière fluide. Ce mécanisme limite la taille de la fenêtre de contexte requise tout en préservant la cohérence temporelle.
Mémoire constante sur la durée
Un pipeline vidéo classique accumule les latents, les images décodées et le contexte d’attention pendant l’exécution, ce qui fait augmenter la consommation de mémoire GPU de manière linéaire avec la durée de la vidéo.
Pour permettre une génération ouverte, ce framework maintient un état déroulant strict. Le système ne conserve que le conditionnement de référence statique et un ensemble minimal de tenseurs d’ancrage nécessaires aux transitions entre segments. Tous les éléments intermédiaires — y compris les caractéristiques audio, les tenseurs de bruit, les activations internes et les images RVB brutes — sont purgés de la mémoire immédiatement après qu’un segment a été décodé et enregistré.
Par conséquent, le profil de mémoire GPU maximale reste constant, que l’on génère un court clip ou une séquence prolongée ; l’utilisation des ressources évolue en fonction de la taille de bloc définie plutôt qu’en fonction de la durée totale de la session.
Étapes de chargement/déchargement au sein du pipeline
Chaque requête s’exécute sur un nœud doté de 8 GPU. Nous utilisons FSDP pour répartir les paramètres des grands modèles entre les GPU. Chaque rang ne possède qu’une fraction des poids, rassemble les paramètres dont il a besoin pour un calcul, puis les libère à nouveau. C’est ce qui permet à plusieurs grands modèles — le DiT de base, le DiT de super‑résolution, l’encodeur de texte, l’encodeur audio et le VAE — de tenir sur un seul nœud.
Il y a un compromis. FSDP introduit une surcharge de communication pendant l’inférence, car les paramètres doivent être rassemblés lors des passes avant. Nous utilisons une combinaison de techniques pour masquer cette surcharge et maintenir les modèles co-localisés hors du GPU lorsqu’ils ne sont pas utilisés :
- Prélecture anticipée. L’AllGather des paramètres du bloc suivant est lancé en avance et se superpose au calcul du bloc actuel, ce qui masque la latence de collecte sur le chemin critique.
- Désagrégation paresseuse par bloc depuis le CPU. Lorsqu’un modèle est rechargé depuis la mémoire CPU épinglée, nous ne préparons pas l’ensemble des poids à l’avance. Chaque bloc de transformeur est désagrégé (copie hôte-vers-périphérique + AllGather) juste avant sa passe avant, de sorte que le transfert H2D du bloc n+1 se chevauche avec le calcul du bloc n.
- Déchargement sur CPU épinglée entre les étapes. Les paramètres d’un modèle qui n’est pas en cours d’exécution sont conservés en mémoire CPU épinglée, de sorte que les modèles co-localisés (DiT de base, DiT de super-résolution, encodeur de texte, encodeur audio, VAE) n’ont pas tous besoin de conserver leurs poids sur le GPU en même temps. C’est la mémoire épinglée qui rend les copies H2D suffisamment rapides pour se chevaucher avec le calcul.
- Placement de processus optimisé pour le NUMA. Chaque processus est épinglé sur le même nœud NUMA que le GPU qui lui est attribué, de sorte que les transferts CPU↔GPU s’effectuent à la pleine bande passante PCIe/NVLink sans traverser l’interconnexion entre sockets.
Changement de modèle en moins de 10 ms entre les étapes
Le bénéfice concret des techniques ci-dessus est que le passage du GPU d’un modèle d’étape à l’autre — par exemple, A2V DiT → Super-Resolution DiT, ou SR DiT → décodeur VAE — est pratiquement gratuit. Comme le modèle sortant est déchargé de façon asynchrone et que le premier bloc du modèle entrant est défragmenté juste à temps, la copie H2D et l’AllGather sont toutes deux masquées derrière des calculs déjà en cours d’exécution. De bout en bout, la surcharge observable par changement de modèle est inférieure à 10 ms — bien en dessous du budget d’une seule image aux fréquences d’images que nous visons. Concrètement, c’est ce qui permet à la boucle du pipeline de streaming (Context Gen → A2V → SR → VAE Decode-and-Publish) de faire tourner plusieurs grands modèles par segment sans que le changement de modèle ne devienne jamais le goulot d’étranglement.
Publication en streaming en temps réel
Afin de rendre le modèle suffisamment rapide pour diffuser en temps réel, nous avons effectué de nombreuses optimisations d’inférence, veuillez vous référer à https://www.heygen.com/research/avatar-v-inference pour plus de détails sur cette partie.
Une fois que le pipeline émet la vidéo morceau par morceau en temps réel, la diffusion en streaming devient une extension naturelle de l’inférence plutôt qu’une étape de post-traitement distincte.
Pour le flux temps réel de type diffusion, nous publions les images générées vers Amazon Kinesis Video Streams (KVS). KVS est généralement évoqué dans le contexte de caméras, de dispositifs IoT et de contenus média téléversés. Dans notre cas, la « caméra » est le pipeline d’inférence lui‑même : les images sont créées par le modèle, immédiatement encodées, puis envoyées dans KVS sous forme de flux en direct.
Le module d’écriture de sortie reçoit les images RVB décodées depuis le VAE en streaming et les envoie dans un pipeline GStreamer. La vidéo est encodée en H.264 et l’audio en AAC, puis les deux pistes sont envoyées vers kvssink, le sink producteur KVS. À partir de là, les spectateurs peuvent lire la session comme un flux en direct pendant qu’elle est encore en cours de génération.
Résultats et enseignements
Le framework a transformé la génération d’Avatar IV et Avatar V, en passant d’un rendu de scènes fixes à une génération en streaming ouverte. Le résultat le plus important est simple : nous avons supprimé les limites de durée de scène pour Avatar IV et Avatar V. Pour la génération en temps réel d’Avatar IV, nous avons obtenu un délai jusqu’à la première image de moins de 5 secondes et une génération à plus de 27 images par seconde pour des vidéos Avatar IV en 720p — plus rapide que la lecture en temps réel.