この記事について
最近、GPT-4oの画像生成がすごいという話題を見かけ、近年の画像生成モデルがどのように進化しているのか気になって調べてみました。
結論としては、テキストや画像(さらには他のモダリティも含めて)をそれぞれトークン化し、Transformerによって自己回帰的に入力・生成を行う方式が主流のようです。
ざっくりとした関連性
VAE周り
GPTまわり
DALL-Eまわり
最近の画像生成まわり
以下各技術紹介(by GTP4-o)
VAE
論文名: Auto-Encoding Variational Bayes
URL: https://arxiv.org/abs/1312.6114
発表日: 2013-12-20
✅ ポイント
- AutoEncoderでは、入力データを**固定ベクトル(潜在変数)で表現していたが、VAEでは確率分布(平均と分散)**で表現する。
- 潜在空間からのサンプリングにより、新しいデータの生成が可能になり、VAEは生成モデルとしての能力を持つ。
- 再パラメータ化トリックによって、サンプリングを含む処理でも勾配を通して学習できるようになった。
🧪 学習の疑似コード
# パラメータ初期化
initialize(encoder, decoder)
while not converged:
x = sample_from_dataset()
# 平均と log 分散を取得
μ, logvar = encoder(x)
std = torch.exp(0.5 * logvar)
ε = sample_normal(0, 1)
z = μ + std * ε # reparameterization trick
# デコーダで再構成
x_recon = decoder(z)
# ロス計算
loss_recon = loss_fn(x_recon, x) # 例: binary cross entropy or mean squared error
loss_kl = -0.5 * torch.sum(1 + logvar - μ.pow(2) - logvar.exp())
loss = loss_recon + loss_kl
# パラメータ更新
update(loss, encoder, decoder)
🔎 推論時の疑似コード
x = input_sample()
μ, logvar = encoder(x)
std = torch.exp(0.5 * logvar)
# 通常は推論時に z = μ を使う(再現性を保つためノイズなし)
z = μ
x_recon = decoder(z)
🧠 モデル例(MNIST)
・Encoder
Input (784次元) → Linear(784→500) → tanh → Linear(500→μ, logσ²) → 出力 μ(200次元), logvar(200次元)
・Decoder
Input z (200次元) → Linear(200→500) → tanh → Linear(500→784) → 出力 x(784次元)
❓ よくある質問
Q. なぜ σ ではなく log(σ²)(logvar)を推定するの?
- 分散(σ²)は常に正の値である必要があるが、ニューラルネットの出力は正負どちらにもなり得る。
- そのため、log(σ²) を出力しておけば、実数全体をカバーしながら σ² を安全に得ることができる(
σ = exp(0.5 * logvar)
)。 - また、KLダイバージェンスの式にも log(σ²) が登場するため、計算も簡単かつ安定になる。
Q. なぜ log(σ) の推定ではダメなの?
-
log(σ)
は数学的には可能だが、KLダイバージェンスの計算ではlog(σ²)
が必要なので、2倍するなどの変換が必要になり、実装が煩雑になる。 - 勾配の安定性や慣習を考慮しても、log(σ²)(= logvar)を推定するのが標準的。
Q. 結局、VAEではどんなタスクが解けるの?
- データを「圧縮 → 再構成」するだけでなく、ランダムサンプリングから新しいデータを生成できる。
- 異常検知(再構成誤差が大きい入力を検出)、潜在空間でのクラスタリング・可視化、入力の意味的な変換(条件付き生成) など、幅広い応用が可能。
GAN
論文名: Generative Adversarial Nets
URL: https://arxiv.org/abs/1406.2661
発表日: 2014-06-10
✅ ポイント
- GANは、生成モデル G と 識別モデル D の敵対的なゲームによって訓練される。Gは本物に似たデータを生成しようとし、Dはそれを見破ろうとする。
- 学習は2プレイヤーのミニマックスゲームとして定式化される。理想的には、Dは常に正しく判別しようとし、GはDを騙すように学習する。
- 理論的には、Gが完全に学習されると、本物のデータ分布と同じ分布を生成するようになる。
- GANではマルコフ連鎖や明示的な確率分布の定義を必要としないため、高速かつ柔軟な生成が可能。
🧪 学習の疑似コード
# パラメータ初期化
initialize(G, D)
while not converged:
# 識別器の更新(k回)
for _ in range(k):
x_real = sample_real_data() # 本物データ
z = sample_noise() # ノイズ
x_fake = G(z).detach() # 生成データ(G固定)
# 識別器 D の損失(本物: 1, 偽物: 0)
loss_D = -log(D(x_real)) - log(1 - D(x_fake))
update(loss_D, D)
# 生成器の更新(1回)
z = sample_noise()
x_fake = G(z)
# Dを騙すようにGを更新(本物と判定されるように)
loss_G = -log(D(x_fake)) # 実用的には log(D(G(z)))
update(loss_G, G)
🔎 推論時の疑似コード
z = sample_noise()
x_gen = G(z) # 学習済みの生成器で新しいデータを生成
🧠 モデル例(MNIST)
・Generator(G)
Input z (100次元) → Linear(100→500) → ReLU → Linear(500→784) → Sigmoid → 出力 x (784次元画像)
・Discriminator(D)
Input x (784次元画像) → Linear(784→500) → ReLU → Linear(500→1) → Sigmoid → 出力 D(x)∈[0,1]
❓ よくある質問
Q. GANは何がすごいの?
- 生成結果が非常にシャープでリアル。多くの生成モデル(VAE等)がぼやけた画像を出力するのに対し、GANは鋭い画像が得られる。
- 明示的な確率密度関数を定義する必要がなく、マルコフ連鎖も不要なので、学習・生成ともにシンプル。
Q. なぜ論文中のlog(1 - D(G(z)))
より log(D(G(z)))
の方がいいの?
-
log(1 - D(G(z)))
は、Dが強いと勾配が小さくなり、Gの学習が進まない(勾配消失)。 - そのため、Dが「Gを騙された」と感じた分だけ大きな勾配を得る
log(D(G(z)))
が実用的。
D(G(z))の値 | log(1 - D(G(z))) | -log(D(G(z))) | 勾配の大きさ(学習のしやすさ) |
---|---|---|---|
0.01(ヘタ) | ≈ log(0.99) ≈ 0.01 | ≈ 4.6 | log(1 - D):小さい(ダメ) log D:大きい(よい) |
0.5(まぁまぁ) | ≈ log(0.5) ≈ -0.69 | ≈ 0.69 | 両方まあまあ学習できる |
0.99(成功) | ≈ log(0.01) ≈ -4.6 | ≈ 0.01 | log D:もう十分学習したので勾配は小さい |
Q. GANにはどんな課題があるの?
- モード崩壊: Gが多様性を失い、似た画像ばかり生成してしまう。
- 訓練が不安定: GとDのバランスが崩れると学習が破綻しやすい。
- 評価が難しい: 明示的な確率分布がないため、log-likelihoodのような評価ができない。
Q. GANはどんな応用がある?
- 高品質な画像生成(例: 顔画像、風景画など)
- スタイル変換(例: モネ→写真)
- 画像の補完、超解像、データ拡張、異常検知 など
PixelRNN
論文名: Pixel Recurrent Neural Networks
URL: https://arxiv.org/abs/1601.06759
発表日: 2016-01-25
✅ ポイント
- 全ての過去ピクセルをコンテキストとして考慮できるように、画像の対角方向に沿って情報を伝播させる2次元LSTM。
- 左上 → 右下の対角方向に 双方向LSTM(BiLSTM) を用いることで、グローバルな依存関係を捉えることが可能。
- 並列計算を実現するため、入力マップをスキュー変換(rowごとにシフト)し、対角方向の処理を列方向の畳み込みに置き換えて実装。
- Masked convolutionとsoftmaxにより、未来のピクセルやRGBチャネルへの情報漏洩を防ぐ。
- MNISTでは最も良い負の対数尤度(NLL: 79.20 nats)を記録。
🧪 学習の疑似コード(Diagonal BiLSTM)
# 1. モデル初期化
initialize(
conv7x7_A, # 最初の特徴抽出 conv(mask A)
diag_bilstm_layers, # Diagonal BiLSTM層
softmax_layer # RGBごとの分類器
)
# 2. 学習ループ
while not converged:
x = sample_image() # 例: shape = (H, W, 3)
# 3. 初期特徴抽出(mask A)
x_feat = conv7x7_A(x) # → shape = (H, W, h)
# 4. skew変換:行ごとに右に1ずつずらして H×(2W-1)
x_skewed = skew_input(x_feat) # shape: (H, 2W-1, h)
# 5. L層のDiagonal BiLSTM
for layer in diag_bilstm_layers:
# 入力→状態(共通)
gate_inputs = conv1x1_input2state(x_skewed) # → shape: (H, 2W-1, 4h)
# 左→右の対角方向(BiLSTM)
h_L, c_L = zeros(), zeros()
for col in range(2W - 1):
gate_L = gate_inputs[:, col, :]
state_L = conv2x1_state2state(h_L) # 前列方向の状態を畳み込み
gates = gate_L + state_L
i, f, o, g = split_gates(gates)
c_L = f * c_L + i * tanh(g)
h_L = o * tanh(c_L)
save h_L into h_L_seq[:, col, :]
# 右→左方向も同様に
h_R, c_R = zeros(), zeros()
for col in reversed(range(2W - 1)):
gate_R = gate_inputs[:, col, :]
state_R = conv2x1_state2state(h_R)
gates = gate_R + state_R
i, f, o, g = split_gates(gates)
c_R = f * c_R + i * tanh(g)
h_R = o * tanh(c_R)
save h_R into h_R_seq[:, col, :]
# 右向き出力は1行下にずらして合成
h_combined = h_L_seq + shift_down(h_R_seq)
# 残差接続あり
x_skewed = h_combined + x_skewed
# 6. skew解除して画像サイズに戻す
x_processed = unskew(h_combined)
# 7. RGB×256 softmaxで各ピクセル生成分布
y_pred = softmax_layer(x_processed)
# 8. ロスと更新
loss = cross_entropy(y_pred, x)
update(loss)
🔎 推論時の疑似コード(Diagonal BiLSTM)
# 1. 入力初期化(空画像)
x_generated = zeros(H, W, 3) # RGB画像の空テンソル
# 2. 画像全体を上から下、左から右へ生成
for i in range(H):
for j in range(W):
for channel in [R, G, B]: # RGB順に
# (1) 現在の部分生成画像を用意
x_context = x_generated.clone()
# (2) conv7x7(mask A)で特徴抽出(未来情報遮断)
feat_map = conv7x7_A(x_context) # shape: (H, W, h)
# (3) skew(対角方向を列に変換)
feat_skewed = skew_input(feat_map) # shape: (H, 2W-1, h)
# (4) 各BiLSTM層を順に処理
for layer in diag_bilstm_layers:
# 入力→状態(1×1 conv, mask B)
gate_inputs = conv1x1_input2state(feat_skewed) # → shape: (H, 2W-1, 4h)
# 左→右方向
h_L, c_L = zeros_like_state()
h_L_seq = zeros(H, 2W-1, h)
for col in range(2W - 1):
gate_L = gate_inputs[:, col, :]
state_L = conv2x1_state2state(h_L) # 前列の状態(2×1 conv)
gates = gate_L + state_L
i, f, o, g = split_gates(gates)
c_L = f * c_L + i * tanh(g)
h_L = o * tanh(c_L)
h_L_seq[:, col, :] = h_L
# 右→左方向
h_R, c_R = zeros_like_state()
h_R_seq = zeros(H, 2W-1, h)
for col in reversed(range(2W - 1)):
gate_R = gate_inputs[:, col, :]
state_R = conv2x1_state2state(h_R)
gates = gate_R + state_R
i, f, o, g = split_gates(gates)
c_R = f * c_R + i * tanh(g)
h_R = o * tanh(c_R)
h_R_seq[:, col, :] = h_R
# 右向きは1段下にシフトして加算
h_combined = h_L_seq + shift_down(h_R_seq)
# 残差接続
feat_skewed = h_combined + feat_skewed
# (5) 最終出力をunskewして元画像サイズに戻す
feat_final = unskew(h_combined) # → shape: (H, W, h)
# (6) RGBごとの softmax 出力(256値)
logits = softmax_layer(feat_final) # shape: (H, W, 3×256)
# (7) 現在位置のchannelに対する分布を抽出しサンプル
prob = logits[i, j, channel * 256 : (channel + 1) * 256]
value = sample_from(prob)
# (8) 生成画像に書き込み
x_generated[i, j, channel] = value
🧠 モデル構成(例: MNIST)
・Diagonal BiLSTM(7層, h=16)
Input: (28×28×1)
Conv2D (7×7, Mask A, padding=3)
Layer 1:
- Skew input → size = 28×(2×28−1)
- Input-to-state: 1×1 conv (mask B)
- State-to-state: 2×1 conv(列方向)
Layers 2〜7:
- 同様のDiagonal BiLSTMブロック(残差接続あり)
Output:
- Unskewして元の画像サイズに戻す
- 1×1 conv → ReLU → 1×1 conv
- Sigmoid(MNIST)または Softmax(カラー画像)
❓ よくある質問
Q. なぜskew(斜めにシフト)するの?
- 対角方向に沿った依存関係を 列方向の畳み込みで効率的に処理するため。
- skew → conv(列方向)→ unskew によって 並列計算と対角依存の両立を実現。
Q. なぜ双方向LSTMを使うの?
- 単方向だと片側のコンテキストしか見れない。
- 双方向にすることで、対角方向における情報の流れを最大化できる。
- ただし未来の情報を見ないよう、片方の出力を1段ずらして加算している。
Q. どんな場面でDiagonal BiLSTMが有効?
- ピクセルの全領域にわたる依存関係が重要な場面(例: 画像生成、補完、インペインティング)
- 実験ではPixelCNNやRow LSTMよりもCIFAR-10やMNISTで高性能(低bits/dimまたは低NLL)だった。
Transformer
論文名: Attention Is All You Need
URL: https://arxiv.org/abs/1706.03762
発表日: 2017-06-12
✅ ポイント
- 再帰や畳み込みを排除し、Attentionのみで構成される新しいネットワーク「Transformer」を提案。
- Attentionにより、系列長に依存せずに全体の依存関係を学習できる。特にSelf-Attentionにより、系列内の関係性を柔軟に捉える。
- Multi-Head Attentionにより、異なる視点での注目が可能となり、表現力が向上。
- 全ての処理が並列化可能で、学習速度と性能が大幅に改善。
- 位置情報を埋め込み(Positional Encoding) で補完。RNNが持つ「順序性」を別手法で担保。
🧪 学習の疑似コード(翻訳タスク例)
# モデル初期化
initialize(encoder, decoder)
while not converged:
x_src, x_tgt = sample_parallel_sentences()
# エンコーダとデコーダの出力取得
z = encoder(x_src, pos_enc=True)
y_pred = decoder(x_tgt, z, pos_enc=True)
# クロスエントロピー + label smoothing
loss = label_smoothing_cross_entropy(y_pred, x_tgt)
# 重み更新
update(loss, transformer)
🔎 推論時の疑似コード
x_src = input_sentence()
z = encoder(x_src, pos_enc=True)
y_gen = [BOS_token]
while not finished:
y_partial = decoder(y_gen, z, pos_enc=True)
next_token = sample(y_partial[-1])
y_gen.append(next_token)
🧠 モデル構成(Baseモデル)
・Encoder Layer (6層)
- Self-Attention (Multi-Head)
- Position-wise FFN(ReLU付き2層MLP)
- 各サブ層にResidual + LayerNorm
- 入力にはPositional Encodingを加算
・Decoder Layer (6層)
- Masked Self-Attention (Multi-Head)
- Encoder-Decoder Attention
- Position-wise FFN
- 同様に Residual + LayerNorm、Positional Encoding使用
・各種サイズ
名前 | 値 |
---|---|
d_model |
512 |
d_ff |
2048 |
num_heads |
8 |
head_dim |
64 (= 512 / 8) |
dropout |
0.1 (base), 0.3 (big) |
📊 注意メカニズム(Scaled Dot-Product Attention)
数式(Q: query, K: key, V: value):
\text{Attention}(Q, K, V) = \text{softmax} \left( \frac{QK^T}{\sqrt{d_k}} \right) V
-
Q = XW_Q
,K = XW_K
,V = XW_V
- マルチヘッド: 複数の W を使って並列に attention 実行し、結果を concat + 線形変換
❓ よくある質問
Q. なぜ再帰を捨てたの?
- RNNは逐次処理のため並列化が困難。
- Self-Attentionでは全入力を同時に処理可能で、計算速度と依存関係の長距離把握に優れる。
Q. Positional Encodingのsin/cosを使うのはなぜ?
- 順序情報が欠落するため、位置に応じた情報を加算。
- sin/cosで周期的なエンコーディングを行い、系列長を超えた汎化も可能と仮定。
- 学習済み埋め込みと同程度の性能だが、固定で使えるという利点がある。
VQ-VAE
論文名: Neural Discrete Representation Learning
URL: https://arxiv.org/abs/1711.00937
発表日: 2017-11-02
✅ ポイント
- 通常のVAEでは潜在変数を連続値のベクトルで表現するが、VQ-VAEでは離散的なインデックスとして表現し、コードブックからベクトルを選ぶ。
- 離散化には ベクトル量子化(Vector Quantization, VQ) を用い、posterior collapse(潜在変数が無視される問題)を回避できる。
- 再パラメータ化できない量子化処理に対しては、straight-through estimator によって近似的に勾配を通して学習する。
- 潜在変数に対して PixelCNN や WaveNet による 自己回帰的事前分布(prior) を後付けで学習することで、新たなサンプル生成が可能。
- 音声・画像・映像など多様なドメインで高品質な生成が可能で、特に無監督で音素のような意味的単位を抽出できることが注目された。
🧪 学習の疑似コード(VQ + AutoEncoder)
# 初期化
initialize(encoder, decoder, codebook) # codebook: (K, D)
while not converged:
x = sample_from_dataset()
# エンコーダの出力(連続ベクトル)
ze = encoder(x) # shape: (B, H, W, D)
# コードブックから最近傍インデックスを取得
k = argmin_j ||ze - e_j||^2 # shape: (B, H, W)
zq = codebook[k] # shape: (B, H, W, D)
# 再構成
x_recon = decoder(zq)
# ロス計算(3項)
loss_recon = loss_fn(x_recon, x)
loss_vq = ||sg[ze] - e_k||^2 # codebook更新用
loss_commit = β * ||ze - sg[e_k]||^2 # encoder更新用
loss = loss_recon + loss_vq + loss_commit
update(loss, encoder, decoder, codebook)
🔎 推論時の疑似コード(PixelCNNによる生成)
# 潜在インデックス z ∈ ℤ^{H×W} を自己回帰的にサンプリング
z = zeros(H, W)
for i in range(H):
for j in range(W):
logits = pixelcnn(z)
probs = softmax(logits[:, i, j])
z[i, j] = sample_from(probs)
# コードブックから埋め込みベクトルを取得
zq = codebook[z] # shape: (H, W, D)
# デコーダで生成画像を得る
x_hat = decoder(zq)
🧠 モデル例(CIFAR-10)
・Encoder
Input (32×32×3) → Conv ×2 + Residual Block ×2 → 出力 ze (8×8×D)
・Codebook
K=512 個のベクトル e_j ∈ ℝ^D(例: D=64)
・Decoder
Input (8×8×D) → Residual Block ×2 + Deconv ×2 → 出力 x_hat (32×32×3)
・Prior(後付け)
PixelCNN(8×8×1のインデックスマップに対して p(z) をモデル化)
❓ よくある質問
Q. なぜ離散表現にするの?
- 言語・音声など本質的に離散的なデータに対して適している。
- 解釈性・クラスタリングのしやすさが向上。
- VAEでありがちな「潜在を無視する問題(posterior collapse)」を自然に回避できる。
Q. 連続VAEより性能が劣るのでは?
- CIFAR-10のlog-likelihood評価では、連続VAEと同等性能を達成(例: 4.67 bits/dim)。
- 離散でありながら、自己回帰的priorを活用することで高品質な生成が可能。
Q. 生成の多様性はコードブックの数に制限されるの?
- いいえ。コードブックの語彙数 K は固定でも、空間サイズ(例: 32×32)との組み合わせで表現力は指数的に増える。
- さらに、PixelCNNで各位置を文脈依存で生成するため、多様性と整合性を両立できる。
Q. 再パラメータ化トリックは使えないのでは?
- 離散化操作には使えないが、代わりにstraight-through estimator(STE) で近似的に勾配を通している。
- 勾配は
zq
からze
にそのままコピーされる(STE)。
GPT1
論文名: Improving Language Understanding by Generative Pre-Training
URL: https://cdn.openai.com/research-covers/language-unsupervised/language_understanding_paper.pdf
発表日: 2018-06-11
✅ ポイント
- 事前学習済みの生成的Transformer(GPT)を、下流の自然言語理解タスクに微調整(fine-tuning)。
- タスクごとの入力を1列のトークン列に変換し、追加の線形層で出力。
- 必要な変更は分類ヘッドとトークンの特殊embeddingのみ。
- 自然言語含意、質問応答、文類似度、文分類など多数のタスクでSOTA。
- 学習時に言語モデル損失(補助目的) を加えることで精度と汎化力が向上。
🧪 学習の疑似コード(自然言語含意タスク)
# モデル構築(事前学習済みGPT+分類ヘッド)
model = load_pretrained_gpt()
classifier_head = Linear(hidden_size, num_classes)
for x, y in dataloader: # 入力例: "premise $ hypothesis"
input_ids = tokenizer(x)
hidden_states = model(input_ids)
# 最終トークンの出力を取得して分類
h_last = hidden_states[-1]
logits = classifier_head(h_last)
loss_cls = cross_entropy(logits, y)
# 任意: 言語モデル損失も加える
lm_logits = model.lm_head(hidden_states)
loss_lm = language_model_loss(lm_logits, input_ids)
loss = loss_cls + λ * loss_lm
loss.backward()
optimizer.step()
🔎 推論時の疑似コード
x = "A man is playing guitar $ A person is performing music"
input_ids = tokenizer(x)
hidden_states = model(input_ids)
h_last = hidden_states[-1]
logits = classifier_head(h_last)
prediction = argmax(softmax(logits))
🧠 モデル構成と各次元(例:GPT-base相当)
🔹 トークン処理
ステップ | 処理 | 入出力次元 |
---|---|---|
1 | 入力トークン列 | [batch, seq_len] |
2 | Embedding |
token_id → [hidden_dim] → [batch, seq_len, 768]
|
3 | Positional Encoding | 学習済み位置ベクトルを加算 → [batch, seq_len, 768]
|
🔹 Transformer Block(12層)
1ブロックあたりの構成(×12)
モジュール | 詳細 | 入出力次元 |
---|---|---|
Masked Self-Attention | 12ヘッド、各 64 dim (合計 768 ) |
[batch, seq_len, 768] → [batch, seq_len, 768]
|
Add & LayerNorm | 残差接続と正規化 | [batch, seq_len, 768] |
Feed Forward |
768 → 3072 → 768 (GELU) |
[batch, seq_len, 768] |
Add & LayerNorm | 同上 | [batch, seq_len, 768] |
🔹 出力処理
ステップ | 処理 | 次元 |
---|---|---|
最終トークンの出力 h_m | [batch, hidden_dim=768] |
|
線形分類器 W_y | [768 → num_classes] |
|
softmax | 出力確率 [batch, num_classes]
|
❓ よくある質問
Q. fine-tuningで何を学習するの?
- 事前学習済みのTransformer全体の重み(12層)と、
- 新たに追加された線形分類層(W_y)
を一緒に微調整します(end-to-end fine-tuning)。
Q. なぜトークン列に変換するの?
- GPTは「左から右に予測する生成モデル」なので、文対やQA形式のような構造化入力はそのままでは扱えません。
- そこで、Premise + $ + Hypothesis のように1つの連続テキスト列に変換します。
Q. なぜ言語モデル損失を加えるの?
- 微調整の過程で事前学習の知識を保ちやすくなり、
- 小規模データでも過学習しにくくなります。
🆚 Q. BERTなど先行研究との違いは?
比較項目 | GPT (本論文) | BERT |
---|---|---|
モデル構造 | Decoder-only(左から右) | Encoder-only(双方向) |
事前学習目標 | 次トークン予測(言語モデリング) | マスクされたトークンの復元(MLM) |
入力形式 | 連続文列(例: A $ B ) |
[CLS] A [SEP] B (セグメント構造) |
出力利用法 | 最終トークン出力で分類 |
[CLS] トークン出力で分類 |
転用の柔軟性 | 高い(タスクに応じて入力整形のみ) | 高い(だが専用ヘッドをよく使う) |
📌 要点:
GPTは「生成的に学習したモデルを分類にも流用可能」であることを初めて明確に示した。
BERTは構造的にはより強力だが、GPTのほうがシンプルな枠組みでありながら汎用性の高さと事前学習の効果を証明した点が革新的。
GPT2
論文名: Language Models are Unsupervised Multitask Learners
URL: https://cdn.openai.com/better-language-models/language_models_are_unsupervised_multitask_learners.pdf
発表日: 2019-02-14
✅ ポイント
- タスク固有のデータやFine-tuningなしに、Zero-shotで多数のNLPタスクをこなすことができる、事前学習だけで使える言語モデル。
- Transformerベースの自己回帰型モデル(GPT)を大規模化し、**WebText(45Mページ、40GB)**という多様なコーパスで事前学習。
- 入力に自然言語でタスクの指示(例: "Translate to French:", "TL;DR:")を与えるだけで、翻訳・要約・質問応答などに対応。
- モデルサイズを大きくすることで、log-linearに性能が向上することを実証(最大モデルは1.5Bパラメータ)。
- BERTとの大きな違いは、教師なしでタスクに即対応できる点(BERTは微調整が必須)。
🧪 学習の疑似コード(簡略)
# WebTextから自然文をサンプル
x = sample_from_webtext()
# Transformer による事前学習(次トークン予測)
for t in range(1, len(x)):
# ある位置 t における次トークン x[t] を予測
context = x[:t]
logits = transformer(context)
loss = cross_entropy(logits, x[t])
# パラメータ更新
update(loss, transformer)
🔎 推論時の疑似コード(Zero-shot)
# 例:要約タスクの場合
input_text = """
Article: AI research is accelerating rapidly due to transformer models.
TL;DR:""" # タスクヒントをつけて入力
# トークンを自動生成
summary = generate_text(transformer, input_text, max_tokens=50)
print(summary)
🧠 モデル構成(GPT-2)
モデル | 層数 | 隠れ次元 | ヘッド数 | パラメータ数 |
---|---|---|---|---|
small | 12 | 768 | 12 | 117M |
medium | 24 | 1024 | 16 | 345M |
large | 36 | 1280 | 20 | 762M |
GPT-2 | 48 | 1600 | 25 | 1.5B |
- 入力長: 最大1024トークン
- トークナイザー: Byte-Level BPE(語彙サイズ 50,257)
- 特徴: LayerNormの位置、残差スケーリングなどを改良
❓ よくある質問
Q. なぜZero-shotでタスクが解けるの?
- 学習時に明示的にタスクを教えていなくても、WebTextに含まれる「自然な形のタスク実演(例: 翻訳例やFAQ形式)」から、
暗黙的にタスクの構造を学習している。 - そのため、タスクの説明を自然言語で提示すれば、それに従って出力することができる。
Q. GPT-1との違いは?
項目 | GPT (2018) | GPT-2 (2019) |
---|---|---|
データ | BooksCorpus (小説中心) | WebText (多ジャンル) |
出力対応 | Fine-tune 必須 | Zero-shot対応可能 |
モデルサイズ | 117M | 最大1.5B |
入力長 | 512 | 1024 |
語彙 | 通常BPE | ByteレベルBPE(Unicode完全対応) |
Q. なぜDecoderのみ(GPT系)なのに多様なタスクに対応できるの?
- 自然言語で入力と出力が明示できるタスク(QA, 翻訳, 要約など)は、
シーケンス予測の形式にうまくマッピングできるため、Decoderのみで完結する。
BERT
論文名: BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding
URL: https://arxiv.org/abs/1810.04805
発表日: 2018-10-11
✅ ポイント
- BERTは、Transformer Encoderをベースにした双方向的な文脈理解モデル。各単語の意味を前後の文脈を同時に考慮して捉える。
- 学習は2つの事前タスクを使う:
① Masked Language Model (MLM) → ランダムに隠した単語を前後の文脈から予測
② Next Sentence Prediction (NSP) → 2つの文が連続しているかを分類 - 事前学習+ファインチューニングの枠組みにより、分類・QA・NERなどの多様なタスクに対応可能。
- 特別な構造の変更なしに、文単位・トークン単位のNLPタスクでSOTA性能を達成。
🧪 学習の疑似コード(事前学習)
# パラメータ初期化
initialize(transformer_encoder)
while not converged:
tokens = tokenize(sample_from_corpus()) # WordPiece
tokens, labels = apply_masking(tokens) # MLM: 15%マスク
is_next_label = sample_nsp_label() # NSP: 正しい/ランダム文
outputs = transformer_encoder(tokens) # [CLS] + 文A + [SEP] + 文B + [SEP]
loss_mlm = cross_entropy(outputs[mask_positions], labels)
loss_nsp = binary_cross_entropy(outputs[CLS_position], is_next_label)
loss = loss_mlm + loss_nsp
update(loss, transformer_encoder)
🔎 推論時の疑似コード(分類タスクなど)
tokens = tokenize("input sentence or sentence pair")
outputs = transformer_encoder(tokens)
# 例えば文分類なら、[CLS]トークンの出力を使う
cls_vector = outputs[0]
prediction = classifier(cls_vector)
🧠 モデル例(BERTBASE)
・ハイパーパラメータ
L = 12層(Transformer Encoder)
H = 768次元の隠れ状態
A = 12ヘッドの自己注意
パラメータ数: 約110M
・入力構成
[CLS] 文Aのトークン ... [SEP] 文Bのトークン ... [SEP]
+ トークン埋め込み(WordPiece)
+ セグメント埋め込み(文A/Bの識別)
+ 位置埋め込み(系列順)
❓ よくある質問
Q. なぜ「双方向」だと生成できないの?
- BERTは前後のトークンを同時に見て1つのトークンを予測するよう学習されており、順に次の単語を出す自己回帰的生成には向いていない。
- GPTなど生成モデルでは、未来の単語を遮断するマスク(causal mask)を使うのに対し、BERTはマスクなしの完全自己注意で全体を見てしまう。
Q. [CLS]トークンとは何?なぜ[CLS]必要?
- [CLS]はTransformer Encoderの最初に強制的に挿入される、文全体の特徴表現を担う特殊トークンで、分類やNSPなどに使われる。
- 学習時に[CLS]の最終ベクトルを分類タスクのロス計算に使うことで、文全体の意味を凝縮させる。
Q. BERTの応用例は?
- 文分類(感情分析、トピック分類)
- QA(SQuADなど):出力ベクトルから答えのスパンを予測
- NER:各トークンの出力をラベル分類に使用
- 文のペア関係:自然言語推論、類似判定(NSPを活かす)
GPT3
論文名: Language Models are Few-Shot Learners
URL: https://arxiv.org/abs/2005.14165
発表日: 2020-07-22
✅ ポイント
- GPT-3 は、1750億パラメータの巨大な自己回帰型Transformerモデルで、前例のないスケールを実現。
- 特徴は、ファインチューニングなしで、多様なNLPタスクに対応できる "in-context learning" 能力。
- in-context learning とは、プロンプト内に例示や指示を埋め込むだけで新しいタスクに適応できる能力のこと。
- GPT-3 は、Zero-shot, One-shot, Few-shot の各設定で多くのNLPベンチマークにおいて強力な性能を発揮。
- 様々なタスクにおいて、モデルサイズが大きくなるほど in-context learning の性能が向上する傾向が見られた。
🧪 学習の疑似コード(自己回帰言語モデル)
# モデル初期化(Transformer Decoderベース)
initialize(gpt3_model)
while not converged:
x = get_tokenized_sequence() # 入力トークン列(例:長文)
# 自己回帰的な次トークン予測タスク
logits = gpt3_model(x[:, :-1]) # 入力: 前のトークン
loss = cross_entropy(logits, x[:, 1:]) # 出力: 次のトークン
# パラメータ更新
update(loss, gpt3_model)
🔎 推論時の疑似コード(Few-shot 学習)
# 例示を含んだプロンプトを準備
prompt = """
Q: Translate English to French: "book"
A: livre
Q: Translate English to French: "house"
A: maison
Q: Translate English to French: "apple"
A:
"""
# 次のトークンを順に生成(自己回帰)
output = generate_tokens(prompt, max_new_tokens=10)
🧠 モデル構成(GPT-3 各サイズ)
モデル名 | パラメータ数 | 層数 | 隠れ次元 | ヘッド数 | トークン長 (n_ctx) |
---|---|---|---|---|---|
GPT-3 Small | 125M | 12 | 768 | 12 | 2048 |
GPT-3 Medium | 350M | 24 | 1024 | 16 | 2048 |
GPT-3 Large | 760M | 24 | 1536 | 16 | 2048 |
GPT-3 XL | 1.3B | 24 | 2048 | 24 | 2048 |
GPT-3 2.7B | 2.7B | 32 | 2560 | 32 | 2048 |
GPT-3 6.7B | 6.7B | 32 | 4096 | 32 | 2048 |
GPT-3 13B | 13B | 40 | 5140 | 40 | 2048 |
GPT-3 175B | 175B | 96 | 12288 | 96 | 2048 |
❓ よくある質問
Q. GPT-3 はファインチューニング不要なの?
- はい。タスクの説明や例を直接プロンプトに与えるだけで動作します(few-shot learning)。
- モデルのパラメータ自体は更新されません(no gradient update)。
Q. "Few-shot" とは何ですか?
- 数個の例示(10〜100)を与えるだけでタスクを解く設定です。
- 他に Zero-shot(例示なし)や One-shot(例示1つ)もあり、人間のような柔軟性を目指した学習形式です。
Q. なぜトークン長が固定(2048)なの?
- Transformer は Positional Encoding によって位置情報を与えるため、事前に最大長を決めておく必要があります。
- GPT-3 ではこの最大長
n_ctx = 2048
を超える文脈は扱えません。
Q. GPT-3 はどんなタスクに強いの?
- 翻訳、質問応答、長文補完、クロズテスト、常識推論、アナロジー、文法訂正、文章生成など。
- 特に、few-shotで最先端性能に迫る精度を出したことが注目されました。
DALL-E1
論文名: Zero-Shot Text-to-Image Generation
URL: arXiv:2102.12092
発表日: 2021-02-26
✅ ポイント
- テキストと画像トークンを一つのストリームとして扱い、Transformerで自己回帰的に学習。
- 高解像度画像を直接扱わず、dVAEで画像を圧縮し離散トークンに変換(32×32、各トークンは8192種類)。
- テキスト(最大256トークン)+画像トークン(1024個)をTransformerで統一的にモデリング。
- Contrastive reranking(CLIP)でサンプルを再ランキングし、整合性の高い画像を選出。
- ゼロショット性能が高く、MS-COCOのキャプションから直接画像生成が可能。
🧪 学習の疑似コード
# Stage 1: dVAEの学習(画像→トークン化と再構成)
for x in image_dataset:
z = encoder_dVAE(x) # 32x32個の離散トークン
x_recon = decoder_dVAE(z)
loss = reconstruction_loss(x_recon, x) + β * KL(q(z|x) || p(z))
update(loss, encoder_dVAE, decoder_dVAE)
# Stage 2: Transformerによる事前学習
for (text, image) in text_image_dataset:
text_tokens = BPE_encode(text, max_len=256)
image_tokens = encoder_dVAE(image) # 32x32 = 1024 tokens
tokens = text_tokens + image_tokens
loss = cross_entropy(transformer(tokens), tokens[1:])
update(loss, transformer)
🔎 推論時の疑似コード
text = "a cat wearing sunglasses"
text_tokens = BPE_encode(text, max_len=256)
# 画像トークンを自己回帰的に生成
tokens = text_tokens
for _ in range(1024):
next_token = transformer(tokens).sample()
tokens.append(next_token)
image_tokens = tokens[-1024:]
image = decoder_dVAE(image_tokens)
🎨 dVAEの説明
dVAE(Discrete Variational Autoencoder)は、本モデルの画像トークン化を担う重要な構成要素です。
🔷 なぜ画像を離散トークンに変換するのか?
- 高解像度画像(256×256×3)は76800次元と非常に高次元。
- Transformerで直接モデリングするとメモリと計算量が爆発する。
- dVAEで圧縮(32×32=1024トークン)し、各トークンは8192種類の辞書から選ぶことで、約192倍の圧縮が可能に。
🔷 アーキテクチャ概要
- Encoder: 7×7 Conv → ResNet(Bottleneck)→ 1×1 Conv → 出力: 32×32×8192 のロジット(softmaxでカテゴリに変換)
- Decoder: 1×1 Conv → ResNet → 1×1 Conv → 出力: 256×256 RGB画像
- 損失関数は Logit-Laplace分布 に基づいた再構成誤差 + KL項(VAE的学習)
🔷 離散化の課題とGumbel-Softmax
VAEでは潜在変数が連続なので再パラメータ化トリックで勾配を通せるが、**dVAEでは離散トークン(one-hot)**のため、勾配が通らない。
そこで導入されるのが Gumbel-Softmax(Concrete Distribution):
y_i = \frac{\exp((\log(\pi_i) + g_i)/\tau)}{\sum_j \exp((\log(\pi_j) + g_j)/\tau)}
- ( \pi_i ):カテゴリiの確率(softmax前のロジット)
- ( g_i ):Gumbelノイズ(= (-\log(-\log(U))), U∼Uniform(0,1))
- ( \tau ):温度パラメータ(小さいほどone-hotに近づく)
→ これにより、「ほぼ離散」の確率ベクトルを通じて勾配が流れるようになる。
✅ 補足: 学習では Gumbel-Softmax を使い、推論時には
argmax
でトークンを確定。
🔷 Logit-Laplace分布とは?
- ピクセルは ([0, 255]) の離散値 → 正規分布やLaplace分布はサポート範囲が ((−∞, ∞)) なので不自然。
- 解決策として、Laplace分布を sigmoidで ((0,1)) にマッピング → logit-Laplace分布 を定義:
f(x | \mu, b) = \frac{1}{2bx(1 - x)} \exp\left(-\frac{|logit(x) - \mu|}{b}\right)
- これにより、出力が [0,1] に制限された画像再構成が可能。
🔷 dVAEの学習スケジュール
- KL係数 ( \beta ): 0 → 6.6 に徐々に上昇(最初は再構成優先、後半は潜在空間の正則化)
- 温度 ( \tau ): 1 → 1/16 にアニーリング(より離散的な出力へ)
- Optimizer: AdamW + Cosine decay, mixed precision 16-bit training
- 全更新数: 約300万ステップ、バッチサイズ512(V100 x 64台)
🧠 モデル例と構成
・dVAE(Stage 1)
-
Encoder:
7×7 Conv → ResNet(Bottleneck)→ 1×1 Conv → 出力:32×32×8192(ロジット) -
Decoder:
1×1 Conv → ResNet → 1×1 Conv → 出力:256×256 RGB画像 -
損失関数:
Logit-Laplace(再構成)+ KL(Gumbel-softmax緩和)
・Transformer(Stage 2)
-
構造:
Decoder-only Sparse Transformer(12Bパラメータ、64層、注意ヘッド62) -
入力:
テキスト(最大256トークン, vocab=16384) + 画像トークン(1024個, vocab=8192) -
マスク:
Row/Column/Conv Attention(11×11カーネル)
❓ よくある質問
Q. なぜ画像をdVAEで離散トークン化するの?
- 高解像度画像を直接扱うのは非効率。トークン化によりメモリ使用量と依存関係を圧縮でき、Transformerの学習が現実的になる。
Q. Gumbel-softmaxを使うのはなぜ?
- トークンは離散値だが、そのままだと勾配が流れない。Gumbel-softmaxで連続化することで勾配による最適化が可能になる。
Q. 推論時はzにノイズを加えるの?
- 通常は再現性を重視して
z = μ
のみを使う。生成の多様性が必要な場合はノイズを加えることも可能。
Q. このモデルでできるタスクは?
- ゼロショット画像生成
- キャプションからの構造的な画像生成
- 画像スタイル変換や編集(例: スケッチ化や反転)
- 構成的理解(「アコーディオンでできたバク」などの新規概念の合成)
dVAEとVQ-VAEの違いを、初心者でも直感的にわかるようにまとめます。
Q. VQ-VAEとdVAEの違いは?
項目 | VQ-VAE | dVAE(Gumbel-Softmax版) |
---|---|---|
離散化方法 | 量子化(nearest neighbor search) | Gumbel-Softmax近似(soft) |
勾配の流し方 | Straight-Through Estimator(無理やり流す) | Gumbel-Softmaxでスムーズに流す |
離散値の更新 | 埋め込みコードブックを直接更新(EMAなど) | クラス確率でsoftmax近似 → 最終的にhard選択 |
温度パラメータ | なし | あり(訓練中に徐々に低くする) |
実装の複雑さ | 簡単(ただしSTトリックが不安定な場合あり) | やや複雑(Gumbel noise生成・温度管理) |
学習安定性 | ⚠️ 難しいことがある | ✅ 比較的安定 |
GLIDE
論文名: GLIDE: Towards Photorealistic Image Generation and Editing with Text-Guided Diffusion Models
URL: arXiv:2112.10741
発表日: 2021-12-21
✅ ポイント
- 拡散モデル (Diffusion Model) をベースに、テキスト条件付きでフォトリアルな画像を生成・編集。
- 生成モデルにはClassifier-Free Guidanceを採用し、テキスト条件なし・ありの出力を補間して誘導。
- インペインティング(部分修復)も自然言語プロンプトで制御可能。
- 画像生成は2段階:
- 64×64のベース拡散モデル
- 256×256へのアップサンプラー拡散モデル
- DALL-Eより高品質な生成結果を達成(人間評価、FIDスコアともに上回る)。
🧪 学習の擬似コード
# 拡散モデル学習
for (image, caption) in dataset:
t = random_time_step() # ランダムな時刻を選ぶ
noise = random_noise()
noisy_image = add_noise(image, noise, t) # ノイズ付加
if random() < 0.2:
caption = None # 20%の確率で無条件に(Classifier-Free用)
pred_noise = diffusion_model(noisy_image, t, caption)
loss = mse_loss(pred_noise, noise)
update(loss, diffusion_model)
🔎 推論時の擬似コード
# 生成開始(完全なノイズから)
xt = random_noise() # x_T
for t in reversed(range(1, T+1)):
noise_pred_cond = diffusion_model(xt, t, caption)
noise_pred_uncond = diffusion_model(xt, t, None)
# Classifier-Free Guidance適用
noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_cond - noise_pred_uncond)
xt = denoise_step(xt, noise_pred, t) # 次時刻 x_{t-1}
🎨 Classifier-Free Guidanceの説明
Classifier-Free Guidanceは、
「条件あり」と「条件なし」の出力を補間することで
サンプルの忠実度と多様性のトレードオフを制御します。
🔷 数式
[
\hat{\varepsilon}{\theta}(x_t|c) = \varepsilon{\theta}(x_t|\emptyset) + s \left( \varepsilon_{\theta}(x_t|c) - \varepsilon_{\theta}(x_t|\emptyset) \right)
]
- (c):キャプション
- (s):ガイダンススケール(大きいと忠実度↑、多様性↓)
🔷 なぜClassifier-Free Guidanceを使う?
- 外部分類器(CLIPなど)に依存せず、モデル単体で誘導できる。
- CLIP Guidanceに比べて安定して高品質な生成ができる。
🧠 モデル例と構成
・ベース拡散モデル(64×64)
-
UNet構造:
- ダウンサンプル→ボトルネック→アップサンプル
- 中間層にSelf-Attention(低解像度層のみ)
- アップサンプル側でCross-Attention(テキスト条件注入)
-
時間埋め込み:
- 各時刻(t)を埋め込んで各ResBlockに加算
-
テキスト埋め込み:
- 独自のTransformerエンコーダ(24層、幅2048)
・アップサンプラー(256×256)
- 拡散モデルによる超解像(64→256)
- より少ないステップ、より強い条件付き
❓ よくある質問
Q. なぜ拡散モデルを使うの?
- GANに比べて学習が安定しやすく、モード崩壊(多様性喪失)も起きにくい。
- 少しずつノイズを除去するプロセスが、細かい制御と自然な生成を両立できる。
Q. DALL-Eとの違いは?
項目 | DALL-E | GLIDE |
---|---|---|
モデルタイプ | 自己回帰Transformer + dVAE | 拡散モデル |
画像表現 | 離散トークン列 | 連続画像空間 |
誘導方法 | Contrastive Reranking (CLIP) | Classifier-Free Guidance |
インペインティング | 弱い | 強力 |
生成品質 | 良好 | DALL-Eより高い |
Q. テキスト条件なし(NULLキャプション)の出力はどう扱う?
- 学習時に「NULL条件」のサンプルを20%混ぜることで、モデルに無条件生成能力も学ばせている。
- 推論時はこの無条件出力をベースに補間して誘導する。
Q. インペインティングはどうやる?
- マスク領域をノイズで初期化。
- コンテキスト(周辺画素)を固定しながら、拡散過程を実行。
- プロンプトに従って自然な穴埋めができる。
DALL-E2
論文名: Hierarchical Text-Conditional Image Generation with CLIP Latents
URL: arXiv:2204.06125
発表日: 2022-04-12
✅ ポイント
- テキストから直接画像を生成せず、いったんCLIPの画像埋め込み(latent)を生成(Prior)。
- Prior(DiffusionまたはAR)でテキストからCLIP latentを生成。
- Decoder(拡散モデル)でCLIP latentから画像を復元。
- テキスト情報もDecoderに補助的に入力できる(スタイル+意味の細部制御)。
- 意味・スタイル・多様性を高次元で両立でき、DALL·EやGLIDEより高品質。
🧪 学習の擬似コード
# --- CLIPエンコーダで意味表現を取得 ---
z_i = clip_image_encoder(x) # (B, 1024) 画像の埋め込み
z_t = clip_text_encoder(y) # (B, 512) テキストの埋め込み
# --- Prior(Diffusion Prior)学習 ---
t = random.randint(1, T)
z_i_noised = q_sample(z_i, t)
prior_inputs = [
text_tokens(y), # (B, 77, 512) テキストトークン列
z_t, # (B, 512) テキスト埋め込み
timestep_embedding(t),# (B, 512) 拡散ステップ埋め込み
z_i_noised, # (B, 1024) ノイズ付き埋め込み
output_placeholder(), # (B, 1024) 出力トークン
]
z_i_pred = prior_transformer(prior_inputs)
prior_loss = mse_loss(z_i_pred, z_i)
update(prior_loss, prior_transformer)
# --- Decoder(拡散モデル)学習 ---
x_t = add_noise(x, t)
decoder_inputs = {
"x_t": x_t, # (B, 3, 256, 256)
"t": t,
"z_i": z_i, # (B, 1024)
"optional_z_t": z_t,
}
epsilon_pred = decoder_unet(decoder_inputs)
decoder_loss = mse_loss(epsilon_pred, true_noise)
update(decoder_loss, decoder_unet)
🔎 推論時の擬似コード
# --- Priorによる潜在生成 ---
z_t = clip_text_encoder(input_text)
z_i = diffusion_prior_sample(z_t)
# --- Decoderで画像生成 ---
x_T = random_noise_image(shape=[1, 3, 64, 64])
for t in reversed(range(T)):
x_t = decoder_unet(x_T, t=t, cond=z_i)
x_T = denoise(x_T, x_t)
🎨 Diffusion PriorとDecoderの説明
🔷 Diffusion Prior(P(zᵢ|y))
- テキストからCLIP画像潜在 ( z_i ) を生成するモデル。
- 拡散モデル(DDPM)により、ノイズから意味ある潜在へ復元。
- 多様性が自然に表現できる上、意味も保持できる。
🔷 Diffusion Decoder(P(x|zᵢ,y))
- 画像潜在 ( z_i ) を条件に64×64の画像を生成。
- GLIDEベースのU-Net型拡散モデル。
- テキスト埋め込み ( z_t ) はオプション補助。
- Classifier-Free Guidance (CFG) により、意味強調と画質を両立。
🧠 モデル例と構成
・Prior Transformer
- Decoder-only Transformer
- テキスト・z_t・タイムステップ・ノイズ付きz_iを順に並べたトークン列を入力。
・Decoder U-Net
- エンコーダ・ボトルネック・デコーダ構造
- Cross-Attentionで z_i, (optional z_t) を条件付け。
❓ よくある質問
Q. なぜ直接テキスト→画像ではなく、一度zᵢを経由するの?
- テキストだけだとスタイルやリアリズムの細部制御が難しい。
- CLIPの画像潜在を経由することで、意味・スタイル・多様性を同時に満たせる。
Q. zᵢとzₜはどう違う?
項目 | zᵢ(画像潜在) | zₜ(テキスト潜在) |
---|---|---|
役割 | スタイル・意味のコア | 属性や関係性の補助 |
使用方法 | デコーダの主条件 | (optional)補助情報 |
Q. Diffusion PriorとAR Priorどっちがいい?
- Diffusion Priorのほうが多様性・意味一致度ともに優秀。
- AR Priorは離散トークン列扱いなので少し重いし、性能も劣る。
Q. CFG(Classifier-Free Guidance)はどこで使う?
- PriorとDecoderの両方で使用。
- 無条件と条件付きの出力を線形補間して、意味との整合性を高める。
DALL·E 1との違いは?
項目 | DALL·E 1 | unCLIP |
---|---|---|
基本戦略 | テキスト+画像トークン列を自己回帰的生成 | テキスト→CLIP埋め込み→画像復元の2段階 |
モデル | Transformerのみ | Prior(Transformer)+Decoder(拡散) |
中間表現 | 画像トークン(離散) | CLIP潜在ベクトル(連続) |
離散/連続 | 離散(dVAE) | 連続(CLIP latent) |
多様性 | やや低い | 非常に高い |
GLIDEとの違いは?
項目 | GLIDE | unCLIP |
---|---|---|
基本戦略 | テキスト→拡散モデル(直接画像復元) | テキスト→CLIP latent→拡散復元 |
条件情報 | テキスト埋め込みのみ | CLIP画像埋め込みが主、テキスト補助 |
多様性 | 高いが意味崩れが起きやすい | 多様性と意味一致が両立 |
編集タスク性能 | 高い(inpainting可能) | 高い(さらに意味一致度が高い) |
モデル規模 | 小型版(64x64→256x256)もあり | 基本は64x64(後段Upsamplerで1024x1024) |
DALL-E3
論文名: Improving Image Captioning with Better Use of Captions
URL: arXiv:2006.11807
発表日: 2020-06-21
✅ ポイント
- キャプションから抽出した関係情報を使い、画像内の物体・関係ノードを構成(CGVRG)。
- 弱教師ありMulti-Instance Learningにより、物体ペアと関係(predicate)をマッチング。
- **Graph Convolutional Network (GCN)**で物体・関係ノードの文脈表現を学習。
- マルチタスク学習で、単語生成+タグ(物体/関係/なし)分類を同時に行い、意味的整合性を高める。
- MSCOCOでSOTA性能を達成(特にCIDEr-Dスコアが大幅向上)。
🧪 学習の擬似コード
# --- 物体検出と関係トリプル抽出 ---
object_regions = faster_rcnn(image)
triples = text_scene_graph_parser(caption)
# --- Weakly Supervised Predicate Matching ---
region_pairs = all_pairs(object_regions)
predicate_probs = predict_predicate(region_pairs, triples)
loss_predicate = multi_instance_loss(predicate_probs, triples)
update(loss_predicate)
# --- Graph Convolution(CGVRG Encoding)---
object_feats = visual_features(object_regions)
predicate_feats = predicate_embeddings(triples)
contextualized_feats = GCN(object_feats, predicate_feats)
# --- Multi-task Caption Generation ---
for each step t:
h1_t = bottom_LSTM(h1_t-1, contextualized_feats, previous_word)
attended_feats = attention(h1_t, contextualized_feats)
h2_t = top_LSTM(h2_t-1, [h1_t, attended_feats])
# マルチタスク出力
word_pred = word_predictor(h2_t)
tag_pred = tag_predictor(h2_t)
loss = cross_entropy(word_pred, target_word) + γ * cross_entropy(tag_pred, target_tag)
update(loss)
🔎 推論時の擬似コード
# --- グラフ構築 ---
object_regions = faster_rcnn(input_image)
triples = text_scene_graph_parser(training_captions)
region_pairs = all_pairs(object_regions)
predicate_probs = predict_predicate(region_pairs, triples)
# --- 文脈特徴抽出 ---
contextualized_feats = GCN(object_feats, predicate_feats)
# --- 生成 ---
h1_0, h2_0 = initialize()
generated_caption = []
for each step:
h1_t = bottom_LSTM(h1_t-1, contextualized_feats, previous_word)
attended_feats = attention(h1_t, contextualized_feats)
h2_t = top_LSTM(h2_t-1, [h1_t, attended_feats])
word = sample(word_predictor(h2_t))
generated_caption.append(word)
🎨 CGVRGとMulti-task学習の説明
🔷 Caption-Guided Visual Relationship Graph (CGVRG)
- キャプションの主語-述語-目的語トリプルを解析。
- 画像内の物体領域と関係を弱教師ありでマッチング。
- 物体ノードと関係ノードを持つグラフ構造を生成。
🔷 Multi-task Learning(単語+タグ予測)
- 1ステップごとに
- 単語を生成(例:"man")
- タグを分類(例:これは物体か?関係か?なし?)
- タグによる情報強調(物体ノード or 関係ノード or 言語状態)を適応的に切り替え。
🧠 モデル例と構成
・Faster R-CNN(物体検出)
- ResNet-101ベース。
- RoI Pooling後の2048次元特徴量を使用。
・Weakly Supervised Predicate Matcher
- 物体ペア間にpredicate確率を出力するMLP。
- ノイズを許容するMulti-Instance Learning (Noisy-OR)。
・Graph Convolutional Network (GCN)
- Object Node ↔ Predicate Node 間で情報伝搬。
- Visual FeatureとTextual Featureを融合。
・2層LSTM+マルチタスクブロック
- 下層: 画像と単語を融合するLSTM
- 上層: 文を生成するLSTM
- マルチタスクブロックでタグ分類&単語生成。
❓ よくある質問
Q. なぜキャプションに沿った関係グラフを作るの?
- 普通のVRD(Visual Relationship Detection)では、キャプションに必要な細かな関係(例:"grab"など)が抜け落ちる。
- キャプション誘導により、キャプションに必要な意味だけを抽出できる。
Q. Multi-Instance Learningって何?
- 「この袋(bag)に1個でも正しい組み合わせがあればOK」という弱い制約で学習。
- 物体検出の曖昧さ(例:同じ犬が2匹いる)に強い。
Q. マルチタスク学習の効果は?
- 単語生成時に「いま物体を言うべき?関係を言うべき?」を動的に判断できる。
- これにより文の一貫性と情報量が向上。
🆚 既存手法との違い
項目 | 従来(Up-Down, GCN-LSTM) | 本論文(CGVRG) |
---|---|---|
関係グラフ | VRDモデルベース | キャプションベースで生成 |
学習方法 | 完全教師あり | 弱教師あり (Multi-Instance) |
文生成 | 単純なAttention | マルチタスク (単語+タグ) |
意味理解 | 不十分な場合あり | キャプションに沿った理解 |
性能 (CIDEr-D) | 120付近 | 129超え |
Vision Transformer (ViT)
論文名: An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale
URL: https://arxiv.org/abs/2010.11929
発表日: 2020-10-22(ICLR 2021 採択)
✅ ポイント
- CNNを一切使わず、Transformerを画像パッチ列に直接適用して画像分類を実現。
- 入力画像を固定サイズ(例:16×16)のパッチに分割し、それぞれをトークン(ベクトル)として処理。
- BERTの
[class]
トークンと同様に、分類用トークンを先頭に追加。 - 位置情報は1Dの学習可能な位置埋め込みで補完(2D位置埋め込みよりもシンプルで効果も十分)。
- 自己注意層により画像全体の情報をグローバルに統合可能。
- 小規模データセットではCNNに劣るが、大規模データセット(JFT-300Mなど)での事前学習によりSOTA性能を達成。
🧪 モデル構成(疑似コード風)
# 画像のパッチ化と埋め込み
image = load_image() # サイズ H x W x C
patches = split_into_patches(image, patch_size=16) # N個のパッチ (N = H*W/patch_size^2)
patch_embeddings = linear_projection(patches) # 各パッチをD次元に
# クラストークンと位置埋め込みを追加
cls_token = learnable_vector(D)
tokens = concat(cls_token, patch_embeddings) # 長さ N+1
tokens += positional_embedding # shape: (N+1, D)
# Transformer Encoder(L層)
for l in range(L):
tokens = tokens + MSA(LN(tokens))
tokens = tokens + MLP(LN(tokens))
# 出力:classトークンのベクトルにMLPを適用して分類
class_representation = LN(tokens[0])
output = MLP_head(class_representation) # 分類結果
🔎 推論時の流れ
image = input_image()
patches = split_into_patches(image, patch_size=16)
patch_embeddings = linear_projection(patches)
tokens = concat(cls_token, patch_embeddings) + positional_embedding
# Transformerエンコーダ通過
encoded = transformer_encoder(tokens)
cls_repr = encoded[0]
logits = MLP_head(cls_repr)
🧠 モデル例(ViT-Base)
名称 | 値 |
---|---|
層数 | 12 |
隠れ次元 D | 768 |
MLP隠れ層 | 3072 |
ヘッド数 | 12 (MSA) |
パラメータ数 | 約86M |
入力画像 | 224×224×3 |
パッチ | 16×16サイズ ⇒ 196パッチ + 1 [cls] トークン |
❓ よくある質問
Q. なぜCNNのような畳み込みを使わなかったの?
- CNNは画像特化の強い帰納バイアス(局所性・平行移動不変性)を持つが、ViTはデータ主導の表現学習を目指す。
- 十分なデータ量があれば、帰納バイアスなしでも高性能が可能であることを示した。
Q. 小さなデータセットではViTが弱い理由は?
- ViTは画像固有のバイアスを持たないため、少数データでは学習が不安定。
- CNNのように局所構造を活用できず、過学習しやすい。
Q. ViTの位置埋め込みはどうして1D?
- パッチを平坦化した後、系列として扱うため1D位置埋め込みで十分。
- 2D-awareな位置埋め込みも試したが、性能向上は限定的だった。
Q. ViTで画像生成や自己教師あり学習もできる?
- 本論文では分類のみだが、ViTの構造は生成タスクや自己教師あり学習にも応用可能。
- 例えば**Masked Patch Prediction(BERT風)**による事前学習も有望。
VQGAN
論文名: Taming Transformers for High-Resolution Image Synthesis
URL: https://arxiv.org/abs/2012.09841
発表日: 2021-06-23
✅ ポイント
- 高解像度画像生成にはTransformerの表現力が有効だが、直接ピクセル列を扱うと計算コストが爆発的に増大。
- CNNベースのVQGAN(改良版VQVAE)で画像を離散表現に変換し、Transformerでそのシーケンスを効率的にモデリング。
- VQGANは知覚損失(perceptual loss)とGAN損失を導入し、高品質な再構成画像を得つつコード長を圧縮。
- Transformerは、VQGANのコードブックインデックス列を自己回帰的に学習することで、画像のグローバル構成を捉える。
- semantic segmentation、depth、pose、classラベルなど多様な条件付き生成に対応。
🧪 学習の疑似コード
# === ステージ1: VQGANの学習 ===
initialize(encoder=E, decoder=G, codebook=Z, discriminator=D)
for x in dataset:
# エンコードして量子化
z_hat = E(x) # ℝ^{h×w×nz}
z_q = quantize(z_hat, Z) # 最近傍codebookベクトルへ量子化
x_hat = G(z_q) # 再構成画像
# 損失計算
perceptual = perceptual_loss(x, x_hat)
gan_loss = log(D(x)) + log(1 - D(x_hat))
codebook_loss = ||stopgrad(z_hat) - z_q||^2
commitment_loss = ||z_hat - stopgrad(z_q)||^2
# 総合損失
loss = perceptual + λ * gan_loss + codebook_loss + commitment_loss
update(E, G, Z, D)
# === ステージ2: Transformerの学習 ===
initialize(transformer)
for s in code_sequences: # VQGANで得たコードインデックス列
for i in range(len(s)):
pred = transformer(s[:i]) # 先行トークンから次を予測
loss = cross_entropy(pred, s[i])
update(transformer)
🔎 推論時の疑似コード
# 条件付き生成(任意のc: ラベルやセグマップなど)
context = encode_condition(c) if c else None
s = []
for i in range(sequence_length):
pred = transformer(s, context) # p(s_i | s_{<i}, c)
s_i = sample(pred) # top-k, nucleus samplingなど
s.append(s_i)
# 復元
z_q = codebook[s]
x_gen = G(z_q)
🧠 モデル例
・VQGAN(Encoder/Decoder)
Encoder:
Input: 画像 ℝ^{H×W×3}
↓ Conv2D
↓ m回の Residual Block + Downsampling
↓ Non-local Attention
↓ GroupNorm + Swish + Conv2D
Output: 潜在ベクトル ℝ^{h×w×nz}
Decoder:
Input: z_q ∈ ℝ^{h×w×nz}
↓ Conv2D
↓ m回の Residual Block + Upsampling
↓ Non-local Attention
↓ GroupNorm + Swish + Conv2D
Output: 再構成画像 ℝ^{H×W×3}
・Transformer(GPT2ベース)
Input: 離散インデックス列 s = [z_1, z_2, ..., z_N]
↓ Positional Embedding
↓ L層の Transformer Block(Self-Attention + FFN)
↓ Softmax による次インデックス予測
Output: p(z_i | z_{<i})
❓ よくある質問
Q. VQVAEとの違いは?
- VQVAEは単なるL2再構成損失に依存していたが、VQGANではGAN損失 + 知覚損失を導入。
- これにより、よりリアルで高精細な再構成画像が得られ、より意味的に圧縮されたコードブックが学習可能。
- 結果としてTransformerが長距離の意味的依存を捉えやすくなる。
Q. なぜTransformerで画像を直接扱わないの?
- 高解像度画像(例: 256×256)では、ピクセル列の長さが 65536 になり、Self-Attentionの計算が非現実的(O(N²))。
- VQGANでシーケンス長を 16×16 などに圧縮し、圧縮表現に対してのみTransformerを適用。
Q. どんな応用ができるの?
- クラス条件付き生成(ImageNetなど)
- セマンティック画像合成(セグマップ → 画像)
- Depth/Edge-to-Image
- Pose-guided人物画像生成
- 超解像(Super-Resolution)
- 高解像度画像(Megapixel)生成
ViT-VQGAN
論文名: VECTOR-QUANTIZED IMAGE MODELING WITH IMPROVED VQGAN
URL: https://arxiv.org/abs/2110.04627
発表日: 2022年6月(ICLR 2022 掲載)
✅ ポイント
- Vision Transformer(ViT)を用いた自己注意型のEncoder/Decoder構造を導入し、CNNベースのVQGANより高精度かつ高速な画像トークナイザを実現。
- エンコーダ出力
z_e ∈ ℝ^D
を低次元空間(ℝ^d)へ線形射影してコードブック検索する Factorized Codebook を提案。これによりコード使用率と再構成精度を大幅改善。 - ViT-VQGANのトレーニングでは、Logit-Laplace損失、L2損失、Perceptual損失、Adversarial損失を組み合わせて、再構成品質とコードブック学習を同時に最適化。
🧪 学習の擬似コード(Stage 1: ViT-VQGAN)
# 初期化
initialize(encoder_vit, decoder_vit, codebook, W_lookup)
for x in dataset:
z_e = encoder_vit(x) # (B, 1024, D)
z_proj = normalize(W_lookup(z_e)) # (B, 1024, d)
# コードブックも射影
code_proj = normalize(W_lookup(codebook)) # (K, d)
dist = ((z_proj.unsqueeze(2) - code_proj)**2).sum(-1) # (B, 1024, K)
index = dist.argmin(dim=-1) # 最近傍インデックス (B, 1024)
z_q = codebook[index] # 量子化後特徴 (B, 1024, D)
x_recon = decoder_vit(z_q)
# 損失
loss_vq = ((z_q.detach() - z_e)**2).mean() + β * ((z_e - z_q.detach())**2).mean()
loss_l2 = ((x - x_recon)**2).mean()
loss_laplace = torch.mean(torch.abs(x - x_recon)) # Laplace近似
loss_perc = perceptual_loss(x, x_recon)
loss_gan = gan_loss(x, x_recon)
loss = loss_vq + 0.1*loss_gan + 0.1*loss_perc + 0.1*loss_laplace + 1.0*loss_l2
update(loss)
🔎 推論時の擬似コード(ViT-VQGANによる画像復元)
# 推論時は z_e のままでも再構成可能
x = input_image()
z_e = encoder_vit(x)
z_proj = normalize(W_lookup(z_e))
code_proj = normalize(W_lookup(codebook))
dist = ((z_proj.unsqueeze(2) - code_proj)**2).sum(-1)
index = dist.argmin(dim=-1)
z_q = codebook[index]
x_recon = decoder_vit(z_q)
🧠 モデル構成例(ViT-VQGAN)
・Encoder(ViTベース)
Input (3×256×256) → Patchify (8×8) → Linear(192→D) → L層 ViT → MLP+tanh → 出力 z_e (1024, D)
・Decoder(ViTベース)
Input z_q (1024, D) → PosEmbed追加 → L層 ViT → MLP → Reshape → ConvTranspose → 画像復元 (3×256×256)
❓ よくある質問
Q. Factorized Codebook の「線形射影」は学習されるの?
- はい、W_lookup ∈ ℝ^{d×D} は学習可能パラメータで、バックプロパゲーションで更新されます。
- 同じ射影行列で
z_e
もcodebook
も低次元空間に投影し、そこで距離比較します。
Q. L2正規化はなぜ使うの?
- 射影後のベクトルを球面に正規化(L2 norm = 1)することで、距離計算がコサイン類似度と等価になり、学習が安定。
- 正規化しないとベクトルのスケールに依存して**コードブック使用率が偏る(=死んだコードが多発)**問題が起きやすい。
Q. Perceptual loss を使うと教師ありになるのでは?
- その通り。VGG特徴空間はImageNetで学習された分類器なので、完全な教師なし表現学習には使わない(評価時は除外)。
- ただし生成品質向上のためには有効なので、生成モデルの学習には用いる。
Q. どの損失が何に効いている?
損失項目 | 効果 |
---|---|
L_vq |
コードブック学習とコミットメント |
L_l2 |
低周波の構造維持、数値安定 |
L_laplace |
ノイズ耐性、L1に近くエッジ保持 |
L_perceptual |
高次特徴の整合性 |
L_adv |
視覚的にリアルな質感(局所的品質) |
Parti
論文名: Scaling Autoregressive Models for Content-Rich Text-to-Image Generation
URL: https://arxiv.org/abs/2206.10789
発表日: 2022-06-21
✅ ポイント
- テキストから画像を生成する問題を、Sequence-to-Sequence(seq2seq)モデリングとして定式化。
- 画像はViT-VQGANで離散トークンに変換し、Transformerでテキスト → 画像トークン列を生成。
- TransformerベースのEncoder-Decoder構成を採用(最大20Bパラメータ)。
- 評価指標であるFIDスコアで最先端性能(MS-COCO: Zero-shot 7.23 / Finetuned 3.22)を達成。
- 多様なカテゴリと難易度を含む新ベンチマーク「PartiPrompts (P2)」を提案(全1600プロンプト)。
- **Classifier-Free Guidance(CFG)とContrastive Reranking(CoCa)**により、テキストとの整合性を強化。
🧪 学習の疑似コード(2段階構成)
# Stage 1: ViT-VQGANのトレーニング
train_vit_vqgan(image_dataset)
# 得られる:image_tokenizer (encoder, decoder, codebook)
# Stage 2: テキスト → 画像トークンのseq2seqトレーニング
for (text, image) in paired_data:
text_tokens = tokenizer.encode(text)
image_tokens = image_tokenizer.encode(image) # 離散トークン列 (32x32 = 1024)
pred_tokens = encoder_decoder.transformer(text_tokens)
loss = cross_entropy(pred_tokens, image_tokens)
update(loss, encoder_decoder)
🔎 推論時の擬似コード(CFG + CoCa使用)
# CFGありの生成
def generate_image(prompt, model, lambda_cfg=1.2):
cond_embed = model.encode_text(prompt)
uncond_embed = model.encode_text("") # 無条件
image_tokens = []
for t in range(1024): # 32x32
logits_cond = model.decode(image_tokens, cond_embed)
logits_uncond = model.decode(image_tokens, uncond_embed)
logits = logits_uncond + lambda_cfg * (logits_cond - logits_uncond)
token = sample_from_logits(logits)
image_tokens.append(token)
image = image_tokenizer.decode(image_tokens)
return image
# CoCaで再ランキング
def rerank_images(prompt, image_list, coca_model):
prompt_embed = coca_model.encode_text(prompt)
scores = [(img, cosine_similarity(prompt_embed, coca_model.encode_image(img)))
for img in image_list]
return max(scores, key=lambda x: x[1])[0] # 最も一致する画像を返す
🧠 モデル例(20B構成)
・ViT-VQGAN(トークナイザー)
- 入出力: 256×256画像 → 32×32 の 8192種類トークン(合計1024)
- encoder: ViTベース
- decoder: transformer型デコーダを後から拡張して微調整(最大600Mパラメータ)
・Text-to-Image Transformer
モデル | Encoder層数 | Decoder層数 | 次元数 | 総パラメータ |
---|---|---|---|---|
Parti-20B | 16 | 64 | 4096 | 20B |
❓ よくある質問
Q. PartiはDiffusion系(DALL-E 2やImagen)と何が違うの?
- PartiはAutoregressive Transformerベースで、トークン列として画像を生成。
- Diffusion系はノイズ除去による画像再構成を反復する形式。
- PartiはTransformerのスケーリング技術(LLMの経験)をそのまま活かせる。
Q. PartiはなぜEncoder-Decoder型?GPT型(decoder-only)ではダメ?
- Decoder-only(GPT型)よりも、テキスト理解力が強化され、少パラメータでも高性能。
- 実験により、350M〜750Mパラメータ時点で明らかに優位。
Q. CFGとCoCaはどう使い分けるの?
- CFG:生成中に強制的にプロンプト整合性を高める。
- CoCa:複数生成後にもっともプロンプトに一致した画像を選ぶ。
Q. なぜ画像トークンを使うの?
- テキストと同じように扱えるため、Transformerのシーケンスモデリングの知見をそのまま使える。
- ViT-VQGANによるトークン化で圧縮・抽象化された表現が得られる。
Q. Partiは公開されてるの?
- モデル自体は未公開。ただし、**PartiPrompts (P2)**や高解像度生成例は 公式サイト で閲覧可能。
CM3Leon
論文名: Scaling Autoregressive Multi-Modal Models: Pretraining and Instruction Tuning
URL: https://arxiv.org/abs/2309.02591
発表日: 2023-09-05
✅ ポイント
- CM3Leon(カメレオン)は、テキストと画像を統一的なトークン系列で扱える自己回帰型マルチモーダルモデル。
- モデルは Decoder-only Transformer(GPT型)で構成され、トークン化されたテキストと画像を順番に自己回帰的に予測。
- 画像は VQトークナイザで8192語彙のcodebook ID(整数)列に変換され、モデルはそのIDを予測する(ベクトルではなく番号)。
- テキストと画像トークンは共通の語彙空間(例:text: 56K、image: 8K → 合計64K)にマッピングされ、softmax over 64Kでトークンを逐次予測。
- Pretrainingではretrieval(検索強化)とmasked infilling学習を組み合わせ、Finetuningでは指示付きのマルチタスク学習を行う。
🧪 Pretraining(Retrieval + CM3 Objective)擬似コード
# テキスト・画像のトークン化
caption = "A dog running in snow"
text_tokens = text_tokenizer(caption) # shape: (Lt,)
image_tokens = image_tokenizer(image) # shape: (Li=1024,)
# クエリ系列(text + image)
query_tokens = text_tokens + ["<break>"] + image_tokens + ["<break>"] # shape: (Lt + 1 + Li + 1,)
# Retrieval(CLIPベース)
query_vec = clip_encoder(caption, image) # shape: (512,)
retrieved = dense_retrieve(query_vec, memory_bank, top_k=2)
retrieved_tokens = flatten([
text_tokenizer(t) + ["<break>"] + image_tokenizer(img) + ["<break>"]
for t, img in retrieved
]) # shape: (Lr ≈ 2×(Lt+Li+2),)
# ランダムスパンをマスク
mask_start, mask_end = select_span(query_tokens)
masked_span = query_tokens[mask_start:mask_end]
masked_query = (
query_tokens[:mask_start] + ["<mask>"] +
query_tokens[mask_end:] + ["<infill>"] +
masked_span
)
# モデル入力(retrieved + masked_query)
input_tokens = retrieved_tokens + masked_query
logits = transformer(input_tokens[:-1]) # shape: (L-1, V=64K)
targets = input_tokens[1:] # shape: (L-1,)
loss = cross_entropy(logits, targets)
update(model, loss)
🧪 Finetuning(Instruction Tuning)擬似コード
# タスク例:画像編集
instruction = "Edit the image: Make the sky look like sunset"
instr_tokens = text_tokenizer(instruction) # shape: (L_instr,)
input_img_tokens = image_tokenizer(input_img) # shape: (1024,)
target_img_tokens = image_tokenizer(target_img) # shape: (1024,)
# 構成:[prefix] + <break> + [画像] + <break> + [出力画像]
input_tokens = (
instr_tokens + ["<break>"] +
input_img_tokens + ["<break>"] +
target_img_tokens
)
logits = transformer(input_tokens[:-1]) # shape: (2047, 64K)
targets = input_tokens[1:] # shape: (2047,)
loss = cross_entropy(logits, targets)
update(model, loss)
🔎 推論時の擬似コード(テキスト→画像)
prompt = text_tokenizer("A golden retriever in a forest") + ["<break>"]
generated_tokens = []
for _ in range(1024):
logits = transformer(prompt + generated_tokens)
probs = softmax(logits[-1]) # shape: (64K,)
token_id = sample_or_argmax(probs)
generated_tokens.append(token_id)
image = vq_decoder(generated_tokens)
🧠 モデル例(CM3Leon-7B)
項目 | 内容 |
---|---|
アーキテクチャ | Decoder-only Transformer |
層数 | 32 |
埋め込み次元 | 4096 |
語彙数 | 56,320 (text) + 8,192 (image) = 約64K |
入力系列長 | 最大 4096トークン(text + image) |
トークンタイプ | 全て整数ID(textもimageも) |
出力 | トークンIDのsoftmax分布 |
画像復元 | VQ decoder(Gafni et al. 2022)で復元 |
❓ よくある質問
Q. テキストと画像で語彙が違うのに、どうやってsoftmaxするの?
- 両方を単一の語彙空間にまとめる(text: 0〜56K、image: 56K〜64K)→ softmax対象は64K次元。
Q. Decoderは埋め込みベクトルを予測する?
- 予測しない。codebookのインデックス(ID)を予測し、Decoderがそれを画像に復元。
Q. / / の役割は?
-
<mask>
:マスク対象の場所 -
<infill>
:補完出力の開始点 -
<break>
:モダリティ(text/image)の切り替えを示すマーカー
Q. PretrainingとFinetuningの違いは?
項目 | Pretraining | Finetuning |
---|---|---|
構文 | <mask>...<infill> |
prefix + <break> + input + <break> + output |
学習目的 | in-filling(補完) | instruction理解 + 自己回帰出力 |
Retrieval | 使用(CLIPで検索) | 使用しない |
タスク形式 | 自己教師あり | 教師あり(指示付き) |
📦 CM3Leonのタスク例(SFTで使用)
タスク | 入力例(prefix) | 出力 |
---|---|---|
画像編集 |
"Edit the image: Add sunset sky" + image |
変換画像 |
キャプション生成 |
"Describe the image" + image |
テキスト |
VQA |
"Question: What is the dog doing?" + image |
"Running" |
ポーズ条件生成 |
"Generate from pose" + pose map |
人物画像 |
AnyGPT
論文名: AnyGPT: Unified Multimodal LLM with Discrete Sequence Modeling
URL: https://arxiv.org/abs/2402.12226
発表日: 2024-03-07(v3)
✅ ポイント
- 画像・音声・音楽・テキストのすべてのモダリティを離散トークン化し、統一的にLLMで処理可能に。
- モデルアーキテクチャや学習パラダイムを変更せずにマルチモーダル対応(LLaMA-2など既存LLMをそのまま利用)。
- 入力も出力も任意のモダリティ(Any-to-Any)を扱えるマルチモーダル汎用LLM。
- Instruction dataset(AnyInstruct-108k)を自動合成し、多ターン対話でマルチモーダル生成を学習。
- 音声生成(SoundStorm)、画像生成(Diffusion)、音楽生成(Encodec)などを分離構成で効率良く復元。
🧪 学習の疑似コード(モダリティ共通・離散トークン列でのNext Token Prediction)
# Step 1: すべてのモダリティをトークン化
tokens = [<sos>] + image_tokens + speech_tokens + text_tokens + music_tokens + [<eos>]
# Step 2: 自己回帰的に次のトークンを予測
for step in range(training_steps):
logits = LLM(tokens[:-1]) # 入力を1つ前まで
loss = cross_entropy(logits, tokens[1:]) # 次トークンを予測
loss.backward()
optimizer.step()
🔎 推論時の疑似コード(例:音声→画像)
# 入力音声をトークン化
speech_tokens = SpeechTokenizer.encode(audio_input)
prompt = [<sos>] + speech_tokens + ["Please generate an image."]
# 自己回帰的に画像トークンを生成
generated_tokens = autoregressive_sampling(LLM, prompt)
# 出力トークンから画像を復元
image_tokens = extract_image_tokens(generated_tokens)
output_image = ImageDetokenizer.decode(image_tokens)
🧠 モデル例(構成)
1. Tokenizer(すべて離散トークン列に変換)
モダリティ | Tokenizer | vocab | 特徴 |
---|---|---|---|
Image | SEED Tokenizer | 8192 | ViT + Q-Former + VQ |
Speech | SpeechTokenizer | 1024 | RVQ(8層、semantic + acoustic) |
Music | Encodec | 8192 (2048×4) | RVQ(4層、50Hz) |
2. LLM本体
- LLaMA-2(7B)をベース
- モダリティごとの語彙を追加 → Embedding行列と出力層を拡張
3. De-tokenizer
モダリティ | 復元方法 |
---|---|
Image | SEED latent → Diffusion Decoder(unCLIP) |
Speech | SoundStorm(acoustic token生成)→ Decoder |
Music | Encodec decoder |
❓ よくある質問(FAQ)
Q. なぜLLM本体を改変せずに済むの?
- 離散トークン列に変換することで、テキストと同じ形式に統一されるため。
- 語彙表とEmbeddingの拡張だけで対応可能。Transformer本体や学習目標はそのまま。
Q. モダリティごとにトークンが異なるのに統一処理できるの?
- トークン列には
<soi>
(start of image)や<som>
(start of music)などの特殊トークンを挿入。 - モダリティの境界が明確になり、Transformerが区別できるようになる。
Q. なぜ離散表現(VQ-VAE、RVQ)を使うの?
- モダリティごとの高周波・ノイズ成分を除去し、意味的な低周波情報だけを抽出できる。
- テキストと同じ形式で学習可能にし、モデルの安定性と効率性が向上する。
Q. CM3Leonとの違いは?
- CM3Leonは画像とテキストのみ、AnyGPTは音声・音楽も含む4モダリティ対応。