[PYTHON] Einführung in Deep Learning ~ CNN Experiment ~

Überblick

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 ...

Änderungsprotokoll

Inhaltsverzeichnis

Ändern Sie die Klasse "_TypeManager"

Fügen Sie es zunächst der Klasse "_TypeManager" hinzu, damit die Klasse "LayerManager" "ConvLayer" und "PoolingLayer" verarbeiten kann.

_type_manager.py

_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.

Hinzufügung der "Trainer" -Klasse

Die Funktionen zum Lernen und Vorhersagen sind von der "LayerManager" -Klasse als "Trainer" -Klasse getrennt.

trainer.py

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 ...

Ändern Sie die LayerManager-Klasse

Mit der Hinzufügung der Klassen "ConvLayer" und "Pooling" waren geringfügige Änderungen erforderlich.

layer_manager.py

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.

CNN-Experimentcodekörper

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.

Für Keras MNIST-Dataset

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.

Für Scikit-Learn MNIST-Datensatz

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.

CNN Lernkörper

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 CNN_test.png DNN_test.png 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. CNN_test_error_transition.png CNN_test_accuracy.png

Anzeige von falsch eingeschätzten Daten

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. incorrect.png Es fühlt sich für Menschen kaum sichtbar an ... Dies kann falsch eingeschätzt werden (so wie es ist).

abschließend

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.

Deep Learning-Serie

Recommended Posts

Einführung in Deep Learning ~ CNN Experiment ~
Einführung in Deep Learning ~ Lernregeln ~
Tiefe Stärkung des Lernens 1 Einführung in die Stärkung des Lernens
Einführung in Deep Learning ~ Backpropagation ~
Einführung in Deep Learning ~ Codierungsvorbereitung ~
Einführung in Deep Learning ~ Dropout Edition ~
Einführung in Deep Learning ~ Forward Propagation ~
Einführung in Deep Learning ~ Falten und Pooling ~
Einführung in das maschinelle Lernen
Einführung in Deep Learning ~ Lokalisierungs- und Verlustfunktion ~
[Lernmemorandum] Einführung in vim
<Kurs> Tiefes Lernen: Day2 CNN
Eine Einführung in das maschinelle Lernen
Super Einführung in das maschinelle Lernen
Einführung in das maschinelle Lernen Schreiben von Notizen
[Details (?)] Einführung in Pytorch ~ CNN von CIFAR10 ~
Tiefes Lernen, um ohne GPU zu beginnen
Einführung in die Bibliothek für maschinelles Lernen SHOGUN
Tiefes Lernen
Einführung in Deep Learning (2) - Versuchen Sie Ihre eigene nichtlineare Regression mit Chainer-
Verbessertes Lernen, um von null bis tief zu lernen
Eine Einführung in OpenCV für maschinelles Lernen
So studieren Sie den Deep Learning G-Test
Bildausrichtung: von SIFT bis Deep Learning
Eine Einführung in Python für maschinelles Lernen
Einführung in TensorFlow - Erläuterung der Begriffe und Konzepte des maschinellen Lernens
Einführung in MQTT (Einführung)
Einführung in Scrapy (1)
Einführung in Scrapy (3)
[Einführung] Stärkung des Lernens
Erste Schritte mit Supervisor
Einführung in Tkinter 1: Einführung
Deep Learning Memorandum
Einführung in PyQt
Einführung in Scrapy (2)
Starten Sie Deep Learning
Eine Geschichte über einen Anfänger im Deep Learning, der versucht, Gitarren mit CNN zu klassifizieren
[Linux] Einführung in Linux
Python Deep Learning
Einführung in Scrapy (4)
Deep Learning × Python
Einführung in discord.py (2)
[Python] Einfache Einführung in das maschinelle Lernen mit Python (SVM)
[Super Einführung in das maschinelle Lernen] Lernen Sie Pytorch-Tutorials
Eine Einführung in maschinelles Lernen für Bot-Entwickler
Ein Amateur versuchte Deep Learning mit Caffe (Einführung)
Deep Learning von Grund auf neu ① Kapitel 6 "Lerntechniken"
Eine Einführung in Cython, ohne tief zu gehen
[Einführung in StyleGAN2] Unabhängiges Lernen mit 10 Anime-Gesichtern ♬
[Super Einführung in das maschinelle Lernen] Lernen Sie Pytorch-Tutorials
"Deep Learning from Grund" Memo zum Selbststudium (Nr. 11) CNN
[Für Anfänger] Einführung in die Vektorisierung beim maschinellen Lernen
Einführung in Cython ohne tief zu gehen -2-
Einführung in Deep Learning (1) --Chainer wird Anfängern leicht verständlich erklärt.
Erstes tiefes Lernen ~ Kampf ~
Deep Learning von Grund auf neu
[Einführung in die Stärkung des Lernens] Teil 1 - Epsilon-Greedy-Algorithmus im Banditenspiel
Einführung in Lightning Pytorch