👻

第2回:Self-Attentionを理解する – 数式と直感的解説

に公開

Transformer入門シリーズ

このブログはシリーズでお届けします。
第1回:Transformerとは何か? Attention Is All You Needについて調べてみた
第2回:Self-Attentionを理解する – 数式と直感的解説(←いまここ)
第3回:Encoderの実装を追いながら理解を深めよう(予定)
第4回:Decoderの仕組みと出力の生成(予定)
第5回:学習と推論 – BLEUスコアで精度を評価(予定)
第6回:最新応用と研究動向まとめ(予定)

はじめに

Transformer の核となる技術が「Self-Attention(自己注意機構)」です。この仕組みこそが、Transformerを従来のRNNなどの手法から一線を画す存在に押し上げました。
この記事では、Self-Attention の概念を直感的に掴みながら、必要な数式もやさしく解説していきます。

Self-Attention とは?

Self-Attention とは、「入力された文章の中で、ある単語が他の単語にどれくらい注意を向けるべきか?」を計算する仕組みです。
たとえば、次の英文を考えてみましょう。

The animal didn't cross the street because it was too tired.

このとき "it" が指しているのは "the animal" です。このような文脈的な関係を掴むために、"it" は "animal" に注意を向ける必要があります。これが 注意(Attention) のイメージです。

直感的な流れ

Self-Attention の内部処理をざっくりまとめると以下のようになります。

  1. 各単語をベクトル(数値のリスト)に変換
  2. 各単語について、「自分:Query他の単語:Keyにどれくらい注目すべきか?」をスコア化
  3. スコアをもとに、他の単語の情報(Value)を重み付けして合成
  4. 合成された結果が、新しい単語表現(文脈付きベクトル)になる

数式で見る Self-Attention

Self-Attention の本質は、Query(Q)、Key(K)、Value(V)の3つのベクトルを使って処理を行うことです。

1. 入力のベクトル化

各単語 x_i に対して、それぞれ以下のように3つの行列を使って変換します。

Q = XW^Q,\quad K = XW^K,\quad V = XW^V

ここで、

  • Xは入力(例:単語埋め込みベクトルの行列)
  • W^Q, W^K, W^V は学習される重み行列

2. Attention スコアの計算

QueryとKeyの内積をとることで、関連度スコア(生の注意度)を求めます。

\text{Attention Score}_{i,j} = Q_i \cdot K_j^T

これを正規化し、スケーリングも加えると、

\text{Attention}(Q, K, V) = \text{softmax}\left( \frac{QK^T}{\sqrt{d_k}} \right)V
  • d_k はKeyの次元数(スケーリングのため)
  • softmax はスコアを0~1に正規化して「重み」に変換します
  • 最終的に、Valueベクトルをこれらの重みで合成して出力を生成します

3. 1単語だけでなく、全単語を一括処理

上記処理はすべての単語間で行われるため、行列演算により高速に処理できます。

  • 入力が n 個の単語なら、n \times n の Attention 行列ができます
  • 各行が「ある単語が、他のどの単語に注意を向けるべきか」を示す重みの集合です

これが「Self-Attention(自己注意)」と呼ばれる理由です。1つの文の中で、自己(self)同士が注意を向け合うためです。

コードで見る Self-Attention

最小実装コード

以下に、Transformer的なSelf-Attentionのコア部分のみを抜き出して実装したコードを示します。

import torch
import torch.nn as nn
import torch.nn.functional as F

class SelfAttention(nn.Module):
    def __init__(self, embed_dim):
        super(SelfAttention, self).__init__()
        self.embed_dim = embed_dim

        # Q, K, V用の線形変換子
        self.W_q = nn.Linear(embed_dim, embed_dim)
        self.W_k = nn.Linear(embed_dim, embed_dim)
        self.W_v = nn.Linear(embed_dim, embed_dim)

    def forward(self, x):
        # x: (batch_size, seq_len, embed_dim)
        Q = self.W_q(x)  # (B, T, D)
        K = self.W_k(x)  # (B, T, D)
        V = self.W_v(x)  # (B, T, D)

        # Attentionスコア = QK^T / √d_k
        scores = torch.matmul(Q, K.transpose(-2, -1)) / self.embed_dim**0.5  # (B, T, T)
        attn_weights = F.softmax(scores, dim=-1)  # (B, T, T)

        # 加重平均
        output = torch.matmul(attn_weights, V)  # (B, T, D)
        return output, attn_weights

実行例

embed_dim = 8
seq_len = 4
batch_size = 2

x = torch.rand(batch_size, seq_len, embed_dim)
sa = SelfAttention(embed_dim)
output, attn = sa(x)

print("出力ベクトルの形状:", output.shape)  # -> (2, 4, 8)
print("Attention重み:", attn.shape)        # -> (2, 4, 4)

ポイント解説

1. Q, K, V を nn.Linear で作る理由

  • 重み行列 W^Q, W^K, W^V は学習可能で、意味的に異なる空間への変換を表現
  • この変換により「どこに注目すべきか?」の空間を制御できる

2. 転置 K.transpose(-2, -1) の意味

  • Q: (B, T, D)、K: (B, T, D) のままでは内積が取れない
  • Kを転置して (B, D, T) にすることで、Q × K^T → (B, T, T) のスコア行列が得られる

3. softmaxの軸

  • dim=-1 は「Key軸」。つまり、あるQueryがどのKeyにどれだけ注目するかを示す確率分布

Multi-Head Attention の意義

実際のTransformerでは、Self-Attentionを1回だけではなく、複数回(Head)並列で行います
それが Multi-Head Attention です。

  • 各Headは異なる重み(W^Q_h, W^K_h, W^V_h)で学習される
  • 複数の視点(例:構文的関係、意味的類似、時系列順序など)を同時に捉える
  • 最終的にHeadの出力を結合して、全体の特徴ベクトルに統合します

Self-Attention の強み

  • 長距離依存の獲得:冒頭と末尾の単語も自在に関連付けられる
  • 並列計算が可能:RNNのような逐次処理ではないため、高速学習が可能
  • スケーラブル:Attentionだけで完結するため、画像や音声にも適用しやすい

まとめ

Self-Attentionは、Transformer の心臓部です。シンプルな行列演算で、文章中のどの単語が他のどの単語にどれだけ注目すべきかを学習します。これにより、従来モデルでは難しかった「長い文脈の理解」「並列化」「柔軟な構造理解」が実現できるようになりました。

次回は、このSelf-Attentionが実際にどのように Encoder や Decoder の中に組み込まれているのか、実装コードとともに解説していきます!

Discussion