Der vorherige Artikel war hier. Erstellen Sie den CNN-Experimentalcode, indem Sie den in hier erstellten Experimentalcode hinzufügen oder ändern. Das Experiment verwendet den MNIST-Datensatz von scicit-learn aufgrund des Problems der Ausführungszeit. Der Unterschied zum normalen MNIST-Datensatz
Das ist. Dank dessen beträgt die Lernzeit (in meiner Umgebung) nur einige zehn Sekunden. Derzeit ist auch der experimentelle Code für den vollständigen Keras-Datensatz enthalten. Dies scheint in meiner Umgebung mehrere Stunden zu dauern, also habe ich aufgegeben ...
Trainer
Klasse hinzufügen](#trainer Klasse hinzufügen)Fügen Sie es zunächst der Klasse "_TypeManager" hinzu, damit die Klasse "LayerManager" "ConvLayer" und "PoolingLayer" verarbeiten kann.
_type_manager.py
class _TypeManager():
"""
Manager-Klasse für Ebenentypen
"""
N_TYPE = 4 #Anzahl der Ebenentypen
BASE = -1
MIDDLE = 0 #Nummerierung der mittleren Schicht
OUTPUT = 1 #Nummerierung der Ausgabeebene
CONV = 2 #Nummerierung der Faltschicht
POOL = 3 #Nummerierung der Pooling-Schicht
REGULATED_DIC = {"Middle": MiddleLayer,
"Output": OutputLayer,
"Conv": ConvLayer,
"Pool": PoolingLayer,
"BaseLayer": None}
@property
def reg_keys(self):
return list(self.REGULATED_DIC.keys())
def name_rule(self, name):
name = name.lower()
if "middle" in name or name == "mid" or name == "m":
name = self.reg_keys[self.MIDDLE]
elif "output" in name or name == "out" or name == "o":
name = self.reg_keys[self.OUTPUT]
elif "conv" in name or name == "c":
name = self.reg_keys[self.CONV]
elif "pool" in name or name == "p":
name = self.reg_keys[self.POOL]
else:
raise UndefinedLayerError(name)
return name
Es wurden "CONV" und "POOL" als Konstanten hinzugefügt und es wurde möglich, das Ebenenobjekt mit "REGULATED_DIC" aus dem Ebenennamen abzurufen. Da es viele Fälle gab, in denen die "Schlüsselliste" von "REGURATED_DIC" benötigt wurde, haben wir den Eigenschafts- und Namensregeln eine Faltungsschicht und eine Poolebene hinzugefügt.
Die Funktionen zum Lernen und Vorhersagen sind von der "LayerManager" -Klasse als "Trainer" -Klasse getrennt.
trainer.py
import time
import numpy as np
softmax = type(get_act("softmax"))
sigmoid = type(get_act("sigmoid"))
class Trainer():
def __init__(self, x, y):
self.x_train, self.x_test = x
self.y_train, self.y_test = y
self.make_anim = False
def forward(self, x, lim_memory=10):
def propagate(x):
x_in = x
n_batch = x.shape[0]
switch = True
for ll in self.layer_list:
if switch and not self.is_CNN(ll.name):
x_in = x_in.reshape(n_batch, -1)
switch = False
x_in = ll.forward(x_in)
#Weil die Vorwärtsausbreitungsmethode auch zur Fehlerberechnung und Vorhersage unbekannter Daten verwendet wird
#Die Speicherkapazität kann groß sein
if np.prod(x.shape)*8/2**20 >= 10:
#Gleitkommazahl mit doppelter Genauigkeit(8byte)Bei 10 MB(=10*2**20)Mehr als
#Wenn Sie Speicher verwenden, teilen Sie ihn in 5 MB oder weniger und führen Sie ihn aus
n_batch = int(5*2**20/(8*np.prod(x.shape[1:])))
y = np.zeros((x.shape[0], lm[-1].n))
n_loop = int(np.ceil(x.shape[0]/n_batch))
for i in range(n_loop):
propagate(x[i*n_batch : (i+1)*n_batch])
y[i*n_batch : (i+1)*n_batch] = lm[-1].y.copy()
lm[-1].y = y
else:
#Ansonsten normal laufen
propagate(x)
def backward(self, t):
y_in = t
n_batch = t.shape[0]
switch = True
for ll in self.layer_list[::-1]:
if switch and self.is_CNN(ll.name):
y_in = y_in.reshape(n_batch, *ll.O_shape)
switch = False
y_in = ll.backward(y_in)
def update(self, **kwds):
for ll in self.layer_list:
ll.update(**kwds)
def training(self, epoch, n_batch=16, threshold=1e-8,
show_error=True, show_train_error=False, **kwds):
if show_error:
self.error_list = []
if show_train_error:
self.train_error_list = []
if self.make_anim:
self.images = []
self.n_batch = n_batch
n_train = self.x_train.shape[0]//n_batch
n_test = self.x_test.shape[0]
#Fang an zu lernen
start_time = time.time()
lap_time = -1
error = 0
error_prev = 0
rand_index = np.arange(self.x_train.shape[0])
for t in range(1, epoch+1):
#Szenenerstellung
if self.make_anim:
self.make_scene(t, epoch)
#Trainingsfehlerberechnung
if show_train_error:
self.forward(self.x_train)
error = lm[-1].get_error(self.y_train)
self.train_error_list.append(error)
#Fehlerberechnung
self.forward(self.x_test)
error = lm[-1].get_error(self.y_test)
if show_error:
self.error_list.append(error)
#Konvergenzurteil
if np.isnan(error):
print("fail training...")
break
if abs(error - error_prev) < threshold:
print("end learning...")
break
else:
error_prev = error
t_percent = int(50*t/epoch)
np.random.shuffle(rand_index)
for i in range(n_train):
i_percent = int(50*(i+1)/n_train)
if i_percent <= t_percent:
time_stamp = ("progress:[" + "X"*i_percent
+ "\\"*(t_percent-i_percent)
+ " "*(50-t_percent) + "]")
else:
time_stamp = ("progress:[" + "X"*t_percent
+ "/"*(i_percent-t_percent)
+ " "*(50-i_percent) + "]")
elapsed_time = time.time() - start_time
print("\r" + time_stamp
+ "{}s/{}s".format(
int(elapsed_time),
int(lap_time*epoch) if lap_time > 0 else "?"),
end="")
rand = rand_index[i*n_batch : (i+1)*n_batch]
self.forward(self.x_train[rand])
self.backward(self.y_train[rand])
self.update(**kwds)
if lap_time < 0:
lap_time = time.time() - start_time
print()
if show_error:
#Fehlerübergangsanzeige
self.show_errors(show_train_error, **kwds)
def pred_func(self, y, threshold=0.5):
if isinstance(self[-1].act, softmax):
return np.argmax(y, axis=1)
elif isinstance(self[-1].act, sigmoid):
return np.where(y > threshold, 1, 0)
else:
raise NotImplemented
def predict(self, x=None, y=None, threshold=0.5):
if x is None:
x = self.x_test
if y is None:
y = self.y_test
self.forward(x)
self.y_pred = self.pred_func(self[-1].y, threshold=threshold)
y = self.pred_func(y, threshold=threshold)
print("correct:", y[:min(16, int(y.shape[0]*0.1))])
print("predict:", self.y_pred[:min(16, int(y.shape[0]*0.1))])
print("accuracy rate:", np.sum(self.y_pred == y, dtype=int)/y.shape[0]*100, "%",
"({}/{})".format(np.sum(self.y_pred == y, dtype=int), y.shape[0]))
return self.y_pred
def show_errors(self, show_train_error=False, title="error transition",
xlabel="epoch", ylabel="error", fname="error_transition.png ",
log_scale=True, **kwds):
fig, ax = plt.subplots(1)
fig.suptitle(title)
if log_scale:
ax.set_yscale("log")
ax.set_xlabel(xlabel)
ax.set_ylabel(ylabel)
ax.grid()
if show_train_error:
ax.plot(self.train_error_list, label="train accuracy")
ax.plot(self.error_list, label="test accuracy")
ax.legend(loc="best")
#fig.show()
if len(fname) != 0:
fig.savefig(fname)
def ready_anim(self, n_image, x, y, title="animation",
xlabel="x", ylabel="y", ex_color="r", color="b",
x_left=0, x_right=0, y_down = 1, y_up = 1):
self.n_image = n_image
self.x = x
self.color = color
self.make_anim = True
self.anim_fig, self.anim_ax = plt.subplots(1)
self.anim_fig.suptitle(title)
self.anim_ax.set_xlabel(xlabel)
self.anim_ax.set_ylabel(ylabel)
self.anim_ax.set_xlim(np.min(x) - x_left, np.max(x) + x_right)
self.anim_ax.set_ylim(np.min(y) - y_down, np.max(y) + y_up)
self.anim_ax.grid()
self.anim_ax.plot(x, y, color=ex_color)
return self.anim_fig, self.anim_ax
def make_scene(self, t, epoch):
#Szenenerstellung
if t % (epoch/self.n_image) == 1:
x_in = self.x.reshape(-1, 1)
for ll in self.layer_list:
x_in = ll.forward(x_in)
im, = self.anim_ax.plot(self.x, ll.y, color=self.color)
self.images.append([im])
Der Grund, warum die Funktionen "vorwärts", "rückwärts" und "aktualisieren" als Funktionen getrennt sind, besteht darin, dass Sie, wenn Sie etwas Originelles tun möchten, einfach die Methode verwenden, die die Funktion "vorwärts" bei der Vorwärtsausbreitung ausführen soll. Machen. Ich denke, es gibt etwas mehr Raum für Einfallsreichtum ... Da die Vorwärtsfunktion auch für die Fehlerberechnung und Vorhersageberechnung verwendet wird, kann auch eine große Datenmenge fließen. Unter der Annahme, dass Gleitkommazahlen mit doppelter Genauigkeit (8 Byte) geflossen sind, wurden sie geändert, wenn sie die geschätzten 10 MB überschreiten, so dass sie in etwa 5 MB unterteilt und übertragen werden.
Die Funktion "Training" beschreibt den Lernfluss. Ich habe es hinzugefügt, weil ich dachte, dass der Fehlerübergang der Trainingsdaten auch so ist. Darüber hinaus ist das Urteil "NaN" auch im Konvergenzurteil enthalten, und das Training wird sofort beendet, wenn das Lernen fehlschlägt. Außerdem wurde der Fortschritt bisher mit dem Modul "tqdm" angezeigt, aber ich habe ihn selbst vorbereitet. "" Zeigt den Fortschritt der Epoche an und "/" zeigt den Aufschlussstatus der Charge.
Die "Vorhersage" -Funktion macht buchstäblich Vorhersagen für Testdaten. Optionale Argumente werden verwendet, und wenn nicht angegeben, werden die vom Layer-Manager gespeicherten Testdaten verwendet. Nach dem Fließen der Testdaten wird das Datenformat durch "pred_func" geändert und die richtige Antwortrate berechnet. Es scheint, dass wir auch hier ein wenig ändern müssen ... Dies gibt nur die richtige Antwortrate für Klassifizierungsfragen ...
Mit der Hinzufügung der Klassen "ConvLayer" und "Pooling" waren geringfügige Änderungen erforderlich.
layer_manager.py
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import tqdm
class LayerManager(_TypeManager, Trainer):
"""
Manager-Klasse zum Verwalten von Ebenen
"""
def __init__(self, x, y):
super().__init__(x, y)
self.__layer_list = [] #Liste der Ebenen
self.__name_list = [] #Namensliste für jede Schicht
self.__ntype = np.zeros(self.N_TYPE, dtype=int) #Anzahl der Ebenen nach Typ
def __repr__(self):
layerRepr= "layer_list: " + repr(self.__layer_list)
nameRepr = "name_list: " + repr(self.__name_list)
ntypeRepr = "ntype: " + repr(self.__ntype)
return (layerRepr + "\n"
+ nameRepr + "\n"
+ ntypeRepr)
def __str__(self):
layerStr = "layer_list: " + str(self.__layer_list)
nameStr = "name_list: " + str(self.__name_list)
ntypeStr = "ntype: " + str(self.__ntype)
return (layerStr + "\n"
+ nameStr + "\n"
+ ntypeStr)
def __len__(self):
"""
In Python integrierte Funktionen`len`Beschreibt den Vorgang beim Aufruf von.
Gibt die Summe der Anzahl der Ebenen nach Typ zurück.
"""
return int(np.sum(self.__ntype))
def __getitem__(self, key):
"""
Zum Beispiel
lm = LayerManager()
+----------------+
| (Element zu lm hinzufügen) |
+----------------+
x = lm[3].~~
Wird aufgerufen, wenn auf ein Element einer Liste oder eines Arrays zugegriffen wird, z
Beschreiben Sie den Vorgang zu diesem Zeitpunkt.
Scheibe und str,Zugriff nur über int zulassen.
"""
if isinstance(key, slice):
#Wenn der Schlüssel ein Slice ist, beziehen Sie sich auf die Liste der Ebenen mit Slice.
#Ungewöhnlicher Wert(Index außerhalb des Bereichs usw.)Wann wird eingegeben
#Python gibt mir einen Fehler.
return self.__layer_list[key]
elif isinstance(key, str):
#Wenn der Schlüssel eine Zeichenfolge ist, rufen Sie den Index aus der Namensliste jeder Ebene und ab
#Gibt die Elemente der Liste der anwendbaren Ebenen zurück.
if key in self.__name_list:
index = self.__name_list.index(key)
return self.__layer_list[index]
else:
#Wenn der Schlüssel nicht vorhanden ist, wird ein KeyError ausgegeben.
raise KeyError("{}: No such item".format(key))
elif isinstance(key, int):
#Wenn key eine Ganzzahl ist, wird das entsprechende Element in der Liste der Ebenen zurückgegeben.
#Ungewöhnlicher Wert(Index außerhalb des Bereichs usw.)Wann wird eingegeben
#Python gibt mir einen Fehler.
return self.__layer_list[key]
else:
raise KeyError(key, ": Undefined such key type.")
def __setitem__(self, key, value):
"""
Zum Beispiel
lm = LayerManager()
+----------------+
| (Element zu lm hinzufügen) |
+----------------+
lm[1] = x
Wird aufgerufen, wenn auf ein Element einer Liste oder eines Arrays zugegriffen wird, z
Beschreiben Sie den Vorgang zu diesem Zeitpunkt.
Es ist nur das Überschreiben von Elementen zulässig, und das Hinzufügen neuer Elemente ist verboten.
"""
value_type = ""
if isinstance(value, list):
#Auf der rechten Seite angegeben'value'Aber'list'Wenn
#Alle Elemente'BaseLayer'Fehler, wenn die Klasse sie erbt oder nicht.
if not np.all(
np.where(isinstance(value, BaseLayer), True, False)):
self.AssignError()
value_type = "list"
elif isinstance(value, BaseLayer):
#Auf der rechten Seite angegeben'value'Aber'BaseLayer'Ist es eine Klasse?
#Fehler, wenn es nicht vererbt wird.
self.AssignError(type(value))
if value_type == "":
value_type = self.reg_keys[self.BASE]
if isinstance(key, slice):
#Wenn der Schlüssel ein Slice ist, überschreiben Sie das Element in der Liste der Ebenen.
#jedoch'value_type'Aber'list'Sonst ein Fehler.
#Ungewöhnlicher Wert(Index außerhalb des Bereichs usw.)Wann wird eingegeben
#Python gibt mir einen Fehler.
if value_type != "list":
self.AssignError(value_type)
self.__layer_list[key] = value
elif isinstance(key, str):
#Wenn der Schlüssel eine Zeichenfolge ist, rufen Sie den Index aus der Namensliste jeder Ebene und ab
#Überschreiben Sie die Elemente in der Liste der zutreffenden Ebenen.
#jedoch'value_type'Aber'BaseLayer'Sonst ein Fehler.
if value_type != self.reg_keys[self.BASE]:
raise AssignError(value_type)
if key in self.__name_list:
index = self.__name_list.index(key)
self.__layer_list[index] = value
else:
#Wenn der Schlüssel nicht vorhanden ist, wird ein KeyError ausgegeben.
raise KeyError("{}: No such item".format(key))
elif isinstance(key, int):
#Wenn key eine Ganzzahl ist, überschreiben Sie das entsprechende Element in der Liste der Ebenen.
#jedoch'value_type'Aber'BaseLayer'Sonst ein Fehler.
#Auch ein abnormaler Wert(Index außerhalb des Bereichs usw.)Wann wird eingegeben
#Python gibt mir einen Fehler.
if value_type != self.reg_keys[self.BASE]:
raise AssignError(value_type)
self.__layer_list[key] = value
else:
raise KeyError(key, ": Undefined such key type.")
def __delitem__(self, key):
"""
Zum Beispiel
lm = LayerManager()
+----------------+
| (Element zu lm hinzufügen) |
+----------------+
del lm[2]
Weil es aufgerufen wird, wenn auf das Element der Liste oder des Arrays von der del-Anweisung wie zugegriffen wird
Beschreiben Sie den Vorgang zu diesem Zeitpunkt.
Wenn das angegebene Element vorhanden ist, wird es gelöscht und umbenannt.
"""
if isinstance(key, slice):
#Wenn der Schlüssel ein Slice ist, löschen Sie das angegebene Element unverändert
#Ungewöhnlicher Wert(Index außerhalb des Bereichs usw.)Wann wird eingegeben
#Python gibt mir einen Fehler.
del self.__layer_list[slice]
del self.__name_list[slice]
elif isinstance(key, str):
#Wenn der Schlüssel eine Zeichenfolge ist, rufen Sie den Index aus der Namensliste jeder Ebene und ab
#Löschen Sie das entsprechende Element.
if key in self.__name_list:
del self.__layer_list[index]
del self.__name_list[index]
else:
#Wenn der Schlüssel nicht vorhanden ist, wird ein KeyError ausgegeben.
raise KeyError("{}: No such item".format(key))
elif isinstance(key, int):
#Wenn der Schlüssel eine Ganzzahl ist, löschen Sie das entsprechende Element in der Ebenenliste.
#Ungewöhnlicher Wert(Index außerhalb des Bereichs usw.)Wann wird eingegeben
#Python gibt mir einen Fehler.
del self.__layer_list[key]
else:
raise KeyError(key, ": Undefined such key type.")
#Umbenennen
self._rename()
def _rename(self):
"""
Wenn die Benennung der Namensliste aufgrund der Listenoperation gegen die Regeln verstößt
Benennen Sie die Namensliste und jede Ebene um, um die Regeln erneut zu erfüllen.
Die Namensregel lautet[Ebenentyp][Welche Nummer]Wird besorgt.
Wenn der Ebenentyp Mittlere Ebene ist, Mittlere Ebene
Ausgabe für Ausgabeschicht
Es wird als abgekürzt.
Die Nummer wird nach Typ gezählt.
Auch hier wieder__Zählt nTypen.
"""
#Initialisieren Sie die Anzahl der Ebenen nach Typ
self.__ntype = np.zeros(self.N_TYPE)
#Zählen Sie jede Ebene neu und benennen Sie sie um
for i in range(len(self)):
for j, reg_name in enumerate(self.REGULATED_DIC):
if reg_name in self.__name_list[i]:
self.__ntype[j] += 1
self.__name_list[i] = (self.reg_keys[j]
+ str(self.__ntype[j]))
self.__layer_list[i].name = (self.reg_keys[j]
+ str(self.__ntype[j]))
break
else:
raise UndefinedLayerType(self.__name_list[i])
def append(self, *, name="Middle", **kwds):
"""
Implementierung der bekannten Append-Methode, bei der Elemente zu einer Liste hinzugefügt werden.
"""
if "prev" in kwds:
# 'prev'Ist im Schlüsselwort enthalten
#Dies bedeutet, dass die Anzahl der Elemente in der vorherigen Ebene angegeben wird.
#Grundsätzlich soll es also an der Zeit sein, die erste Schicht einzufügen
#Davon abgesehen wird es grundsätzlich automatisch ermittelt und nicht angegeben.
if len(self) != 0:
if kwds["prev"] != self.__layer_list[-1].n:
#Fehler, wenn er nicht mit der Anzahl der Einheiten am Ende übereinstimmt.
raise UnmatchUnitError(self.__layer_list[-1].n,
kwds["prev"])
elif not self.is_CNN(name):
if len(self) == 0:
#Die erste DNN-Schicht muss immer die Anzahl der Eingabeeinheiten angeben.
raise UnmatchUnitError("Input units", "Unspecified")
else:
#Die Anzahl der Einheiten in der letzten Ebene'kwds'Hinzufügen
kwds["prev"] = self.__layer_list[-1].n
#Lesen Sie den Layertyp und ändern Sie den Namen gemäß der Namensregel
name = self.name_rule(name)
#Fügen Sie eine Ebene hinzu.
for i, reg_name in enumerate(self.REGULATED_DIC):
if name in reg_name:
#Erhöhen Sie die Ebene nach Typ
self.__ntype[i] += 1
#Zum Namen hinzufügen
name += str(self.__ntype[i])
#Zur Namensliste hinzufügen
self.__name_list.append(name)
#Erstellen Sie abschließend eine Ebene und fügen Sie sie der Liste hinzu.
self.__layer_list.append(self.REGULATED_DIC[reg_name](name=name,**kwds))
def extend(self, lm):
"""
Ein weiterer Layer-Manager, der bereits in der Extend-Methode vorhanden ist'lm'Elemente von
Füge alle Hinzu.
"""
if not isinstance(lm, LayerManager):
# 'lm'Fehler, wenn die Instanz von nicht LayerManager ist.
raise TypeError(type(lm), ": Unexpected type.")
if len(self) != 0:
if self.__layer_list[-1].n != lm[0].prev:
#Mit der Anzahl der Einheiten in Ihrer letzten Ebene
# 'lm'Fehler, wenn die Anzahl der Eingaben in der ersten Schicht von nicht gleich ist.
raise UnmatchUnitError(self.__layer_list[-1].n,
lm[0].prev)
#Beziehungsweise'extend'Nach Methode hinzufügen
self.__layer_list.extend(lm.layer_list)
self.__name_list.extend(lm.name_list)
#Umbenennen
self._rename()
def insert(self, prev_name, name="Middle", **kwds):
"""
Geben Sie in der Einfügemethode den Namen der vorherigen Ebene an und kombinieren Sie ihn mit dieser Ebene.
Fügen Sie ein Element hinzu.
"""
# 'prev_name'Fehler, wenn nicht vorhanden.
if not prev_name in self.__name_list:
raise KeyError(prev_name, ": No such key.")
# 'prev'Ist im Schlüsselwort enthalten
# 'prev_name'Fehler, wenn er nicht mit der Anzahl der Einheiten in der in angegebenen Ebene übereinstimmt.
if "prev" in kwds:
if kwds["prev"] \
!= self.__layer_list[self.index(prev_name)].n:
raise UnmatchUnitError(
kwds["prev"],
self.__layer_list[self.index(prev_name)].n)
# 'n'Ist im Schlüsselwort enthalten
if "n" in kwds:
# 'prev_name'Wenn ist nicht der letzte
if prev_name != self.__name_list[-1]:
#Fehler, wenn er nicht mit der Anzahl der Einheiten in der nächsten Ebene übereinstimmt.
if kwds["n"] != self.__layer_list[
self.index(prev_name)+1].prev:
raise UnmatchUnitError(
kwds["n"],
self.__layer_list[self.index(prev_name)].prev)
#Wenn es noch keine Elemente gibt'append'Geben Sie einen Fehler bei der Verwendung der Methode ein.
if len(self) == 0:
raise RuntimeError(
"You have to use 'append' method instead.")
#Index der Einfügeposition abrufen
index = self.index(prev_name) + 1
#Lesen Sie den Layertyp und ändern Sie den Namen gemäß der Namensregel
name = self.name_rule(name)
#Element einfügen
for i, reg_name in enumerate(self.REGULATED_DIC):
if reg_name in name:
self.__layer_list.insert(index,
self.REGULATED_DIC[reg_name](name=name,**kwds))
self.__name_list.insert(index,
self.REGULATED_DIC[reg_name](name=name,**kwds))
#Umbenennen
self._rename()
def extend_insert(self, prev_name, lm):
"""
Dies ist die ursprüngliche Funktion.
Es verhält sich wie eine Kombination aus der Extend-Methode und der Insert-Methode.
Einfach ausgedrückt ist es wie das Einfügen eines weiteren Ebenenmanagers.
"""
if not isinstance(lm, LayerManager):
# 'lm'Fehler, wenn die Instanz von nicht LayerManager ist.
raise TypeError(type(lm), ": Unexpected type.")
# 'prev_name'Fehler, wenn nicht vorhanden.
if not prev_name in self.__name_list:
raise KeyError(prev_name, ": No such key.")
#Die Anzahl der Einheiten der Schichten vor und nach dem angegebenen Ort sowie der ersten und letzten Schicht von lm
#Wenn sie nicht übereinstimmen, tritt ein Fehler auf.
if len(self) != 0:
if self.__layer_list[self.index(prev_name)].n \
!= lm.layer_list[0].prev:
#Mit der Anzahl der Einheiten an Ihrem angegebenen Standort'lm'Die erste Anzahl von Einheiten in
#Wenn sie nicht übereinstimmen, tritt ein Fehler auf.
raise UnmatchUnitError(
self.__layer_list[self.index(prev_name)].n,
lm.layer_list[0].prev)
if prev_name != self.__name_list[-1]:
# 'prev_name'Ist nicht meine letzte Schicht
if lm.layer_list[-1].n \
!= self.__layer_list[self.index(prev_name)+1].prev:
# 'lm'Die Anzahl der Einheiten am Ende und die nächste Schicht Ihres festgelegten Standorts
# 'prev'Fehler, wenn er nicht mit der Anzahl der Einheiten übereinstimmt.
raise UnmatchUnitError(
lm.layer_list[-1].n,
self.__layer_list[self.index(prev_name)+1].prev)
else:
#Wenn Sie keine Elemente haben'extend'Ich erhalte eine Fehlermeldung bei der Verwendung der Methode.
raise RuntimeError(
"You have to use 'extend' method instead.")
#Index der Einfügeposition abrufen
index = self.index(prev_name) + 1
#Elemente nach der Einfügeposition'buf'Entfernen Sie es nach dem Evakuieren einmal
#Fügen Sie ein Element mit der Extend-Methode hinzu
layer_buf = self.__layer_list[index:]
name_buf = self.__name_list[index:]
del self.__layer_list[index:]
del self.__name_list[index:]
self.extend(lm)
#Fügen Sie das evakuierte Element hinzu
self.__layer_list.extend(layer_buf)
self.__name_list.extend(name_buf)
#Umbenennen
self._rename()
def remove(self, key):
"""
Die Methode remove entfernt das Element mit dem angegebenen Namen.
Es darf auch durch Index angegeben werden.
"""
#Bereits implementiert'del'Der Satz ist in Ordnung.
del self[key]
def index(self, target):
return self.__name_list.index(target)
def name(self, indices):
return self.__name_list[indices]
@property
def layer_list(self):
return self.__layer_list
@property
def name_list(self):
return self.__name_list
@property
def ntype(self):
return self.__ntype
def is_CNN(self, name=None):
if name is None:
if self.__ntype[self.CONV] > 0 \
or self.__ntype[self.POOL] > 0:
return True
else:
return False
else:
name = self.name_rule(name)
if self.reg_keys[self.CONV] in name \
or self.reg_keys[self.POOL] in name:
return True
else:
return False
Von den kleinen Änderungen werde ich die Teile weglassen, die keine Rolle spielen.
Der Teil, der weggelassen werden muss, ist die Änderung aufgrund der Erweiterung der Klasse "_TypeManager". Die Hauptänderung ist die Verwendung der Eigenschaft reg_keys
.
Die große Änderung besteht darin, dass es zu verschwenderisch ist, die bedingte Verzweigung jedes Mal zu erhöhen, wenn der Ebenentyp erhöht wird. Deshalb habe ich es möglich gemacht, dies in einer Schleife zu tun. Schauen wir uns als Beispiel den relevanten Teil der Methode "Anhängen" an.
layer_manager.py
#Fügen Sie eine Ebene hinzu.
for i, reg_name in enumerate(self.REGULATED_DIC):
if name in reg_name:
#Erhöhen Sie die Ebene nach Typ
self.__ntype[i] += 1
#Zum Namen hinzufügen
name += str(self.__ntype[i])
#Zur Namensliste hinzufügen
self.__name_list.append(name)
#Erstellen Sie abschließend eine Ebene und fügen Sie sie der Liste hinzu.
self.__layer_list.append(self.REGULATED_DIC[reg_name](name=name,**kwds))
Das REGULATED_DIC
wird mit der Funktion enumerate
wiederholt, und wenn der Layername im reg_name
enthalten ist, wird die Layernummer i
zur Verarbeitung verwendet.
** Daher muss die Ebenenkonstante der Klasse "_TypeManager" an dem Registrierungsindex "REGULATED_DIC" ausgerichtet werden. ** **.
Die anderen Teile sind ähnlich.
Schließlich haben wir die Funktion is_CNN
vorbereitet.
Dies gibt zurück, ob das Netzwerk der LayerManger-Klasse ein CNN ist, sofern dies nicht im Argumentname angegeben ist.
Wenn ein Layername für "Name" angegeben wird, wird zurückgegeben, ob der Layername eine CNN verdient (dh ob es sich um eine Faltungsschicht oder eine Poolschicht handelt).
Es wird für die Vorwärts- und Rückwärtsausbreitung der "Trainer" -Klasse verwendet.
Fahren wir nun mit dem CNN-Experiment fort. Den gesamten Code finden Sie hier [https://github.com/kuroitu/DNN_test]. Fühlen Sie sich frei zu klonen / kopieren und zu experimentieren.
Beginnen wir mit dem Keras-Dataset. Der MNIST-Datensatz von Keras enthält 60.000 Trainingsdaten und 10.000 Testdaten. Die Bildgröße beträgt $ (28, 28) $. Selbst wenn es sich um einen kleinen Datensatz für maschinelles Lernen handelt, können Sie ihn auf einem Notebook-PC usw. lernen. Ist ein ziemlich großer Datensatz.
keras_data.py
import numpy as np
from keras.datasets import mnist
#from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import tqdm
#Datensatzerfassung
n_class=10
(x_train, y_train), (x_test, y_test) = mnist.load_data()
C, B, I_h, I_w = 1, *x_train.shape
B_test = x_test.shape[0]
#Standardisierung
sc = StandardScaler()
x_train = sc.fit_transform(x_train.reshape(B, -1)).reshape(B, C, I_h, I_w)
x_test = sc.fit_transform(x_test.reshape(B_test, -1)).reshape(B_test, C, I_h, I_w)
# one-Umstellung auf Hot Label
def to_one_hot(data, n_class):
vec = np.zeros((len(data), n_class))
for i in range(len(data)):
vec[i, data[i]] = 1.
return vec
t_train = to_one_hot(y_train, n_class)
t_test = to_one_hot(y_test, n_class)
Dieses Mal werden keine Validierungsdaten erstellt. Wenn Sie es erstellen möchten, verwenden Sie die Funktion train_test_split
von scicit-learn, um die Trainingsdaten zu teilen.
Später wird es mit der Klasse "StandardScaler" von scicit-learn standardisiert. Die Verarbeitung ist nicht schwierig, sodass Sie den Code selbst schreiben können. Da es sich um eine Bilderkennung handelt, ist die Normalisierung in Ordnung.
Bitte beachten Sie, dass die Klasse "StandardScaler" von scikit-learn nur Daten unterstützt, deren Eingabe $ (B, N) $ ist.
Da das richtige Antwortetikett die numerischen Daten eines eindimensionalen Arrays aus $ (60000,) $ und $ (10000,) $ sind, ändern Sie es schließlich in einen so genannten One-Hot-Ausdruck.
Der One-Hot-Ausdruck entspricht beispielsweise in der 10-Klassen-Klassifizierung korrekten Antwortdaten mit einer numerischen Bezeichnung von $ 3 $, wie z. B. $ [0, 0, 0, 1, 0, 0, 0, 0, 0, 0] $. Eine Datendarstellung, die nur für das Teil $ 1 $ benötigt.
Dies führt zu den korrekten Bezeichnungen $ (60000, 10) $ und $ (10000, 10) $.
Damit ist die Datenverarbeitung abgeschlossen.
Als nächstes werde ich den Fall des MNIST-Datensatzes von scicit-learn vorstellen. Wie eingangs erwähnt, handelt es sich hierbei um einen relativ kleinen Datensatz, sodass Sie das maschinelle Lernen ausprobieren können.
scikit_learn_data.py
import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import tqdm
#Datensatzerfassung
n_class=10
C, I_h, I_w = 1, 8, 8
digits = datasets.load_digits()
x = digits.data
t = digits.target
n_data = len(x)
#Standardisierung
sc = StandardScaler()
x = sc.fit_transform(x).reshape(n_data, I_h, I_w)
x_train, x_test, y_train, y_test = train_test_split(x, t, test_size=0.2, shuffle=True)
# one-Umstellung auf Hot Label
def to_one_hot(data, n_class):
vec = np.zeros((len(data), n_class))
for i in range(len(data)):
vec[i, data[i]] = 1.
return vec
t_train = to_one_hot(y_train, n_class)
t_test = to_one_hot(y_test, n_class)
Was wir tun, ist fast das gleiche wie in Keras. Der Unterschied besteht darin, dass der Datensatz im Format $ (1797, 64) $ übergeben wird. Daher werden die Daten nach der Standardisierung "umgeformt" und durch die Funktion "train_test_split" geteilt.
Sobald der Datensatz fertig ist, ist es Zeit zu lernen.
cnn_test.py
#Erstellen Sie eine Faltungsschicht und eine Ausgabeebene
M, F_h, F_w = 10, 3, 3
lm = LayerManager((x_train, x_test), (t_train, t_test))
lm.append(name="c", I_shape=(C, I_h, I_w), F_shape=(M, F_h, F_w), pad=1,
wb_width=0.1, opt="AdaDelta", opt_dic={"eta": 1e-2})
lm.append(name="p", I_shape=lm[-1].O_shape, pool=2)
lm.append(name="m", n=100, wb_width=0.1,
opt="AdaDelta", opt_dic={"eta": 1e-2})
lm.append(name="o", n=n_class, act="softmax", err_func="Cross", wb_width=0.1,
opt="AdaDelta", opt_dic={"eta": 1e-2})
#Lernen
epoch = 50
threshold = 1e-8
n_batch = 8
lm.training(epoch, threshold=threshold, n_batch=n_batch, show_train_error=True)
#Vorhersagen
print("training dataset")
lm.predict(x=lm.x_train, y=lm.y_train)
print("test dataset")
lm.predict()
Dieses Mal bauen wir ein sehr einfaches CNN. Die Anzahl der Lernepochen beträgt 50 und die Mini-Batch-Größe 8. Der Rest bleibt dem Layer Manager überlassen lol Die Struktur von CNN ist wie in der obigen Abbildung gezeigt. Das Ergebnis der Ausführung mit scikit-learn sollte wie in der folgenden Abbildung dargestellt sein.
Lassen Sie uns übrigens visualisieren, welche Art von Daten Sie fehlerhaft gemacht haben.
cnn_test.py
#Falsche Daten anzeigen
col=4
dpi=125
y = lm.pred_func(lm.y_test)
fail_index = np.where(y_pred != y)[0]
print("incorrect index:", fail_index)
if fail_index.size:
row = int(np.ceil(fail_index.size/col))
if row * dpi >= 2 ** 16:
row = int(np.ceil((2 ** 16 // dpi - 1)/col))
fig, ax = plt.subplots(row, col, figsize=(col, row + 1), dpi=dpi, facecolor="w")
if row != 1:
for i, f in enumerate(fail_index):
ax[i // col, i % col].imshow(lm.x_test[f], interpolation='nearest', cmap='gray')
ax[i // col, i % col].tick_params(labelbottom=False, labelleft=False, labelright=False, labeltop=False)
ax[i // col, i % col].set_title(str(y[f]) + " => " + str(y_pred[f]))
if i >= row * col:
break
else:
for i, f in enumerate(fail_index):
ax[i % col].imshow(lm.x_test[f], interpolation='nearest', cmap='gray')
ax[i % col].tick_params(labelbottom=False, labelleft=False, labelright=False, labeltop=False)
ax[i % col].set_title(str(y[f]) + ' => ' + str(y_pred[f]))
if i >= row * col:
break
fig.tight_layout()
Wenn dies ausgeführt wird, sieht es wie in der folgenden Abbildung aus. Bitte beachten Sie übrigens, dass es sich vom vorherigen Versuchsergebnis unterscheidet. Es fühlt sich für Menschen kaum sichtbar an ... Dies kann falsch eingeschätzt werden (so wie es ist).
Wenn die Chargengröße während des Experiments größer als 1 war, verlief das Lernen nicht gut und ich hatte es schwer. Immerhin lag es daran, dass die Aktivierungsfunktion nicht chargenfähig war. Gewöhnliche Aktivierungsfunktionen können dank "numpy" stapelaktiviert werden, aber einige außergewöhnliche Funktionen, wie die "softmax" -Funktion, müssen stapelbar sein. tat. Wenn jemand auf die gleiche Weise leidet, seien Sie bitte vorsichtig.
Recommended Posts