Connect with us

Entwicklung & Code

Die wichtigsten Neuerungen von Java 25: Schneller Startup mit Stable Values


Die Veröffentlichung für das OpenJDK 25 ist für den 16. September 2025 vorgesehen. Diesmal sind 18 JEPs (JDK Enhancement Proposals) eingeplant. Das ist zwar etwas weniger als noch bei Java 24, aber dafür werden dieses Mal einige Features nach vorangehenden Previews finalisiert. Außerdem bieten die meisten Distributoren für Java 25 wieder einen verlängerten Support (LTS) an. Viele Java-Projekte wechseln in den nächsten Monaten vom letzten LTS Release 21 zu 25.

Wir werfen hier einen ersten Blick auf die aus Developer-Sicht interessantesten Punkte. Weitere Details lassen sich über die jeweiligen JEPs nachvollziehen. In einem zweiten Post betrachten wir dann die weiteren Änderungen, die mehr unter der Haube versteckt oder nicht so relevant für die Java-Entwicklerinnen und -Entwickler sind. Die meisten in diesem Beitrag beschriebenen JEPs sind übrigens Wiedervorlagen, die bereits in früheren Java-Versionen als Inkubator oder Preview veröffentlicht wurden. Ganz neu sind die Stable Values, die eine größere Flexibilität bei der Initialisierung von final-Feldern bieten.

Stable Values sind Objekte, die genau einmal zum Einsatz kommen und danach unveränderlich sind. Die JVM (Java Virtual Machine) behandelt ihren Inhalt wie eine echte Konstante und kann dieselben Optimierungen (zum Beispiel Constant Folding) anwenden, wie bei final‑Feldern – nur, dass die Initialisierung beliebig spät erfolgen darf. Damit bekommt Java eine „Deferred Immutability“, die Start‑ und Aufwärmzeiten verkürzt, ohne Sicherheitsrisiken mit Thread einzugehen. Die Ziele sind:

  • Schnelleres Start‑Up: Keine monolithische Initialisierung aller Komponenten mehr.
  • Entkopplung von Erzeugung & Initialisierung eines unveränderlichen Werts – ohne Performance‑Kosten.
  • Garantierte At‑Most‑Once‑Initialisierung auch in hoch parallelen Szenarien.
  • Konstanten‑Optimierungen für Anwendungscode zugänglich machen, nicht nur für JDK‑internen Code.

Stable Values sind eine reine Library‑API. Es wird kein neues Schlüsselwort geben. Außerdem gibt es keine Änderung an der Semantik von final, bestehender Code bleibt unberührt. Bisher müssen final‑Felder eager (sofort) initialisiert werden – die Initialisierung eines Loggers oder eine Datenbank‑Connection bremst so den Programmstart aus. Workarounds wie Lazy Holder, Double‑Checked Locking oder ConcurrentHashMap.computeIfAbsent sind entweder eingeschränkt, fehleranfällig oder verhindern JIT‑Optimierungen (Just in Time). Stable Values schließen die Lücke zwischen strenger Immutability und flexibler Lazy‑Initialisierung.

Der Lambda-Ausdruck in orElseSet wird garantiert genau einmal ausgeführt, selbst wenn mehrere Virtual Threads gleichzeitig logger() aufrufen. Danach kann der JIT (Just in Time Compiler der Hotspot VM) alle Zugriffe optimieren, wie durch Constant Folding.


class OrderController {
    private final StableValue logger = StableValue.of();

    private Logger logger() {
        return logger.orElseSet(() -> Logger.create(OrderController.class));
    }
}


Stable Values sind besonders für parallele Programmierung interessant. Im Umfeld von Virtual Threads arbeitet das Entwicklerteam schon seit Längerem an weiteren Bausteinen. Während Structured Concurrency bereits zum sechsten Mal als Preview dabei ist, gelten die Scoped Values nach vier Previews jetzt als final.

Scoped Values sind eine Alternative zu ThreadLocal-Variablen, die Threads unterschiedliche Zugriffe auf einen globalen Zustand ermöglichen. ThreadLocal lässt sich beliebig ändern, hat eine unbegrenzte Lebensdauer (Leak-Gefahr, vor allem in Pools) und ist teurer durch Vererbung an Kind-Threads. Scoped Values begrenzen die Lebensdauer klar, sind read-only und lassen sich über viele Threads hinweg platz- und zeitsparend nutzen. Gegenüber den Previews wird die API finalisiert. Einzige Änderung: ScopedValue.orElse akzeptiert kein null mehr. Ein Web-Framework bindet beispielsweise einmal pro Request den FrameworkContext (zum Beispiel Spring ApplicationContext oder ein AuthenticationContext). Die Handler und interne Framework-Aufrufe (etwa DB-Zugriff) können auf diesen FrameworkContext zugreifen, ohne dass er immer als zusätzlicher Parameter durchgereicht werden muss. Und auch in Kind-(Virtual) Threads ist er automatisch verfügbar.

Mit dem StructuredTaskScope erhält eine API Einzug, die zusammengehörige Nebenläufigkeit als eine Einheit behandelt: Subtasks werden im Block geforkt und über ein join() wieder zusammengeführt. Fehler sorgen gegebenenfalls für einen direkten Abbruch anderer Subtasks. Beim Debugging lässt sich in den Thread-Dumps die Task-Hierarchie leicht nachvollziehen. Damit erlaubt Structured Concurrency die Implementierung auf eine besonders les- und wartbare Art und Weise. Alternativ konnten Entwicklerinnen und Entwickler für diesen Zweck bisher die Parallel Streams, den ExecutorService oder reaktive Programmierung einsetzen. Alles sehr mächtige Ansätze, die aber gerade einfache Umsetzungen unnötig kompliziert und fehleranfällig machen.

Neu gegenüber der letzten Preview: Scopes lassen sich über statische Fabrikmethoden öffnen (open() für den Standardfall, d. h. entweder sind alle erfolgreich oder bei einem Fehler bricht alles ab). Alternative Abschluss-Policies und Rückgabewerte werden über Joiner-basierte open(...)-Varianten gesteuert. Ziele sind unter anderem das Vermeiden von Thread-Leaks und Cancellation-Delays sowie bessere Observability. Nicht beabsichtigt ist der Ersatz von ExecutorService/Future, neue Channel-Primitiven oder eine neue Cancel-Mechanik. Gerade bei I/O-Fan-out mit Virtual Threads wird die Koordination einfacher, robuster und nachvollziehbarer.


// --enable-preview beim Kompilieren und Starten 
record Response(String user, int order) {}

Response handle() throws InterruptedException {
  try (var scope = StructuredTaskScope.


Trotz seiner 30 Jahre zieht Java auch weiter viele Programmierneulinge an. Die JEPs 511 (Module Import Declarations) und 512 (Compact Source Files and Instance Main Methods) helfen aber nicht nur Anfängern, sie machen auch erfahrenen Developern das Leben einfacher. Beide JEPs erreichen nun nach mehreren vorangegangenen Previews den finalen Status.

Dieses JEP ergänzt die Sprache um import module , sodass alle exportierten Pakete eines Moduls mit einer Deklaration verfügbar sind. Das reduziert Import-Boilerplate, erleichtert die Wiederverwendung modularer Bibliotheken (ohne dass der eigene Code modular sein muss) und funktioniert reibungslos neben den bekannten import-Deklarationen. Für Einsteiger oder auch für Anwender des Prototypings in JShell sinkt die Hürde, weil sie nicht mehr wissen müssen, in welchem Paket sich einzelne Typen befinden. In Szenarien mit transitiven Modulabhängigkeiten (zum Beispiel java.sql, java.xml) vereinfacht sich die Nutzung zusammengehöriger APIs spürbar.


import module java.base;

List xs = List.of("a", "b");
Path p = Path.of("data.txt");
// List/Map/Stream/Path sind ohne einzelne Paket-Imports nutzbar


Wir können jetzt in einer einzelnen Java-Datei viel schlanker kleine Programme abspeichern und ausführen. Ziel ist eine sanfte Einstiegskurve: Grundkonzepte zuerst, Programmieren-im-Kleinen ohne überflüssige Zeremonie. Bei wachsender Komplexität lässt sich der Code nahtlos zu Klassen/Packages/Modulen ausbauen. Gegenüber den Previews wird das Feature finalisiert und umbenannt (aus simple wird compact). Die neue IO-Klasse liegt nun in java.lang und wird damit implizit importiert, ihre statischen Methoden aber nicht. Außerdem basiert IO jetzt auf System.out/System.in statt java.io.Console.


// > java Main.java
void main() {
    IO.println("Hello, World!");
}


Konstruktoren dürfen nun Anweisungen vor dem Aufruf von super(...) oder this(...) enthalten. Diese Prolog-Phase dient beispielsweise zur Validierung von Argumenten, zum Initialisieren eigener Felder, ohne das entstehende Objekt anderweitig zu referenzieren, oder zum Transformieren von Inputwerten. Danach folgt der gewohnte Epilog. Das macht viele Konstruktoren natürlicher (z. B. fail-fast) und erhöht die Sicherheit, weil Unterklassen-Felder vor einem möglichen Zugriff durch Superklassen-Konstruktoren oder dort aufgerufene Methoden bereits gesetzt sind. Ziel ist es, überflüssige Beschränkungen zu entfernen und zusätzliche Garantien für einen vollständig initialisierten Objektzustand zu geben. Das umgeht die praktischen Probleme der bisherigen Regel „super() muss zuerst kommen“, durch die es zu unnötiger Arbeit, zu tückischen Initialisierungsfehlern bei mehreren Threads und zu Integritätsverletzungen kommen konnte.

Dieser JEP macht Pattern Matching einheitlich für alle Typen, inklusive Primitive. Er erlaubt primitive Typmuster in allen Pattern-Kontexten (top-level und verschachtelt), erweitert instanceof auf Primitive (inklusive sichere, verlustfreie Umwandlungen) und lässt switch über alle primitiven Typen laufen (auch boolean, long, float, double). Ziel ist weniger Reibung und mehr Ausdrucksstärke: unsichere Casts und Boxing entfallen, da ein Pattern nur matcht, wenn die Konversion verlustfrei ist. Außerdem lassen sich Typmuster, instanceof und „safe casting“ semantics sauber aufeinander ausrichten.


// switch mit primitive type pattern
switch (x.getStatus()) {
  case 0 -> "okay";
  case 1 -> "warning";
  case 2 -> "error";
  case int i -> "unknown: " + i;   // zeigt unbekannten int-Status
}

// instanceof mit sicherer Konversion (matcht nur verlustfrei)
if (i instanceof byte b) { /* b nutzen */ }

// Record-Pattern mit sicherem Narrowing
if (json instanceof JsonObject(var map)
    && map.get("age") instanceof JsonNumber(int age)) {
  // nur wenn double → int verlustfrei passt
}


Die Vector-API erlaubt, Vektorberechnungen in Java auszudrücken, die der HotSpot-Compiler zu nativen SIMD-Instruktionen (x64: SSE/AVX, AArch64: NEON/SVE) kompiliert – mit stabilerer Performance als rein skalare Implementierungen. In JDK 25 ist diese API erneut im Inkubatorstatus, da man noch auf die Implementierung von Value Classes als Grundlage für die Vector API wartet. Neu sind:

  • VectorShuffle ↔ MemorySegment-Zugriff
  • Anbindung nativer Mathe-Bibliotheken über die FFM-API statt HotSpot-C++-Code (wartungsärmer)
  • Auto-Vektorisierung von Float16-Operationen (Add/Sub/Mul/Div/Sqrt/FMA) auf unterstützten x64-CPUs.

Ziel ist eine klare, plattform-agnostische API mit verlässlicher Abbildung auf Vektorbefehle und „graceful degradation“.

Das waren 8 der insgesamt 18 neuen JEPs. Die anderen, die weniger für Entwicklerinnen und Entwickler relevant, aber trotzdem spannende Themen sind, schauen wir uns in einem zweiten Post an. Zusätzlich sind die vielen kleineren Neuerungen, für die es keine JEPs gibt, in den Release-Notes zum Nachlesen. Änderungen am JDK (Java Klassenbibliothek) wie die Stable Values oder Structured Concurrency kann man sich zudem über den Java Almanac anzeigen lassen.


(mdo)



Source link

Entwicklung & Code

software-architektur.tv: Webperformance mit Lucas Dohmen und Lisa Maria Schäfer


In dieser Folge des Videocasts software-architektur.tv sprechen Lucas Dohmen und Lisa Maria Schäfer über Webperformance. Sie klären, was sich dahinter verbirgt und warum das Thema wichtig ist – und zwar für alle, die Webseiten entwickeln. Des Weiteren stellen sie Tools zum Messen der Webperformance vor und geben Impulse, wie man seine Website schneller machen kann.

Lisa Maria Schäfer malt dieses Mal keine Sketchnotes, da sie vor der Kamera ist.

Die Ausstrahlung findet am Freitag, 5. September 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, Bluesky, Mastodon, Slack-Workspace 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. Seit Juni 2020 sind über 250 Folgen entstanden, die unterschiedliche Bereiche der Softwarearchitektur beleuchten – mal mit Gästen, mal Wolff 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.


(mdo)



Source link

Weiterlesen

Entwicklung & Code

Microsoft gibt 6502 BASIC frei


Microsoft hat den Quellcode seines historischen 6502-BASIC-Interpreters aus dem Jahr 1976 erstmals offiziell als Open Source freigegeben und somit einen wichtigen Beitrag zur Bewahrung der Geschichte der Heimcomputer geleistet. Die Software wurde ursprünglich von Bill Gates und Ric Weiland für den beliebten 8-Bit-Prozessor MOS 6502 entwickelt. Später lag sie den einflussreichen Computern Commodore PET, VIC-20 und Commodore 64 zugrunde – Millionen von Nutzern lernten damit programmieren und prägen bis heute die Kulturszene rund ums Retro-Computing.

Die jetzt unter der MIT-Lizenz veröffentlichte Version 1.1 enthält laut Microsoft unter anderem Verbesserungen an der Speicherverwaltung, die 1978 gemeinsam mit Commodore-Entwickler John Feagans umgesetzt wurden. Damit handelt es sich um die Variante, die als BASIC V2 auf dem PET ausgeliefert wurde und bis heute in den ROM-Archiven von Computermuseen und Emulatoren lebt. Der 6955 Zeilen umfassende Assembly-Sourcecode steht ab sofort auf GitHub bereit – samt Unterstützung für verschiedene historische Systeme wie Apple II oder Commodore PET.

Mit an Bord sind außerdem die dokumentierten Easter Eggs von Bill Gates selbst. Denn in den Quellcodes von Microsoft 6502 BASIC sind mehrere versteckte Funktionen verborgen, darunter die berühmten Labels STORDO und STORD0, wie Bill Gates selbst 2010 bestätigte. Besonders bekannt ist das WAIT 6502,1-Kommando, das auf einem Commodore PET mit BASIC V2 das Wort MICROSOFT! auf dem Bildschirm erscheinen lässt – das versteckte Markenzeichen hatte Bill Gates gezielt eingebaut.

Microsoft betont mit der Veröffentlichung die historische Bedeutung des BASIC-Interpreters für die eigenen Anfänge. Gleichzeitig verweist der Konzern in seiner Ankündigung darauf, dass sich die Community – von Museumsarchiven bis hin zu modernen FPGA- und Emulator-Projekten – schon seit Jahrzehnten für dessen Erhalt und praktische Anwendung eingesetzt hat. 2020 hatte Microsoft bereits den GW-BASIC-Interpreter als Open Source veröffentlicht, der aus dem Jahr 1983 stammt und für IBM-kompatible PCs beziehungsweise MS-DOS zum Einsatz kam.


(fo)



Source link

Weiterlesen

Entwicklung & Code

Künstliche Neuronale Netze im Überblick 7: Rekursive neuronale Netze


Neuronale Netze sind der Motor vieler Anwendungen in KI und GenAI. Diese Artikelserie gibt einen Einblick in die einzelnen Elemente. Der siebte Teil widmet sich rekursiven neuronalen Netzen, nachdem der sechste Teil der Serie Convolutional Neural Networks vorgestellt hat.


Michael Stal

Michael Stal

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.

Rekursive neuronale Netze sind für die Verarbeitung sequenzieller Daten ausgelegt, indem sie einen versteckten Zustand aufrechterhalten, der sich im Laufe der Zeit weiterentwickelt. Im Gegensatz zu Feedforward-Netzwerken, die davon ausgehen, dass jede Eingabe unabhängig von allen anderen ist, ermöglichen rekursive Netzwerke das Speichern von Informationen über Zeiträume hinweg. Bei jedem Schritt t empfängt eine rekurrente Zelle sowohl den neuen Eingabevektor xₜ als auch den vorherigen versteckten Zustand hₜ₋₁. Die Zelle berechnet einen neuen versteckten Zustand hₜ gemäß einer gelernten Transformation und erzeugt (optional) eine Ausgabe yₜ.

In ihrer einfachsten Form berechnet eine Vanilla-RNN-Zelle einen Präaktivierungsvektor zₜ als Summe einer Eingabetransformation und einer versteckten Zustandstransformation plus einer Verzerrung:

zₜ = Wₓ · xₜ + Wₕ · hₜ₋₁ + b

Der neue Zustand entsteht durch elementweise Anwendung einer nicht linearen Aktivierung σ:

hₜ = σ(zₜ)

Wenn bei jedem Zeitschritt eine Ausgabe yₜ erforderlich ist, kann eine Ausleseschicht hinzugefügt werden:

yₜ = V · hₜ + c

wobei V und c eine Ausgabegewichtungsmatrix und ein Bias-Vektor sind.

In PyTorch kapselt die Klasse torch.nn.RNN dieses Verhalten und verarbeitet das Stapeln mehrerer Schichten und Batches nahtlos. Das folgende Beispiel zeigt, wie man eine einlagige RNN-Zelle erstellt, ihr einen Stapel von Sequenzen zuführt und den endgültigen versteckten Zustand extrahiert:


import torch
import torch.nn as nn

# Angenommen, wir haben Sequenzen der Länge 100, jedes Element ist ein 20-dimensionaler Vektor,
# und wir verarbeiten sie in Batches der Größe 16.
seq_len, batch_size, input_size = 100, 16, 20
hidden_size = 50

# Erstellen Sie einen zufälligen Stapel von Eingabesequenzen: Form (seq_len, batch_size, input_size)
inputs = torch.randn(seq_len, batch_size, input_size)

# Instanziieren Sie ein einlagiges RNN mit tanh-Aktivierung (Standard)
rnn = nn.RNN(input_size=input_size,
             hidden_size=hidden_size,
             num_layers=1,
             nonlinearity='tanh',
             batch_first=False)

# Initialisiere den versteckten Zustand: Form (Anzahl_Schichten, Batchgröße, versteckte Größe)
h0 = torch.zeros(1, batch_size, hidden_size)

# Vorwärtspropagierung durch das RNN
outputs, hn = rnn(inputs, h0)

# `outputs` hat die Form (seq_len, batch_size, hidden_size)
# `hn` ist der versteckte Zustand beim letzten Zeitschritt, Form (1, batch_size, hidden_size)


Jede Zeile dieses Ausschnitts hat eine klare Aufgabe. Durch das Erstellen von Eingaben simuliert man ein Batch von Zeitreihendaten. Das RNN-Modul weist zwei Parametermatrizen zu: eine mit der Form (hidden_size, input_size) für Wₓ und eine mit der Form (hidden_size, hidden_size) für Wₕ sowie einen Bias-Vektor der Länge hidden_size. Beim Aufrufen des Moduls für Eingaben und den Anfangszustand h0 durchläuft es die 100 Zeitschritte und berechnet bei jedem Schritt die Rekursionsbeziehung. Der Ausgabetensor sammelt alle Zwischenzustände, während hn nur den letzten zurückgibt.

Obwohl Vanilla-RNNs konzeptionell einfach sind, haben sie Schwierigkeiten, langfristige Abhängigkeiten zu lernen, da über viele Zeitschritte zurückfließende Gradienten dazu neigen, zu verschwinden oder zu explodieren. Um das abzumildern, führen Gated Recurrent Units wie LSTM und GRU interne Gates ein, die steuern, wie stark die Eingabe und der vorherige Zustand den neuen Zustand beeinflussen sollen.

Die LSTM-Zelle (Long Short-Term Memory) verwaltet sowohl einen versteckten Zustand hₜ als auch einen Zellzustand cₜ. Sie verwendet drei Gates – Forget Gate fₜ, Input Gate iₜ und Output Gate oₜ –, die als Sigmoid-Aktivierungen berechnet werden, sowie eine Kandidaten-Zellaktualisierung ĉₜ, die sich mit einer Tanh-Aktivierung berechnen lässt. Konkret:

fₜ = σ( W_f · xₜ + U_f · hₜ₋₁ + b_f )

iₜ = σ( W_i · xₜ + U_i · hₜ₋₁ + b_i )

oₜ = σ( W_o · xₜ + U_o · hₜ₋₁ + b_o )

ĉₜ = tanh( W_c · xₜ + U_c · hₜ₋₁ + b_c )

Der Zellzustand wird dann durch Kombination des vorherigen Zellzustands und des Kandidaten aktualisiert, gewichtet durch die Vergessens- und Eingangsgatter:

cₜ = fₜ * cₜ₋₁ + iₜ * ĉₜ

Schließlich erstellt das System den neuen versteckten Zustand, indem es das Ausgangs-Gate auf die Nichtlinearität des Zellzustands anwendet:

hₜ = oₜ * tanh(cₜ)

PyTorchs torch.nn.LSTM kapselt all diese Berechnungen unter der Haube. Folgender Code zeigt ein Beispiel für eine Reihe von Sequenzen:


import torch
import torch.nn as nn

# Sequenzparameter wie zuvor
seq_len, batch_size, input_size = 100, 16, 20
hidden_size, num_layers = 50, 2

# Zufälliger Eingabebatch
inputs = torch.randn(seq_len, batch_size, input_size)

# Instanziieren eines zweischichtigen LSTM
lstm = nn.LSTM(input_size=input_size,
               hidden_size=hidden_size,
               num_layers=num_layers,
               batch_first=False)

# Initialisiere versteckte und Zellzustände: jeweils mit der Form (Anzahl_Schichten, Batchgröße, versteckte Größe)
h0 = torch.zeros(num_layers, batch_size, hidden_size)
c0 = torch.zeros(num_layers, batch_size, hidden_size)

# Vorwärtsdurchlauf durch das LSTM
outputs, (hn, cn) = lstm(inputs, (h0, c0))

# `outputs` hat die Form (seq_len, batch_size, hidden_size)
# `hn` und `cn` haben jeweils die Form (num_layers, batch_size, hidden_size)


Die Gated Recurrent Unit (GRU) vereinfacht das LSTM, indem sie die Vergessens- und Eingangsgatter zu einem einzigen Aktualisierungsgatter zₜ kombiniert und die Zell- und versteckten Zustände zusammenführt. Die Gleichungen lauten:

zₜ = σ( W_z · xₜ + U_z · hₜ₋₁ + b_z )

rₜ = σ( W_r · xₜ + U_r · hₜ₋₁ + b_r )

ħₜ = tanh( W · xₜ + U · ( rₜ * hₜ₋₁ ) + b )

hₜ = (1 − zₜ) * hₜ₋₁ + zₜ * ħₜ

In PyTorch bietet torch.nn.GRU diese Funktionalität mit derselben Schnittstelle wie nn.LSTM, außer dass nur die versteckten Zustände zurückgegeben werden.

Bei der Arbeit mit Sequenzen variabler Länge benötigt man häufig torch.nn.utils.rnn.pack_padded_sequence und pad_packed_sequence, um Sequenzen effizient im Batch zu verarbeiten, ohne Rechenleistung für das Auffüllen von Tokens zu verschwenden.

Rekursive Netzwerke eignen sich hervorragend für Aufgaben wie Sprachmodellierung, Zeitreihenprognosen und Sequenz-zu-Sequenz-Übersetzungen, wurden jedoch in vielen Anwendungsbereichen von aufmerksamkeitsbasierten Modellen übertroffen.

Zunächst widmet sich der nächste Teil dieser Serie jedoch der Kombination aus konvolutionalen und rekursiven Schichten, um Daten mit sowohl räumlicher als auch zeitlicher Struktur zu verarbeiten. Bei der Videoklassifizierung kann ein Convolutional Neural Network beispielsweise Merkmale auf Frame-Ebene extrahieren, die dann in ein LSTM eingespeist werden, um Bewegungsdynamiken zu erfassen.


(rme)



Source link

Weiterlesen

Beliebt