Vorbereitung auf LLM- und Generative-KI-Interviews
LLM- und Generative-KI-Ingenieur-Interviewfragen
15 ausgewählte LLM- und Generative-KI-Interviewfragen, gruppiert nach Erfahrungsstufe. Nutzen Sie diese zur Überprüfung von Grundlagen, praktischen Abwägungen und dem produktionsrelevanten Denken auf Senior-Niveau.
1Wie funktioniert Subword-Tokenisierung, und warum verwenden Sprachmodelle sie häufig?
Sie zerlegt Text in Einheiten variabler Länge. Häufige Wörter oder Teilstücke können eigene Tokens bekommen. Seltene Wörter lassen sich aus kleineren Teilen zusammensetzen. BPE, WordPiece und Unigram lernen die Einheiten aus Textdaten. Die Teile müssen keine sprachlichen Morpheme sein. Gegenüber ganzen Wörtern spart der Ansatz Vokabular, gegenüber einzelnen Zeichen oft Sequenzlänge. Bei dichter Attention senkt eine kürzere Sequenz typischerweise den Rechenaufwand. Ob ein unbekanntes Zeichen darstellbar ist, hängt vom Tokenizer ab. Eine vollständige Byte-Abdeckung kann unbekannte Tokens vermeiden.
2Worin unterscheiden sich statische Worteinbettungen, kontextuelle Einbettungen und Hidden States im Transformer?
Eine statische Einbettung ordnet einem Wort unabhängig vom Satz einen Vektor zu. Das englische bank hat etwa bei Flussufer und Finanzinstitut denselben statischen Vektor. Eine kontextuelle Einbettung hängt von den umgebenden Tokens ab. Hidden States sind die internen Repräsentationen der einzelnen Modellschichten für jede Position. Sie entstehen nach und nach aus den Eingabeeinbettungen. Kontextuelle Einbettungen können aus diesen Zuständen gewonnen werden, etwa aus einer Schicht oder durch Pooling. Welche Merkmale eine Schicht trägt, hängt vom Modell und der Aufgabe ab.
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())
3Was ist ein Einbettungsraum und wie interpretierst du Kosinusähnlichkeit bei Texteinbettungen?
Ein Modell bildet Texte als Vektoren in einem gemeinsamen Raum ab. Die Kosinusähnlichkeit zweier von null verschiedener Vektoren ist ihr Skalarprodukt geteilt durch das Produkt ihrer Längen. Sie liegt zwischen −1 und 1 und misst die Ausrichtung unabhängig von der Länge. Ein hoher Wert kann unter dem verwendeten Modell auf semantische Nähe hindeuten. Null bedeutet geometrische Orthogonalität und kein Nachweis semantischer Unabhängigkeit. Negative Werte bedeuten entgegengesetztere Ausrichtung, nicht zwingend gegensätzliche Aussagen. Kalibriere Schwellen an passenden Beispielen.
4Erklären Sie den Unterschied zwischen Token-Embeddings, Positional-Embeddings und Segment- oder Typ-Embeddings in Transformer-Eingaben.
Bei Transformer-Eingaben (insbesondere bei Architekturen im BERT-Stil) wird die Eingabedarstellung für jedes Token typischerweise durch die elementweise Summation von drei verschiedenen Embedding-Vektoren gebildet:
1. **Token-Embeddings**: Bilden diskrete Vokabular-Token-IDs in dichte Vektoren ab, die die semantische und lexikalische Kernidentität der Tokens repräsentieren.
2. **Positionale Embeddings**: Fügen Informationen über die Token-Reihenfolge und sequentielle Indizes in die Darstellung ein, um auszugleichen, dass unmaskierte Self-Attention ohne Positionsinformationen permutationsäquivariant ist: Eine Umordnung der Eingaben ordnet die Token-Ausgaben entsprechend um. Dies allein kodiert keine Reihenfolge. Andere Architekturen verwenden rotierende oder relative Positionsmechanismen, statt Positions-Embeddings zu addieren.
3. **Segment- (oder Token-Typ-) Embeddings**: Unterscheiden zwischen verschiedenen Textabschnitten oder Sätzen, die in einer einzigen Eingabesequenz zusammengefasst sind (z. B. Satz A vs. Satz B in gepaarten Klassifikations- oder Frage-Antwort-Aufgaben).
Die Kombination dieser Embeddings liefert einen einzigen dichten Eingabetensor, der die Token-Bedeutung, -Position und -Sequenzgruppierung kodiert, bevor er an die erste Transformer-Schicht weitergegeben wird.
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)
5Wie verknüpft Attention Tokens mithilfe von Queries, Keys, Values und mehreren Köpfen?
Gelernte Projektionen bilden Queries, Keys und Values. Eine Query wird mit den zulässigen Keys verglichen. Bei Scaled Dot-Product Attention teilst du das Skalarprodukt durch die Quadratwurzel der Key-Dimension, damit die Scores mit größerer Dimension nicht zu stark streuen und Softmax weniger leicht sättigt. Softmax macht daraus Gewichte. Das Ergebnis ist die gewichtete Summe der Values. Mehrere Köpfe verwenden unterschiedliche gelernte Projektionen. Ihre Ergebnisse werden zusammengefügt und zurückprojiziert. Die Köpfe können verschiedene Beziehungen erfassen, müssen aber keine eindeutige linguistische Funktion haben.
6Wie unterscheidet sich die Attention-Maske eines kausalen Decoders von der eines bidirektionalen Encoders?
Eine kausale Maske erlaubt einer Position i nur Zugriff auf Positionen bis einschließlich i. Damit sieht ein Decoder beim Training keine zukünftigen Zielinformationen und kann bei der Inferenz das nächste Token aus dem bisherigen Präfix vorhersagen. Ein bidirektionaler Encoder kann ohne kausale Maske links und rechts Kontext nutzen. Padding-Masken können in beiden Architekturen unbenutzte Positionen ausschließen. Die bidirektionale Repräsentation eignet sich für Verständnisaufgaben. Direkte autoregressive Generierung braucht eine geeignete Vorhersage- und Maskierungsstrategie.
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)
7Wie unterscheiden sich Encoder-Only-, Decoder-Only- und Encoder-Decoder-Transformer?
Encoder-Only-Modelle wie BERT verwenden typischerweise bidirektionale Self-Attention und eignen sich für Klassifikation oder extraktive Antworten. Decoder-Only-Modelle wie GPT nutzen kausale Self-Attention und erzeugen Text schrittweise aus dem bisherigen Präfix. Encoder-Decoder-Modelle wie T5 kodieren die Eingabe und erzeugen die Ausgabe mit einem kausalen Decoder. Cross-Attention verbindet den Decoder dabei mit den Encoder-Zuständen. Das passt etwa zu Übersetzung und Zusammenfassung. Die Architektur begünstigt diese Aufgaben, legt sie aber nicht abschließend fest.
8Wie beeinflusst das Design eines Tokenizers Qualität, Kosten und Sprachgerechtigkeit?
Ein Tokenizer mit einem vorwiegend an Englisch trainierten Vokabular zerlegt manche anderen Sprachen stärker. Mehr Token für vergleichbare Inhalte belegen mehr Kontext, erhöhen bei tokenbasierter Abrechnung die Kosten und können die Latenz steigern. Vergleiche deshalb reale Texte pro Sprache mit derselben Bezugsgröße, etwa Token pro Byte oder pro vergleichbarer Aufgabe. Ein vollständiges Byte-Vokabular oder ein Byte-Fallback kann unbekannte Zeichen durch UTF-8-Bytes darstellen. Zeichen- oder Unicode-bewusste Segmentierung und ein ausgewogen trainiertes mehrsprachiges Vokabular können häufige Einheiten zusammenhalten. Ein größeres Vokabular erhöht typischerweise die Größe der Ein- und Ausgabematrizen. Es garantiert weder ausgewogene Segmentierung noch bessere Aufgabenqualität.
9Welche Tokenisierungsartefakte können bei Zahlen, Quellcode und seltenen Unicode-Zeichen auftreten, und wie lassen sie sich verringern?
Häufigkeitsbasierte Subwortverfahren können ähnlich lange Zahlen unterschiedlich aufteilen. Das erschwert das Lernen einheitlicher Stellenwertmuster; Einzelziffern oder feste Gruppen können dabei helfen. Bei Code können unregelmäßige Aufteilungen von Einrückungen und Operatoren längere Sequenzen und schwerer erlernbare Syntaxmuster verursachen. Häufige Leerraumfolgen und Operatoren lassen sich gezielt im Vokabular berücksichtigen. Seltene Zeichen und Emoji-Sequenzen können in viele Byte-Token zerfallen. Korrektes Dekodieren setzt sie wieder zusammen, aber bei freier Generierung kann das Modell auch ungültige Folgen erzeugen. Eine regelbasierte Vorsegmentierung kann unerwünschte Verschmelzungen über Kategorien hinweg verhindern; Byte-Fallback stellt die Darstellbarkeit sicher.
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)])
10Erklären Sie die Kompromisse bei der Attention mit langem Kontext, einschließlich quadratischer vollständiger Attention, Sliding-Window-Attention, spärlicher oder globaler Attention, KV-Cache-Kosten (Zwischenspeicher für Schlüssel und Werte) und Attention-Verdünnung.
Die Skalierung der Attention auf lange Kontextfenster bringt Kompromisse hinsichtlich Berechnung, Speicherbedarf und Modellgenauigkeit mit sich:
1. **Quadratische vollständige Attention vs. Sliding-Window- / spärliche Attention:** Dichte Attention hat einen quadratischen Rechenaufwand in der Sequenzlänge $N$. Auch das explizite Speichern ihrer Score-Matrix benötigt quadratischen Speicher. Exakte, an Datenbewegungen angepasste Algorithmen wie FlashAttention vermeiden jedoch dieses Speichern. Sliding-Window- (lokale) Attention beschränkt die Attention auf eine feste Nachbarschaft $W$, wodurch die Komplexität auf $O(N \cdot W)$ reduziert wird, aber mehrere Schichten erfordert, um Informationen über entfernte Tokens hinweg zu verbreiten. Spärliche Muster können lokale Fenster fester Größe mit einer begrenzten Anzahl globaler Tokens kombinieren. Dadurch skaliert das Attention-Muster linear und ermöglicht zugleich Kommunikation über große Entfernungen.
2. **KV-Cache-Speicherkosten:** Während der autoregressiven Generierung werden Schlüssel (Keys) und Werte (Values) für alle vorherigen Tokens zwischengespeichert, um redundante Berechnungen zu vermeiden. Der KV-Cache-Speicher skaliert linear mit der Sequenzlänge ($O(B \cdot L \cdot H_{KV} \cdot D \cdot N)$). Bei sehr langen Kontexten (32k–128k+ Tokens) verbraucht der KV-Cache Dutzende von Gigabyte Grafikspeicher (VRAM) auf GPUs pro Batch, was die maximale Batch-Größe und Speicherbandbreite einschränkt.
3. **Attention-Verdünnung (Lost-in-the-Middle):** Längere Kontexte können ablenkende Tokens sowie Positions- und Generalisierungsprobleme mit sich bringen. „Lost-in-the-Middle“ ist eine beobachtete positionsabhängige Schwäche beim Abrufen von Informationen, keine universelle Folge wachsender Softmax-Entropie; Attention kann weiterhin scharf konzentriert bleiben. Messen Sie die Aufgabengenauigkeit über verschiedene Kontextlängen und Positionen relevanter Belege hinweg, statt sie allein aus dem Nenner abzuleiten.
11Wie unterscheiden sich Multi-Head Attention (MHA), Multi-Query Attention (MQA) und Grouped-Query Attention (GQA), und was bedeutet das für Cache und Durchsatz?
Bei MHA hat jeder Query-Kopf ein eigenes Paar aus Key- und Value-Kopf. Bei MQA teilen sich alle Query-Köpfe ein solches Paar. GQA teilt die Query-Köpfe in Gruppen auf, die jeweils ein Paar verwenden. Bei H Query-Köpfen und G Gruppen braucht der Key-Value-Cache unter sonst gleichen Bedingungen bei GQA nur G/H des Speichers von MHA, bei MQA 1/H. Weniger Cache-Daten können größere Batches oder längere Kontexte erlauben und beim Decoding Speicherzugriffe verringern. Der tatsächliche Durchsatz hängt unter anderem von Kernel, Hardware und Batchgröße ab. GQA bietet einen einstellbaren Kompromiss zwischen Cache-Größe und Modellqualität.
# 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}%")
12Wie beschleunigt FlashAttention die exakte Attention-Berechnung?
FlashAttention zerlegt Query, Key und Value in Blöcke, die auf dem Chip verarbeitet werden können. Online-Softmax hält pro Zeile das bisherige Maximum und die Normierungssumme nach und passt Teilergebnisse bei jedem neuen Block an. So muss die vollständige N×N-Score- oder Wahrscheinlichkeitsmatrix nicht im Grafikspeicher abgelegt werden. Beim Rückwärtslauf lassen sich benötigte Blöcke mit gespeicherten Statistiken neu berechnen. Das verringert Speicherbedarf und Datentransfers zwischen Chip und HBM (Speicher mit hoher Bandbreite). Dichte Attention bleibt rechnerisch quadratisch in N. Das mathematische Ergebnis entspricht bis auf Rundungseffekte dem der Standardberechnung.
13Wie entwirfst du einen LLM-Workflow mit festem Kontrollfluss, typisiertem Zustand und überprüften Werkzeugergebnissen?
Modelliere zulässige Schritte und Übergänge als gerichteten azyklischen Graphen (DAG) oder Zustandsmaschine. Ein Modell darf innerhalb dieses Rahmens etwa einen Plan aus erlaubten Schritten vorschlagen. Die Anwendung validiert ihn und hält gemeinsamen Zustand in einem typisierten Schema. Prüfe Werkzeugergebnisse fachlich und strukturell, bevor sie Zustand ändern. Setze Zeit- und Versuchslimits, eine Gesamtgrenze auch für Zyklen sowie definierte Fehler- und Freigabepfade. Sichere wiederholbare Seiteneffekte mit Idempotenz. Der kontrollierte Ablauf kann nachvollziehbar sein, obwohl Modellausgaben und externe Dienste variieren.
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
14Wie entwirfst du für einen LLM-Assistenten in einem regulierten Bereich eine Strategie für Sicherheitsschätzung, Enthaltung und Eskalation?
Definiere zuerst erlaubte Aufgaben, autoritative Quellen und Fälle, die unabhängig vom Score fachliche Freigabe benötigen. Signale wie Quellenabdeckung, Widersprüche und Konsistenz können die Antwortbewertung ergänzen; kalibriere sie auf repräsentativen, fachlich geprüften Fällen. Gib Antworten im zulässigen Bereich mit überprüfbaren Belegen aus. Frage bei fehlenden Angaben gezielt nach. Bei unzureichender Evidenz oder hohem Risiko enthalte dich oder leite an zuständige Fachpersonen weiter. Erfasse nur erforderliche Auditdaten mit Zugriffsschutz, etwa Quellenversionen, Prüfergebnis und Eskalationsgrund. Miss sowohl fehlerhafte Antworten als auch unnötige Enthaltungen.
15Wie wählst du per Routing zwischen kleineren und größeren Modellen anhand von Komplexität, Kosten, Risiko und Qualitätszielen?
Setze zuerst feste Regeln für Datenschutz, Freigaben und Aufgaben, die ein Modell gar nicht selbstständig bearbeiten darf. Route einfache, überprüfbare Aufgaben an günstige Modelle. Ein leichter Klassifikator kann Komplexität und Domäne schätzen. Eskaliere in einer begrenzten Kaskade, wenn etwa eine Schema- oder Fachprüfung fehlschlägt. Tokenwahrscheinlichkeiten allein ersetzen keine Qualitätsprüfung. Reserviere Zeit und Budget für Fallbacks und miss Kosten, Latenz sowie Fehler je Aufgabentyp. Ein größeres Modell kann in bestimmten Fällen besser sein, ersetzt aber keine fachliche Freigabe.
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)