Die Jedi-Technik zur Reduzierung von Convolutional Networks — Pruning

Die Jedi-Technik zur Reduzierung von Convolutional Networks — Pruning

Du stehst erneut vor der Aufgabe der Objekterkennung. Priorität hat die Geschwindigkeit bei akzeptabler Genauigkeit. Du nimmst die Architektur YOLOv3 und feinstimmst sie. Genauigkeit (mAp75) über 0,95. Doch die Geschwindigkeit ist immer noch niedrig. Verdammt.

Heute lassen wir die Quantisierung beiseite. Im Anschluss betrachten wir Model Pruning — das Entfernen überflüssiger Teile des Netzwerks zur Beschleunigung der Inferenz bei Erhaltung der Genauigkeit. Anschaulich — wo, wie viel und wie kann ausgeschnitten werden. Wir werden besprechen, wie man dies manuell macht und wo man es automatisieren kann. Am Ende — ein Repository für Keras.

Einführung

Bei meinem vorherigen Arbeitgeber, dem Permer Macroscop, habe ich mir eine Angewohnheit angeeignet — stets die Laufzeit der Algorithmen zu überwachen. Und die Laufzeit der Netzwerke immer über einen Angemessenheitsfilter zu überprüfen. In der Regel bestehen state-of-the-art Modelle in der Produktion diesen Filter nicht, was mich zum Pruning geführt hat.

Pruning — ein altes Thema, das in den Stanford-Vorlesungen behandelt wurde. Im Jahr 2017. Die grundlegende Idee ist, die Größe des trainierten Netzwerks zu verringern, ohne dass die Genauigkeit leidet, indem verschiedene Knoten entfernt werden. Klingt cool, aber ich höre selten von seiner Anwendung. Wahrscheinlich mangelt es an Implementierungen, es gibt keine deutschsprachigen Artikel oder einfach alle halten pruning für ein Geheimnis und schweigen.
Aber lass uns das auseinandernehmen.

Ein Blick in die Biologie

Ich liebe es, wenn Ideen aus der Biologie im Deep Learning Anwendung finden. Auf sie, ebenso wie auf die Evolution, kann man vertrauen (wusstest du, dass ReLU sehr ähnlich ist wie die Aktivierungsfunktion von Neuronen im Gehirn??)

Der Prozess des Model Pruning ist ebenfalls eng mit der Biologie verbunden. Die Reaktion des Netzwerks lässt sich mit der Plastizität des Gehirns vergleichen. Einige interessante Beispiele findet man im Buch von Norman Doidge.:

  1. Das Gehirn einer Frau, die von Geburt an nur eine Gehirnhälfte hatte, hat sich selbst umprogrammiert, um die Funktionen der fehlenden Hälfte auszuführen.
  2. Ein Mann hatte sich einen Teil seines Gehirns entfernt, der für das Sehen verantwortlich war. Im Laufe der Zeit übernahmen andere Teile des Gehirns diese Funktionen. (Wir versuchen nicht, das zu wiederholen.)

So kann auch aus deinem Modell ein Teil schwacher Schichten entfernt werden. Im Zweifelsfall helfen die verbleibenden Schichten, die entfernten zu ersetzen.

Liebst du Transfer Learning oder lernst du von Grund auf?

Option Nummer eins. Sie nutzen Transfer Learning mit Yolov3, Retina, Mask-RCNN oder U-Net. Doch häufig müssen wir nicht 80 Objektklassen wie im COCO-Datensatz erkennen. In meiner Erfahrung beschränkt sich das alles auf 1-2 Klassen. Man könnte annehmen, dass die Architektur für 80 Klassen hier übertrieben ist. Daher liegt der Gedanke nahe, die Architektur zu verkleinern. Und das möchte ich gerne tun, ohne die bestehenden vortrainierten Gewichte zu verlieren.

Option Nummer zwei. Vielleicht haben Sie viele Daten und Rechenressourcen oder benötigen einfach eine hochgradig angepasste Architektur. Das ist irrelevant. Aber Sie trainieren das Netzwerk von Grund auf. Der übliche Ablauf besteht darin, sich die Datenstruktur anzusehen, eine ÜBERFLÜSSIGE leistungsstarke Architektur auszuwählen und Dropouts gegen Überanpassung anzuwenden. Ich habe Dropouts von 0.6 gesehen, Karl.

In beiden Fällen lässt sich das Netzwerk verkleinern. Wir haben einen Grund dafür formuliert. Jetzt wollen wir herausfinden, was es mit dem Pruning auf sich hat.

Algorithmus insgesamt

Wir haben entschieden, dass wir Faltungen entfernen können. Das sieht ziemlich einfach aus:

Die Jedi-Technik zur Reduzierung von Convolutional Networks — Pruning

Das Entfernen von Faltungen belastet das Netzwerk und führt in der Regel zu einem gewissen Anstieg der Fehlerquote. Einerseits zeigt dieser Anstieg, wie gut wir die Faltungen entfernen (ein großer Anstieg deutet darauf hin, dass wir etwas falsch machen). Ein kleiner Anstieg ist jedoch durchaus akzeptabel und kann oft durch anschließendes leichtes Nachtraining mit einer kleinen Lernrate behoben werden. Fügen wir einen Nachtrainingsschritt hinzu:

Die Jedi-Technik zur Reduzierung von Convolutional Networks — Pruning

Jetzt müssen wir verstehen, wann wir unseren Lern-Pruning-Zyklus stoppen möchten. Es kann exotische Szenarien geben, in denen wir das Netzwerk auf eine bestimmte Größe und Geschwindigkeit reduzieren müssen (z. B. für mobile Geräte). Das häufigste Szenario ist jedoch das Fortsetzen des Zyklus, bis der Fehler über den akzeptablen Wert steigt. Fügen wir eine Bedingung hinzu:

Die Jedi-Technik zur Reduzierung von Convolutional Networks — Pruning

Somit wird der Algorithmus klar. Es bleibt zu klären, wie die zu entfernenden Faltungen identifiziert werden.

Suche nach entfernbaren Faltungen

Wir müssen einige Faltungen entfernen. Einfach unüberlegt und willkürlich Faltungen zu entfernen, ist keine gute Idee, auch wenn es funktionieren könnte. Aber wenn wir klug sind, können wir versuchen, "schwache" Faltungen zur Entfernung auszuwählen. Es gibt mehrere Ansätze:

  1. Die kleinste L1-Metrik oder Low-Magnitude-Pruning. Die Idee besagt, dass Faltungen mit geringen Gewichtswerten nur einen kleinen Einfluss auf die endgültige Entscheidungsfindung haben.
  2. Die kleinste L1-Metrik unter Berücksichtigung des Mittelwerts und der Standardabweichung. Ergänzt durch eine Bewertung der Verteilungsart.
  3. Maskierung von Faltungen und Ausschluss der am wenigsten relevanten für die endgültige Genauigkeit.. Eine genauere Bestimmung von unbedeutenden Faltungen, aber sehr zeit- und ressourcenintensiv.
  4. Andere

Jede der Varianten hat ihre Daseinsberechtigung und spezifische Implementierungsmerkmale. Hier betrachten wir die Variante mit der kleinsten L1-Metrik.

Manueller Prozess für YOLOv3

Die ursprüngliche Architektur enthält Restblöcke. Aber egal wie effektiv sie für tiefe Netzwerke sind, sie stören uns ein wenig. Das Problem ist, dass wir in diesen Schichten keine Faltungen mit unterschiedlichen Indizes löschen können:

Die Jedi-Technik zur Reduzierung von Convolutional Networks — Pruning

Deshalb heben wir die Schichten hervor, aus denen wir Faltungen ohne Bedenken entfernen können:

Die Jedi-Technik zur Reduzierung von Convolutional Networks — Pruning

Jetzt bauen wir einen Arbeitszyklus auf:

  1. Aktivierungen exportieren
  2. Schätzen, wie viel ausgeschnitten werden soll
  3. Ausschneiden
  4. Trainieren über 10 Epochen mit LR=1e-4
  5. Testen

Es ist hilfreich, Faltungen zu exportieren, um zu bewerten, welchen Teil wir zu einem bestimmten Zeitpunkt entfernen können. Beispiele für den Export:

Die Jedi-Technik zur Reduzierung von Convolutional Networks — Pruning

Wir sehen, dass praktisch überall 5 % der Faltung eine sehr niedrige L1-Norm aufweisen, sodass wir sie entfernen können. Bei jedem Schritt wiederholte sich diese Auslagerung und es wurde bewertet, aus welchen Schichten und wie viel ausgeschnitten werden kann.

Der gesamte Prozess wurde in 4 Schritte gegliedert (hier und überall beziehen sich die Zahlen auf die RTX 2060 Super):

SchrittmAp75Anzahl der Parameter, MillionenNetzwerkgröße, MBVon der ursprünglichen, %Durchlaufzeit, msBedienung des Zuschneidens
00.965660241100180
10.962255218911755 % von allem
20.962550197831685 % von allem
30.9633391556415515 % für Schichten mit 400+ Faltungen
40.9555311245114610 % für Schichten mit 100+ Faltungen

Zu Schritt 2 kam ein positiver Effekt hinzu — der Batch-Size 4 konnte in den Speicher geladen werden, was den Prozess des Nachtrainierens erheblich beschleunigte.
Im Schritt 4 wurde der Prozess gestoppt, da selbst umfangreiches Nachtrainieren den mAp75 nicht auf die alten Werte hob.
Letztendlich gelang es, die Inferenz um 15%, die Größe um 35% zu reduzieren und dabei die Genauigkeit nicht zu verlieren.

Automatisierung für einfachere Architekturen

Für einfachere Netzwerkarchitekturen (ohne bedingte Additionen, Konkatenierungen und Residualblöcke) kann man sich gut auf die Verarbeitung aller Faltungen konzentrieren und den Prozess des Ausschneidens von Faltungen automatisieren.

Diese Option habe ich implementiert. hier.
Es ist ganz einfach: Sie benötigen lediglich die Verlustfunktion, den Optimierer und die Batch-Generatoren:

import pruning
from keras.optimizers import Adam
from keras.utils import Sequence

train_batch_generator = BatchGenerator...
score_batch_generator = BatchGenerator...

opt = Adam(lr=1e-4)
pruner = pruning.Pruner("config.json", "categorical_crossentropy", opt)

pruner.prune(train_batch, valid_batch)

Bei Bedarf können die Konfigurationsparameter geändert werden:

{
    "input_model_path": "model.h5",
    "output_model_path": "model_pruned.h5",
    "finetuning_epochs": 10, # Anzahl der Epochen für das Training zwischen den Pruning-Schritten
    "stop_loss": 0.1, # Verlust zum Stoppen des Prozesses
    "pruning_percent_step": 0.05, # Anteil der Convolutionen, die bei jedem Pruning-Schritt gelöscht werden
    "pruning_standart_deviation_part": 0.2 # Verschiebung zur Begrenzung des Pruning-Anteils
}

Zusätzlich wurde eine Einschränkung basierend auf der Standardabweichung implementiert. Ziel ist es, den Anteil der entfernten Elemente zu begrenzen, indem Convolutions mit bereits "ausreichenden" L1-Messungen ausgeschlossen werden:

Die Jedi-Technik zur Reduzierung von Convolutional Networks — Pruning

So erlauben wir es, nur schwache Convolutionen aus Verteilungen zu entfernen, die der rechten ähnlich sind, und beeinflussen nicht die Entfernung aus Verteilungen, die der linken ähnlich sind:

Die Jedi-Technik zur Reduzierung von Convolutional Networks — Pruning

Wenn sich die Verteilung dem Normalverteilung annähert, kann der Koeffizient pruning_standart_deviation_part aus folgendem ausgewählt werden:

Die Jedi-Technik zur Reduzierung von Convolutional Networks — Pruning
Ich empfehle einen Toleranzwert von 2 Sigma. Alternativ kann man sich auch nicht an dieser Besonderheit orientieren und einen Wert < 1,0 beibehalten.

Das Ergebnis ist ein Diagramm, das die Netzgröße, den Verlust und die Laufzeit über den gesamten Test hinweg anzeigt, normiert auf 1.0. Zum Beispiel wurde hier die Netzgröße fast halbiert, ohne Qualitätsverlust (eine kleine Faltungsnetzwerk mit 100k Gewichten):

Die Jedi-Technik zur Reduzierung von Convolutional Networks — Pruning

Die Laufgeschwindigkeit unterliegt normalen Schwankungen und hat sich praktisch nicht verändert. Das hat seine Erklärung:

  1. Die Anzahl der Faltungen wechselt von bequemen (32, 64, 128) zu weniger praktischen für Grafikkarten — 27, 51 usw. Ich könnte mich irren, aber wahrscheinlich hat das einen Einfluss.
  2. Die Architektur ist nicht breit, aber sequenziell. Durch Verkleinerung der Breite berühren wir nicht die Tiefe. Dadurch reduzieren wir die Last, ändern jedoch nicht die Geschwindigkeit.

Daher zeigt sich die Verbesserung in der Reduzierung der CUDA-Last während des Laufs um 20-30%, jedoch nicht in der Verringerung der Laufzeit.

Ergebnisse

Lassen Sie uns reflektieren. Wir haben zwei Varianten des Pruning betrachtet — für YOLOv3 (wenn man manuell arbeiten muss) und für Netzwerke mit einfacheren Architekturen. Es ist evident, dass man in beiden Fällen die Netzgröße reduzieren und die Geschwindigkeit erhöhen kann, ohne an Genauigkeit zu verlieren. Ergebnisse:

  • Reduzierung der Größe
  • Beschleunigung des Laufs
  • Reduzierung der CUDA-Last
  • Infolgedessen legen wir Wert auf Nachhaltigkeit (Wir optimieren die zukünftige Nutzung von Rechenressourcen. Irgendwo freut sich jemand. Greta Thunberg)

Anhang

  • Nach dem Schritt pruning kann man auch die Quantisierung anwenden (zum Beispiel mit TensorRT)
  • TensorFlow bietet Möglichkeiten für low_magnitude_pruning. Es funktioniert.
  • Repository Ich möchte mich weiterentwickeln und freue mich über Hilfe.

Quelle: habr.com

Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen 🔥 Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen | ProHoster