Entwicklung & Code
Künstliche Neuronale Netze im Überblick 11: Implementierung eines Transformers
Neuronale Netze sind der Motor vieler Anwendungen in KI und GenAI. Diese Artikelserie gibt einen Einblick in die einzelnen Elemente. Der elfte und letzte Teil implementiert einen Transformer.

Prof. Dr. Michael Stal arbeitet seit 1991 bei Siemens Technology. Seine Forschungsschwerpunkte umfassen Softwarearchitekturen für große komplexe Systeme (Verteilte Systeme, Cloud Computing, IIoT), Eingebettte Systeme und Künstliche Intelligenz.
Er berät Geschäftsbereiche in Softwarearchitekturfragen und ist für die Architekturausbildung der Senior-Software-Architekten bei Siemens verantwortlich.
Der Transformer ist eine neuronale Architektur, die vollständig auf Aufmerksamkeitsmechanismen basiert und ohne Rekursion und Faltung auskommt, um Sequenzen parallel zu verarbeiten. Seine wichtigste Innovation ist die skalierte Skalarprodukt-Attention-Unterschicht, die gleichzeitig die Beziehungen zwischen allen Positionen in der Eingabe berechnet. Ein Transformer-Encoder stapelt mehrere Schichten von Multi-Head-Self-Attention- und positionsbezogenen Feed-Forward-Netzwerken, die jeweils in Restverbindungen und Schichtnormalisierung eingebettet sind. Der Decoder fügt maskierte Selbstaufmerksamkeit und Encoder-Decoder-Aufmerksamkeit hinzu, um eine autoregressive Generierung zu ermöglichen.
Wir beginnen mit der Formalisierung der skalierten Skalarprodukt-Aufmerksamkeit. Bei gegebenen Abfrage-, Schlüssel- und Wertematrizen Q, K und V mit den Formen (batch_size, num_heads, seq_len, d_k) berechnen wir Rohwerte, indem wir das Punktprodukt von Q mit der Transponierten von K bilden. Anschließend skalieren wir diese Werte mit √d_k, um zu verhindern, dass Extremwerte zu verschwindenden Gradienten führen, wenden Softmax an, um Aufmerksamkeitsgewichte zu erhalten, und multiplizieren mit V, um die beachtete Ausgabe zu erhalten:
Attention(Q, K, V) = softmax( (Q · Kᵀ) / √d_k ) · V
In PyTorch lässt sich die wie folgt umsetzen:
import torch
import torch.nn.functional as F
def scaled_dot_product_attention(Q, K, V, mask=None):
"""
Berechnet die skalierte Skalarprodukt-Aufmerksamkeit.
Q, K, V haben die Form (batch_size, num_heads, seq_len, d_k).
Mask, falls angegeben, wird zu den Bewertungen hinzugefügt, um die Aufmerksamkeit auf bestimmte Positionen zu verhindern.
"""
d_k = Q.size(-1)
# Berechne die rohen Aufmerksamkeitswerte.
scores = torch.matmul(Q, K.transpose(-2, -1)) / torch.sqrt(torch.tensor(d_k, dtype=torch.float32))
# Wende die Maske an (z. B. um zu verhindern, dass nachfolgende Token im Decoder Beachtung finden).
if mask is not None:
scores = scores + mask
# Normalisieren, um Aufmerksamkeitsgewichte zu erhalten.
attn_weights = F.softmax(scores, dim=-1)
# Berechne die gewichtete Summe der Werte.
output = torch.matmul(attn_weights, V)
return output, attn_weights
In dieser Funktion extrahieren wir die Dimension d_k aus Q, berechnen die Skalarprodukte, skalieren sie und fügen optional vor dem Softmax eine Maske hinzu. Die Maske enthält große negative Werte (−∞) an unzulässigen Positionen, sodass diese Positionen nach dem Softmax das Gewicht Null erhalten.
Multi-Head-Attention erweitert diesen Ansatz, indem es dem Modell ermöglicht, Informationen aus mehreren Darstellungsunterräumen gemeinsam zu berücksichtigen. Zunächst projizieren wir den Eingabetensor X der Form (batch_size, seq_len, d_model) mithilfe gelernter linearer Schichten auf Abfragen, Schlüssel und Werte. Anschließend teilen wir jede dieser Projektionen entlang der Merkmalsdimension in num_heads separate Köpfe auf, wenden die Skalarprodukt-Aufmerksamkeit parallel auf jeden Kopf an, verknüpfen die Ergebnisse und projizieren sie zurück auf das ursprüngliche d_model:
import torch
import torch.nn as nn
class MultiHeadAttention(nn.Module):
def __init__(self, d_model, num_heads):
super(MultiHeadAttention, self).__init__()
assert d_model % num_heads == 0, "d_model muss durch num_heads teilbar sein"
self.num_heads = num_heads
self.d_k = d_model // num_heads
# Lineare Projektionen für Abfragen, Schlüssel, Werte und die endgültige Ausgabe.
self.W_q = nn.Linear(d_model, d_model)
self.W_k = nn.Linear(d_model, d_model)
self.W_v = nn.Linear(d_model, d_model)
self.W_o = nn.Linear(d_model, d_model)
def forward(self, X, mask=None):
batch_size, seq_len, _ = X.size()
# Projektionen der Eingaben auf Q, K, V.
Q = self.W_q(X)
K = self.W_k(X)
V = self.W_v(X)
# Umformen und transponieren, um Köpfe zu trennen.
Q = Q.view(batch_size, seq_len, self.num_heads, self.d_k).transpose(1, 2)
K = K.view(batch_size, seq_len, self.num_heads, self.d_k).transpose(1, 2)
V = V.view(batch_size, seq_len, self.num_heads, self.d_k).transpose(1, 2)
# Skalarprodukt-Aufmerksamkeit anwenden.
attn_output, _ = scaled_dot_product_attention(Q, K, V, mask)
# Köpfe verknüpfen und zurück auf d_model projizieren.
concat = attn_output.transpose(1, 2).contiguous().view(batch_size, seq_len, -1)
output = self.W_o(concat)
return output
Da der Transformer keine integrierte Vorstellung von Reihenfolge hat, werden den Token-Einbettungen Positionscodierungen hinzugefügt, um dem Modell Informationen über die Position jedes Elements in der Sequenz zu liefern. Der ursprüngliche Transformer verwendet sinusförmige Codierungen, die wie folgt definiert sind:
P[pos, 2i ] = sin( pos / (10000^(2i/d_model)) )
P[pos, 2i+1 ] = cos( pos / (10000^(2i/d_model)) )
für pos in [0, L−1] und i in [0, d_model/2−1].
Wir implementieren dies wie folgt:
import torch
import math
def get_sinusoidal_positional_encoding(L, d_model):
# Erstelle einen Tensor der Form (L, d_model).
P = torch.zeros(L, d_model)
position = torch.arange(0, L).unsqueeze(1).float()
div_term = torch.exp(torch.arange(0, d_model, 2).float() * -(math.log(10000.0) / d_model))
# Sinus auf gerade Indizes anwenden
P[:, 0::2] = torch.sin(position * div_term)
# Cosinus auf ungerade Indizes anwenden.
P[:, 1::2] = torch.cos(position * div_term)
return P
Jede Encoder-Schicht besteht aus einer Selbstaufmerksamkeits-Unterschicht, gefolgt von einem positionsbezogenen Feedforward-Netzwerk. Beide Unterschichten sind in Restverbindungen eingeschlossen. Darauf folgen eine Schichtnormalisierung und ein Dropout. Das Feedforward-Netzwerk hat die Form:
FFN(x) = ReLU(x·W₁ + b₁)·W₂ + b₂
und wird unabhängig auf jede Position angewendet. Wir erstellen eine Encoder-Schicht in PyTorch:
class TransformerEncoderLayer(nn.Module):
def __init__(self, d_model, num_heads, d_ff, dropout=0.1):
super(TransformerEncoderLayer, self).__init__()
self.self_attn = MultiHeadAttention(d_model, num_heads)
self.ffn = nn.Sequential(
nn.Linear(d_model, d_ff),
nn.ReLU(),
nn.Linear(d_ff, d_model),
)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.dropout1 = nn.Dropout(dropout)
self.dropout2 = nn.Dropout(dropout)
def forward(self, x, mask=None):
# Selbstaufmerksamkeit mit Restverbindung und Normalisierung.
attn_out = self.self_attn(x, mask)
x = x + self.dropout1(attn_out)
x = self.norm1(x)
# Feed-Forward mit Restverbindung und Normalisierung.
ffn_out = self.ffn(x)
x = x + self.dropout2(ffn_out)
x = self.norm2(x)
return x
Um den vollständigen Encoder zu erstellen, stapeln wir N solcher Schichten und wenden Positionscodierungen auf die Eingabe an:
class TransformerEncoder(nn.Module):
def __init__(self, num_layers, d_model, num_heads, d_ff, dropout):
super(TransformerEncoder, self).__init__()
self.pos_encoder = get_sinusoidal_positional_encoding
self.layers = nn.ModuleList([
TransformerEncoderLayer(d_model, num_heads, d_ff, dropout)
for _ in range(num_layers)
])
self.norm = nn.LayerNorm(d_model)
def forward(self, src, src_mask=None):
# src: (batch_size, seq_len, d_model)
seq_len = src.size(1)
# Positionskodierung hinzufügen.
pos_enc = self.pos_encoder(seq_len, src.size(2)).to(src.device)
x = src + pos_enc.unsqueeze(0)
# Durchlaufen jeder Encoder-Schicht.
for layer in self.layers:
x = layer(x, src_mask)
return self.norm(x)
Die Implementierung eines Decoders folgt dem gleichen Muster, umfasst jedoch eine maskierte Selbstaufmerksamkeits-Unterschicht, um die Berücksichtigung nachfolgender Positionen zu verhindern, sowie eine Encoder-Decoder-Aufmerksamkeits-Unterschicht, die die Ausgabe des Encoders berücksichtigt. Eine abschließende lineare Softmax-Schicht ordnet die Decoder-Ausgabe den Wahrscheinlichkeiten im Zielvokabular zu.
Durch die Codierung jeder Komponente – von der Skalarprodukt-Aufmerksamkeit über Multi-Head-Aufmerksamkeit, Positionskodierung, Feed-Forward-Netzwerke bis hin zu Encoder-Schichten – erhalten Sie Einblicke in den Informationsfluss durch den Transformer. Auf dieser Grundlage lässt sich das Modell leicht an Aufgaben wie maschinelle Übersetzung, Textzusammenfassung oder sogar Bildgenerierung anpassen oder erweitern.
Vollständiger Encoder-Decoder-Transformer
Um Sequenz-zu-Sequenz-Aufgaben wie maschinelle Übersetzung oder Zusammenfassung durchzuführen, müssen wir den Encoder zu einem gepaarten Decoder erweitern, der jeweils ein Token generiert und dabei die Ausgabe des Encoders berücksichtigt. Ein vollständiger Transformer umfasst somit Token-Einbettungen, Positionskodierungen, einen Stapel von Encoder-Schichten, einen Stapel von Decoder-Schichten und eine abschließende lineare Projektion in das Zielvokabular.
Nachfolgend finden Sie eine schrittweise Implementierung in PyTorch, wobei zu jeder Zeile eine Erläuterung erfolgt.
import math
import torch
import torch.nn as nn
import torch.nn.functional as F
def scaled_dot_product_attention(Q, K, V, mask=None):
"""
Berechnet die skalierte Skalarprodukt-Aufmerksamkeit.
Q, K, V sind Form (batch_size, num_heads, seq_len, d_k).
Mask, falls angegeben, enthält -inf unzulässige Positionen.
"""
d_k = Q.size(-1)
# Berechne die rohen Aufmerksamkeitswerte durch Matrixmultiplikation der Abfragen mit den Schlüsseln.
scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(d_k)
# Wenn eine Maske angegeben ist, füge sie hinzu (Positionen mit -inf bleiben nach Softmax Null).
if mask is not None:
scores = scores + mask
# Normalisiere die Werte zu Wahrscheinlichkeiten.
attn_weights = F.softmax(scores, dim=-1)
# Multipliziere die Wahrscheinlichkeiten mit den Werten, um die beachteten Ausgaben zu erhalten.
output = torch.matmul(attn_weights, V)
return output, attn_weights
class MultiHeadAttention(nn.Module):
def __init__(self, d_model, num_heads):
super(MultiHeadAttention, self).__init__()
# Sicherstellen, dass d_model gleichmäßig durch die Anzahl der Köpfe teilbar ist.
assert d_model % num_heads == 0
self.num_heads = num_heads
self.d_k = d_model // num_heads
# Lineare Projektionen für Abfragen, Schlüssel, Werte.
self.W_q = nn.Linear(d_model, d_model)
self.W_k = nn.Linear(d_model, d_model)
self.W_v = nn.Linear(d_model, d_model)
# Endgültige lineare Projektion nach Verkettung aller Köpfe.
self.W_o = nn.Linear(d_model, d_model)
def forward(self, query, key, value, mask=None):
batch_size = query.size(0)
# Projektion der Eingabetensoren in Q, K, V.
Q = self.W_q(query)
K = self.W_k(key)
V = self.W_v(value)
# In (batch, heads, seq_len, d_k) umformen und transponieren.
Q = Q.view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
K = K.view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
V = V.view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
# Skalierte Skalarprodukt-Aufmerksamkeit pro Kopf anwenden.
attn_output, _ = scaled_dot_product_attention(Q, K, V, mask)
# Köpfe verknüpfen: zurück transponieren und Kopfdimension zusammenführen.
concat = attn_output.transpose(1, 2).contiguous().view(batch_size, -1, self.num_heads * self.d_k)
# Endgültige lineare Projektion.
output = self.W_o(concat)
return output
class PositionalEncoding(nn.Module):
def __init__(self, d_model, max_len=5000):
super(PositionalEncoding, self).__init__()
# Erstelle einmal sinusförmige Positionskodierungen.
pe = torch.zeros(max_len, d_model)
position = torch.arange(0, max_len).unsqueeze(1).float()
div_term = torch.exp(torch.arange(0, d_model, 2).float() * -(math.log(10000.0) / d_model))
pe[:, 0::2] = torch.sin(position * div_term)
pe[:, 1::2] = torch.cos(position * div_term)
# Batch-Dimension hinzufügen und als Puffer registrieren, damit sie sich mit dem Modell mitbewegt.
self.register_buffer('pe', pe.unsqueeze(0))
def forward(self, x):
# x hat die Form (batch_size, seq_len, d_model).
# Die Positionskodierungen bis zur Eingabelänge hinzufügen.
x = x + self.pe[:, :x.size(1)]
return x
class TransformerEncoderLayer(nn.Module):
def __init__(self, d_model, num_heads, d_ff, dropout):
super(TransformerEncoderLayer, self).__init__()
# Selbstaufmerksamkeits-Unterschicht.
self.self_attn = MultiHeadAttention(d_model, num_heads)
# Positionsbezogenes Feedforward-Netzwerk.
self.ffn = nn.Sequential(
nn.Linear(d_model, d_ff),
nn.ReLU(),
nn.Linear(d_ff, d_model),
)
# Layer-Normalisierungsmodule.
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
# Dropout zur Regularisierung.
self.dropout1 = nn.Dropout(dropout)
self.dropout2 = nn.Dropout(dropout)
def forward(self, x, src_mask=None):
# Selbstaufmerksamkeit anwenden, dann addieren und normieren.
attn_out = self.self_attn(x, x, x, src_mask)
x = self.norm1(x + self.dropout1(attn_out))
# Feedforward-Netzwerk anwenden, dann addieren und normieren.
ffn_out = self.ffn(x)
x = self.norm2(x + self.dropout2(ffn_out))
return x
class TransformerDecoderLayer(nn.Module):
def __init__(self, d_model, num_heads, d_ff, dropout):
super(TransformerDecoderLayer, self).__init__()
# Maskierte Selbstaufmerksamkeit für Zielsequenz.
self.self_attn = MultiHeadAttention(d_model, num_heads)
# Encoder-Decoder-Aufmerksamkeit für die Quelle.
self.src_attn = MultiHeadAttention(d_model, num_heads)
# Feed-Forward-Netzwerk.
self.ffn = nn.Sequential(
nn.Linear(d_model, d_ff),
nn.ReLU(),
nn.Linear(d_ff, d_model),
)
# Layer-Normen und Dropouts für jede Unterschicht.
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.norm3 = nn.LayerNorm(d_model)
self.dropout1 = nn.Dropout(dropout)
self.dropout2 = nn.Dropout(dropout)
self.dropout3 = nn.Dropout(dropout)
def forward(self, x, memory, src_mask=None, tgt_mask=None):
# Maskierte Selbstaufmerksamkeit auf dem Decodereingang.
self_attn_out = self.self_attn(x, x, x, tgt_mask)
x = self.norm1(x + self.dropout1(self_attn_out))
# Encoder-Decoder-Aufmerksamkeit über Encoder-Ausgaben.
src_attn_out = self.src_attn(x, memory, memory, src_mask)
x = self.norm2(x + self.dropout2(src_attn_out))
# Feed-forward und add & norm.
ffn_out = self.ffn(x)
x = self.norm3(x + self.dropout3(ffn_out))
return x
class Transformer(nn.Module):
def __init__(self,
src_vocab_size,
tgt_vocab_size,
d_model=512,
num_heads=8,
d_ff=2048,
num_encoder_layers=6,
num_decoder_layers=6,
dropout=0.1):
super(Transformer, self).__init__()
# Token-Einbettung für Quelle und Ziel.
self.src_embed = nn.Sequential(
nn.Embedding(src_vocab_size, d_model),
PositionalEncoding(d_model)
)
self.tgt_embed = nn.Sequential(
nn.Embedding(tgt_vocab_size, d_model),
PositionalEncoding(d_model)
)
# Gestapelte Encoder- und Decoder-Schichten.
self.encoder_layers = nn.ModuleList([
TransformerEncoderLayer(d_model, num_heads, d_ff, dropout)
for _ in range(num_encoder_layers)
])
self.decoder_layers = nn.ModuleList([
TransformerDecoderLayer(d_model, num_heads, d_ff, dropout)
for _ in range(num_decoder_layers)
])
# Endgültige lineare Projektion auf die Vokabulargröße.
self.generator = nn.Linear(d_model, tgt_vocab_size)
self.d_model = d_model
def encode(self, src, src_mask=None):
# Positionskodierung einbetten und hinzufügen.
x = self.src_embed(src) * math.sqrt(self.d_model)
# Durch jede Encoder-Schicht hindurchlaufen.
for layer in self.encoder_layers:
x = layer(x, src_mask)
return x
def decode(self, tgt, memory, src_mask=None, tgt_mask=None):
# Ziel einbetten und Positionskodierung hinzufügen.
x = self.tgt_embed(tgt) * math.sqrt(self.d_model)
# Durchlaufen jeder Decoderschicht.
for layer in self.decoder_layers:
x = layer(x, memory, src_mask, tgt_mask)
return x
def forward(self, src, tgt, src_mask=None, tgt_mask=None):
# Encoder-Ausgabe berechnen.
memory = self.encode(src, src_mask)
# Decoder-Ausgabe unter Berücksichtigung des Encoder-Speichers berechnen.
output = self.decode(tgt, memory, src_mask, tgt_mask)
# Auf Vokabular-Logits projizieren.
return self.generator(output)
def generate_square_subsequent_mask(sz):
"""
Erstellt eine Maske für kausale Aufmerksamkeit, sodass Position i nur
auf Positionen ≤ i achten kann. Masken-Einträge sind 0, wo erlaubt, und
-inf, wo nicht erlaubt.
"""
mask = torch.triu(torch.full((sz, sz), float('-inf')), diagonal=1)
return mask
# Anwendungsbeispiel mit Dummy-Daten:
# Vokabulargrößen und Sequenzlängen definieren.
src_vocab_size, tgt_vocab_size = 10000, 10000
batch_size, src_len, tgt_len = 2, 20, 22
# Transformer instanziieren.
model = Transformer(src_vocab_size, tgt_vocab_size)
# Beispiel für Quell- und Zieltoken-Indizes.
src = torch.randint(0, src_vocab_size, (batch_size, src_len))
tgt = torch.randint(0, tgt_vocab_size, (batch_size, tgt_len))
# Keine Füllmaske für dieses Beispiel.
src_mask = None
# Kausale Maske für den Decoder.
tgt_mask = generate_square_subsequent_mask(tgt_len)
# Der Vorwärtsdurchlauf liefert Logits der Form (batch_size, tgt_len, tgt_vocab_size).
logits = model(src, tgt, src_mask, tgt_mask)
In dieser Implementierung wendet jede Encoder-Schicht Multi-Head-Selbstaufmerksamkeit und ein positionsbezogenes Feedforward-Netzwerk an, jeweils mit Restverbindungen und Schichtnormalisierung. Jede Decoder-Schicht fügt einen maskierten Selbstaufmerksamkeitsschritt hinzu, um das Einsehen zukünftiger Token zu verhindern, sowie eine zusätzliche Encoder-Decoder-Aufmerksamkeit, die es dem Decoder ermöglicht, sich auf relevante Teile der Quellsequenz zu konzentrieren. Mit Sinuskurven erstellte Positionscodierungen fügen dem Modell Reihenfolgeinformationen hinzu, und die letzte lineare Schicht projiziert die Decoder-Ausgaben auf rohe Token-Scores.
Auf dieser Grundlage können Sie das Modell anhand von gelabelten (gepaarten) Textdaten trainieren, indem Sie einen geeigneten Verlust definieren (beispielsweise die Kreuzentropie zwischen den vorhergesagten Logits und den tatsächlichen Token-Indizes) und einen der zuvor beschriebenen Optimierer verwenden.
… und noch mehr
Diese Blogserie hat einen Rundflug über das große Terrain der Künstlichen Neuronalen Netze (KNNs) geboten. Dabei haben wir noch gar nicht alle Anwendungen betrachtet, in denen KNNs eine Rolle spielen. Hier sei exemplarisch das Beispiel Reinforcement Learning erwähnt, etwa DQN (Deep-Q Learning).
Durch ihre Struktur erweisen sich KNNs als adäquates Mittel, um statistisches Pattern-Matching durchzuführen, also um bestimmte Muster in Eingangsdaten aufzuspüren. Diese Fähigkeit kommt speziell bei Transformer-Architekturen zum Tragen, die Prompts verarbeiten, um daraus Rückmeldungen zu liefern. Zusätzlich findet dort häufig Reinforcement Learning statt, um die Large Language Models zu bestimmten gewünschten Abläufen zu „überreden“, etwa zum sogenannten Reasoning.
Heutige KNNs sind an biologische neuronale Netze (BNNs) angelehnt, sind diesen gegenüber aber gewaltig eingeschränkt. In Zukunft könnten Wissenschaftler versuchen, sich bei KNNs noch deutlicher ihren biologischen Vorbildern anzunähern. Insgesamt dürften Künstliche Neuronale Netze in der Zukunft Generative KI eine fundamentale und zentrale Rolle spielen.
(rme)
Entwicklung & Code
Angular 21 vollzieht den Abschied von zone.js
Angular 21 ist erschienen. Die neue Hauptversion des Webframeworks bietet experimentelle Signal Forms, eine Developer-Preview für Angular Aria für barrierefreie Headless-Komponenten und weitere Neuerungen. Die Bibliothek zone.js ist zugunsten der Zoneless Change Detection in neuen Angular-Anwendungen nicht mehr enthalten.
Weiterlesen nach der Anzeige
(Bild: jaboy/123rf.com)

Die enterJS 2026 wird am 16. und 17. Juni in Mannheim stattfinden. Das Programm wird sich rund um JavaScript und TypeScript, Frameworks, Tools und Bibliotheken, Security, UX und mehr drehen. Vergünstigte Blind-Bird-Tickets sind bis zum Programmstart erhältlich.
Barrierefreie Komponenten dank Angular Aria
Als Developer-Preview ist Angular Aria enthalten. Diese Bibliothek für gängige UI-Pattern legt den Fokus auf Accessibility – für barrierefreie Headless-Komponenten gemäß der Webstandard-Suite WAI-ARIA (Web Accessibility Initiative – Accessible Rich Internet Applications) des World Wide Web Consortium (W3C).
Derzeit umfasst Angular Aria ein Set aus acht UI-Pattern – Accordion, Combobox, Grid, Listbox, Menu, Tabs, Toolbar und Tree – mit 13 Komponenten. Diese sind nicht gestylt und lassen sich nach Belieben mit eigenen Styles versehen.
Angular Aria lässt sich mit npm i @angular/aria installieren. Für dieses neue Feature steht ein Guide bereit, der Codebeispiele für alle Komponenten, Skins zum Ausprobieren und weitere Informationen enthält.
Experimentelle Signal Forms
Weiterlesen nach der Anzeige
Angular-Entwickler können nun Signal Forms ausprobieren, eine experimentelle Bibliothek für das Verwalten von Form-State auf Basis der reaktiven Signals. Sie soll sowohl eine gute Developer Experience als auch vollständige Typsicherheit für den Zugriff auf Formularfelder ermöglichen und zentraler, Schema-basierter Validationslogik folgen.
Für den Einsatz der experimentellen Signal Forms halten der entsprechende Guide und die Dokumentation alle Details bereit.
Abschied von zone.js und Begrüßung von Vitest
Vitest ist in Angular 21 zum Standard-Test-Runner gewählt worden. Das gilt für neue Projekte, während die bisher genutzten Technologien Karma und Jasmine weiterhin vollständigen Support durch das Angular-Team genießen, sodass Entwicklerinnen und Entwickler noch nicht zu Vitest migrieren müssen. Wer es allerdings testen möchte, kann die experimentelle Migration durchführen.
Und schließlich verabschiedet sich Angular 21 endgültig von zone.js – ein Prozess, der sich schon seit Angular 18 abzeichnete. Die eigenständige Bibliothek zone.js, die von Beginn an in Angular zum Einsatz kam, ist nun nicht mehr standardmäßig enthalten. Neue Angular-Anwendungen nutzen demnach das Zoneless-Verfahren, während für bestehende Anwendungen eine Migrationsanleitung zur Verfügung steht.
Die Zoneless Change Detection bietet laut dem Angular-Team zahlreiche Vorteile: verbesserte Core Web Vitals, natives async/await, Ökosystem-Kompatibilität, verringerte Bundle-Größe, vereinfachtes Debugging und eine verbesserte Kontrolle. Dennoch habe zone.js eine wichtige Rolle bei der Gestaltung von Angular gespielt, hebt das Entwicklungsteam hervor.
Weitere Informationen zu den Updates in Angular 21 hält der Angular-Blog bereit.
(mai)
Entwicklung & Code
software-architektur.tv: Digitale Souveränität in der Softwarearchitektur
Digitale Souveränität ist aktuell ein sehr heiß diskutiertes Thema und im Mainstream der Gesellschaft angekommen. In dieser Episode spricht Eberhard Wolff mit Sven Müller über den Einfluss von digitaler Souveränität auf Softwarearchitektur. Ziel ist es, die digitale Handlungsfähigkeit unserer Organisationen zu stärken. Aspekte sind nicht nur die häufig genannte Unabhängigkeit von US-Cloud-Anbietern, sondern vor allem gezielte Make-or-buy-Enscheidungen, der Einsatz von offenen Standards oder Open Source nicht nur zum Sparen von Lizenzkosten.
Weiterlesen nach der Anzeige
Sven Müller ist Softwareentwickler, -architekt und Berater bei synyx. Seine Leidenschaft gilt der modernen agilen Softwareentwicklung an der Schnittstelle zwischen Fachbereich, Entwicklung, Anwender und Betrieb, die am Ende einen echten Mehrwert fürs Unternehmen schafft.
Lisa Maria Schäfer malt dieses Mal keine Sketchnotes.
Livestream am 21. November
Die Ausstrahlung findet am Freitag, 21. November 2025, live von 13 bis 14 Uhr statt. Die Folge steht im Anschluss als Aufzeichnung bereit. Während des Livestreams können Interessierte Fragen via Twitch-Chat, YouTube-Chat oder anonym über das Formular auf der Videocast-Seite einbringen.
software-architektur.tv ist ein Videocast von Eberhard Wolff, Blogger sowie Podcaster auf iX und bekannter Softwarearchitekt, der als Head of Architecture bei SWAGLab arbeitet. Zum Team gehören außerdem Lisa Maria Schäfer (Socreatory) und Ralf D. Müller (DB Systel). Seit Juni 2020 sind über 250 Folgen entstanden, die unterschiedliche Bereiche der Softwarearchitektur beleuchten – mal mit Gästen, mal Wolff, Schäfer oder Müller solo. Seit mittlerweile mehr als zwei Jahren bindet iX (heise Developer) die über YouTube gestreamten Episoden im Online-Channel ein, sodass Zuschauer dem Videocast aus den Heise Medien heraus folgen können.
Weitere Informationen zur Folge finden sich auf der Videocast-Seite.
Weiterlesen nach der Anzeige
(mdo)
Entwicklung & Code
Stack Overflow: Kuratierte Knowledge Base für KI-Agenten im Unternehmen
Das Entwicklerforum Stack Overflow stellt ein Knowledge-Base-Tool für Unternehmen vor. Es verbindet kuratiertes Wissen mit KI-Unterstützung und Microsoft-365-Anbindung. So soll es als zuverlässiger Informations-Pool im Unternehmen dienen.
Weiterlesen nach der Anzeige
Stack Internal soll das verteilte Wissen im Unternehmen in einer einheitlichen, von Menschen geprüften Basis zusammenführen, die den Qualitätsansprüchen und Compliance-Regeln der Firma entspricht. KI kommt beim Zusammenstellen der Informationen aus Quellen wie Confluence oder Teams zum Einsatz. Die Maschine klassifiziert die Daten beim Einlesen und präsentiert sie den menschlichen Prüferinnen und Prüfern in sortierter und vorbewerteter Form. Die Kuratoren korrigieren die Vorschläge und geben sie frei.
Enge Verknüpfung mit Microsoft 365
Die Inhalte von Stack Internal dienen einerseits als Basis für den KI-Chat in Microsoft-Tools, also Office 365, Teams und dem Coding-Copiloten für Entwicklerinnen und Entwickler. Ein MCP-Server ermöglicht andererseits die Anbindung an weitere GenAI-Modelle und Agenten. Umgekehrt sollen Interaktionen mit der Knowledge Base das Wissen darin verbessern und erweitern. Auf welche Art das geschehen soll, erklärt der Anbieter nicht.
Stack Internal ist eine Weiterentwicklung von Stack Overflow for Teams und läuft auf Azure. Es gibt Cloud- und On-Premises-Varianten, darunter eine kostenlose für bis zu fünfzig Mitglieder.
Stack Overflow macht mit Stack Internal aus der Not eine Tugend: Gestartet als Forum für Entwicklerinnen und Entwickler, war Stack Overflow mit der Verbreitung von Coding-Assistenten zu einer reinen Trainingsfundgrube für KI-Modelle abgesunken. Dass solide Trainingsdaten aber einen Wert eigener Art darstellen, vermarktet der Anbieter Stack Exchange nun.
(who)
-
UX/UI & Webdesignvor 3 MonatenAdobe Firefly Boards › PAGE online
-
UX/UI & Webdesignvor 1 MonatIllustrierte Reise nach New York City › PAGE online
-
Apps & Mobile Entwicklungvor 3 MonatenGalaxy Tab S10 Lite: Günstiger Einstieg in Samsungs Premium-Tablets
-
Datenschutz & Sicherheitvor 3 MonatenHarte Zeiten für den demokratischen Rechtsstaat
-
Datenschutz & Sicherheitvor 2 MonatenJetzt patchen! Erneut Attacken auf SonicWall-Firewalls beobachtet
-
Online Marketing & SEOvor 3 Monaten„Buongiorno Brad“: Warum Brad Pitt für seinen Werbejob bei De’Longhi Italienisch büffeln muss
-
Online Marketing & SEOvor 3 MonatenCreator und Communities: Das plant der neue Threads-Chef
-
Entwicklung & Codevor 3 MonatenPosit stellt Positron vor: Neue IDE für Data Science mit Python und R
