Forberedelse til LLM og Generativ AI-interview

LLM og Generativ AI Engineer Interviewspørgsmål

15 udvalgte LLM og generativ AI interviewspørgsmål grupperet efter anciennitetsniveau. Brug dem til at gennemgå grundlæggende principper, praktiske afvejninger og ræsonnement på seniorniveau i produktion.

Start et LLM og Generativ AI AI-interviewIntet kreditkort kræves. 1 gratis session er tilgængelig.
Øv tekniske jobsamtaler på engelskEn tilstand, hvor ikke-modersmålstalende kan øve sig i at bestå tekniske interviews.

Junior-spørgsmål

1Forklar subword-tokenisering, og hvorfor det foretrækkes frem for ord-niveau- eller tegn-niveau-tokenisering i moderne sprogmodeller.

Subword-tokenisering er en hybrid tekstsegmenteringsmetode, der opdeler tekst i morfologiske bidder af variabel længde eller frekvensbaserede understrenge (såsom 'un', 'break', 'able') i stedet for hele ord eller individuelle tegn. Algoritmer som Byte-Pair Encoding (BPE), WordPiece og Unigram LM lærer et vokabular af fast størrelse fra et træningskorpus, hvor hyppige ord forbliver intakte som enkelt-tokens, mens sjældne eller usete ord dekomponeres til kendte subword-enheder. Subword-tokenisering foretrækkes i moderne sprogmodeller, fordi det afbalancerer vokabularstørrelse, sekvenslængde og robusthed over for ukendte ord (OOV). Ren ord-niveau-tokenisering kræver et overdrevent stort vokabular (hvilket fører til enorme embedding-matricer) og lider stadig under OOV-tokens, der mappes til generiske '[UNK]'-tokens. Omvendt eliminerer ren tegn-niveau-tokenisering OOV-problemer, men resulterer i meget lange sekvenser, der drastisk øger beregningskompleksiteten i attention-mekanismer (som skalerer kvadratisk med sekvenslængden) og fortynder den semantiske tæthed per token. Subword-tokenisering opnår et optimalt kompromis ved at holde sekvenslængder håndterbare, vokabularstørrelser praktiske (typisk 32k til 128k tokens) og OOV-rater på nul (især når kombineret med byte-niveau-fallbacks).

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained('gpt2')
text = 'unbelievable'
tokens = tokenizer.tokenize(text)
token_ids = tokenizer.encode(text)

print('Tokens:', tokens)
print('Token IDs:', token_ids)
Prøv at besvare dette spørgsmål med en AI-coach

2Forklar forskellen mellem statiske ordindkapslinger (word embeddings), kontekstuelle indkapslinger (embeddings) og transformerens skjulte tilstande (hidden states).

Statiske ordindkapslinger (word embeddings), kontekstuelle indkapslinger (embeddings) og transformerens skjulte tilstande (hidden states) repræsenterer progressive evolutioner i, hvordan tekstrepræsentationer indfanger betydning og syntaktisk kontekst. 1. **Statiske ordindkapslinger (word embeddings)** (f.eks. Word2Vec, GloVe, FastText) tildeler en enkelt, fast vektor til hvert vokabulartoken uafhængigt af dets sætningskontekst. I dette paradigme har polyseme ord som 'bank' (flodbred vs. finansiel bank) eller 'apple' (frugt vs. teknologivirksomhed) identiske vektorrepræsentationer i alle kontekster, baseret på en statisk opslagstabel. 2. **Kontekstuelle indkapslinger (embeddings)** (f.eks. tidlig ELMo, BERT token-repræsentationer eller sætningsindkapslinger fra Bi-Encodere) producerer repræsentationer, hvor vektoren for et token er en dynamisk funktion af dets omgivende kontekst. I BERT eller ELMo modtager 'bank' i 'river bank' en helt anden indkapslingsvektor end 'bank' i 'deposit money at the bank'. 3. **Transformerens skjulte tilstande (hidden states)** refererer til de mellemliggende vektorrepræsentationer, der produceres ved hvert individuelle lag i et transformer-netværk under et forward pass. Givet input-token-indkapslinger ved lag 0, anvender hvert successive transformer-lag self-attention- og feed-forward-transformationer, der producerer en sekvens af skjulte tilstandsvektorer h_l ved lag l. Mens det sidste lags skjulte tilstande fungerer som kontekstuelle indkapslinger på højt niveau, indfanger lavere og midterste skjulte tilstande lavniveau syntaktiske, leksikalske og strukturelle træk. Således omfatter transformerens skjulte tilstande det fulde vertikale kontinuum af lag-for-lag repræsentationer på tværs af netværket.

import torch
from transformers import AutoTokenizer, AutoModel

tokenizer = AutoTokenizer.from_pretrained('bert-base-uncased')
model = AutoModel.from_pretrained('bert-base-uncased', output_hidden_states=True)

text1 = 'River bank'
text2 = 'Bank deposit'

inputs1 = tokenizer(text1, return_tensors='pt')
inputs2 = tokenizer(text2, return_tensors='pt')

with torch.no_grad():
    out1 = model(**inputs1)
    out2 = model(**inputs2)

# Layer 0 (Input token embeddings - static lookup before self-attention)
static_bank_1 = out1.hidden_states[0][0, 2] # 'bank'
# Layer 12 (Final contextual hidden state after all attention layers)
contextual_bank_1 = out1.hidden_states[12][0, 2]
contextual_bank_2 = out2.hidden_states[12][0, 1]

print('Cosine similarity of bank in different contexts (Layer 12):',
      torch.cosine_similarity(contextual_bank_1, contextual_bank_2, dim=0).item())
Prøv at besvare dette spørgsmål med en AI-coach

3Forklar, hvad et indlejringsrum (embedding space) repræsenterer, og hvordan cosinuslighed (cosine similarity) fortolkes for tekstindlejringer (text embeddings).

Et indlejringsrum er et kontinuerligt, højdimensionelt vektorrum R^d, hvor diskrete tekstuelle enheder (ord, sætninger eller dokumenter) afbildes, så semantiske, syntaktiske eller relationelle ligheder svarer til geometrisk nærhed og retningsbestemte relationer. I dette rum afspejler afstande og vinkler semantisk tilknytning. Cosinuslighed måler cosinus til vinklen theta mellem to vektorer u og v, beregnet som: ``` Cosinuslighed(u, v) = (u . v) / (||u|| ||v||) ``` Cosinuslighed fortolkes i tekstindlejringer som følger: - **Interval og retning:** Den producerer en skalarværdi, der typisk er begrænset til intervallet [-1, 1] (eller [0, 1] for ikke-negative indlejringer). En værdi tæt på 1,0 indikerer, at de to vektorer peger i stort set samme retning, hvilket afspejler høj semantisk lighed eller emnemæssig overensstemmelse. En værdi nær 0,0 indebærer ortogonalitet (semantisk uafhængighed eller manglende sammenhæng), og negative værdier indikerer modsatte retninger. - **Uafhængighed af længde:** I modsætning til Euklidisk afstand (L2-afstand) eller skalarproduktet normaliserer cosinuslighed for vektorens længde. I tekstindlejringer kan vektorens længde nogle gange korrelere med sekvenslængde, token-frekvens eller term-specificitet. Ved udelukkende at fokusere på retningsmæssig justering isolerer cosinuslighed semantisk orientering fra forskelle i vektorens længde.

import numpy as np

def cosine_sim(a, b):
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

# Hypothetical 3D embeddings
v_king = np.array([0.9, 0.1, 0.4])
v_queen = np.array([0.85, 0.15, 0.42])
v_apple = np.array([0.1, 0.9, -0.2])

print('Sim(king, queen):', round(cosine_sim(v_king, v_queen), 4))
print('Sim(king, apple):', round(cosine_sim(v_king, v_apple), 4))
Prøv at besvare dette spørgsmål med en AI-coach

4Forklar forskellen mellem token-indlejringer (token embeddings), positionelle indlejringer (positional embeddings) og segment- eller type-indlejringer (segment or type embeddings) i transformer-input.

I transformer-input (især BERT-lignende arkitekturer) dannes input-repræsentationen for hvert token typisk ved elementvis at summere tre forskellige indlejringsvektorer (embedding vectors): 1. **Token-indlejringer (Token Embeddings)**: Omdanner diskrete ordforråds-token-ID'er til tætte vektorer, der repræsenterer tokenets kerne-semantiske og leksikale identitet. 2. **Positionelle indlejringer (Positional Embeddings)**: Tilfører information om tokenrækkefølge og sekventiel indeks til repræsentationen, hvilket kompenserer for, at self-attention i sagens natur er permutationsinvariant. 3. **Segment- (eller Token Type) Indlejringer (Segment or Token Type Embeddings)**: Skelner mellem forskellige tekstsekvenser eller sætninger, der er pakket ind i en enkelt inputsekvens (såsom Sætning A vs. Sætning B i parvis klassifikation eller spørgsmål-svar-opgaver). Kombinationen af disse indlejringer giver en enkelt tæt input-tensor, der koder tokenets betydning, position og sekvensgruppering, før den sendes videre til det første transformer-lag.

import torch
import torch.nn as nn

vocab_size, max_seq_len, num_segments, d_model = 30522, 512, 2, 768
tok_embed = nn.Embedding(vocab_size, d_model)
pos_embed = nn.Embedding(max_seq_len, d_model)
seg_embed = nn.Embedding(num_segments, d_model)

input_ids = torch.tensor([[101, 7592, 102, 2023, 102]]) # Token IDs
type_ids = torch.tensor([[0,   0,    0,   1,    1  ]]) # Segment IDs (Sentence A vs B)
positions = torch.arange(input_ids.size(1)).unsqueeze(0)   # Indices: [0, 1, 2, 3, 4]

# Final representation is the element-wise sum
input_rep = tok_embed(input_ids) + pos_embed(positions) + seg_embed(type_ids)
print(input_rep.shape)
Prøv at besvare dette spørgsmål med en AI-coach

5Forklar attention som en mekanisme til at relatere tokens i en sekvens, herunder forespørgsler (queries), nøgler (keys), værdier (values) og multi-head attention.

Attention er en mekanisme, der gør det muligt for tokens i en sekvens dynamisk at dirigere information og vægte relevansen af alle andre tokens baseret på kontekstuel matching. Lineære projektioner konverterer hvert tokens input til tre vektorer: - Forespørgsel (Q): Repræsenterer hvilken information det aktuelle token søger. - Nøgle (K): Repræsenterer hvilke attributter eller hvilket indhold et token tilbyder at matche mod forespørgsler. - Værdi (V): Indeholder den faktiske informationspayload, der skal aggregeres. I scaled dot-product attention beregnes attention-score ved at multiplicere forespørgsler og nøgler ($Q K^T$), skaleret med $\frac{1}{\sqrt{d_k}}$ for at forhindre gradient vanishing på tværs af store dimensioner, og normaliseret med en softmax-funktion. Det endelige output er den vægtede sum af værdier: $$\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V$$ Multi-Head Attention (MHA) projicerer $Q$, $K$ og $V$ ind i flere uafhængige repræsentations-underrum (heads) parallelt. Dette gør det muligt for modellen samtidigt at 'attend' til forskellige typer relationer (f.eks. syntaktisk struktur, coreference, langtrækkende afhængigheder) på tværs af positioner. Outputtet fra heads'ene konkateneres og projiceres lineært tilbage til modeldimensionen.

import torch
import torch.nn.functional as F

# Q, K, V: [batch_size, seq_len, head_dim]
Q = torch.randn(1, 4, 64)
K = torch.randn(1, 4, 64)
V = torch.randn(1, 4, 64)
d_k = Q.size(-1)

scores = torch.matmul(Q, K.transpose(-2, -1)) / (d_k ** 0.5)
attn_weights = F.softmax(scores, dim=-1)
output = torch.matmul(attn_weights, V)

print("Output shape:", output.shape)
Prøv at besvare dette spørgsmål med en AI-coach

6Forklar, hvordan opmærksomhedsmaskering adskiller sig for kausale dekodere versus bidirektionale encodere, og hvilke adfærdsmønstre den muliggør eller forhindrer.

Opmærksomhedsmaskering styrer, hvilke tokens der må rette opmærksomhed mod hvilke andre tokens, ved at sætte attention logits (scores før softmax) til $-\infty$ for ikke-tilladte par, hvilket sikrer, at deres post-softmax attention-vægt er strengt 0. 1. Kausale Dekodere (f.eks. GPT, LLaMA): Bruger en nedre-triangulær kausal (autoregressiv) maske. Et token på position $i$ kan kun rette opmærksomhed mod positioner $j \le i$. Dette forhindrer opmærksomhed mod fremtidige tokens, hvilket muliggør autoregressiv token-for-token generering under inferens og forhindrer lækage af fremtidige token-labels under paralleliseret træning. 2. Bidirektionale Encodere (f.eks. BERT): Bruger ikke en kausal maske; hvert token kan rette opmærksomhed mod alle tidligere og fremtidige tokens i sekvensen. De bruger padding-masker til at forhindre gyldige tokens i at rette opmærksomhed mod tomme `[PAD]` tokens i batchede sekvenser. Bidirektional opmærksomhed producerer rige, all-round kontekstuelle repræsentationer, der er ideelle til forståelsesopgaver, men forhindrer direkte single-pass autoregressiv tekstgenerering.

import torch
import torch.nn.functional as F

scores = torch.randn(3, 3)
# Create upper-triangular mask for future positions
causal_mask = torch.triu(torch.ones(3, 3, dtype=torch.bool), diagonal=1)

# Mask future positions with -inf before softmax
masked_scores = scores.masked_fill(causal_mask, float('-inf'))
atten_weights = F.softmax(masked_scores, dim=-1)
print(atten_weights)
Prøv at besvare dette spørgsmål med en AI-coach

7Forklar forskellen mellem encoder-only, decoder-only og encoder-decoder transformer-arkitekturer for sprogopgaver.

De tre primære transformer-arkitekturer adskiller sig fundamentalt i deres opmærksomhedsmaskeringsmønstre og tilsigtede operationelle mål: 1. Encoder-Only (f.eks. BERT, RoBERTa): Bruger bidirektionel selvopmærksomhed, hvor hvert token kan fokusere på alle andre tokens i sekvensen samtidigt. Den producerer rige kontekstuelle repræsentationer for en hel inputsekvens, hvilket gør den ideel til klassifikation, ekstraktiv QA (Question Answering) og feature-repræsentation. Den kan ikke naturligt generere autoregressiv tekst. 2. Decoder-Only (f.eks. GPT-3, Llama, Mistral): Bruger kausal (unidirektionel) selvopmærksomhed, hvor token `i` kun kan fokusere på tokens ved positioner `j <= i`. Den trænes autoregressivt ved hjælp af næste-token-forudsigelse og fungerer som standardarkitekturen for generative sprogmodeller, kode-generering og åben samtale. 3. Encoder-Decoder (f.eks. T5, BART): Kombinerer en bidirektionel encoder med en autoregressiv kausal dekoder. Ud over kausal selvopmærksomhed over genererede tokens bruger dekoderen cross-attention-lag, der forespørger encoderens output-repræsentationer. Denne arkitektur er specielt bygget til sekvens-til-sekvens-transformationopgaver såsom oversættelse og resumégenerering.

# Causal mask (Decoder-Only) vs Full mask (Encoder-Only)
import torch

seq_len = 4
encoder_mask = torch.ones(seq_len, seq_len)  # Full bidirectional attention
decoder_causal_mask = torch.tril(torch.ones(seq_len, seq_len))  # Lower-triangular

print("Encoder Mask:
", encoder_mask)
print("Decoder Causal Mask:
", decoder_causal_mask)
Prøv at besvare dette spørgsmål med en AI-coach

Middle-spørgsmål

8Forklar, hvordan valg af byte-niveau, Unicode-bevidste og flersprogede tokenizers påvirker modelkvalitet, omkostninger og retfærdighed på tværs af sprog.

Tokenizer-designvalg – såsom segmentering på byte-niveau versus Unicode-bevidste segmenteringer og allokering af flersproget ordforråd – påvirker direkte den efterfølgende modelkvalitet, inferens/træningsomkostninger og sproglig retfærdighed. Med hensyn til omkostninger og retfærdighed allokerer tokenizers, der primært er trænet på engelske eller latinske korpora, de fleste ordforrådsposter til engelske ord og morfemer. Som et resultat opnår engelsk høj komprimering (f.eks. ~1,3 tokens per ord), hvorimod ikke-latinske skrifttegn (f.eks. arabisk, devanagari, thai, kinesisk) eller sprog med få ressourcer ofte fragmenteres i flere underord (subwords) eller rå UTF-8 bytes (ofte 3 til 6 tokens per ord). Denne forskel kaldes ofte 'token-afgiften' eller 'ubalance i fertilitetsrate': ikke-engelske brugere betaler betydeligt mere per enhed semantisk indhold i API-fakturering, forbruger kontekstvinduesbegrænsninger (context window limits) meget hurtigere og lider under højere latens (latency). Med hensyn til kvalitet undgår byte-niveau tokenizers (som Byte-level BPE i GPT-2/GPT-4 eller SentencePiece med byte-fallback i LLaMA) helt nedbrud pga. ukendte tegn og ud-af-ordforråd (out-of-vocabulary, UNK) fejl, fordi enhver gyldig UTF-8 streng dekomponeres til byte-tokens. Dog forringer overdreven byte-fragmentering repræsentationskvaliteten, fordi transformeren skal bruge lag på at genkombinere byte-fragmenter til semantiske koncepter, før den udfører højniveau ræsonnement. Forøgelse af det flersprogede ordforråd (f.eks. udvidelse fra 32k til 128k+ tokens) balancerer fertilitetsrater og forbedrer efterfølgende opgaveydeevne på tværs af forskellige sprog, på bekostning af et moderat større input/output embedding-lag.

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained('gpt2')

english_text = 'Hello world'
hindi_text = 'नमस्ते दुनिया'

print('English tokens:', tokenizer.tokenize(english_text))
print('Hindi tokens:', tokenizer.tokenize(hindi_text))
print('English token count:', len(tokenizer.encode(english_text)))
print('Hindi token count:', len(tokenizer.encode(hindi_text)))
Prøv at besvare dette spørgsmål med en AI-coach

9Hvilke tokeniseringsartefakter opstår i numerisk ræsonnement, kodegenerering eller sjælden Unicode-tekst, og hvordan kan specialiserede tokenisatorer reducere dem?

Tokeniseringsartefakter opstår, når subord-tokenisatorer opdeler struktureret, numerisk eller sjælden tekst inkonsekvent, hvilket forhindrer modellen i at genkende underliggende semantisk eller syntaktisk regelmæssighed. Centrale artefakter omfatter: 1. Numeriske ræsonnementsartefakter: Standard BPE-tokenisatorer (Byte Pair Encoding) trænet på generel tekst opdeler tal i arbitrære bloklængder baseret på frekvens (f.eks. kan '12345' tokeniseres som ['12', '345'], mens '12346' tokeniseres som ['123', '46']). Denne inkonsekvente gruppering bryder cifferplaceringens justering (cifre, tiere, hundreder) og hæmmer aritmetisk ræsonnement. 2. Kodegenereringsartefakter: Indrykning (indledende mellemrum/tabulatorer) og operatorer med flere tegn (f.eks. '==', '!=', '->') opdeles ofte uregelmæssigt over mellemrumstegnsgrænser, hvilket fører til indrykningsfejl, oppustede tokenantal i dybt indrykket kode og syntaksfejl. 3. Sjældne Unicode- og emoji-artefakter: Multi-byte UTF-8-sekvenser (som komplekse emojis med zero-width joiners eller sjældne skrifttegn) opdeles i rå byte-tokens, der ikke bærer individuel semantisk betydning, hvilket forårsager hallucinerede tegn eller korrupt gengivelse af glyffer ved generering. Specialiserede tokenisatorer reducerer disse artefakter ved hjælp af skræddersyede præ-tokeniseringsregler og ordforrådsbegrænsninger: - Cifferopdeling: Håndhævelse af enkeltcifret tokenisering (f.eks. regex-opdeling af hvert ciffer `0-9` i sit eget token) sikrer ensartet repræsentation af cifferplacering for matematisk ræsonnement. - Dedikerede mellemrums-/indrykningstokens: Tilføjelse af eksplicitte tokens for indrykninger med flere mellemrum (f.eks. 2, 4, 8 mellemrum) og bevaring af programmeringssprog-nøgleord/operatorer. - Regex præ-tokenisering / byte-niveau fallbacks: Brug af regex-splittere (såsom GPT-4/tiktoken regexer), der adskiller tegnsætning, bogstaver og tal i strenge kategorier, før BPE-fletninger beregnes, hvilket forhindrer fletninger på tværs af kategorier (f.eks. forhindrer 'a=10' i at blive flettet til et enkelt token).

import tiktoken

# tiktoken cl100k_base (GPT-4 / ChatGPT) enforces digit and whitespace handling
enc = tiktoken.get_encoding('cl100k_base')

num1 = '12345'
num2 = '12346'
code_indent = '    def foo():'

print('Tokens num1:', [enc.decode([t]) for t in enc.encode(num1)])
print('Tokens num2:', [enc.decode([t]) for t in enc.encode(num2)])
print('Tokens code:', [enc.decode([t]) for t in enc.encode(code_indent)])
Prøv at besvare dette spørgsmål med en AI-coach

10Diskuter afvejningerne ved opmærksomhed med lang kontekst, herunder kvadratisk fuld opmærksomhed, glidende vinduesopmærksomhed, sparsom eller global opmærksomhed, KV-cache-omkostninger og opmærksomhedsfortynding.

Skalering af opmærksomhed til lange kontekstvinduer medfører afvejninger mellem beregning, hukommelsesforbrug og modeltroværdighed: 1. Kvadratisk fuld opmærksomhed vs. glidende vinduesopmærksomhed / sparsom opmærksomhed: Standard fuld opmærksomhed skalerer kvadratisk ($O(N^2)$) i beregning og aktiveringshukommelse med sekvenslængde $N$. Glidende vinduesopmærksomhed (lokal opmærksomhed) begrænser opmærksomheden til et fast naboskab $W$, hvilket reducerer kompleksiteten til $O(N \cdot W)$, men kræver flere lag for at udbrede information mellem fjerne tokens. Sparsomme eller globale opmærksomhedsmønstre kombinerer lokale vinduer med udvalgte globale anker-tokens for at bevare $O(N)$-skalering, samtidig med at langtrækkende kommunikation muliggøres. 2. KV-Cache hukommelsesomkostning: Under autoregressiv generering caches nøgler og værdier for alle tidligere tokens for at undgå redundant beregning. KV-cache hukommelsen skalerer lineært med sekvenslængden ($O(B \cdot L \cdot H_{KV} \cdot D \cdot N)$). For meget lange kontekster (32k–128k+ tokens) forbruger KV-cachen snesevis af gigabyte GPU (Graphics Processing Unit) VRAM (Video Random Access Memory) per batch, hvilket forårsager flaskehalsproblemer for maksimal batchstørrelse og hukommelsesbåndbredde. 3. Opmærksomhedsfortynding (Lost-in-the-Middle): Efterhånden som konteksten vokser, summerer softmax-nævneren over titusinder af tokens, hvilket spreder sandsynlighedsmasse tyndt ud over irrelevant kontekst. Denne stigning i entropi fortynder opmærksomhedens skarphed, hvilket forringer modellens evne til pålideligt at genkalde specifik information, der er indlejret midt i lange prompter.

def kv_cache_gb(batch_size, seq_len, layers=32, kv_heads=8, head_dim=128, bytes_per_elem=2):
    # 2 for Key and Value
    total_bytes = batch_size * seq_len * layers * kv_heads * head_dim * 2 * bytes_per_elem
    return total_bytes / (1024 ** 3)

print(f"32k context: {kv_cache_gb(4, 32768):.2f} GB")
print(f"128k context: {kv_cache_gb(4, 131072):.2f} GB")
Prøv at besvare dette spørgsmål med en AI-coach

11Sammenlign MHA (Multi-Head Attention), MQA (Multi-Query Attention) og GQA (Grouped-Query Attention), og forklar, hvordan de påvirker KV-cache-hukommelsen og decode-gennemstrømningen.

Multi-Head Attention (MHA), Multi-Query Attention (MQA) og Grouped-Query Attention (GQA) adskiller sig i, hvordan Key ($K$) og Value ($V$) heads deles på tværs af Query ($Q$) heads: 1. **Multi-Head Attention (MHA):** Har et lige antal $Q$-, $K$- og $V$-heads ($H_Q = H_{KV}$, 1:1-forhold). Hver query-head behandler sine egne uafhængige key/value-repræsentationer. Selvom det er udtryksfuldt, kræver det caching af separate KV (Key-Value) matricer for hvert head. 2. **Multi-Query Attention (MQA):** Bruger flere $Q$-heads ($H$), men kun 1 delt $K$-head og 1 delt $V$-head ($H:1$-forhold). Dette reducerer KV-cachestørrelsen med en faktor $H$, men kan føre til et lille kvalitetstab eller ustabilitet under træning. 3. **Grouped-Query Attention (GQA):** Grupperer $Q$-heads i $G$ partitioner, hvor hver gruppe deler et enkelt $K$- og $V$-head (f.eks. 8 $Q$-heads per KV-head). GQA tilbyder en optimal afvejning, idet den genvinder stort set al MHA's modelleringskvalitet, samtidig med at den bevarer MQA's hukommelsesfordele. **Indvirkning på KV-cache og Decode-gennemstrømning:** Autoregressiv token-generering (dekodning) er begrænset af hukommelsesbåndbredden, fordi GPU'en (Graphics Processing Unit) skal overføre hele KV-cachen fra HBM (High-Bandwidth Memory) til on-chip SRAM (Static Random-Access Memory) for hver eneste genererede token. Ved at reducere antallet af KV-heads med $H/G$ (f.eks. 4x til 8x i GQA, eller 32x+ i MQA): - KV-cache-hukommelsesforbruget reduceres proportionalt, hvilket muliggør meget større serving batch-størrelser i GPU VRAM (Video Random-Access Memory). - HBM-hukommelseslæsetrafik per token falder betydeligt, hvilket dramatisk øger decode-token-gennemstrømningen.

# Model with 32 Query Heads
num_q_heads = 32

mha_kv_heads = 32 # 1:1 ratio
gqa_kv_heads = 8  # 4:1 ratio (4 query heads per KV head)
mqa_kv_heads = 1  # 32:1 ratio (1 shared KV head)

print(f"KV Cache Size Relative to MHA:")
print(f"MHA: {mha_kv_heads / mha_kv_heads * 100:.1f}%")
print(f"GQA: {gqa_kv_heads / mha_kv_heads * 100:.1f}%")
print(f"MQA: {mqa_kv_heads / mha_kv_heads * 100:.1f}%")
Prøv at besvare dette spørgsmål med en AI-coach

12Forklar hvordan FlashAttention accelererer præcis opmærksomhedsberegning uden at ændre opmærksomhedsoutput.

FlashAttention accelererer opmærksomhedsberegningen ved at gøre algoritmen IO-bevidst – den minimerer læse- og skrivehukommelsestrafikken mellem langsom GPU High Bandwidth Memory (HBM) og hurtig on-chip SRAM, i stedet for at forsøge at reducere det totale aritmetiske FLOP-antal. Standard opmærksomhed materialiserer mellemliggende N x N opmærksomhedsscore og sandsynlighedsmatricer i HBM, hvilket forårsager en stor hukommelsesbåndbredde-flaskehals. FlashAttention overvinder dette gennem tre nøgle-mekanismer: 1. Fliseinddeling (Tiling): Den opdeler Query-, Key- og Value-matricerne i blokke, der passer helt inden for GPU'ens on-chip SRAM. 2. Online Softmax: Den beregner softmax inkrementelt over blokke ved at spore løbende maksimummer og normaliseringssummer, og opdaterer delvise outputs uden at skulle have den fuldt materialiserede N x N matrix i hukommelsen. 3. Præcis Genberegning: Under det bagudgående pas læser den ikke gemte mellemliggende opmærksomhedsmatricer fra HBM; i stedet genberegner den dem i farten i SRAM fra de gemte løbende statistikker. Da der ikke anvendes tilnærmelser, lavrangs-faktoreringer eller token-dropping heuristikker, er outputtet matematisk præcist op til flydende-komma numerisk præcision, samtidig med at HBM-hukommelsesforbruget reduceres fra O(N^2) til O(N).

import torch

def online_softmax_step(m_prev, l_prev, out_prev, scores_block, v_block):
    # scores_block: (B, H, Br, Bc), v_block: (B, H, Bc, D)
    m_block = scores_block.max(dim=-1, keepdim=True).values
    m_new = torch.maximum(m_prev, m_block)
    
    # Rescale previous and current accumulators
    p_prev_scale = torch.exp(m_prev - m_new)
    p_block = torch.exp(scores_block - m_new)
    
    l_new = p_prev_scale * l_prev + p_block.sum(dim=-1, keepdim=True)
    out_new = (p_prev_scale * l_prev * out_prev + p_block @ v_block) / l_new
    return m_new, l_new, out_new
Prøv at besvare dette spørgsmål med en AI-coach

Senior-spørgsmål

13Design deterministiske agentiske arbejdsgange ved hjælp af planlæggere, tilstandsmaskiner, DAG'er (Directed Acyclic Graphs), typet mellemtilstand, begrænsede genforsøg og værktøjsresultatverifikation i stedet for åbne agent-loops.

Åbne agent-loops (f.eks. ubestemte autonome ReAct-loops) i produktion lider ofte under ikke-deterministisk forgrening, uendelige loops, løbsk token-forbrug og tilstandsdrift. En deterministisk agentisk arbejdsgang erstatter frie loops med struktureret, observerbart kontrolflow: 1. Tilstandsmaskiner og DAG'er: Kontrolflowet defineres som en eksplicit Directed Acyclic Graph (DAG) eller finit tilstandsmaskine (f.eks. LangGraph, Temporal, AWS Step Functions). Node-overgange afhænger af eksplicitte betingelser og typede udfald snarere end åbne modelbeslutninger. 2. Typet mellemtilstand: Tilstand, der deles på tværs af noder, modelleres med strenge skemaer (f.eks. Pydantic-modeller eller dataclasses). Noder udfører validerede læse- og skriveoperationer, hvilket forhindrer skemadrift eller fejlformet tilstand. 3. Planlæggere: Strukturerede planlæggere udsender en begrænset plan på forhånd (f.eks. en ordnet liste af enum-baserede trin) eller vælger fra et begrænset sæt af gyldige tilstandsovergange i stedet for frit at beslutte næste handlinger uden begrænsninger. 4. Værktøjsresultatverifikation: Output returneret af værktøjer valideres deterministisk mod skemaer og forretningsregler, før tilstanden opdateres eller sendes til nedstrøms LLM-trin. 5. Begrænsede genforsøg og fallbacks: Hvert trin håndhæver eksplicitte genforsøgsbudgetter, eksponentielle backoffs, timeouts og fallback-overgange (f.eks. eskalering til menneskelig gennemgang eller udløsning af sikker afholdenhed) for at garantere afslutning.

from pydantic import BaseModel
from typing import Optional, Literal

class WorkflowState(BaseModel):
    user_query: str
    extracted_id: Optional[str] = None
    verification_status: Literal["PENDING", "VERIFIED", "FAILED"] = "PENDING"
    retry_count: int = 0
    max_retries: int = 3

def execute_validation_node(state: WorkflowState) -> WorkflowState:
    if state.retry_count >= state.max_retries:
        state.verification_status = "FAILED"
        return state
    try:
        result = call_verification_service(state.extracted_id)
        state.verification_status = "VERIFIED" if result.is_valid else "FAILED"
    except Exception:
        state.retry_count += 1
    return state
Prøv at besvare dette spørgsmål med en AI-coach

14Design en politik for konfidens og afholdenhed for en LLM (Large Language Model) assistent, der besvarer spørgsmål inden for regulerede domæner.

Inden for regulerede domæner (såsom sundhedspleje, bankvæsen, jura og compliance) medfører ukorrekte svar regulatoriske bøder, juridisk ansvar og sikkerhedsrisici. En robust politik for konfidens og afholdenhed kombinerer kalibrering af konfidensscorer baseret på flere signaler, tærskler for respons på flere niveauer og deterministiske eskaleringsarbejdsgange: 1. **Kalibrering af konfidens baseret på flere signaler:** Rå LLM log-sandsynligheder (logprobs) er ofte fejlkalibrerede ved forespørgsler uden for domænet. Konfidensscoren bør syntetisere flere uafhængige signaler: * **Score for forankring af hentet indhold (Retrieval Grounding Score):** Semantisk lighed og re-rangering af konfidens for hentede evidensbidder. * **Entailment (implikation) på udsagnsniveau (NLI - Natural Language Inference):** Modeller for Natural Language Inference, der verificerer, at hvert uddraget udsagn er impliceret af den hentede kildekontekst. * **Semantisk entropi / Selv-konsistens (Semantic Entropy / Self-Consistency):** Måling af semantisk konsistens på tværs af flere samplede generationer. * **Model-token log-sandsynligheder:** Minimum og gennemsnitlige log-sandsynligheder for centrale navngivne entiteter og faktuelle tokens. 2. **Afholdenhedspolitik på flere niveauer (Tiered Abstention Policy):** * **Høj konfidens (Score >= Høj tærskel):** Præsenter det genererede svar direkte med indbyggede citationer. * **Medium konfidens / Tvetydig (Lav tærskel <= Score < Høj tærskel):** Præsenter et konservativt svar med eksplicitte forbehold, ansvarsfraskrivelser eller bed brugeren om uddybende detaljer. * **Lav konfidens / Uden for anvendelsesområde (Score < Lav tærskel):** Streng afholdenhed med en standardiseret afvisningsmeddelelse. 3. **Eskalering og revisionsspor for compliance (Escalation and Compliance Auditability):** * **Deterministisk eskalering:** Afholdenheder eller kritiske uoverensstemmelser dirigeres automatisk til human-in-the-loop (HITL) køer eller agent ticketing-systemer med fuld kontekst. * **Revisionsspor og herkomst (Audit Trail & Lineage):** Fuld telemetri – herunder prompt-hashes, hentede dokument-ID'er, individuelle konfidenskomponentscorer og endelige rutingsbeslutninger – skal logges for regulatorisk revisionsspor.

from dataclasses import dataclass
from typing import Literal

@dataclass
class DecisionResult:
    action: Literal["SERVE", "SERVE_WITH_CAVEAT", "ABSTAIN_AND_ESCALATE"]
    confidence_score: float
    reason: str

def evaluate_confidence_policy(retrieval_score: float, nli_entailment_score: float, semantic_entropy: float) -> DecisionResult:
    # Composite calibrated confidence index [0, 1]
    composite_score = (0.4 * retrieval_score) + (0.4 * nli_entailment_score) + (0.2 * (1.0 - semantic_entropy))
    
    if composite_score >= 0.85:
        return DecisionResult("SERVE", composite_score, "High evidence grounding")
    elif composite_score >= 0.60:
        return DecisionResult("SERVE_WITH_CAVEAT", composite_score, "Partial evidence support")
    else:
        return DecisionResult("ABSTAIN_AND_ESCALATE", composite_score, "Insufficient ground truth")
Prøv at besvare dette spørgsmål med en AI-coach

15Design en model-routeringsstrategi, der vælger mellem små, mellemstore og store modeller baseret på anmodningskompleksitet, omkostninger, risiko og kvalitetskrav.

En produktions-model-routeringsarkitektur dirigerer indkommende anmodninger på tværs af små (f.eks. 1B–8B SLM'er (Small Language Models)), mellemstore (f.eks. 14B–70B modeller) og store (f.eks. frontier eller store MoE (Mixture of Experts) modeller) lag ved at balancere kompleksitet, latenstid, risiko og beregningsomkostninger. Routerings-workflowet kombinerer generelt statiske regler, prædiktiv routering og dynamiske fallback-kaskader: 1. Deterministiske/statiske politikporte: Filtrer anmodninger efter kundelag, strenge latenstids-SLA'er (Service Level Agreements), lovgivningsmæssig/domæne-risiko (f.eks. medicinsk diagnose eller juridisk udarbejdelse dirigeret direkte til top-tier modeller) eller simple regel-matchede opgaver (f.eks. grundlæggende regulære udtryk/formatering til små modeller). 2. Prædiktiv kompleksitets-routering: En hurtig, letvægtsklassifikator (såsom et opslag af indlejrings-lighed, en kryds-encoder eller en lille SLM-router) scorer anmodningskompleksitet, ræsonnementsdybde og domæne-tvetydighed for at vælge det mest omkostningseffektive lag på forhånd. 3. Dynamisk eksekvering og eskaleringskaskader: Send prompten først til en mindre model og evaluer output-konfidens (via token logprobs/entropi, struktureret skemavaliditet eller guardrail-tjek). Hvis konfidensen er under tærsklen, eller valideringen fejler, eskalerer routeren til en mellemstor eller stor model. Vigtige systemmæssige afvejninger inkluderer routerens latenstids-overhead kontra beregningsbesparelser, fallback-timeout-budgetter under trafikspidser og kontinuerlig evaluering (f.eks. skygge-evaluering for at spore afvigelse i outputkvalitet på tværs af lag).

class DynamicModelRouter:
    def __init__(self, small_client, medium_client, large_client, classifier, guardrail):
        self.small = small_client
        self.medium = medium_client
        self.large = large_client
        self.classifier = classifier
        self.guardrail = guardrail

    async def route_and_execute(self, request):
        # 1. Deterministic Risk Gate
        if request.risk_level == "high" or request.domain in ["legal", "medical_compliance"]:
            return await self.large.generate(request.prompt)
        
        # 2. Predictive Complexity Classifier
        complexity = self.classifier.predict_complexity(request.prompt) # 0.0 to 1.0
        
        if complexity < 0.35:
            response = await self.small.generate(request.prompt)
            if self.guardrail.is_acceptable(response):
                return response
            return await self.medium.generate(request.prompt) # Fallback
            
        if complexity < 0.75:
            response = await self.medium.generate(request.prompt)
            if self.guardrail.is_acceptable(response):
                return response
            return await self.large.generate(request.prompt) # Fallback
            
        # 3. High complexity frontier execution
        return await self.large.generate(request.prompt)
Prøv at besvare dette spørgsmål med en AI-coach