LLM and Generative AI面接対策

LLM and Generative AIエンジニア面接問題

シニアリティ別にまとめた、LLM と生成 AI の厳選インタビュー質問 15 件です。基礎の確認、実務上のトレードオフ、シニアレベルの本番運用を見据えた判断の整理にご利用ください。

LLM と生成 AI の AI インタビューを開始クレジットカードは不要です。1回分の無料セッションがあります。
英語での技術面接練習非ネイティブ話者が技術面接の合格を目指して練習できるモードです。

初級向け質問

1サブワードトークン化(subword tokenization)について説明し、現代の言語モデルにおいて単語レベルや文字レベルのトークン化よりも好まれる理由を述べてください。

サブワードトークン化(subword tokenization)は、完全な単語や個別の文字単位ではなく、可変長の形態素チャンクや頻度ベースの部分文字列(例: 'un'、'break'、'able')にテキストを分割するハイブリッドなテキスト分割手法です。Byte-Pair Encoding(BPE)、WordPiece、Unigram LMなどのアルゴリズムは、学習コーパスから固定サイズの語彙(ボキャブラリ)を学習します。頻出単語は単一のトークンとしてそのまま保持され、低頻度語や未知語は既知のサブワード単位に分解されます。 現代の言語モデルでサブワードトークン化が好まれる理由は、語彙サイズ、系列長(シーケンス長)、および未知語(OOV: out-of-vocabulary)に対する堅牢性のバランスが取れているためです。純粋な単語レベルのトークン化では、語彙サイズが過度に肥大化し(埋め込み行列が巨大化する原因になります)、それでもなお未知のトークンが汎用的な '[UNK]' トークンにマッピングされる問題が生じます。逆に、純粋な文字レベルのトークン化ではOOVの問題は解消されるものの、系列長が非常に長くなり、アテンション機構の計算量が劇的に増加する(系列長の2乗でスケールする)うえ、トークンあたりの意味密度が希薄化します。サブワードトークン化は、系列長を扱いやすい長さに抑え、語彙サイズを実用的な範囲(通常32k〜128kトークン)に保ち、さらに(バイトレベルのフォールバックと組み合わせることで)OOV率をゼロにできるため、最適なトレードオフを実現します。

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)
AI コーチを使ってこの質問に答えてみる

2静的単語埋め込み(static word embeddings)、コンテキストを考慮した埋め込み(contextual embeddings)、およびTransformerの隠れ状態(hidden states)の違いを説明してください。

静的単語埋め込み、コンテキストを考慮した埋め込み、およびTransformerの隠れ状態は、テキスト表現が意味や構文の文脈をどのように捉えるかという技術的進化の各段階を表しています。 1. 静的単語埋め込み(例: Word2Vec、GloVe、FastText): 文中の文脈に関係なく、語彙内の各トークンに対して単一の固定ベクトルを割り当てます。この手法では、「bank」(川の堤防 vs. 金融機関の銀行)や「apple」(果物 vs. テクノロジー企業)のような多義語であっても、静的なルックアップテーブルに依存しているため、あらゆる文脈で全く同じベクトル表現が割り当てられます。 2. コンテキストを考慮した埋め込み(例: 初期のELMo、BERTのトークン表現、Bi-Encoderによる文埋め込み): トークンのベクトルが周囲の文脈の動的な関数となるような表現を生成します。BERTやELMoでは、「river bank」における「bank」と、「deposit money at the bank」における「bank」には、全く異なる埋め込みベクトルが付与されます。 3. Transformerの隠れ状態: Transformerネットワークの順伝播処理中に、各層で生成される中間ベクトル表現を指します。第0層の入力トークン埋め込みが与えられると、後続の各Transformer層は自己注意機構(Self-Attention)と順伝播型ネットワーク変換を適用し、層 $l$ における隠れ状態ベクトルの系列 $h_l$ を生成します。最終層の隠れ状態は高レベルなコンテキスト埋め込みとして機能しますが、下位層および中間層の隠れ状態は低レベルな構文的、語彙的、構造的特徴を捉えます。したがって、Transformerの隠れ状態は、ネットワーク全体における層ごとの表現の垂直的な連続体全体を網羅しています。

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())
AI コーチを使ってこの質問に答えてみる

3埋め込み空間が何を表しているかを説明し、テキストの埋め込みにおいてコサイン類似度がどのように解釈されるかを述べてください。

埋め込み空間とは、単語、文、文書などの離散的なテキストエンティティがマッピングされる連続的な高次元ベクトル空間 R^d です。この空間では、意味的、統語的、あるいは関係性における類似性が、幾何学的な近接性や方向関係に対応付けられます。この空間内では、距離や角度が意味的な関連性を反映します。 コサイン類似度は2つのベクトル u と v の間の角度 theta のコサインを測定するもので、次のように計算されます。 Cosine Similarity(u, v) = (u . v) / (||u|| ||v||) テキスト埋め込みにおけるコサイン類似度は、次のように解釈されます。 - 範囲と向き: 通常、[-1, 1](非負の埋め込みの場合は [0, 1])の範囲に収まるスカラー値を出力します。1.0 に近い値は、2つのベクトルがほぼ同じ方向を向いていることを示し、高い意味的類似度やトピックの一致を反映します。0.0 付近の値は直交(意味的な独立性または無関係)を意味し、負の値は反対の向きを示します。 - 大きさへの不変性: ユークリッド距離(L2距離)や内積とは異なり、コサイン類似度はベクトルの長さ(ノルム)を正規化します。テキスト埋め込みにおいて、ベクトルの大きさは系列長、トークンの出現頻度、または用語の専門性の高さと相関することがあります。純粋に方向の一致に焦点を当てることで、コサイン類似度はベクトルの大きさの違いから意味的な方向性を切り離して評価できます。

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))
AI コーチを使ってこの質問に答えてみる

4Transformerの入力におけるトークン埋め込み(Token Embeddings)、位置埋め込み(Positional Embeddings)、セグメント埋め込みまたはタイプ埋め込み(Segment / Type Embeddings)の違いを説明してください。

Transformerの入力(特にBERTスタイルのアーキテクチャ)では、各トークンの入力表現は通常、以下の3つの異なる埋め込みベクトルを要素ごとに加算(element-wise sum)することによって構成されます。 1. トークン埋め込み(Token Embeddings): 語彙内の離散的なトークンIDを、トークンの核となる意味論的・語彙的同一性を表す密ベクトルにマッピングします。 2. 位置埋め込み(Positional Embeddings): 自己注意機構(self-attention)が本質的に順序不変(置換不変)であるという特性を補うため、トークンの順序と並びのインデックスに関する情報を表現に注入します。 3. セグメント(またはトークンタイプ)埋め込み(Segment / Token Type Embeddings): 単一の入力シーケンスにまとめられた異なるテキストスパンや文(ペア分類や質問応答タスクにおける文Aと文Bなど)を区別します。 これらを組み合わせることで、最初のTransformer層に入力される前に、トークンの意味、位置、およびシーケンス内のグループ情報を符号化した単一の密な入力テンソルが提供されます。

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)
AI コーチを使ってこの質問に答えてみる

5クエリ、キー、バリュー、およびマルチヘッドアテンションを含め、シーケンス内のトークン同士を関連付けるメカニズムとしてのアテンションについて説明してください。

アテンションは、シーケンス内の各トークンが文脈に応じたマッチングに基づいて情報を動的にルーティングし、他のすべてのトークンの関連度を重み付けできるようにするメカニズムです。線形射影により、各トークンの入力は3つのベクトルに変換されます: - クエリ(Query / Q):現在のトークンがどのような情報を求めているかを表します。 - キー(Key / K):クエリとの照合用として、トークンが提供できる属性やコンテンツを表します。 - バリュー(Value / V):集約される実際の情報ペイロードを保持します。 スケールド・ドットプロダクト・アテンションでは、クエリとキーの積($Q K^T$)を計算し、次元数が大きい場合の勾配消失を防ぐために $\frac{1}{\sqrt{d_k}}$ でスケーリングし、softmax関数で正規化することでアテンションスコアを算出します。最終的な出力はバリューの重み付き和となります: $$\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V$$ マルチヘッドアテンション(MHA: Multi-Head Attention)は、$Q$、$K$、$V$ を並列に複数の独立した表現部分空間(ヘッド)へ射影します。これにより、モデルは各位置にわたって異なる種類のリレーション(構文構造、照応関係、長距離の依存関係など)へ同時にアテンションを向けることができます。各ヘッドの出力は連結され、元のモデル次元へと線形射影されます。

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)
AI コーチを使ってこの質問に答えてみる

6因果的デコーダ(causal decoder)と双方向エンコーダ(bidirectional encoder)でアテンションマスキングがどのように異なるか、またそれによって可能になる挙動や防止される挙動を説明してください。

アテンションマスキングは、許可されていないトークンのペアに対してアテンションロジット(softmax適用前のスコア)を $-\infty$ に設定することで、どのトークンが他のどのトークンを参照できるかを制御し、softmax適用後のアテンション重みを厳密に0にします。 1. 因果的デコーダ(例: GPT、LLaMA): 下三角行列の因果的(自己回帰)マスクを使用します。位置 $i$ のトークンは $j \le i$ の位置にのみアテンションを向けることができます。これにより未来のトークンへのアテンションが遮断され、推論時の自己回帰的な1トークンずつの生成が可能になり、並列学習時の未来トークンのラベルリークが防止されます。 2. 双方向エンコーダ(例: BERT): 因果的マスクは使用せず、すべてのトークンがシーケンス全体の過去および未来のすべてのトークンを参照できます。バッチ処理されたシーケンス内の無効な `[PAD]` トークンを有効なトークンが参照しないよう、パディングマスクを使用します。双方向アテンションは包括的な文脈表現を生成するため理解タスクには理想的ですが、直接的な1パスの自己回帰テキスト生成は行えません。

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)
AI コーチを使ってこの質問に答えてみる

7言語タスクにおけるTransformerアーキテクチャのEncoder-Only、Decoder-Only、Encoder-Decoderの違いについて説明してください。

これら3つの主要なTransformerアーキテクチャは、アテンションのマスキングパターンと主な運用目的に根本的な違いがあります。 1. Encoder-Only(例: BERT、RoBERTa): 双方向の自己アテンション(self-attention)を使用し、すべてのトークンが系列内の他のすべてのトークンに同時にアテンションを向けることができます。入力系列全体に対して豊かな文脈表現を生成するため、分類、抽出型QA(質問応答)、特徴量表現の獲得に最適です。自己回帰的なテキスト生成を自然に行うことはできません。 2. Decoder-Only(例: GPT-3、Llama、Mistral): 因果的(一方向)自己アテンションを使用し、トークン $i$ は位置 $j \le i$ のトークンにのみアテンションを向けることができます。次のトークン予測(next-token prediction)を用いて自己回帰的に学習され、生成言語モデル、コード生成、自由対話における標準的なアーキテクチャとして利用されています。 3. Encoder-Decoder(例: T5、BART): 双方向エンコーダと自己回帰型の因果的デコーダを組み合わせた構成です。デコーダは生成されたトークンに対する因果的自己アテンションに加えて、エンコーダの出力表現をクエリするクロスアテンション(cross-attention)層を備えています。このアーキテクチャは、翻訳や要約などのSequence-to-Sequence変換タスク専用に設計されています。

# 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)
AI コーチを使ってこの質問に答えてみる

中級向け質問

8バイトレベル、Unicode対応、および多言語対応のトークナイザーの選択が、言語間でのモデル品質、コスト、公平性にどのように影響するかを説明してください。

トークナイザーの設計方針(バイトレベルとUnicode対応のセグメンテーションの選択や、多言語ボキャブラリの割り当てなど)は、後続のモデル品質、推論および学習コスト、言語間の公平性に直接影響を与えます。 コストと公平性の観点では、英語やラテン文字コーパスを中心に学習されたトークナイザーは、ボキャブラリの大部分のエントリを英語の単語や形態素に割り当てます。その結果、英語は高い圧縮率(例: 1単語あたり約1.3トークン)を達成するのに対し、非ラテン文字(アラビア文字、デーヴァナーガリー文字、タイ文字、漢字など)や低リソース言語は、複数のサブワードや生のUTF-8バイトへと細かく分割されがちです(1単語あたり3〜6トークンになることも珍しくありません)。この格差は「トークン税(token tax)」または「トークン生成効率の不均衡(fertility rate imbalance)」と呼ばれ、非英語ユーザーは同一の意味内容に対してAPI利用料を大幅に多く支払い、コンテキストウィンドウの上限を急速に消費し、より高いレイテンシを被ることになります。 品質の観点では、バイトレベルのトークナイザー(GPT-2/GPT-4のByte-level BPEやLLaMAのbyte fallback付きSentencePieceなど)は、有効なUTF-8文字列であれば任意の文字列をバイトトークンに分解できるため、未知文字によるクラッシュや語彙外(UNK: Out-Of-Vocabulary)エラーを完全に回避できます。しかし、過度なバイト分割は、Transformerが高レベルな推論を行う前にバイト片を意味概念へと再統合することに層のリソースを割かなければならなくなるため、表現の品質を低下させます。多言語ボキャブラリサイズを拡大する(例: 32kから128kトークン以上に増やす)ことで、トークン生成効率(fertility rate)が均等化され、多様な言語にわたる下流タスクの性能が向上しますが、入力および出力の埋め込み(Embedding)層が適度に肥大化するというトレードオフがあります。

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)))
AI コーチを使ってこの質問に答えてみる

9数値推論、コード生成、または珍しいUnicodeテキストにおいてどのようなトークン化アーティファクトが発生し、専用のトークナイザーはそれらをどのように軽減できるでしょうか?

トークン化アーティファクトは、サブワードトークナイザーが構造化テキスト、数値、または出現頻度の低いテキストを一貫性のない形で分割し、モデルが潜在的な意味的・構文的規則性を認識できなくなることで発生します。主なアーティファクトには以下があります。 1. 数値推論におけるアーティファクト: 一般的なテキストでトレーニングされた標準的なBPEトークナイザーは、出現頻度に基づいて数値を任意の長さのチャンクに分割します(例: 「12345」が `['12', '345']` にトークン化される一方で、「12346」が `['123', '46']` にトークン化されるなど)。この不規則なグループ化によって桁の位置関係(一の位、十の位、百の位など)が崩れ、算術推論が妨げられます。 2. コード生成におけるアーティファクト: インデント(行頭のスペースやタブ)や複数文字の演算子(`==`、`!=`、`->` など)が空白と文字の境界を跨いで不規則に分割されることが多く、インデントエラー、深くインデントされたコードでのトークン数の肥大化、構文の破損を引き起こします。 3. 珍しいUnicodeや絵文字におけるアーティファクト: マルチバイトのUTF-8シーケンス(ゼロ幅接合子を含む複雑な絵文字や珍しい用字など)が、単体では意味を持たない生バイトトークンへと分割され、生成時に存在しない文字の幻覚(ハルシネーション)やグリフの描画崩れが発生します。 専用のトークナイザーは、カスタマイズされた事前トークン化ルールと語彙の制約を用いてこれらのアーティファクトを軽減します。 - 数字の個別分割: 1桁ごとのトークン化を強制し(例: 正規表現で `0-9` の各数字を個別のトークンに分割)、数学的推論のための一様な位取り表現を保証します。 - 専用の空白・インデントトークン: 連続する空白インデント(2、4、8スペースなど)用の明示的なトークンを追加し、プログラミング言語のキーワードや演算子を保護します。 - 正規表現による事前トークン化 / バイトレベルのフォールバック: BPEのマージが計算される前に、記号・文字・数値を厳格なカテゴリに分離する正規表現スプリッター(GPT-4/tiktokenの正規表現など)を採用し、カテゴリを跨いだマージ(例: 「a=10」が1つのトークンに結合されること)を防止します。

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)])
AI コーチを使ってこの質問に答えてみる

10二次計算量のフルアテンション、スライディングウィンドウアテンション、スパースまたはグローバルアテンション、KV(Key-Value)キャッシュのコスト、およびアテンション希薄化(attention dilution)を含め、長いコンテキストにおけるアテンションのトレードオフについて考察してください。

長いコンテキストウィンドウへのアテンションのスケーリングには、計算量、メモリフットプリント、およびモデル精度の面でトレードオフが存在します。 1. 二次計算量のフルアテンション vs スライディングウィンドウ / スパースアテンション: 標準的なフルアテンションは、シーケンス長 $N$ に対して計算量と活性化メモリが二次関数的($O(N^2)$)に増加します。スライディングウィンドウ(局所)アテンションはアテンションを固定近傍 $W$ に制限し、計算量を $O(N \cdot W)$ に削減しますが、離れたトークン間で情報を伝播させるには複数層を経由する必要があります。スパースまたはグローバルアテンションのパターンは、局所ウィンドウと一部の選択されたグローバルアンカートークンを組み合わせることで、$O(N)$ のスケーリングを維持しながら長距離通信を可能にします。 2. KVキャッシュのメモリコスト: 自己回帰生成時、冗長な計算を避けるために先行する全トークンのKeyとValueがキャッシュされます。KVキャッシュメモリはシーケンス長に対して線形に増加します($O(B \cdot L \cdot H_{KV} \cdot D \cdot N)$)。非常に長いコンテキスト(32k〜128k+ トークン)では、KVキャッシュがバッチあたり数十ギガバイトのGPU VRAMを消費し、最大バッチサイズやメモリ帯域幅のボトルネックとなります。 3. アテンション希薄化(Lost-in-the-Middle): コンテキストが長くなるにつれて、ソフトマックスの分母は何万ものトークンの総和となり、無関係なコンテキスト全体に確率質量が薄く分散します。このエントロピーの増大によってアテンションの鋭さが希薄化し、長いプロンプトの中央部に埋め込まれた特定の情報を確実に呼び出すモデルの能力が低下します。

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")
AI コーチを使ってこの質問に答えてみる

11MHA (Multi-Head Attention)、MQA (Multi-Query Attention)、およびGQA (Grouped-Query Attention) を比較し、これらがKVキャッシュ (Key-Value cache) のメモリおよびデコードのスループットにどのように影響するか説明してください。

Multi-Head Attention (MHA)、Multi-Query Attention (MQA)、およびGrouped-Query Attention (GQA) は、Query($Q$)ヘッド間でKey($K$)ヘッドおよびValue($V$)ヘッドをどのように共有するかが異なります。 1. Multi-Head Attention (MHA): $Q$、$K$、$V$のヘッド数が同数です($H_Q = H_{KV}$、1:1の比率)。各クエリヘッドは独立したキー/バリュー表現にアテンションを向けます。表現力は高いものの、ヘッドごとに個別のKV行列をキャッシュする必要があります。 2. Multi-Query Attention (MQA): 複数の$Q$ヘッド($H$)に対して、共有された1つの$K$ヘッドと1つの$V$ヘッドのみを使用します($H:1$の比率)。これによりKVキャッシュのサイズは$H$分の1に削減されますが、わずかな品質低下や学習の不安定化を招く可能性があります。 3. Grouped-Query Attention (GQA): $Q$ヘッドを$G$個のグループに分割し、各グループで単一の$K$および$V$ヘッドを共有します(例: KVヘッド1つあたり8つの$Q$ヘッド)。GQAは最適なトレードオフを提供し、MQAのメモリ効率の利点を維持しながら、MHAのモデル品質をほぼ損なわずに維持します。 KVキャッシュおよびデコードのスループットへの影響: 自己回帰的なトークン生成(デコード)では、GPUが生成トークンごとにKVキャッシュ全体を高帯域幅メモリ(HBM: High-Bandwidth Memory)からチップ上のSRAMへ転送する必要があるため、メモリ帯域幅律速になります。KVヘッド数を$H/G$に削減すること(例: GQAで4倍〜8倍、MQAで32倍以上の削減)により、以下の効果が得られます。 - KVキャッシュのメモリフットプリントが比例して削減され、GPU VRAM上でより大きなサービングバッチサイズを扱えるようになります。 - トークンあたりのHBMメモリ読み出しトラフィックが大幅に減少し、デコード時のトークンスループットが飛躍的に向上します。

# 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}%")
AI コーチを使ってこの質問に答えてみる

12FlashAttentionが、アテンションの出力を変更することなく厳密なアテンション計算を高速化する仕組みを説明してください。

FlashAttentionは、全体の演算量(FLOP数)を削減しようとするのではなく、アルゴリズムをIO-aware(I/Oを意識した設計)にし、低速なGPUの高帯域幅メモリ(HBM: High Bandwidth Memory)と高速なオンチップSRAM間の読み書きトラフィックを最小限に抑えることで、アテンション計算を高速化します。 標準的なアテンション計算では、中間の $N \times N$ のアテンションスコア行列および確率行列をHBM上に実体化(materialize)するため、深刻なメモリ帯域幅のボトルネックが発生します。FlashAttentionは以下の3つの主要メカニズムによってこれを克服します。 1. タイリング(Tiling): Query、Key、Valueの各行列を、GPUのオンチップSRAM内に完全に収まるブロックに分割します。 2. オンラインSoftmax(Online Softmax): 累積の最大値と正規化用の総和を追跡することで、ブロック単位で逐次的にsoftmaxを計算し、完全な $N \times N$ 行列をメモリ上に実体化することなく部分的な出力を更新します。 3. 厳密な再計算(Exact Recomputation): 逆伝播(backward pass)時、HBMに保存された中間アテンション行列を読み出すのではなく、保存されている統計情報からSRAM上でオンザフライに再計算します。 近似計算、低ランク分解、トークンドロップなどのヒューリスティクスを一切使用しないため、出力結果は浮動小数点数の数値精度の範囲内で数学的に厳密(exact)でありながら、HBMのメモリ使用量を $O(N^2)$ から $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
AI コーチを使ってこの質問に答えてみる

上級向け質問

13オープンエンドなエージェントループの代わりに、プランナー、ステートマシン、DAG(Directed Acyclic Graph: 有向非巡回グラフ)、型付けされた中間状態、制限付きリトライ、およびツール実行結果の検証を用いて、決定論的なエージェントワークフローを設計してください。

本番環境におけるオープンエンドなエージェントループ(制約のない自律的なReActループなど)は、非決定論的な分岐、無限ループ、想定外のトークン消費、状態の不整合といった問題に直面することがよくあります。決定論的なエージェントワークフローでは、制約のないループを構造化された可観測性の高い制御フローに置き換えます。 1. ステートマシンとDAG: 制御フローを明示的な有向非巡回グラフ(DAG)または有限ステートマシン(LangGraph、Temporal、AWS Step Functionsなど)として定義します。ノード間の遷移は、モデルの制約のない判断ではなく、明示的な条件や型付けされた結果に基づいて行われます。 2. 型付けされた中間状態: ノード間で共有される状態は、厳格なスキーマ(Pydanticモデルやdataclassなど)を用いてモデル化します。各ノードは検証済みの読み込みおよび書き込み操作を行うため、スキーマの乖離や不正な状態の発生を防ぐことができます。 3. プランナー: 構造化プランナーは、事前に制約付きの計画(列挙型に基づく順序付きステップリストなど)を出力するか、許可された有効な状態遷移のセットから選択を行うことで、制約なしに自由に行動が決定されることを防ぎます。 4. ツール実行結果の検証: ツールから返された出力は、状態を更新したり後続のLLMステップに渡したりする前に、スキーマやビジネスルールに照らして決定論的に検証されます。 5. 制限付きリトライとフォールバック: 各ステップに明示的なリトライ回数上限、指数バックオフ、タイムアウト、およびフォールバック遷移(人手による確認へのエスカレーションや安全な処理中断の起動など)を強制し、処理が確実に終了することを保証します。

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
AI コーチを使ってこの質問に答えてみる

14規制対象ドメインの質問に回答するLLM(Large Language Model: 大規模言語モデル)アシスタントにおいて、確信度評価および回答棄権(abstention)ポリシーをどのように設計しますか?

医療、金融、法務、コンプライアンスなどの規制対象ドメインでは、不正確な回答が規制上のペナルティ、法的責任、安全性リスクに直結します。堅牢な確信度評価および回答棄権ポリシーは、複数シグナルによるキャリブレーション済み確信度スコアリング、段階的な応答しきい値、決定論的なエスカレーションワークフローを組み合わせて設計します。 1. 複数シグナルによる確信度のキャリブレーション: LLMの生の対数確率(logprobs)は、ドメイン外のクエリに対してキャリブレーションが崩れがちです。確信度スコアは、複数の独立したシグナルを統合して算出する必要があります。 - 検索グラウンディングスコア: 検索されたエビデンスチャンクの意味的類似度およびリランキング確信度。 - 主張レベルの含意関係(NLI: Natural Language Inference): 自然言語推論モデルを用いて、抽出されたすべての主張が検索元のコンテキストによって含意されているかを検証。 - セマンティックエントロピー / 自己整合性(Self-Consistency): 複数回サンプリング生成した結果間の意味的一貫性の測定。 - モデルトークンの対数確率: 主要な固有表現や事実トークンにおける最小および平均対数確率。 2. 段階的回答棄権ポリシー: - 高確信度(スコア >= 高しきい値): インライン出典引用を付与した上で、生成された回答を直接返却。 - 中確信度 / 曖昧(低しきい値 <= スコア < 高しきい値): 明示的な注意事項や免責事項を添えた保守的な回答を返すか、ユーザーに明確化のための詳細を質問。 - 低確信度 / スコープ外(スコア < 低しきい値): 定型化された拒絶メッセージにより厳格に回答を棄権。 3. エスカレーションとコンプライアンス監査性: - 決定論的エスカレーション: 回答棄権時や重大な不整合が発生した際は、完全なコンテキストを付与してHuman-in-the-Loop(HITL)キューまたはオペレーターのチケット管理システムに自動ルーティング。 - 監査証跡と系統管理(Lineage): 規制監査に備え、プロンプトのハッシュ値、検索されたドキュメントID、個々の確信度構成スコア、最終的なルーティング決定を含むテレメトリ全体を記録。

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")
AI コーチを使ってこの質問に答えてみる

15リクエストの複雑さ、コスト、リスク、品質要件に基づいて、小型・中型・大型モデルの中から適切なものを選択するモデルルーティング戦略を設計してください。

本番環境のモデルルーティングアーキテクチャは、複雑さ、レイテンシ、リスク、計算コストのバランスを取りながら、小型(例: 1B〜8BのSLM)、中型(例: 14B〜70Bモデル)、大型(例: フロンティアモデルや大規模MoEモデル)の各ティアに着信リクエストを振り分けます。ルーティングワークフローは通常、静的ルール、予測型ルーティング、動的フォールバックカスケードを組み合わせて構成されます。1. 決定論的/静的ポリシーゲート: 顧客ティア、厳密なレイテンシSLA、規制やドメインリスク(例: 医療診断や法務文書作成は最上位モデルへ直接ルーティング)、または単純なルール一致タスク(例: 基本的な正規表現やフォーマット変換は小型モデルへ)によってリクエストをフィルタリングします。2. 予測型複雑度ルーティング: 高速かつ軽量な分類器(埋め込み類似度検索、クロスエンコーダ、小型SLMルーターなど)がリクエストの複雑さ、推論の深さ、ドメインの曖昧さをスコアリングし、最も費用対効果の高いモデルティアをはじめに選択します。3. 動的実行とエスカレーションカスケード: まず小型モデルにプロンプトを送信し、出力の確信度(トークンの対数確率/エントロピー、構造化スキーマの妥当性、ガードレールチェックなどを通じて)を評価します。確信度が閾値を下回るか検証に失敗した場合、ルーターは中型または大型モデルへとエスカレーションします。主なシステム設計上のトレードオフには、ルーター自体のレイテンシオバーヘッドと計算コスト削減効果のバランス、トラフィック急増時のフォールバックタイムアウト予算、継続的な評価(各ティア間での出力品質ドリフトを追跡するシャドウ評価など)が含まれます。

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)
AI コーチを使ってこの質問に答えてみる