Direkt zum Inhalt

Tutorial zum rekurrenten neuronalen Netz (RNN)

Lerne das beliebteste Deep-Learning-Modell RNN kennen und sammle praktische Erfahrungen, indem du einen MasterCard-Aktienkursvorhersager baust.
Aktualisierte 16. Jan. 2025  · 14 Min. Lesezeit

Was sind rekurrente neuronale Netze (RNN)?

Ein rekurrentes neuronales Netzwerk (RNN) ist die Art von künstlichem neuronalem Netzwerk (ANN), die in Apples Siri und Googles Sprachsuche verwendet wird. RNN erinnern sich dank eines internen Speichers an vergangene Eingaben, was für die Vorhersage von Aktienkursen, die Generierung von Texten, Transkriptionen und maschinelle Übersetzungen nützlich ist.

Im traditionellen neuronalen Netz sind die Eingänge und die Ausgänge unabhängig voneinander, während der Ausgang im RNN von früheren Elementen innerhalb der Sequenz abhängig ist. Rekurrente Netze teilen sich auch Parameter in jeder Schicht des Netzes. In Feedforward-Netzen gibt es unterschiedliche Gewichte für jeden Knoten. Während RNN in jeder Schicht des Netzes die gleichen Gewichte verwendet, werden die Gewichte und die Basis während des Gradientenabstiegs individuell angepasst, um den Verlust zu reduzieren.

RNN

Das Bild oben ist eine einfache Darstellung von rekurrenten neuronalen Netzen. Wenn wir Aktienkurse anhand einfacher Daten [45,56,45,49,50,...] prognostizieren, enthält jede Eingabe von X0 bis Xt einen Wert aus der Vergangenheit. X0 hat zum Beispiel die Zahl 45, X1 die Zahl 56, und diese Werte werden verwendet, um die nächste Zahl in einer Folge vorherzusagen.

Wie rekurrente neuronale Netze funktionieren

In RNN durchlaufen die Informationen die Schleife, sodass die Ausgabe durch die aktuelle Eingabe und die zuvor erhaltenen Eingaben bestimmt wird.

Die Eingabeschicht X verarbeitet die anfängliche Eingabe und leitet sie an die mittlere Schicht A weiter. Die mittlere Schicht besteht aus mehreren versteckten Schichten, jede mit eigenen Aktivierungsfunktionen, Gewichten und Vorspannungen. Diese Parameter werden für die versteckte Schicht vereinheitlicht, so dass statt mehrerer versteckter Schichten nur eine erstellt wird und diese in einer Schleife durchlaufen wird.

Anstelle der traditionellen Backpropagation verwenden rekurrente neuronale Netze Backpropagation through Time (BPTT) Algorithmen, um den Gradienten zu bestimmen. Bei der Backpropagation passt das Modell die Parameter an, indem es Fehler von der Ausgangs- zur Eingangsschicht berechnet. BPTT summiert den Fehler bei jedem Zeitschritt, da RNN die Parameter in jeder Schicht teilt. Erfahre mehr über RNNs und wie sie funktionieren unter Was sind rekurrente neuronale Netze?

Arten von rekurrenten neuronalen Netzen

Feedforward-Netze haben nur einen Eingang und einen Ausgang, während rekurrente neuronale Netze flexibel sind, da die Länge der Ein- und Ausgänge verändert werden kann. Diese Flexibilität ermöglicht es RNNs, Musik zu erzeugen, Gefühle zu klassifizieren und maschinell zu übersetzen.

Es gibt vier Arten von RNN, die auf der unterschiedlichen Länge der Eingänge und Ausgänge basieren.

  • One-to-One ist ein einfaches neuronales Netz. Sie wird häufig für maschinelle Lernprobleme verwendet, die eine einzige Eingabe und Ausgabe haben.
  • One-to-many hat einen einzigen Eingang und mehrere Ausgänge. Dies wird für die Erstellung von Bildunterschriften verwendet.
  • Many-to-One nimmt eine Folge von mehreren Eingaben und sagt eine einzige Ausgabe voraus. Sie wird häufig bei der Sentiment-Klassifizierung eingesetzt, bei der die Eingabe ein Text und die Ausgabe eine Kategorie ist.
  • Many-to-many benötigt mehrere Eingänge und Ausgänge. Die häufigste Anwendung ist die maschinelle Übersetzung.
Arten von RNN

Beliebte Kurse zum Maschinellen Lernen

Rekurrente neuronale Netze (RNNs) für die Sprachmodellierung mit Keras

BeginnerSkill Level
4
14.6K learners
Lerne, wie man RNNs verwendet, um die Stimmung von Texten zu klassifizieren, Sätze zu generieren und Texte zwischen Sprachen zu übersetzen.
See DetailsRight Arrow

CNN vs. RNN

Das Convolutional Neural Network (CNN) ist ein neuronales Netzwerk mit Vorwärtskopplung, das räumliche Daten verarbeiten kann. Sie wird häufig für Computer-Vision-Anwendungen wie die Bildklassifizierung verwendet. Die einfachen neuronalen Netze sind gut bei einfachen binären Klassifizierungen, aber sie können keine Bilder mit Pixelabhängigkeiten verarbeiten. Die Architektur des CNN-Modells besteht aus Faltungsschichten, ReLU-Schichten, Pooling-Schichten und vollständig verbundenen Ausgabeschichten. Du kannst CNN lernen, indem du an einem Projekt wie " Convolutional Neural Networks in Python" arbeitest.

CNN Model Architecture

Hauptunterschiede zwischen CNN und RNN

  • CNN ist für spärliche Daten wie Bilder geeignet. RNN ist für Zeitreihen und sequentielle Daten geeignet.
  • Beim Trainieren des Modells verwendet CNN eine einfache Backpropagation und RNN verwendet Backpropagation über die Zeit, um den Verlust zu berechnen.
  • Bei RNN gibt es keine Längenbeschränkung für Eingänge und Ausgänge, aber bei CNN sind die Eingänge und Ausgänge endlich.
  • CNN hat ein Feedforward-Netzwerk und RNN arbeitet mit Schleifen, um sequentielle Daten zu verarbeiten.
  • CNN kann auch für die Video- und Bildverarbeitung eingesetzt werden. RNN wird hauptsächlich für die Sprach- und Textanalyse verwendet.

Einschränkungen von RNN

Bei einfachen RNN-Modellen gibt es in der Regel zwei große Probleme. Diese Probleme hängen mit dem Gradienten zusammen, der die Steigung der Verlustfunktion zusammen mit der Fehlerfunktion darstellt.

  1. Das Problem des verschwindenden Gradienten tritt auf, wenn der Gradient so klein wird, dass die Aktualisierung der Parameter unbedeutend wird; schließlich hört der Algorithmus auf zu lernen.
  2. Das Problem des explodierenden Gradienten tritt auf, wenn der Gradient zu groß wird, wodurch das Modell instabil wird. In diesem Fall häufen sich größere Fehlergradienten an, und die Modellgewichte werden zu groß. Dieses Problem kann zu längeren Trainingszeiten und schlechter Modellleistung führen.

Die einfache Lösung für diese Probleme besteht darin, die Anzahl der versteckten Schichten innerhalb des neuronalen Netzes zu reduzieren, was die Komplexität der RNNs verringert. Diese Probleme können auch durch den Einsatz fortschrittlicher RNN-Architekturen wie LSTM und GRU gelöst werden.

Erweiterte RNN-Architekturen

Die einfachen RNN-Wiederholungsmodule haben eine Grundstruktur mit einer einzigen tanh-Schicht. Die einfache RNN-Struktur leidet unter einem kurzen Gedächtnis, da sie Schwierigkeiten hat, die Informationen des vorherigen Zeitschritts in größeren sequenziellen Daten zu behalten. Diese Probleme können durch das Langzeitgedächtnis (Long Short Term Memory, LSTM) und die Gated Recurrent Unit (GRU) leicht gelöst werden, da sie in der Lage sind, sich lange Zeiträume von Informationen zu merken.

Simple RNN Cell

Langes Kurzzeitgedächtnis (LSTM)

Das Long Short Term Memory (LSTM) ist ein fortschrittlicher Typ von RNN, der entwickelt wurde, um sowohl das Problem des zerfallenden als auch des explodierenden Gradienten zu vermeiden. Genau wie RNN hat LSTM sich wiederholende Module, aber die Struktur ist anders. Anstelle einer einzigen Tanh-Schicht hat die LSTM vier interagierende Schichten, die miteinander kommunizieren. Diese vierschichtige Struktur hilft dem LSTM, das Langzeitgedächtnis zu bewahren, und kann bei verschiedenen sequentiellen Problemen wie maschineller Übersetzung, Sprachsynthese, Spracherkennung und Handschrifterkennung eingesetzt werden. Du kannst praktische Erfahrungen mit LSTM sammeln, indem du die Anleitung befolgst: Python LSTM für Aktienvorhersagen.

LSTM Cell

Gated Recurrent Unit (GRU)

Die Gated Recurrent Unit (GRU) ist eine Abwandlung der LSTM, da beide Ähnlichkeiten im Design aufweisen und in einigen Fällen ähnliche Ergebnisse liefern. GRU verwendet ein Update-Gate und ein Reset-Gate, um das Problem des verschwindenden Gradienten zu lösen. Diese Gates entscheiden, welche Informationen wichtig sind und geben sie an den Ausgang weiter. Die Gates können darauf trainiert werden, Informationen zu speichern, die lange zurückliegen, ohne dass sie mit der Zeit verschwinden oder irrelevante Informationen entfernt werden.

Im Gegensatz zum LSTM hat die GRU keinen Zellzustand Ct. Es hat nur einen versteckten Zustand ht, und aufgrund der einfachen Architektur hat GRU im Vergleich zu LSTM-Modellen eine geringere Trainingszeit. Die GRU-Architektur ist einfach zu verstehen, denn sie nimmt die Eingabe xt und den versteckten Zustand vom vorherigen Zeitstempel ht-1 und gibt den neuen versteckten Zustand ht aus. Ausführliche Informationen über GRU findest du unter GRU-Netzwerke verstehen.

GRU Cell

MasterCard Stock Price Prediction Using LSTM & GRU

In diesem Projekt werden wir den MasterCard-Aktiendatensatz von Kaggle vom 25. Mai 2006 bis zum 11. Oktober 2021 verwenden und die LSTM- und GRU-Modelle zur Vorhersage des Aktienkurses trainieren. Dies ist ein einfaches, projektbasiertes Tutorial, in dem wir Daten analysieren, vorverarbeiten, um sie auf fortgeschrittene RNN-Modelle zu trainieren, und schließlich die Ergebnisse auswerten werden.

Das Projekt benötigt Pandas und Numpy für die Datenbearbeitung, Matplotlib.pyplot für die Datenvisualisierung, scikit-learn für die Skalierung und Auswertung und TensorFlow für die Modellierung. Wir werden auch den Grundstein für die Reproduzierbarkeit legen.

# Importing the libraries
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, LSTM, Dropout, GRU, Bidirectional
from tensorflow.keras.optimizers import SGD
from tensorflow.random import set_seed

set_seed(455)
np.random.seed(455)

Datenanalyse

In diesem Teil werden wir den MasterCard-Datensatz importieren, indem wir die Datumsspalte zum Index hinzufügen und in das DateTime-Format konvertieren. Wir werden auch irrelevante Spalten aus dem Datensatz streichen, da wir nur an den Aktienkursen, dem Volumen und dem Datum interessiert sind.

Der Datensatz hat das Datum als Index und Open, High, Low, Close und Volume als Spalten. Es sieht so aus, als hätten wir erfolgreich einen bereinigten Datensatz importiert.

dataset = pd.read_csv(
    "data/Mastercard_stock_history.csv", index_col="Date", parse_dates=["Date"]
).drop(["Dividends", "Stock Splits"], axis=1)
print(dataset.head())
               Open      High       Low     Close     Volume
Date                                                         
2006-05-25  3.748967  4.283869  3.739664  4.279217  395343000
2006-05-26  4.307126  4.348058  4.103398  4.179680  103044000
2006-05-30  4.183400  4.184330  3.986184  4.093164   49898000
2006-05-31  4.125723  4.219679  4.125723  4.180608   30002000
2006-06-01  4.179678  4.474572  4.176887  4.419686   62344000

Die Funktion .describe() hilft uns, die Daten eingehend zu analysieren. Konzentrieren wir uns auf die Spalte Hoch, da wir sie zum Trainieren des Modells verwenden werden. Wir können auch die Spalten "Close" oder " Open" für ein Modellmerkmal wählen, aber "High" macht mehr Sinn, da es uns Informationen darüber liefert, wie hoch die Werte der Aktie an dem jeweiligen Tag gestiegen sind.

Der niedrigste Aktienkurs liegt bei 4,10 $ und der höchste bei 400,5 $. Der Mittelwert liegt bei $105,9 und die Standardabweichung bei $107,3, was bedeutet, dass die Aktien eine hohe Varianz haben.

print(dataset.describe())
             Open         High          Low        Close        Volume
count  3872.000000  3872.000000  3872.000000  3872.000000  3.872000e+03
mean    104.896814   105.956054   103.769349   104.882714  1.232250e+07
std     106.245511   107.303589   105.050064   106.168693  1.759665e+07
min       3.748967     4.102467     3.739664     4.083861  6.411000e+05
25%      22.347203    22.637997    22.034458    22.300391  3.529475e+06
50%      70.810079    71.375896    70.224002    70.856083  5.891750e+06
75%     147.688448   148.645373   146.822013   147.688438  1.319775e+07
max     392.653890   400.521479   389.747812   394.685730  3.953430e+08

Mit .isna().sum() können wir die fehlenden Werte im Datensatz ermitteln. Es scheint, dass der Datensatz keine fehlenden Werte enthält.

dataset.isna().sum()
Open      0
High      0
Low       0
Close     0
Volume    0
dtype: int64

Die Funktion train_test_plot benötigt drei Argumente: dataset, tstart und tend und zeichnet ein einfaches Liniendiagramm. Die Angaben tstart und tend sind Zeitgrenzen in Jahren. Wir können diese Argumente ändern, um bestimmte Zeiträume zu analysieren. Das Liniendiagramm ist in zwei Teile unterteilt: Training und Test. So können wir die Verteilung des Testdatensatzes bestimmen.

Die Aktienkurse von MasterCard sind seit 2016 gestiegen. Im ersten Quartal 2020 gab es einen Einbruch, aber in der zweiten Jahreshälfte hat er sich stabilisiert. Unser Testdatensatz besteht aus einem Jahr, von 2021 bis 2022, und der Rest des Datensatzes wird zum Training verwendet.

tstart = 2016
tend = 2020

def train_test_plot(dataset, tstart, tend):
    dataset.loc[f"{tstart}":f"{tend}", "High"].plot(figsize=(16, 4), legend=True)
    dataset.loc[f"{tend+1}":, "High"].plot(figsize=(16, 4), legend=True)
    plt.legend([f"Train (Before {tend+1})", f"Test ({tend+1} and beyond)"])
    plt.title("MasterCard stock price")
    plt.show()

train_test_plot(dataset,tstart,tend)

Datenvorverarbeitung

Die Funktion train_test_split teilt den Datensatz in zwei Teilmengen: training_set und test_set.

def train_test_split(dataset, tstart, tend):
    train = dataset.loc[f"{tstart}":f"{tend}", "High"].values
    test = dataset.loc[f"{tend+1}":, "High"].values
    return train, test
training_set, test_set = train_test_split(dataset, tstart, tend)

Wir werden die Funktion MinMaxScaler verwenden, um unser Trainingsset zu standardisieren, was uns hilft, Ausreißer oder Anomalien zu vermeiden. Du kannst auch StandardScaler oder eine andere skalare Funktion verwenden, um deine Daten zu normalisieren und die Modellleistung zu verbessern.

sc = MinMaxScaler(feature_range=(0, 1))
training_set = training_set.reshape(-1, 1)
training_set_scaled = sc.fit_transform(training_set)

Die Funktion split_sequence verwendet einen Trainingsdatensatz und wandelt ihn in Eingaben (X_train) und Ausgaben (y_train) um.

Wenn die Sequenz beispielsweise [1,2,3,4,5,6,7,8,9,10,11,12] lautet und n_step drei ist, wird die Sequenz in drei Eingangszeitstempel und einen Ausgang umgewandelt, wie unten gezeigt:

X y
1,2,3 4
2,3,4 5
3,4,5 6
4,5,6 7

In diesem Projekt verwenden wir 60 n_steps. Wir können auch die Anzahl der Schritte reduzieren oder erhöhen, um die Modellleistung zu optimieren.

def split_sequence(sequence, n_steps):
    X, y = list(), list()
    for i in range(len(sequence)):
        end_ix = i + n_steps
        if end_ix > len(sequence) - 1:
            break
        seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
        X.append(seq_x)
        y.append(seq_y)
    return np.array(X), np.array(y)


n_steps = 60
features = 1
# split into samples
X_train, y_train = split_sequence(training_set_scaled, n_steps)

Wir arbeiten mit univariaten Reihen, also ist die Anzahl der Merkmale eins, und wir müssen den X_train so umformen, dass er auf das LSTM-Modell passt. Der X_train hat [Samples, Timesteps], und wir werden ihn in [Samples, Timesteps, Features] umwandeln.

# Reshaping X_train for model
X_train = X_train.reshape(X_train.shape[0],X_train.shape[1],features)

LSTM-Modell

Das Modell besteht aus einer einzelnen versteckten Schicht des LSTM und einer Ausgabeschicht. Du kannst mit der Anzahl der Einheiten experimentieren, da du mit mehr Einheiten bessere Ergebnisse erzielst. Für dieses Experiment stellen wir die LSTM-Einheiten auf 125, tanh als Aktivierung und die Eingabegröße ein.

Anmerkung des Autors: Die Tensorflow-Bibliothek ist benutzerfreundlich, so dass wir keine LSTM- oder GRU-Modelle von Grund auf erstellen müssen. Wir werden einfach die LSTM- oder GRU-Module verwenden, um das Modell zu erstellen.

Schließlich werden wir das Modell mit einem RMSprop-Optimierer und dem mittleren quadratischen Fehler als Verlustfunktion zusammenstellen.

# The LSTM architecture
model_lstm = Sequential()
model_lstm.add(LSTM(units=125, activation="tanh", input_shape=(n_steps, features)))
model_lstm.add(Dense(units=1))
# Compiling the model
model_lstm.compile(optimizer="RMSprop", loss="mse")

model_lstm.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
lstm (LSTM)                  (None, 125)               63500     
_________________________________________________________________
dense (Dense)                (None, 1)                 126       
=================================================================
Total params: 63,626
Trainable params: 63,626
Non-trainable params: 0
_________________________________________________________________

Das Modell wird mit 50 Epochen und 32 Stapelgrößen trainiert. Du kannst die Hyperparameter ändern, um die Trainingszeit zu verkürzen oder die Ergebnisse zu verbessern. Das Modelltraining wurde erfolgreich mit dem bestmöglichen Verlust abgeschlossen.

model_lstm.fit(X_train, y_train, epochs=50, batch_size=32)
Epoch 50/50
38/38 [==============================] - 1s 30ms/step - loss: 3.1642e-04

Ergebnisse

Wir werden die Vorverarbeitung wiederholen und die Testmenge normalisieren. Zunächst werden wir den Datensatz transformieren, dann in Stichproben aufteilen, umformen, vorhersagen und die Vorhersagen in die Standardform umkehren.

dataset_total = dataset.loc[:,"High"]
inputs = dataset_total[len(dataset_total) - len(test_set) - n_steps :].values
inputs = inputs.reshape(-1, 1)
#scaling
inputs = sc.transform(inputs)

# Split into samples
X_test, y_test = split_sequence(inputs, n_steps)
# reshape
X_test = X_test.reshape(X_test.shape[0], X_test.shape[1], features)
#prediction
predicted_stock_price = model_lstm.predict(X_test)
#inverse transform the values
predicted_stock_price = sc.inverse_transform(predicted_stock_price)

Mit der Funktion plot_predictions kannst du ein Liniendiagramm erstellen, in dem der tatsächliche Wert dem vorhergesagten Wert gegenübergestellt wird. So können wir den Unterschied zwischen den tatsächlichen und den vorhergesagten Werten sichtbar machen.

Die Funktion return_rmse nimmt Test- und Vorhersageargumente entgegen und gibt den mittleren quadratischen Fehler (root mean square error, rmse) aus.

def plot_predictions(test, predicted):
    plt.plot(test, color="gray", label="Real")
    plt.plot(predicted, color="red", label="Predicted")
    plt.title("MasterCard Stock Price Prediction")
    plt.xlabel("Time")
    plt.ylabel("MasterCard Stock Price")
    plt.legend()
    plt.show()


def return_rmse(test, predicted):
    rmse = np.sqrt(mean_squared_error(test, predicted))
    print("The root mean squared error is {:.2f}.".format(rmse))

Wie die folgende Grafik zeigt, hat das einschichtige LSTM-Modell gut abgeschnitten.

plot_predictions(test_set,predicted_stock_price)

Die Ergebnisse sehen vielversprechend aus, da das Modell im Testdatensatz 6,70 rmse erreichte.

return_rmse(test_set,predicted_stock_price)
>>> The root mean squared error is 6.70.

GRU Modell

Wir lassen alles beim Alten und ersetzen nur die LSTM-Schicht durch die GRU-Schicht, um die Ergebnisse richtig vergleichen zu können. Die Modellstruktur enthält eine einzelne GRU-Schicht mit 125 Einheiten und eine Ausgabeschicht.

model_gru = Sequential()
model_gru.add(GRU(units=125, activation="tanh", input_shape=(n_steps, features)))
model_gru.add(Dense(units=1))
# Compiling the RNN
model_gru.compile(optimizer="RMSprop", loss="mse")

model_gru.summary()
Model: "sequential_5"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
gru_4 (GRU)                  (None, 125)               48000     
_________________________________________________________________
dense_5 (Dense)              (None, 1)                 126       
=================================================================
Total params: 48,126
Trainable params: 48,126
Non-trainable params: 0
_________________________________________________________________

Das Modell wurde erfolgreich mit 50 Epochen und einer Stapelgröße von 32 trainiert.

model_gru.fit(X_train, y_train, epochs=50, batch_size=32)
Epoch 50/50
38/38 [==============================] - 1s 29ms/step - loss: 2.6691e-04

Ergebnisse

Wie wir sehen können, liegen die realen und die vorhergesagten Werte relativ nah beieinander. Das vorhergesagte Liniendiagramm stimmt fast mit den tatsächlichen Werten überein.

GRU_predicted_stock_price = model_gru.predict(X_test)
GRU_predicted_stock_price = sc.inverse_transform(GRU_predicted_stock_price)
plot_predictions(test_set, GRU_predicted_stock_price)

Das GRU-Modell erreichte im Testdatensatz 5,50 rmse, was eine Verbesserung gegenüber dem LSTM-Modell darstellt.

return_rmse(test_set,GRU_predicted_stock_price)
>>> The root mean squared error is 5.50.

Fazit

Die Welt bewegt sich auf hybride Lösungen zu, bei denen Datenwissenschaftler hybride CNN-RNN-Netzwerke im Bereich der Bilduntertitelung, der Emotionserkennung, der Videountertitelung und der DNA-Sequenzierung einsetzen. Hybride Netzwerke liefern sowohl visuelle als auch zeitliche Merkmale für das Modell. Erfahre mehr über RNN, indem du den Kurs besuchst: Rekurrente neuronale Netze für die Sprachmodellierung in Python.

In der ersten Hälfte des Tutoriums werden die Grundlagen rekurrenter neuronaler Netze, ihre Grenzen und Lösungen in Form fortschrittlicherer Architekturen behandelt. In der zweiten Hälfte des Tutorials geht es um die Entwicklung von MasterCard-Aktienkursvorhersagen mithilfe von LSTM- und GRU-Modellen. Die Ergebnisse zeigen deutlich, dass das GRU-Modell bei ähnlicher Struktur und ähnlichen Hyperparametern besser abschneidet als das LSTM.

Dieses Projekt ist auf dem DataCamp-Arbeitsbereich verfügbar.

Themen