Zurück zur Startseite

Self-Attention und Multi-Head in Transformers

Der Artikel zerlegt Attention-Mechanismen in Transformers auf: Self-Attention für intra-sequenzielle Verbindungen, Cross-Attention für seq2seq-Aufgaben, Multi-Head für multidimensionale Analyse. Mathematische Formeln, numerisches Beispiel und PyTorch-Implementierungen für Middle-/Senior-Entwickler.

Self-Attention, Cross- und Multi-Head: vollständiger Leitfaden
Advertisement 728x90

# Attention-Mechanismen in Transformern: Self-Attention, Cross-Attention und Multi-Head

Attention-Mechanismen ermöglichen es KI-Modellen, die Wichtigkeit von Elementen in einer Eingabesequenz dynamisch zu gewichten. Für jedes Token werden Gewichte mithilfe von Query-, Key- und Value-Matrizen berechnet, wodurch kontextualisierte Repräsentationen entstehen. Self-Attention untersucht Verbindungen innerhalb einer einzigen Sequenz, Cross-Attention verknüpft Encoder und Decoder, und Multi-Head-Attention parallelisiert den Prozess, um verschiedene Datenaspekte zu erfassen.

Theorie zu Self-Attention

Die Eingabesequenz wird durch die Matrix $X \in \mathbb{R}^{n \times d}$ dargestellt, wobei $n$ die Anzahl der Tokens und $d$ die Embedding-Dimension ist. Aus $X$ werden die Matrizen $Q = XW^Q$, $K = XW^K$, $V = XW^V$ projiziert, mit Gewichten $W^{Q,K,V} \in \mathbb{R}^{d \times d_k}$.

Die Attention-Scores-Matrix $A_{ij} = \frac{Q_i K_j^T}{\sqrt{d_k}}$ misst die Relevanz des $j$-ten Tokens für das $i$-te. Die Skalierung mit $\sqrt{d_k}$ stabilisiert die Gradienten und verhindert vanishing gradients im Softmax.

Google AdInline article slot

Die normalisierte Matrix $N = \mathrm{softmax}(A)$ über die Zeilen liefert eine Wahrscheinlichkeitsverteilung. Ausgabe: $\mathrm{Attention}(Q, K, V) = N V$.

Beispiel für Self-Attention

Betrachten Sie den Satz „Karina geht in den Laden“, tokenisiert als ["karina", "geht", "in", "den", "laden"] mit $n=5$. Embeddings (vereinfacht):

| Token | Embedding |

Google AdInline article slot

|--------|-----------|

| karina | [1, 0] |

| geht | [0, 1] |

Google AdInline article slot

| in | [1, 1] |

| den | [1, 1] |

| laden | [0.5, 1] |

Matrix $X_{5\times2} = \begin{bmatrix} 1 & 0 \\ 0 & 1 \\ 1 & 1 \\ 1 & 1 \\ 0.5 & 1 \end{bmatrix}$. Zur Vereinfachung $W^Q = W^K = W^V = I$, also $Q=K=V=X$.

$QK^T = \begin{bmatrix} 1 & 0 & 1 & 1 & 0.5 \\ 0 & 1 & 1 & 1 & 1 \\ 1 & 1 & 2 & 2 & 1.5 \\ 1 & 1 & 2 & 2 & 1.5 \\ 0.5 & 1 & 1.5 & 1.5 & 1.25 \end{bmatrix}$, geteilt durch $\sqrt{2} \approx 1.41$:

$A \approx \begin{bmatrix} 0.71 & 0 & 0.71 & 0.71 & 0.35 \\ 0 & 0.71 & 0.71 & 0.71 & 0.71 \\ 0.71 & 0.71 & 1.42 & 1.42 & 1.06 \\ 0.71 & 0.71 & 1.42 & 1.42 & 1.06 \\ 0.35 & 0.71 & 1.06 & 1.06 & 0.89 \end{bmatrix}$.

Nach Softmax pro Zeile: $N \approx \begin{bmatrix} 0.25 & 0.15 & 0.25 & 0.25 & 0.10 \\ 0.17 & 0.23 & 0.23 & 0.23 & 0.23 \\ 0.15 & 0.15 & 0.30 & 0.30 & 0.10 \\ 0.15 & 0.15 & 0.30 & 0.30 & 0.10 \\ 0.14 & 0.23 & 0.28 & 0.28 & 0.07 \end{bmatrix}$.

Ausgabe $N \times V = \begin{bmatrix} 0.73 & 0.68 \\ 0.57 & 0.87 \\ 0.68 & 0.81 \\ 0.68 & 0.81 \\ 0.63 & 0.84 \end{bmatrix}$. Die neuen Embeddings integrieren nun Kontext: Der Vektor für „geht“ spiegelt menschliche Bewegung wider, nicht nur eine abstrakte Zeitform.

import torch  
import torch.nn as nn  

class SelfAttention(nn.Module):
    def __init__(self, embed_dim):
        super().__init__()
        self.embed_dim = embed_dim
        self.q_linear = nn.Linear(embed_dim, embed_dim)
        self.k_linear = nn.Linear(embed_dim, embed_dim)
        self.v_linear = nn.Linear(embed_dim, embed_dim)
        self.scale = embed_dim ** 0.5

    def forward(self, x):
        Q = self.q_linear(x)
        K = self.k_linear(x)
        V = self.v_linear(x)
        scores = torch.bmm(Q, K.transpose(1, 2)) / self.scale
        attn = torch.softmax(scores, dim=-1)
        out = torch.bmm(attn, V)
        return out

Cross-Attention: Verknüpfung von Sequenzen

Bei Cross-Attention stammt die Query aus dem Decoder ($Q = X_{dec}W^Q$), während Key und Value aus dem Encoder kommen ($K = X_{enc}W^K$, $V = X_{enc}W^V$). Die Berechnung ist identisch: $\mathrm{Attention}(Q, K, V) = \mathrm{softmax}\left( \frac{QK^T}{\sqrt{d_k}} \right) V$.

Wichtige Unterschiede:

| Komponente | Self-Attention | Cross-Attention |

|------------|----------------|------------------|

| Q | X | X_dec |

| K | X | X_enc |

| V | X | X_enc |

Dadurch kann der Decoder sich auf relevante Teile des Encoder-Kontexts konzentrieren.

import torch  
import torch.nn as nn  

class CrossAttention(nn.Module):
    def __init__(self, embed_dim):
        super().__init__()
        self.embed_dim = embed_dim
        self.q_linear = nn.Linear(embed_dim, embed_dim)
        self.k_linear = nn.Linear(embed_dim, embed_dim)
        self.v_linear = nn.Linear(embed_dim, embed_dim)
        self.scale = embed_dim ** 0.5

    def forward(self, x_dec, x_enc):
        Q = self.q_linear(x_dec)
        K = self.k_linear(x_enc)
        V = self.v_linear(x_enc)
        scores = torch.bmm(Q, K.transpose(1, 2)) / self.scale
        attn = torch.softmax(scores, dim=-1)
        out = torch.bmm(attn, V)
        return out

Multi-Head-Attention: Parallele Perspektiven

Multi-Head-Attention führt $h$ unabhängige Attention-Berechnungen mit Projektionen $Q^{(i)} = X W_Q^{(i)}$, $K^{(i)} = X W_K^{(i)}$, $V^{(i)} = X W_V^{(i)}$ durch. Jeder Head: $\mathrm{head}_i = \mathrm{Attention}(Q^{(i)}, K^{(i)}, V^{(i)})$.

Die Ergebnisse werden verkettet und projiziert: $\mathrm{MultiHead}(X) = \mathrm{Concat}(head_1, \dots, head_h) W^O$. Head-Größe $d_k = d / h$, wobei $d$ die embed_dim ist.

import torch
import torch.nn as nn

class MultiHeadSelfAttention(nn.Module):  
    def __init__(self, embed_dim, num_heads):
        super().__init__()
        assert embed_dim % num_heads == 0
        self.embed_dim = embed_dim
        self.num_heads = num_heads
        self.head_dim = embed_dim // num_heads
        self.q = nn.Linear(embed_dim, embed_dim)
        self.k = nn.Linear(embed_dim, embed_dim)
        self.v = nn.Linear(embed_dim, embed_dim)
        self.out = nn.Linear(embed_dim, embed_dim)

    def forward(self, x):
        B, T, D = x.shape
        Q = self.q(x).view(B, T, self.num_heads, self.head_dim).transpose(1, 2)
        K = self.k(x).view(B, T, self.num_heads, self.head_dim).transpose(1, 2)
        V = self.v(x).view(B, T, self.num_heads, self.head_dim).transpose(1, 2)
        scores = torch.matmul(Q, K.transpose(-2, -1)) / (self.head_dim ** 0.5)
        attn = torch.softmax(scores, dim=-1)
        out = torch.matmul(attn, V).transpose(1, 2).contiguous().view(B, T, D)
        return self.out(out)

Wichtige Erkenntnisse

  • Self-Attention kontextualisiert Tokens: Jeder Ausgabvektor hängt von der gesamten Sequenz ab.
  • Skalierung mit $\sqrt{d_k}$ ist essenziell: Sie stabilisiert Softmax und Gradienten.
  • Cross-Attention für Seq2Seq: Verknüpft Encoder und Decoder in Transformern.
  • Multi-Head erfasst vielfältige Aspekte: Grammatik, Semantik, Fernabhängigkeiten.
  • PyTorch-Implementierungen nutzen Linear und bmm: Für Batch-Effizienz.

— Editorial Team

Advertisement 728x90

Weiterlesen