Character-embeddings: een technische primer
Hoe character-lock-systemen daadwerkelijk werken in moderne AI-videopipelines: architectuur, ontwerpkeuzes, failure modes en open problemen.
Dit artikel is voor engineers — onderzoekers, ML-praktijkmensen en developers die AI-videotooling bouwen of evalueren. Wil je een niet-technisch overzicht van waarom character-consistentie ertoe doet, begin dan met de volledige gids.
Hier lopen we door hoe character-embedding-systemen daadwerkelijk werken in moderne AI-videostacks: de architectuur, de ontwerpkeuzes, de failure modes en de open problemen die we nog niet hebben opgelost.
De probleemstelling
Gegeven een generatief videomodel M en een character C, willen we een procedure zodat voor elke prompt p_i in een reeks p_1, p_2, …, p_n die naar C verwijst, de gegenereerde outputs allemaal de identiteit van C behouden.
De naïeve aanpak — de character-beschrijving in elke prompt zetten — faalt omdat diffusion-sampling stochastisch is en prompts categorieën beschrijven, geen identiteiten. Elke generatie is een trekking uit de verdeling van geldige characters die bij de beschrijving passen; de identiteit drift tussen trekkingen.
We hebben een manier nodig om de output van het model te conditioneren op een specifieke geleerde identiteit, niet alleen op een beschrijving.
De architectuur
Een modern character-consistentiesysteem heeft zes componenten:
1. Feature-extractie — produceert identity-embedding uit de referentie
2. Storage — bewaart embedding gekoppeld aan character_id
3. Negative-prompt-synthese — bouwt automatisch negative_prompts uit drift-catalogus
4. Conditioning-injectie — injecteert embedding in de modelconditioning
5. Generatie — diffusion-sampling met geconditioneerd model
6. Consistentie-verificatie — post-hoc similariteitscheck, opnieuw genereren indien nodigWe lopen ze stuk voor stuk langs.
1. Feature-extractie
Bij upload van een character draaien meerdere gespecialiseerde modellen op de referentie:
- Face-encoder: ArcFace, FaceNet of vergelijkbaar. Levert een 512-dim identity-embedding, geoptimaliseerd voor gezichtsherkenning. Vangt identiteits-invariante features.
- Body-parser: PIFu of Sapiens, voor lichaamsproporties en houding. Laagdimensionale vector die lengte, bouw, houding codeert.
- Appearance-encoder: CLIP-image-encoder, voor haarkleur, huidskleur, kledingstijl. 768-dim semantische embedding.
- Style-classifier: codeert apart of de referentie realistisch, gestileerd, animatie etc. is. Kleine categorische vector.
Deze worden geconcateneerd (of via attention samengevoegd) tot een hoogdimensionale character-embedding e_C. Totale dimensionaliteit ligt typisch tussen 1500 en 3000.
Waarom meerdere modellen in plaats van één? Omdat identiteit meerdere assen heeft die geen enkele encoder volledig vangt. Face-encoders zijn goed in “is dit hetzelfde gezicht?” maar blind voor lichaamsproporties. Body-parsers zien geen gezichtsdetails. CLIP is sterk in semantisch uiterlijk maar verliest fijne identiteit. Concateneren geeft orthogonale dekking.
Trade-off: een complexere extractie-pipeline betekent meer compute bij upload (~30-90 seconden in sommige systemen). Voor consumenten-tools is dat prima. Voor pipelines met hoge throughput kun je embeddings één keer pre-computen bij upload en bij generatie refereren.
2. Storage
Elke character wordt opgeslagen als (character_id, embedding_vector, metadata). Metadata bevat:
- Bron-referentiebeeld (voor debugging en re-extractie)
- Eigenaar / project-koppeling
- Sub-variant-pointers (zie sectie over form-variants)
- Style-anchors (voor cross-stylewerk)
- Drift-mode-override-lijst (per-character maatwerk)
Storage is doorgaans een vectordatabase (Pinecone, Qdrant, Weaviate) of een eigen geïndexeerde structuur. Lookups moeten snel zijn — sub-100ms — omdat ze bij elke generatie gebeuren.
Voor privacy-gevoelige deployments kunnen embeddings versleuteld worden opgeslagen met per-tenant sleutels. De extractie is een one-way functie (je kunt het referentiebeeld niet reconstrueren uit de embedding), maar embeddings als PII behandelen is de juiste default voor systemen die met echte mensen werken.
3. Negative-prompt-synthese
Dit is het niet-vanzelfsprekende deel van het systeem en waar het meeste engineeringwerk zit.
In de praktijk houdt men een catalogus bij van veelvoorkomende drift modes —categorische failure-types waargenomen over duizenden generaties. Voor elke mode is er een corresponderend negative_prompt-fragment dat die failure onderdrukt.
Voorbeelden uit de catalogus:
| Drift mode | Negative-prompt-fragment |
|---|---|
| Oogkleurverschuiving (bruin → groen) | “green eyes, hazel eyes” (wanneer referentie bruin is) |
| Versmalling van de kaaklijn | “narrow jaw, weak chin, soft jawline” |
| Wijkende haargrens | “high hairline, thinning hair, receding hairline” |
| Warmer wordende huidstoon | “warm skin tone, golden complexion” (wanneer referentie koel is) |
| Asymmetrie-drift | “asymmetric face, uneven features” |
| Verschuivende oogafstand | “wide-set eyes, close-set eyes” |
Deze catalogus opbouwen vergt gelabelde data. In de praktijk labelt men ~10.000 generaties van publieke AI-videotools (Runway, Pika, Sora etc.) met de specifieke drift modes die verschenen. Clustering levert typisch ~30 distincte modes die ~85% van waargenomen drift dekken.
Voor elke generatie:
- Haalt het systeem de referentie-attributen van de character op
- Berekent het “tegenovergestelde” van elk attribuut (bv. als de referentie donkere ogen heeft, is het tegengestelde lichte ogen)
- Stelt een character-specifieke negative prompt samen door de relevante drift-suppressors te bundelen
Het resultaat is een veel sterker conditioning-signaal dan vanille prompt-only generatie.
4. Conditioning-injectie
Verschillende videomodellen accepteren conditioning op verschillende manieren:
- Reference-image-gebaseerde modellen (de meeste publieke API’s): je kunt een referentiebeeld doorgeven; we coderen de embedding via een geleerde projectie terug naar een “synthetisch referentiebeeld” en geven dat door.
- Text-only conditioning: doorgeven van een geleerde soft-prompt-projectie van de embedding.
- API-level modeltoegang (waar beschikbaar): de embedding direct injecteren in cross-attention-lagen, vergelijkbaar met IP-Adapter-conditioning.
In de praktijk is API-level injectie veel effectiever dan reference-image-gebaseerd, maar de meeste publieke API’s bieden deze diepte niet. Werkend op de beschikbare API-oppervlakte: een sterke negative prompt combineren met een als referentiebeeld gecodeerde embedding levert ongeveer 80-90% van het effect van API-level injectie.
Mede daarom is een character-consistentie-laag bouwen zinvol, ook als je het onderliggende model niet beheert — er is significante speelruimte op de conditioning-oppervlakte die publieke API’s al bieden.
5. Generatie
Standaard diffusion-sampling, met als nuance dat de conditioning nu een combinatie is van:
- Originele prompt (scène, actie, kadrering)
- Character-embedding (geïnjecteerd via bovenstaand mechanisme)
- Negative prompt (auto-gesynthetiseerd)
- Style-anchor (indien van toepassing op het segment)
Generatiekosten zijn typisch 1.0-1.2× die van een vanille generatie. De marginale kosten zijn klein.
6. Consistentie-verificatie
Na generatie laten we een apart identity-model (gewoonlijk dezelfde face-encoder als in stap 1) los op de output. We berekenen de cosine similarity tussen de identity-embedding van de output en de oorspronkelijke referentie-embedding.
Drempel: typisch 0,85 cosine similarity. Boven de drempel wordt de output geaccepteerd. Eronder wordt regeneratie getriggerd met strakker conditioning (hoger negative-prompt-gewicht, sterkere embedding-injectie).
Dit voegt gemiddeld ~5-10% generatiekosten toe (de meeste shots passeren in één keer) en houdt de ergste drift-gevallen weg van de gebruiker.
Wat goed werkt, wat niet
Wat werkt:
- 30+ shots van één character met hoge consistentie, bij standaard scène-variatie
- Hergebruik van character-bibliotheek over projecten heen (één extractie, oneindig hergebruik)
- Cross-platform consistentie (zelfde character_id, zelfde identiteit over verschillende scènes / stijlen binnen redelijke grenzen)
- Multi-character-scènes met duidelijk verschillende features (verschillende leeftijd, geslacht, etniciteit)
Wat moeilijker is:
- Form-variants: dezelfde character maar gewond, ouder, in andere kleding. Gebruikelijk zijn sub-embeddings die gekoppeld zijn aan een master, waar de master de invariante identiteit codeert en de sub het delta. Werkt voor matige variatie; breekt bij grote transformaties (bv. 8-jarige versie van dezelfde character).
- Identity-bleed in multi-character-scènes: wanneer twee gelockte characters een frame delen en gelijksoortige features hebben (beide bv. Aziatische vrouwen van 30), vertoont ongeveer 10% van de generaties gedeeltelijke feature-bleed.
- Cross-style-coherentie: gelockte realistische character geplaatst in een gestileerd “aquarel”-segment. Gedeeltelijk opgelost via per-segment style-anchors, maar degradatie is zichtbaar.
- Dier-/niet-menselijke characters: dezelfde architectuur is van toepassing, maar de kwaliteit van face-encoders zakt sterk buiten menselijke gezichten.
- Long-form-coherentie boven ~3 minuten: drift-suppression werkt per shot, maar opgehoopte subtiele verschillen over 50+ shots kunnen voor een aandachtige kijker nog steeds zichtbaar inconsistent zijn.
Open onderzoeksproblemen
Wie in dit veld werkt, hier zijn problemen die we graag opgelost zouden zien:
- Form-variant-invarianten. Wat is de juiste geleerde representatie die identiteits-invariante gezichtsstructuur vangt en tegelijk willekeurige toestandstransformaties toelaat?
- Actieve drift-detectie tijdens sampling. Huidige consistentiechecks zijn post-hoc. Kunnen we drift detecteren tijdens het diffusion-proces en mid-sampling corrigeren?
- Trade-off impliciete vs. expliciete identiteit. Wanneer overtreft een kleine per-character LoRA embedding-gebaseerde conditioning? Waar ligt de grens?
- Modellering van multi-character-interactie. Hoe vangen we niet alleen twee gelockte identiteiten maar ook hun relationele dynamiek zo dat het over shots stand houdt?
- Identity-uncertainty kwantificeren. Wanneer het model onzeker is over identiteit, kan het die onzekerheid expliciet maken in plaats van een zelfverzekerde drift te produceren?
Werk je aan een van deze en wil je sparren? Het team achter Juying heeft echt belangstelling. Neem contact op.
Praktisch advies voor builders
Overweeg je een character-consistentielaag voor je eigen product te bouwen, drie adviezen:
1. Begin met de negative-prompt-catalogus. Dit is de hoogste-impact-laagste-kosten-winst. Je hebt geen API-level modeltoegang nodig; de negative prompt wordt door elke publieke API ondersteund. Besteed een week aan het labelen van 1000 generaties en je hebt een catalogus die het meeste drift dekt.
2. Onderschat post-hoc verificatie niet. Een simpele “opnieuw genereren als similarity < 0,85”-loop toevoegen vangt de slechtste 10% van failures en verhoogt de waargenomen kwaliteit drastisch. Het is de goedkoopste 90/100 → 95/100 quality-bump die er is.
3. Investeer vroeg in storage. Character-embeddings als persistente assets is het architectuurinzicht dat compoundt. Bouw de juiste primitives één keer en elke toekomstige feature (style-locks, scènebibliotheken, asset-hergebruik) breidt zich natuurlijk uit.
Verwante leesstof
- Character-consistentie in AI-video: de complete gids 2026
- Wat is character-drift in AI-video?
- Runway vs Pika vs Sora vs Juying: tool-vergelijking
Bouw je in dit gebied en wil je praten — info@juying.art