In diesem Leitfaden werde ich Ihnen vier der wichtigsten Aktivierungsfunktionen vorstellen, die in Deep Learning verwendet werden: Sigmoid, Tanh, ReLU & Leaky ReLU. Insbesondere wird es in diesem Leitfaden darum gehen, was Aktivierungsfunktionen sind, wann wir welche Aktivierungsfunktionen verwenden müssen und wie diese in der Praxis in TensorFlow implementiert werden können.
Kurze Antwort: Wir müssen Aktivierungsfunktionen wie ReLu, Sigmoid und tanh verwenden, um dem neuronalen Netzwerk eine nicht lineare Eigenschaft zu verleihen. Auf diese Weise kann das Netz komplexere Beziehungen und Muster in den Daten modellieren.
Falls Sie zunächst Ihr Wissen über neuronalen Netze auffrischen möchten, empfehle ich Ihnen meinen Artikel „Künstliche neuronale Netze“
Das Wichtigste auf einen Blick
- Aktivierungsfunktionen fügen dem neuronalen Netz eine nicht lineare Eigenschaft hinzu. Auf diese Weise kann das Netz komplexere Daten modellieren.
- Es gibt drei gängige Aktivierungsfunktionen: Sigmoid, tanh und ReLU
- ReLU sollte generell als Aktivierungsfunktion in den verborgenen Schichten verwendet werden.
- Bei der Ausgabeschicht muss immer der Erwartungswertbereich der Vorhersagen berücksichtigt werden.
- Für Klassifizierungsaufgaben empfehle ich, in der Ausgabeschicht ausschließlich die Softmax-Aktivierung zu verwenden
Inhaltsübersicht
- Was ist eine Aktivierungsfunktion?
- Rekapitulation: Vorwärtspropagierung
- Neuronales Netz ist eine Funktion
- Warum brauchen wir Aktivierungsfunktionen?
- Verschiedene Arten von Aktivierungsfunktionen (sigmoid, tanh, ReLU, leaky ReLU, softmax)
- Welche Aktivierungsfunktionen sollten wir verwenden?
- Take-Home-Message
1. Was sind Aktivierungsfunktionen?
Eine Aktivierungsfunktion bestimmt den Wertebereich der Aktivierung eines künstlichen Neurons. Diese wird angewandt auf die Summe der gewichteten Eingangsdaten des Neurons. Eine Aktivierungsfunktion zeichnet sich durch
die Eigenschaft der Nicht-Linearität aus. Ohne die Anwendung einer Aktivierungsfunktion wären die einzigen Operationen bei der Berechnung der Ausgabe eines Multilayer Perceptrons die linearen Produkte zwischen den Gewichten und den Eingabewerten. Hintereinander ausgeführte, lineare Operationen können als eine einzige lineare Operation angesehen werden.
Die Anwendung der nicht-linearen Aktivierungsfunktion führt dagegen zu einer Nicht-Linearität des künstlichen neuronalen Netzes und damit zu einer Nicht-Linearität der Funktion, die das neuronale Netz approximiert. Laut dem Approximationstheorem ist ein Multilayer Perceptron mit nur einer versteckten Schicht, in der eine nicht-lineare Aktivierungsfunktion verwendet wird, ein universeller Funktionsapproximator.
Im Folgenden werden vier der gängigsten Aktivierungsfunktionen vorgestellt, die in der Praxis Verwendung finden.
2. Rekapitulation: Vorwärtspropagation
Um die Bedeutung von Aktivierungsfunktionen zu verstehen, müssen wir zunächst rekapitulieren, wie ein neuronales Netz eine Vorhersage/Ausgabe berechnet. Dies wird im Allgemeinen als Vorwärtspropagation bezeichnet. Im folgenden werde ich diesen Prozess nur kurz erläutern. Eine detaillierte Erklärung der Vorwärtspropogation finden Sie in meinem Artikel „Training der künstlichen neuronalen Netze.“
Bei der Vorwärtspropagation erhält das neuronale Netz einen Eingangsvektor und berechnet einen Vorhersagevektor
. Wie läuft sowas ab?
Betrachten Sie bitte das folgende neuronale Netz mit einer Eingabe, einer Ausgabe und drei versteckten Schichten:
Jede Schicht des Netzes ist über eine sogenannte Gewichtsmatrix mit der nächsten Schicht verbunden. Insgesamt haben wir im Beispiel in Abb. 1 vier Gewichtsmatrizen ,
,
und
.
Bei einem Eingangsvektor berechnen wir ein Produkt mit der ersten Gewichtsmatrix
und wenden die Aktivierungsfunktion auf das Ergebnis dieses Produkts an. Das Ergebnis ist ein neuer Vektor
, der die Werte der Neuronen in der ersten Schicht darstellt. Dieser Vektor
wird als neuer Eingangsvektor für die nächste Schicht verwendet, wo die gleichen Operationen erneut durchgeführt werden. Dies wird so lange wiederholt, bis wir den endgültigen Ausgangsvektor
erhalten, der als Vorhersage des neuronalen Netzes betrachtet wird.
Die gesamte Reihe von Operationen kann durch die folgenden Gleichungen dargestellt werden, wobei σ eine beliebige Aktivierungsfunktion darstellt:

3. Neuronales Netzwerk ist eine Funktion
An dieser Stelle möchte ich mit Ihnen eine andere Interpretation diskutieren, die zur Beschreibung eines neuronalen Netzwerks verwendet werden kann. Anstatt ein neuronales Netzwerk als eine Sammlung von Neuronen und Verbindungen zu betrachten, können wir das neuronale Netz schlicht als eine Funktion ansehen.
Wie jede gewöhnliche mathematische Funktion führt ein neuronales Netz eine mathematische Abbildung von Eingabe auf Ausgabe
durch.
Das Konzept der Berechnung einer Ausgabe für eine Eingabe
dürfte Ihnen bereits bekannt sein. Es ist das Konzept einer gewöhnlichen mathematischen Funktion. In der Mathematik können wir eine Funktion
wie folgt definieren:
Funktion
Diese Funktion nimmt drei Eingaben ,
und
entgegen.
,
,
sind die Funktionsparameter, die bestimmte Werte annehmen. Angesichts der Eingaben
,
und
berechnet die Funktion eine Ausgabe
.
Im Grunde genommen funktioniert ein neuronales Netz genau auf diese Art und Weise. Wir nehmen einen Eingangsvektor und geben ihn in das neuronale Netz ein. Das Netz wiederum berechnet mit
eine Ausgabe
.
Anstatt also ein neuronales Netz als eine einfache Ansammlung von Neuronen und Verbindungen zu betrachten, können wir uns ein neuronales Netz als eine Funktion vorstellen. Diese Funktion umfasst alle Berechnungen, die wir zuvor in Formel 1 separat betrachtet haben, als eine einzige, verkettete Berechnung:
In Gleichung 2 hatte die einfache mathematische Funktion, die wir betrachtet haben, die Parameter ,
und
, die den Ausgabewert von
für eine Eingabe
bestimmen.
Im Falle eines neuronalen Netzes sind die Parameter der entsprechenden Funktion die Gewichte. Das bedeutet, dass unser Ziel beim Training eines neuronalen Netzes darin besteht, einen bestimmten Satz von Gewichten oder Parametern zu finden, sodass wir mit einem gegebenem Eingabevektor eine Vorhersage
berechnen können, die dem tatsächlichen Zielwert (Label)
entspricht.
Mit anderen Worten: Wir versuchen, eine Funktion zu erstellen, die unsere Trainingsdaten modellieren kann.
Eine Frage, die Sie sich vielleicht stellen, lautet: Können wir beliebige Daten mit einem neuronalen Netz immer modellieren? Können wir immer Gewichte finden, die eine Funktion definieren, mit der eine bestimmte Vorhersage y für gegebene Merkmale x berechnet werden kann? Die Antwort lautet nein. Wir können die Daten nur dann modellieren, wenn es eine mathematische Abhängigkeit zwischen dem Eingabevektor und den Labels
existiert.
Diese mathematische Abhängigkeit kann unterschiedlich komplex sein. Und in den meisten Fällen können wir als Menschen diese Beziehung nicht mit unseren Augen sehen, wenn wir einen Blick auf die Daten werfen. Wenn jedoch eine mathematische Abhängigkeit zwischen den Eingabevektoren und den Labels besteht, können wir sicher sein, dass das neuronale Netz diese Abhängigkeit während des Trainings erkennt und die Gewichte so anpasst, dass es diese Abhängigkeit in den Trainingsdaten modellieren werden kann. Oder anders ausgedrückt: damit es eine mathematische Abbildung von Eingangsmerkmalen auf die Ausgabe
realisieren werden kann.
4. Warum brauchen wir Aktivierungsfunktionen?
Der Zweck einer Aktivierungsfunktion besteht darin, der Funktion, die ein neuronales Netz darstellt, eine Art nicht lineare Eigenschaft zu verleihen. Ohne die Aktivierungsfunktionen könnte das neuronale Netz nur lineare Zuordnungen von Eingaben zu den Ausgaben
vornehmen. Warum ist das so?
Ohne die Aktivierungsfunktionen bestünde die einzige mathematische Operation während der Vorwärtspropagation in Produkten zwischen einem Eingangsvektor und einer Gewichtsmatrix.
Da ein einzelnes Produkt eine lineare Operation darstellt, wären mehrere aufeinanderfolgende Produkte nichts anderes als mehrere lineare Operationen, die nacheinander wiederholt werden. Und mehrere, aufeinanderfolgende lineare Operationen können als eine einzige lineare Operation betrachtet werden.
Um wirklich interessante Dinge berechnen zu können, müssen neuronale Netze in der Lage sein, nicht lineare Beziehungen zwischen Eingabevektoren und Ausgaben
zu approximieren. Je komplexer die Daten sind, aus denen wir etwas zu lernen versuchen, desto „nicht linearer“ ist in der Regel die Abbildung von
auf
.
Ein neuronales Netz, das keine Aktivierungsfunktion in der versteckten Schicht aufweist, wäre nicht in der Lage, solche komplexen Beziehungen mathematisch zu realisieren, und wäre nicht in der Lage, die Aufgaben zu lösen, die wir mit dem Netz lösen wollen.
5. Die vier wichtigsten Aktivierungsfunktionen
An dieser Stelle sollten wir die wichtigsten Aktivierungsfunktionen, die beim Deep Learning verwendet werden, sowie ihre Vor- und Nachteile diskutieren.
4.1 Sigmoidfunktion
Vor einigen Jahren war die wahrscheinlich häufigste Aktivierungsfunktion die Sigmoidfunktion. Die Sigmoidfunktion bildet die eingehenden Eingaben auf einen Bereich zwischen 0 und 1 ab:

Die mathematische Definition der Sigmoidfunktion sieht wie folgt aus:

Die Funktion nimmt einen Eingangswert entgegen und gibt eine Ausgabe im Intervall (0, 1]) zurück. In der Praxis ist die sigmoide Nichtlinearität in letzter Zeit in Ungnade gefallen und wird nur noch selten verwendet. Sie hat zwei Hauptnachteile:
Sigmoid „tötet“ Gradienten
Der erste ist, dass bei Sigmoidfunktionen Gradienten verschwinden können. Eine sehr unerwünschte Eigenschaft der Funktion ist, dass die Aktivierung der Neuronen entweder in der Nähe von 0 oder in der Nähe von1 (blaue Bereiche) gesättigt wird:

Die Ableitung der Sigmoidfunktion wird für diese blauen Bereiche (d. h. große negative oder positive Eingangswerte) sehr klein. In diesem Fall würde die Ableitung nahe null den Gradienten der Verlustfunktion sehr klein machen, was die Aktualisierung der Gewichte und damit den gesamten Lernprozess verhindert.
Sigmoid ist nicht null-zentriert
Eine weitere unerwünschte Eigenschaft der Sigmoid-Aktivierung ist die Tatsache, dass die Ausgaben der Funktion nicht null-zentriert sind. Dies macht das Training des neuronalen Netzes in der Regel schwieriger und instabiler.
Betrachten wir ein „sigmoides“ Neuron mit den Eingängen
und
, gewichtet mit
und
:

und
sind die Ausgänge einer vorherigen versteckten Schicht mit sigmoidaler Aktivierung. Somit sind
und
immer positiv, da das Sigmoid nicht null-zentriert ist. Abhängig von dem Gradienten des gesamten Ausdrucks
ist der Gradient in Bezug auf
und
immer entweder positiv für
und
oder negativ für
und
.
Häufig erfordert der optimale Schritt des Gradientenabstiegs eine Erhöhung von und eine Verringerung von
. Da
und
also immer positiv sind, können wir die Gewichte nicht gleichzeitig erhöhen und verringern, sondern nur alle Gewichte gleichzeitig erhöhen oder verringern.
Mit folgendem Code können wir in TensorFlow die Sigmoid-Funktion implementieren.
import tensorflow as tf
from tensorflow.keras.activations import sigmoid
z = tf.constant([-1.5, -0.2, 0, 0.5], dtype=tf.float32)
output = sigmoid(z)
print(output.numpy()) #[0.18242553, 0.45016602, 0.5, 0.62245935]
4.2 Tanh-Aktivierungsfunktion
Eine weitere, beim Deep Learning sehr häufig verwendete Aktivierungsfunktion ist die Tanh-Funktion. Die Tangens-Hyperbolicus Funktion ist in der folgenden Abbildung dargestellt:

Die Funktion bildet eine reellwertige Zahl gemäß der folgenden Gleichung auf den Bereich [-1, 1] ab:

Wie bei der Sigmoidfunktion sättigen die Neuronen bei großen negativen und positiven Werten, und die Ableitung der Funktion geht gegen null (blauer Bereich in Abb. 3). Im Gegensatz zur Sigmoidfunktion sind ihre Ausgänge jedoch null-zentriert. Daher wird in der Praxis die tanh-Funktion immer der sigmoiden Funktion vorgezogen.
Mit folgendem Code können wir in TensorFlow die tanh-Funktion implementieren:
import tensorflow as tf
from tensorflow.keras.activations import tanh
z = tf.constant([-1.5, -0.2, 0, 0.5], dtype=tf.float32)
output = tanh(z)
print(output.numpy()) # [-0.90514827, -0.19737533, 0., 0.46211714]
4.3 Gleichgerichtete Lineareinheit – ReLU
Die Rectified Linear Unit oder einfach nur ReLU ist in den letzten Jahren sehr populär geworden. Die Aktivierung ist linear für Eingabewerte größer null: oder genauer gesagt:

Für Eingaben, die größer als Null sind, erhalten wir eine lineare Abbildung:

Es gibt mehrere Vor- und Nachteile der Verwendung von ReLUs:
- (+) In der Praxis hat sich gezeigt, dass ReLU die Konvergenz des Gradientenabstiegs in Richtung des globalen Minimums der Verlustfunktion im Vergleich zu anderen Aktivierungsfunktionen beschleunigt. Dies ist auf seine lineare, nicht sättigende Eigenschaft zurückzuführen.
- (+) Während andere Aktivierungsfunktionen (tanh und sigmoid) sehr rechenaufwändige Operationen wie z.B. Exponenten usw. erfordern, kann ReLU dagegen einfach durch Schwellenwertbildung eines Wertevektors bei null implementiert werden.
- (-) Leider gibt es auch ein Problem mit der ReLU-Aktivierungsfunktion. Da die Ausgänge dieser Funktion bei Eingangswerten unter Null gleich Null sind, können die Neuronen des Netzes beim Training sehr fragil werden und sogar „sterben“. Was ist damit gemeint? Es kann (muss nicht, kann aber) passieren, dass bei der Aktualisierung der Gewichte, die Gewichte auf eine Art und Weise angepasst werden, dass für bestimmte Neuronen einer versteckten Schicht die Eingaben
immer unter null liegen. Das bedeutet, dass die Werte
dieser Neuronen (mit
als ReLU Funktion) immer null (
) sind und damit keinen Beitrag zum Trainingsprozess leisten. Das bedeutet, dass der Gradient, der durch diese ReLU-Neuronen fließt, von diesem Zeitpunkt an ebenfalls Null ist. Wir sagen, dass die Neuronen „tot“ sind. Es ist zum Beispiel sehr häufig zu beobachten, dass 20-50 % des gesamten neuronalen Netzes, das die ReLU-Funktion verwendet hat, „tot“ sind. Mit anderen Worten, diese Neuronen werden im gesamten beim Training verwendeten Datensatz nie aktiviert.
Mit folgendem Code können wir in TensorFlow die ReLU-Funktion implementieren:
import tensorflow as tf
from tensorflow.keras.activations import relu
z = tf.constant([-1.5, -0.2, 0, 0.5], dtype=tf.float32)
output = relu(z)
print(output.numpy()) #[0. 0. 0. 0.5]
4.4 Leaky ReLU
Leaky ReLu ist nichts anderes als eine verbesserte Version der ReLU-Aktivierungsfunktion. Wie im vorangegangenen Abschnitt erwähnt, kann es vorkommen, dass durch die Verwendung von ReLU einige Neuronen in unserem neuronalen Netz „getötet“ werden und diese Neuronen nie mehr aktiv werden.
Leaky ReLU wurde definiert, um dieses Problem zu lösen. Im Gegensatz zur „Vanilla„-ReLU, bei der alle Werte der Neuronen für Eingabewerte
Null sind, fügen wir im Fall von Leaky ReLU eine kleine lineare Komponente zur Funktion hinzu:

Die Leaky-ReLU-Aktivierung sieht wie folgt aus:

Im Grunde haben wir die horizontale Linie für Werte unter Null durch eine nicht-horizontale, lineare Linie ersetzt. Die Steigung dieser linearen Linie kann durch den Parameter α eingestellt werden, der mit der Eingabe multipliziert wird.
Der Vorteil der Verwendung von Leaky ReLU und der Ersetzung der horizontalen Linie besteht darin, dass wir null-Gradienten vermeiden. Denn in diesem Fall haben wir keine „toten“ Neuronen mehr, die immer Null sind und damit keinen Beitrag zum Training mehr leisten.
Mit folgendem Code können wir in TensorFlow die Leaky-ReLU-Funktion implementieren:
import tensorflow as tf
from tensorflow.keras.layers import LeakyReLU
leaky_relu = LeakyReLU(alpha=0.01)
z = tf.constant([-1.5, -0.2, 0, 0.5], dtype=tf.float32)
output=leaky_relu(z)
print(output.numpy()) # [-0.015, -0.002, 0.,0.5]
4.5 Softmax-Aktivierungsfunktion
Zu guter Letzt möchte ich noch die Softmax-Aktivierungsfunktion vorstellen. Diese Aktivierungsfunktion ist ziemlich einzigartig.
Softmax wird nur in der letzten Schicht angewandt und nur dann, wenn das neuronale Netz bei Klassifizierungsaufgaben Wahrscheinlichkeitswerte vorhersagen soll.
Einfach gesagt, zwingt die Softmax-Aktivierungsfunktion die Werte der Ausgangsneuronen dazu, Werte zwischen Null und Eins anzunehmen, sodass diese Wahrscheinlichkeitswerte im Intervall [0, 1] darstellen können.
Ein weiterer Punkt, den wir berücksichtigen müssen, ist, dass bei der Klassifizierung von Eingabemerkmalen in verschiedene Klassen, diese Klassen sich gegenseitig ausschließen. Das bedeutet, dass jeder Merkmalsvektor nur zu einer Klasse gehört. Das bedeutet, dass ein Merkmalsvektor, der ein Bild eines Hundes ist, nicht mit einer Wahrscheinlichkeit von 50 % die Klasse Hund und mit einer Wahrscheinlichkeit von 50 % die Klasse Katze repräsentieren kann. Dieser Merkmalsvektor muss die Hundeklasse mit einer Wahrscheinlichkeit von 100 % repräsentieren.
Außerdem müssen bei sich gegenseitig ausschließenden Klassen die Wahrscheinlichkeitswerte aller Ausgangsneuronen in der Summe eins ergeben. Nur auf diese Weise repräsentiert das neuronale Netz eine korrekte Wahrscheinlichkeitsverteilung. Ein Gegenbeispiel wäre ein neuronales Netz, das das Bild eines Hundes mit einer Wahrscheinlichkeit von 80 % in die Klasse Hund und mit einer Wahrscheinlichkeit von 60 % in die Klasse Katze einordnet.
Glücklicherweise zwingt die Softmax-Funktion die Ausgaben nicht nur in den Bereich zwischen 0 und 1, sondern sorgt auch dafür, dass die Summe der Ausgaben über alle möglichen Klassen hinweg eins ergibt. Schauen wir uns nun an, wie die Softmax-Funktion funktioniert.
Stellen Sie sich vor, dass die Neuronen in der Ausgabeschicht einen Eingabevektor erhalten, der das Ergebnis eines Produkts zwischen einer Gewichtsmatrix der aktuellen Schicht und der Ausgabe der vorherigen Schicht ist. Ein Neuron in der Ausgabeschicht mit einer Softmax-Aktivierung erhält einen einzelnen Wert
, der ein Eintrag im Vektor
ist, und gibt den Wert
aus.
Wenn wir die Softmax-Aktivierung verwenden, wird jede einzelne Ausgabe eines Neurons in der Ausgabeschicht gemäß der folgenden Gleichung berechnet:

Wie Sie sehen können, hängt jeder Wert eines bestimmten Neurons nicht nur von dem Wert
ab, den das Neuron erhält, sondern von allen Werten im Vektor
. Dies macht jeden Wert
eines Ausgangsneurons zu einem Wahrscheinlichkeitswert zwischen 0 und 1. Und die Wahrscheinlichkeitsvorhersagen über alle Ausgangsneuronen summieren sich zu eins.
Auf diese Weise repräsentieren die Ausgangsneuronen nun eine Wahrscheinlichkeitsverteilung über die sich gegenseitig ausschließenden Klassenbezeichnungen.
5. Welche Aktivierungsfunktionen sollten wir verwenden?
Ich werde diese Frage mit der besten Antwort beantworten, die es gibt: Es kommt darauf an. ¯\_(ツ)_/
Insbesondere hängt es von dem Problem ab, das Sie zu lösen versuchen, und von dem Wertebereich der Ausgabe, die Sie erwarten.
Wenn Ihr neuronales Netz beispielsweise Werte vorhersagen soll, die größer als 1 sind, dann sind tanh oder Sigmoid nicht für die Ausgabeschicht geeignet, und wir müssen stattdessen ReLU verwenden.
Erwartet man hingegen, dass die Ausgabewerte im Bereich [0,1] oder [-1, 1] liegen, dann ist ReLU keine gute Wahl für die Ausgabeschicht und man muss sigmoid oder tanh verwenden.
Wenn Sie eine Klassifizierungsaufgabe durchführen und möchten, dass das neuronale Netz eine Wahrscheinlichkeitsverteilung über die sich gegenseitig ausschließenden Klassenlabels vorhersagt, dann sollte die Softmax-Aktivierungsfunktion in der letzten Schicht verwendet werden. Sie sollten den Kreuzentropie-Verlust immer dann verwenden, wenn es um Wahrscheinlichkeiten geht. Das heißt, wenn Sie eine Art von Klassifizierung durchführen. Ein praktisches Beispiel für eine Aufgabe der Klassifizierung, bei der die Softmax-Funktion in der letzten Schicht verwendet wird, finden Sie in meinem Artikel „Klassifizierung von Emotionen in Netflix Reviews mittels KI„.
Was jedoch die versteckten Schichten betrifft, so würde ich als Faustregel empfehlen, immer ReLU als Aktivierung für diese Schichten zu verwenden.