Matrixmultiplikation für die Vorwärtspropagation neuronaler Netze
Schreibe \(y = Wx + b\) von Hand, gib Matrizen unterschiedlicher Formen ein und beobachte, wie sich die Dimensionen ändern und zusammenpassen.
Nach Abschluss dieser Fallstudie wirst du verstehen:Wenn beim Training neuronaler Netze ein Fehler wegen nicht übereinstimmender Formen gemeldet wird, ist der Kern, dass die Dimensionen der Matrixmultiplikation nicht zusammenpassen.
Einführung in den Alltag
Express-Sortierzentrum
Eine Charge Pakete kommt vom Förderband (Eingabe), wird von Sortiermaschinen auf verschiedene Lastwagen umgeleitet (versteckte Schicht) und schließlich an verschiedene Wohnsiedlungen geliefert (Ausgabe).
Jede Sortiermaschine ist ein "Neuron" — sie empfängt alle Pakete und entscheidet nach Regeln, welche Pakete ihren eigenen Weg nehmen. Die Regeln der Sortiermaschine sind die "Gewichte": mehr Pakete in die eine Richtung verteilen und weniger in die andere.
Die Funktionsweise des gesamten Sortierzentrums ist:Matrixmultiplikation + Aktivierungsfunktion + Bias。
Intuitive Vorstellung
Eine Schicht eines neuronalen Netzes = drei Schritte, in einer Codezeile erledigt:
\( z = X @ W + b \)
Jeder Teil hat eine klare Rolle:
(batch, in)
(in, out)
(out,)
(batch, out)
Die Anzahl der Eingabemerkmale (in_features) muss gleich der Zeilenanzahl der Gewichtsmatrix sein. Die Anzahl der Ausgabemerkmale (out_features) ist gleich der Spaltenanzahl der Gewichtsmatrix. batch_size bleibt während des gesamten Prozesses unverändert.
Mathematische Definition
Einschichtige Vorwärtspropagation
\[ z_j = \sum_{i=1}^{n} x_i \cdot W_{ij} + b_j \]Jede Ausgabe \(z_j\) ist das Skalarprodukt des Eingabevektors mit der \(j\)-ten Spalte der Gewichtsmatrix plus dem Bias.
Zweischichtige Netzwerk-Kaskade
\[ \begin{aligned} h &= \text{ReLU}(X W_1 + b_1) \quad & (m \times 4) \cdot (4 \times 6) &= (m \times 6) \\ o &= \text{Sigmoid}(h W_2 + b_2) \quad & (m \times 6) \cdot (6 \times 2) &= (m \times 2) \end{aligned} \]Hinweis: Die Anzahl der Ausgabemerkmale (6) der ersten Schicht muss gleich der Zeilenanzahl (6) der Gewichtsmatrix der zweiten Schicht sein. Dies ist die "dimensionale Verbindung zwischen Schichten".
Python-Praxisübung
Verwenden Sie eine generische dense_layer-Funktion, um drei Szenarien zu demonstrieren: einzelne Stichprobe, Batch-Verarbeitung und zweischichtiges Netzwerk.
Beispiel
def dense_layer(X, W, b, activation=None):
"""
Vorwärtspropagation einer einzelnen vollständig verbundenen Schicht
X: Eingabematrix (batch_size, in_features)
W: Gewichtsmatrix (in_features, out_features)
b: Bias-Vektor (out_features,)
"""
z = X @ W + b # Matrixmultiplikation + Broadcast-Addition
if activation == "relu":
return np.maximum(0, z) # ReLU: negative Werte werden zu 0
if activation == "sigmoid":
return 1 / (1 + np.exp(-z)) # Sigmoid: staucht auf (0,1)
return z
print("=" * 55)
print("BEISPIEL Szenario 1: einzelne Stichprobe, 4 Dimensionen -> 3 Dimensionen")
print("=" * 55)
X = np.array([[1.0, 2.0, 0.5, -1.0]]) # (1, 4)
W = np.random.randn(4, 3) * 0.5 # (4, 3)
b = np.zeros(3) # (3,)
y = dense_layer(X, W, b, activation="relu")
print("X.shape:", X.shape, " W.shape:", W.shape,
" -> y.shape:", y.shape)
print("\n" + "=" * 55)
print("BEISPIEL Szenario 2: Batch von 8 Stichproben, gleiche Gewichte")
print("=" * 55)
X_batch = np.random.randn(8, 4) # (8, 4)
y_batch = dense_layer(X_batch, W, b, activation="relu")
print("X_batch.shape:", X_batch.shape,
" -> y_batch.shape:", y_batch.shape)
print("Batch-Größe 8 unverändert, Merkmalsdimension 4 -> 3")
print("\n" + "=" * 55)
print("BEISPIEL Szenario 3: Stapeln eines zweischichtigen Netzwerks")
print("=" * 55)
# Erste Schicht: 4 Dimensionen -> 6 Dimensionen + ReLU
W1 = np.random.randn(4, 6) * 0.5
b1 = np.zeros(6)
# Zweite Schicht: 6 Dimensionen -> 2 Dimensionen + Sigmoid
W2 = np.random.randn(6, 2) * 0.5
b2 = np.zeros(2)
h = dense_layer(X_batch, W1, b1, activation="relu")
out = dense_layer(h, W2, b2, activation="sigmoid")
print(f"Eingabeschicht: {X_batch.shape}")
print(f"Versteckte Schicht: {h.shape} (4->6, W1=(4,6))")
print(f"Ausgabeschicht: {out.shape} (6->2, W2=(6,2))")
# Überprüfung: Was passiert, wenn die Dimensionen nicht übereinstimmen?
print("\n" + "=" * 55)
print("BEISPIEL Szenario 4: Dimensionskonflikt verursacht einen Fehler")
print("=" * 55)
try:
W_wrong = np.random.randn(5, 3) * 0.5 # Anzahl der Zeilen ist falsch
dense_layer(X_batch, W_wrong, b)
except ValueError as e:
print("Fehlergrund: X_batch hat 4 Spalten,"
"W_wrong hat 5 Zeilen -> kann nicht multipliziert werden")
print(f" Richtiger Ansatz: Die Anzahl der Zeilen von W ({W_wrong.shape[0]})"
f" sollte gleich der Anzahl der Spalten von X ({X_batch.shape[1]}) sein")
======================================================= EXAMPLE 场景 1:单样本,4 维 -> 3 维 ======================================================= X.shape: (1, 4) W.shape: (4, 3) -> y.shape: (1, 3) ======================================================= EXAMPLE 场景 2:8 个样本批量,同权重 ======================================================= X_batch.shape: (8, 4) -> y_batch.shape: (8, 3) 批大小 8 不变,特征维度 4 -> 3 ======================================================= EXAMPLE 场景 3:堆叠两层网络 ======================================================= 输入层: (8, 4) 隐藏层: (8, 6) (4->6, W1=(4,6)) 输出层: (8, 2) (6->2, W2=(6,2)) ======================================================= EXAMPLE 场景 4:维度不匹配会报错 ======================================================= 报错原因:X_batch 有 4 列,W_wrong 有 5 行 -> 无法相乘 正确做法:W 的行数 (5) 应等于 X 的列数 (4)
Anwendungsszenarien in der KI
| KI-Szenario | Beispiel für Dimensionsänderung | Beschreibung |
|---|---|---|
| Vollständig verbundene Schicht | (32, 784) @ (784, 128) = (32, 128) | MNIST-Klassifikation: 28x28=784 Pixel → 128-dimensionale Merkmale |
| QKV des Transformers | (B, L, D) @ (D, D) = (B, L, D) | Self-Attention: Eingabe- und Ausgabe-Token-Dimensionen bleiben unverändert |
| Embedding-Lookup-Tabelle | (B, L) → (B, L, D) | Wort-ID-Sequenz → Wortvektor-Sequenz |
| Klassifikationskopf | (B, 768) @ (768, 10) = (B, 10) | Klassifikationsausgabe der letzten Schicht von BERT |