Dies ist ein Studienmemo (7. Ausgabe) zur Bildklassifizierung (Google Colaboratory-Umgebung) mit TensorFlow2 + Keras. Das Thema ist die Klassifizierung von handgeschriebenen numerischen Bildern (MNIST), die ein Standardelement ist.
Letztes Mal über das von ihm erstellte Bild "[Einführung in TensorFlow 2.0 für Anfänger](https: //www.tensorflow. Die Vorhersage (Klassifizierung) wurde unter Verwendung des in "org / tutorials / quickstart / beginner? Hl = ja)" eingeführten Modells durchgeführt.
Dieses Mal habe ich das in diesem Tutorial vorgestellte neuronale Netzwerkmodell, die Arten der Schichten, aus denen es besteht (Dense
, Dropout
, Flatten
) und die Aktivierungsfunktion untersucht.
Der folgende Code ist eine Kopie von "Einführung in TensorFlow 2.0 für Anfänger".
Konstruktion des NN-Modells (Beschreibung 1)
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(10, activation='softmax')
])
Im obigen Code wird das Schlüsselwortargument "Aktivierung", das die ** Aktivierungsfunktion ** angibt, als Zeichenfolge angegeben. Es kann jedoch auch angegeben werden, indem die Funktion direkt wie folgt angegeben wird.
Konstruktion des NN-Modells (Beschreibung 2)
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(128, activation=tf.nn.relu), #Veränderung
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(10, activation=tf.nn.softmax) #Veränderung
])
Auch hier wird die Schichtzusammensetzungsinformation des neuronalen Netzes als Listentyp als Argument von "Sequential (...)" angegeben, aber die ** -Schicht wird unter Verwendung von "add (...)" wie folgt angegeben. Sie können auch ** einzeln hinzufügen.
Konstruktion des NN-Modells (Beschreibung 3)
model = tf.keras.models.Sequential() # (0)
model.add( tf.keras.layers.Flatten(input_shape=(28, 28)) ) # (1)
model.add( tf.keras.layers.Dense(128, activation=tf.nn.relu) ) # (2)
model.add( tf.keras.layers.Dropout(0.2) ) # (3)
model.add( tf.keras.layers.Dense(10, activation=tf.nn.softmax) ) # (4)
Sie können sich einen Überblick über das NN-Modell verschaffen, indem Sie die Ebenen wie oben mit "summary ()" festlegen.
Überprüfen Sie den Umriss des Modells
model.summary()
Ausführungsergebnis
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
flatten (Flatten) (None, 784) 0
_________________________________________________________________
dense (Dense) (None, 128) 100480
_________________________________________________________________
dropout (Dropout) (None, 128) 0
_________________________________________________________________
dense_1 (Dense) (None, 10) 1290
=================================================================
Total params: 101,770
Trainable params: 101,770
Non-trainable params: 0
_________________________________________________________________
Von oben nach unten ist die Tabelle ** Eingabeebene **, ** Zwischenebene (versteckte Ebene) **, ..., ** Ausgabeebene **.
Der Wert ganz links in der Tabelle ist "Layername". Es wird automatisch zugewiesen, wenn name =
in add ()
weggelassen wird und jedes Mal, wenn das Modell erstellt wird, wie flatten_1
, flatten_2
nummeriert wird.
Der zweite ()
Wert von links ist der "Layertyp". Hier gibt es drei Arten: "Abflachen", "Dicht" und "Aussetzer". Dieser Kommentar befindet sich im nächsten Abschnitt.
Der zweite numerische Wert des Taples des Elements "Ausgabeform" ist ** die Anzahl der Neuronen in der relevanten Ebene (= die Anzahl der Ausgaben der relevanten Ebene) **. Wenn es "(None, 128)" ist, bedeutet dies, dass sich 128 Neuronen (Knoten) in dieser Schicht befinden.
Als nächstes ist der Punkt "Param" die Gesamtzahl der ** Parameter ** (** Gewichte ** und ** Bias ** bezogen auf die Eingabe der Ebene).
Zum Beispiel ist $ 100480 $ der zweiten Schicht "dicht (dicht)" ein Parameter von ** weight **, dh die Anzahl der Ausgänge der ersten Schicht $ 784 $ multipliziert mit der Anzahl der Knoten der zweiten Schicht $ 128 $, der zweiten Schicht. Die Gesamtzahl der Parameter einschließlich der ** Verzerrung ** von $ 128 $ Knoten. Das heißt, 784 $ mal 128 + 128 = 100480 $. Training (Training / Lernen) ist die Operation zum Finden der optimalen Werte für diese Parameter.
Am Ende der Tabelle steht die Anzahl der Gesamtparameter, trainierbaren Parameter (Parameter, die für das Training erforderlich sind) und nicht trainierbarer Parameter (Parameter, die für das Training nicht erforderlich sind).
Ein Bild von handgeschriebenen numerischen Zeichen ist 28 $ \ mal $ 28 Pixel groß und vom Typ (28,28) numpy.ndarray, einem zweidimensionalen Array. In der Ebene "Reduzieren" wird dies ** abgeflacht ** und in einem eindimensionalen Array festgelegt. Daher beträgt die Anzahl der Ausgaben $ 28 \ times 28 = 784 $, wie durch model.summary ()
bestätigt.
Das Programm fügt dem Modell eine Ebene zum Reduzieren hinzu:
model.add( tf.keras.layers.Flatten(input_shape=(28, 28)) ) # (1)
Im Argument "input_shape" wird "(28, 28)" angegeben, um mit "x_train [*] .shape" übereinzustimmen. Wenn Sie ein Bild mit 32 $ \ mal $ 32 Pixel eingeben möchten, verwenden Sie input_shape = (32, 32)
. Die Referenz ist hier.
Dies bedeutet eine ** vollständig verbundene Schicht **, die vollständig (fest verbunden) zwischen der vorherigen Schicht und der relevanten Schicht gebunden ist. Es ist die Standardschicht, aus der ein neuronales Netzwerk besteht.
Das Programm fügt dem Modell eine dichte Ebene wie folgt hinzu:
Vollständig verbundene Schicht als Zwischenschicht
model.add( tf.keras.layers.Dense(128, activation=tf.nn.relu) ) # (2)
Vollständig verbundene Schicht als Ausgangsschicht
model.add( tf.keras.layers.Dense(10, activation=tf.nn.softmax) ) # (4)
Das erste Argument ist die Anzahl der Knoten (Anzahl der Neuronen), aus denen die Schicht besteht. Wie viele Knoten der vollständig verbundenen Schicht sollten wie in (2) oben als Zwischenschicht festgelegt werden? ** ist ein Element, das die Leistung des Modells beeinflusst (es ist ein Hyperparameter, den der Benutzer durch Ausprobieren errät und festlegt). Beachten Sie, dass eine große Anzahl von Knoten nicht bedeutet, dass das Modell ein Hochleistungsmodell ist (zumindest steigt mit zunehmender Anzahl von Knoten die Anzahl von Parametern, sodass der Rechenaufwand zunimmt und das Lernen Zeit kostet).
Wenn Sie sich jedoch mit einem Klassifizierungsproblem für mehrere Klassen befassen, muss ** die Anzahl der Knoten in der vollständig verbundenen Ebene, die als Ausgabeebene festgelegt wurde, mit der Anzahl der Klassen übereinstimmen, die Sie klassifizieren möchten **. Im Fall von MNIST handelt es sich um eine Klassifizierung von Zahlen von 0 bis 9, dh ein ** 10-Klassifizierungsproblem **, daher müssen Sie hier "10" einstellen.
Geben Sie außerdem dem Argument "Aktivierung" eine ** Aktivierungsfunktion **. Hier werden die ** ReLU-Funktion ** (tf.nn.relu
) und die ** SoftMax-Funktion ** (tf.nn.softmax) verwendet, deren Details im nächsten Abschnitt erläutert werden. Wenn "Aktivierung =" weggelassen wird, wird die Aktivierungsfunktion nicht angewendet und der berechnete Wert wird so ausgegeben, wie er ist. Die Referenz ist hier.
Beim Trainieren des Modells ** wird die Ausgabe von der vorherigen Schicht zur nächsten Schicht gemäß der angegebenen Wahrscheinlichkeit (knotenweise) blockiert (Inaktivierung des entsprechenden Knotens in der vorherigen Schicht gemäß der Wahrscheinlichkeit). / Es wird auch als fallend ausgedrückt). Durch die Bereitstellung dieser Ebene scheint es schwierig zu sein, in die Situation des ** Überlernens ** zu gelangen.
In Bezug darauf war die Erklärung von "[Neuronales Netzwerk] Dropout" sehr einfach zu verstehen.
Das Programm fügt dem Modell eine Dropout-Ebene wie folgt hinzu:
model.add( tf.keras.layers.Dropout(0.2) ) # (3)
Geben Sie im Argument den Prozentsatz der Knoten an, die Sie im Bereich von 0,0 bis 1,0 deaktivieren möchten. Das Einstellen auf 0,0 entspricht im Wesentlichen dem Fehlen einer Dropout-Ebene. Wenn der Wert auf 1,0 eingestellt ist, wird das Netzwerk auf der Dropout-Ebene vollständig blockiert, sodass kein Lernen funktioniert (tatsächlich muss ValueError: rate ein skalarer Tensor oder ein Float im Bereich [0, 1] sein). Ich bekomme den Fehler 1
).
Beachten Sie, dass der inaktivierte Knoten entsprechend der angegebenen Wahrscheinlichkeit ** zufällig ** ausgewählt wird. Wenn Sie diese Dropout-Ebene haben, ** ändert sich das trainierte Modell daher mit jedem Training (geringfügig) **. Geben Sie daher bei der Untersuchung des Einflusses anderer Hyperparameter wie der Beziehung zwischen der Anzahl der Knoten in der dichten Schicht und der korrekten Antwortrate ein Argument wie "seed = 1" an und legen Sie den zufälligen Startwert fest (das Training ist jedoch besser). Wenn ein zufälliges Element vorhanden ist, ändert sich das generierte trainierte Modell bei jeder Ausführung, auch wenn es hier festgelegt ist.
Die Referenz ist hier.
Bereiten Sie ein Modell vor, in dem die Parameter der Dropout-Ebene (Rate inaktivierter Knoten) in Schritten von 0,2 von 0,0 auf 0,8 geändert werden. Ist es gegen Überlernen wirksam, indem es mit der Epochenzahl = 100 trainiert und bewertet und eine Dropout-Ebene hinzufügt? Ich habe beobachtet **.
Für jede Trainingsepoche wurden die korrekte Antwortrate (Genauigkeit) und der Verlustfunktionswert (Verlust) für die Trainingsdaten "x_train" sowie die korrekte Antwortrate (val_accuracy) und der Verlustfunktionswert (val_loss) für die Testdaten "x_test" erfasst und aufgezeichnet. ..
python
mport numpy as np
import tensorflow as tf
# (1)Laden Sie einen Datensatz mit handgeschriebenen numerischen Bildern herunter
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# (2)Datennormalisierung
x_train, x_test = x_train / 255.0, x_test / 255.0
# (3)Erstellen Sie ein NN-Modell
#■■ Abbrecherquote auf 0.0 bis 0.Ändern Sie bis zu 8 ■■
epochs = 100
results = list()
for p in np.arange(0.0, 1.0, 0.2) :
print(f'■ Dropout p={p:.1f}')
tf.keras.backend.clear_session()
model = tf.keras.models.Sequential()
model.add( tf.keras.layers.Flatten(input_shape=(28, 28)) )
model.add( tf.keras.layers.Dense(128, activation=tf.nn.relu) )
model.add( tf.keras.layers.Dropout(p) ) #Sehen Sie hier die Wirkung von Parameter p
model.add( tf.keras.layers.Dense(10, activation=tf.nn.softmax) )
# (4)Modell kompilieren
model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=['accuracy'])
# (5)Modelltraining (Lernen / Training)
r = model.fit(x_train, y_train, validation_data=(x_test,y_test), epochs=epochs)
print(r.history)
results.append( dict( rate=p, hist=r.history ) )
#■■ Grafikausgabe ■■
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as tk
ylim = dict( )
ylim['accuracy'] = (0.90, 1.00)
ylim['val_accuracy'] = (0.95, 1.00)
ylim['loss'] = (0.00, 0.20)
ylim['val_loss'] = (0.05, 0.30)
xt_style = lambda x, pos=None : f'{x:.0f}'
for v in ['accuracy','loss','val_accuracy','val_loss'] :
plt.figure(dpi=96)
for r in results :
plt.plot( range(1,epochs+1),r['hist'][v],label=f"rate={r['rate']:.1f}")
plt.xlim(1,epochs)
plt.ylim( *(ylim[v]) )
plt.gca().xaxis.set_major_formatter(tk.FuncFormatter(xt_style))
plt.tick_params(direction='in')
plt.legend(bbox_to_anchor=(1.02, 1), loc='upper left', borderaxespad=0)
plt.xlabel('epoch')
plt.ylabel(v)
plt.show()
In beiden Modellen ist der Wert umso besser, je mehr Sie lernen. Insbesondere wird mit einer Rate von = 0,0, was der tatsächlichen Dropout-Schichtbirne entspricht, die höchste Punktzahl von 1,0 erreicht. Grundsätzlich gilt: Je kleiner die Inaktivierungsrate, desto schneller das Lernen und desto besser die endgültige richtige Antwortrate.
Grundsätzlich hat es die gleiche Tendenz wie die richtige Antwortrate (Genauigkeit).
Von hier aus wird es zu einem echten Bewertungsindex einschließlich der Generalisierungsleistung.
Raten = 0,2 und 0,4 konvergieren schnell und die Endwerte sind gut. Andererseits ist 0,6 0,2 und 0,4 deutlich unterlegen. 0.0 ist etwas weniger stabil als die anderen.
Solange Sie nur den Prozentsatz der richtigen Antworten betrachten, können Sie die Tendenz des Überlernens nicht lesen.
Bei einer Rate von = 0,0 (entspricht keiner Dropout-Schicht) verschlechtert sich der Wert nach Epoche = 8 allmählich. Sie können die Tendenz des Überlernens deutlich erkennen.
Durch Beobachtung der Steigung der einzelnen Daten nach Epoche = 20 ist ersichtlich, dass es umso schwieriger ist, zu übertrainieren, je größer die Inaktivitätsrate (Rate) ist. Wir konnten die Wirksamkeit der Dropout-Schicht bestätigen.
Eine umfassende Bewertung bestätigte, dass die im Lernprogramm festgelegten Werte für Rate = 0,2 und Epochen = 5 gute Parameter waren, die gut abgestimmt waren.
Wenn die Aktivierungsfunktion ** nicht angewendet ** wird, wird die Ausgabe $ y_1 $ des ersten Knotens in der zweiten Schicht wie folgt berechnet ($ x_i $ ist die Ausgabe des $ i $ -ten Knotens in der vorherigen Schicht $ w_ {i1} $ ist das Gewicht, $ b_ {1} $ ist die Vorspannung).
Wenn Sie andererseits die Aktivierungsfunktion $ f $ anwenden, lautet $ y_1 $:
Es gibt verschiedene Aktivierungsfunktionen, die im neuronalen Netz verwendet werden, aber sie können je nach Problemtyp grob in ** häufig in der mittleren Schicht ** und ** in der Ausgabeschicht ** verwendet werden. Ich werde.
In der mittleren Schicht werden die ** ReLU-Funktion ** und die ** Sigmoid-Funktion ** verwendet. Aufgrund seiner Natur verwendet die Ausgabeschicht von Klassifizierungsproblemen mit mehreren Klassen die ** SoftnMax-Funktion **, und das Klassifizierungsproblem mit zwei Klassen verwendet die ** Sigmoid-Funktion **.
** Es scheint die am häufigsten verwendete Aktivierungsfunktion in der mittleren Schicht zu sein **. Wenn der Eingang kleiner als 0 ist, ist der Ausgang 0, und wenn der Eingang 0 oder mehr ist, wird der Eingang so ausgegeben, wie er ist. tf.nn.relu ()
. Die Referenz ist hier.
ReLU-Funktion
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
xmin, xmax = -10,10
x = np.linspace(xmin, xmax,1000)
y = tf.nn.relu(x) #ReLU-Funktion
#Überprüfen Sie die Form in der Grafik
plt.figure(dpi=96)
plt.plot(x,y,lw=3)
plt.xlim(xmin, xmax)
plt.ylim(-1, 12)
plt.hlines([0],*(plt.xlim()),ls='--',lw=0.5)
plt.vlines([0],*(plt.ylim()),ls='--',lw=0.5)
Es scheint, dass es "Leaky Relu-Funktion" und "Parametric ReLU-Funktion" als Varianten der ReLU-Funktion gibt.
Eine der Aktivierungsfunktionen, die häufig in der mittleren Schicht verwendet werden. Wenn jedoch die Sigmoidfunktion als Aktivierungsfunktion im ** NN-Modell mit einer großen Anzahl von Schichten ** verwendet wird, scheint die ReLU-Funktion aufgrund des Problems des Verschwindens des Gradienten ihrer Popularität beraubt zu sein. tf.math.sigmoid ()
. Die Referenz ist hier.
python
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
xmin, xmax = -10,10
x = np.linspace(xmin, xmax,1000)
y = tf.math.sigmoid(x) #Sigmaid-Funktion
#Überprüfen Sie die Form in der Grafik
plt.figure(dpi=96)
plt.plot(x,y,lw=3)
plt.xlim(xmin, xmax)
plt.ylim(-0.2, 1.2)
plt.hlines([0,0.5,1],*(plt.xlim()),ls='--',lw=0.5)
plt.vlines([0],*(plt.ylim()),ls='--',lw=0.5)
Wird häufig in der ** Ausgabeschicht ** von Klassifizierungsproblemen mit mehreren Klassen verwendet. Unabhängig von der Eingabe reicht die Ausgabe von 0,0 bis 1,0, und die Gesamtausgabe beträgt 1,0. tf.nn.softmax ()
. Die Referenz ist hier.
Wenn Sie beispielsweise die SoftMax-Funktion wie folgt auf die Eingabe "[2, -1, 1, 1]" anwenden, erhalten Sie eine Ausgabe wie "[0,56, 0,03, 0,21, 0,21]" (die Summe der Elemente beträgt 1,0). Du kannst es haben.
ReLU-Funktion
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patheffects as pe
import tensorflow as tf
x = np.array( [2, -1, 1, 1], dtype=np.float64 )
fx = tf.nn.softmax(x)
fx = fx.numpy() # 'numpy.ndarray'Holen Sie sich Inhalt mit
np.set_printoptions(precision=2)
print(f'fx = {fx}')
print(f'fx.sum() = {fx.sum():.2f}')
fig, ax = plt.subplots(nrows=2, ncols=1, dpi=96)
plt.subplots_adjust(hspace=0.12)
ep = (pe.Stroke(linewidth=3, foreground='white'),pe.Normal())
tp = dict(horizontalalignment='center',verticalalignment='center')
ax[0].bar( np.arange(0,len(x)), x, fc='tab:red' )
ax[1].bar( np.arange(0,len(fx)), fx )
ax[1].set_ylim(0,1)
for i, p in enumerate([x,fx]) :
ax[i].tick_params(axis='x', which='both', bottom=False, labelbottom=False)
ax[i].set_xlim(-0.5,len(p)-0.5)
for j, v in enumerate(p):
t = ax[i].text(j, v/2, f'{v:.2f}',**tp)
t.set_path_effects(ep)
ax[0].hlines([0],*(plt.xlim()),lw=1)
ax[0].set_ylabel('x')
ax[1].set_ylabel('SoftMax(x)')
Unentschieden
Recommended Posts