Jedi-Technik zur Reduzierung von konvolutionalen Netzen — Pruning

Jedi-Technik zur Reduzierung von konvolutionalen Netzen — Pruning

Vor dir steht wieder die Aufgabe der Objekterkennung. PrioritĂ€t hat die Arbeitsgeschwindigkeit bei akzeptabler Genauigkeit. Du nimmst die Architektur YOLOv3 und bildest sie weiter. Die Genauigkeit (mAp75) liegt ĂŒber 0,95. Aber die Laufgeschwindigkeit ist immer noch niedrig. Verdammt.

Heute umgehen wir die Quantisierung. Im Nachfolgenden betrachten wir Model Pruning — das Entfernen ĂŒberflĂŒssiger Teile des Netzwerks zur Beschleunigung der Inferenz ohne Verlust der Genauigkeit. Verdeutlicht wird, wo, wie viel und wie man schneiden kann. Wir werden untersuchen, wie man dies manuell durchfĂŒhrt und wo Automatisierung möglich ist. Am Ende — ein Repository auf Keras.

EinfĂŒhrung

An meinem vorherigen Arbeitsplatz, dem permischen Macroscop, habe ich mir eine Gewohnheit angeeignet — immer die AusfĂŒhrungszeit der Algorithmen zu ĂŒberwachen. Die Laufzeit der Netzwerke ĂŒberprĂŒfe ich immer durch einen Angemessenheitsfilter. Normalerweise bestehen state-of-the-art Modelle diesen Filter nicht, was mich zum Pruning gefĂŒhrt hat.

Pruning — ein altes Thema, das in den Stanford-Vorlesungen im Jahr 2017 behandelt wurde. Die Hauptidee ist die Reduzierung der GrĂ¶ĂŸe eines trainierten Netzwerks, ohne die Genauigkeit zu verlieren, indem verschiedene Knoten entfernt werden. Klingt cool, aber ich höre selten von seiner Anwendung. Wahrscheinlich fehlen Umsetzungen, es gibt keine russischsprachigen Artikel oder alle halten Pruning einfach fĂŒr ein Geheimnis und schweigen.
Aber lass uns das betrachten.

Ein Blick in die Biologie

Ich liebe es, wenn im Deep Learning Ideen aus der Biologie aufblitzen. Ihnen, wie der Evolution, kann man vertrauen (wusstest du, dass ReLU sehr Àhnlich ist wie die Aktivierungsfunktion von Neuronen im Gehirn?)

Der Prozess des Model Pruning ist auch nahe an der Biologie. Die Reaktion des Netzwerks hier kann mit der PlastizitÀt des Gehirns verglichen werden. Einige interessante Beispiele finden sich 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 Junge hat sich einen Teil seines Gehirns entfernt, der fĂŒr das Sehen verantwortlich war. Im Laufe der Zeit haben andere Teile des Gehirns diese Funktionen ĂŒbernommen. (Wiederholen wir nicht.)

So kann auch aus deinem Modell ein Teil schwacher Faltungen entfernt werden. Im schlimmsten Fall können die verbleibenden Faltungen die entfernten ersetzen.

Liebst du Transfer Learning oder lernst du von Grund auf neu?

Option Nummer eins. Du verwendest Transfer Learning fĂŒr Yolov3. Retina, Mask-RCNN oder U-Net. Aber meistens mĂŒssen wir nicht 80 Klassen von Objekten erkennen, wie in COCO. In meiner Praxis beschrĂ€nken sich die Klassen auf 1-2. Man könnte annehmen, dass die Architektur fĂŒr 80 Klassen hier ĂŒberdimensioniert ist. Es liegt nahe, die Architektur zu reduzieren. Und das möchte ich gerne tun, ohne die vorhandenen vortrainierten Gewichte zu verlieren.

Variante Nummer zwei. Vielleicht hast du viele Daten und Rechenressourcen oder brauchst einfach eine super individuelle Architektur. Egal. Aber du trainierst das Netzwerk von Grund auf. Der ĂŒbliche Ablauf — wir schauen uns die Datenstruktur an, wĂ€hlen eine ÜBERDIMENSIONIERTE Architektur aus und drĂŒcken Dropouts gegen Überanpassung. Ich habe Dropouts von 0.6 gesehen, Karl.

In beiden FÀllen kann das Netzwerk verkleinert werden. Das waren die Anreize. Jetzt lass uns klÀren, was es mit dem Pruning auf sich hat.

Allgemeiner Algorithmus

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

Jedi-Technik zur Reduzierung von konvolutionalen Netzen — Pruning

Die Entfernung einer beliebigen Faltung ist ein Stress fĂŒr das Netzwerk, der normalerweise mit einem gewissen Anstieg des Fehlers einhergeht. Einerseits ist dieser Anstieg des Fehlers ein Maß dafĂŒr, wie korrekt wir die Faltungen entfernen (zum Beispiel zeigt ein großer Anstieg, dass wir etwas falsch machen). Aber ein kleiner Anstieg ist durchaus akzeptabel und wird oft durch anschließendes leichtes Nachtraining mit einem niedrigen LR behoben. Wir fĂŒgen einen Schritt zum Nachtraining hinzu:

Jedi-Technik zur Reduzierung von konvolutionalen Netzen — Pruning

Jetzt mĂŒssen wir verstehen, wann wir unseren Zyklus Learning<->Pruning stoppen wollen. Hier kann es exotische FĂ€lle geben, in denen wir das Netzwerk auf eine bestimmte GrĂ¶ĂŸe und Geschwindigkeit reduzieren mĂŒssen (zum Beispiel fĂŒr mobile GerĂ€te). Der hĂ€ufigste Fall ist jedoch, dass wir den Zyklus fortsetzen, bis der Fehler den zulĂ€ssigen Wert ĂŒberschreitet. Wir fĂŒgen eine Bedingung hinzu:

Jedi-Technik zur Reduzierung von konvolutionalen Netzen — Pruning

So wird der Algorithmus klar. Es bleibt zu klÀren, wie wir die zu entfernenden Faltungen bestimmen.

Suche nach zu entfernenden Faltungen

Wir mĂŒssen einige Faltungen entfernen. Einfach unverblĂŒmt „wegzuschießen“ ist keine gute Idee, auch wenn es funktioniert. Aber wenn man einen Kopf hat, kann man nachdenken und versuchen, „schwache“ Faltungen herauszufiltern, die man entfernen kann. Es gibt mehrere Optionen:

  1. Kleinster L1-Maßstab oder low_magnitude_pruning. Die Idee besagt, dass Faltungen mit kleinen Gewichtswerten, einen geringen Beitrag zur endgĂŒltigen Entscheidungsfindung leisten.
  2. Kleinster L1-Maßstab unter BerĂŒcksichtigung von Mittelwert und Standardabweichung. Wir ergĂ€nzen die Bewertung der Verteilung.
  3. Maskierung von Faltungen und Ausschluss der am wenigsten einflussreichen auf die Gesamtabweichung. Eine genauere Bestimmung der unwesentlichen Faltungen, jedoch zeit- und ressourcenintensiv.
  4. Andere

Jede der Varianten hat das Recht auf Existenz und eigene Umsetzungsmerkmale. Hier betrachten wir die Variante mit der kleinsten L1-Metrik

Manueller Prozess fĂŒr YOLOv3

In der ursprĂŒnglichen Architektur sind Residualblöcke enthalten. Doch egal wie beeindruckend sie fĂŒr tiefe Netzwerke sind, sie stören uns etwas. Das Problem ist, dass Faltungen mit unterschiedlichen Indizes in diesen Schichten nicht entfernt werden können:

Jedi-Technik zur Reduzierung von konvolutionalen Netzen — Pruning

Deshalb bestimmen wir die Schichten, aus denen wir Faltungen frei entfernen können:

Jedi-Technik zur Reduzierung von konvolutionalen Netzen — Pruning

Jetzt erstellen wir den Arbeitszyklus:

  1. Wir laden die Aktivierungen hoch
  2. Wir schÀtzen ein, wie viel ausgeschnitten werden soll
  3. Wir schneiden aus
  4. Wir trainieren 10 Epochen mit LR=1e-4
  5. Wir testen

Das Entfernen der Faltungen ist nĂŒtzlich, um zu bewerten, welchen Teil wir in einem bestimmten Schritt entfernen können. Beispiele fĂŒr das Entfernen:

Jedi-Technik zur Reduzierung von konvolutionalen Netzen — Pruning

Wir sehen, dass fast ĂŒberall 5% der Faltungen eine sehr niedrige L1-Norm aufweisen und wir sie entfernen können. In jedem Schritt wurde solch ein Entfernen wiederholt und eine Bewertung durchgefĂŒhrt, aus welchen Schichten und wie viel entfernt werden kann.

Der gesamte Prozess war in 4 Schritte unterteilt (hier und ĂŒberall die Zahlen fĂŒr RTX 2060 Super):

SchrittmAp75Anzahl der Parameter, Mio.GrĂ¶ĂŸe des Netzwerks, MBVon der ursprĂŒnglichen, %Laufzeit, msBedingung zum Ausschneiden
00.965660241100180—
10.962255218911755% von allen
20.962550197831685% von allen
30.9633391556415515% fĂŒr Schichten mit 400+ Faltungen
40.9555311245114610% fĂŒr Schichten mit 100+ Faltungen

Zu Schritt 2 kam ein positiver Effekt hinzu – eine Batch-GrĂ¶ĂŸe von 4 passte in den Speicher, was den Prozess des weiteren Trainings erheblich beschleunigte.
Im Schritt 4 wurde der Prozess gestoppt, da auch ein lÀngeres weiteres Training mAp75 nicht auf die alten Werte erhöhen konnte.
Letztendlich konnte der Inferenzprozess beschleunigt werden um 15%, die GrĂ¶ĂŸe reduziert werden um 35% und es ging keine Genauigkeit verloren.

Automatisierung fĂŒr einfachere Architekturen

FĂŒr einfachere Netzwerkarchitekturen (ohne bedingte add, concatenate und residual Blöcke) kann man sich ganz auf die Verarbeitung aller Faltungsschichten konzentrieren und den Prozess des Ausschneidens von Faltungen automatisieren.

Solch eine Variante habe ich implementiert hier.
Es ist ganz einfach: Sie benötigen nur 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)

Falls nötig, können die Konfigurationsparameter geÀndert werden:

{
    "input_model_path": "model.h5",
    "output_model_path": "model_pruned.h5",
    "finetuning_epochs": 10, # die Anzahl der Epochen zum Trainieren zwischen den Pruning-Schritten
    "stop_loss": 0.1, # Verlust zum Stoppen des Prozesses
    "pruning_percent_step": 0.05, # Anteil der Konvolutionen, 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 Teil der zu löschenden Elemente zu begrenzen, wobei Konvolutionen mit bereits "ausreichenden" L1-Maßen ausgeschlossen werden:

Jedi-Technik zur Reduzierung von konvolutionalen Netzen — Pruning

Damit erlauben wir es, nur schwache Konvolutionen aus Verteilungen Àhnlich der rechten zu löschen und nicht die Löschung aus Verteilungen Àhnlich der linken zu beeinflussen:

Jedi-Technik zur Reduzierung von konvolutionalen Netzen — Pruning

Bei der AnnÀherung der Verteilung an die Normalverteilung kann der Koeffizient pruning_standart_deviation_part ausgewÀhlt werden aus:

Jedi-Technik zur Reduzierung von konvolutionalen Netzen — Pruning
Ich empfehle eine Toleranz von 2 Sigma. Alternativ kann man diese Besonderheit ignorieren und den Wert < 1.0 belassen.

Das Ergebnis ist ein Diagramm der NetzwerkgrĂ¶ĂŸe, Verluste und der Laufzeit des Netzwerks ĂŒber den gesamten Test, normiert auf 1.0. Zum Beispiel wurde hier die NetzwerkgrĂ¶ĂŸe fast halbiert, ohne an QualitĂ€t zu verlieren (ein kleines Convolutional-Netzwerk mit 100k Gewichten):

Jedi-Technik zur Reduzierung von konvolutionalen Netzen — Pruning

Die Laufgeschwindigkeit unterliegt normalen Schwankungen und hat sich praktisch nicht verÀndert. Das hat einen Grund:

  1. Die Anzahl der Konvolutionen wechselt von bequemen (32, 64, 128) zu weniger bequemen fĂŒr Grafikkarten — 27, 51 usw. Hier kann ich mich irren, aber wahrscheinlich hat das einen Einfluss.
  2. Die Architektur ist nicht breit, sondern durchgÀngig. Indem wir die Breite reduzieren, Àndern wir nicht die Tiefe. So verringern wir die Last, ohne die Geschwindigkeit zu beeinflussen.

Daher zeigt sich die Verbesserung in der Reduzierung der CUDA-Last beim Durchlauf um 20-30%, jedoch nicht in der Verringerung der Durchlaufzeit.

Ergebnisse

Lass 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 klar, dass in beiden FĂ€llen eine Verringerung der NetzwerkgrĂ¶ĂŸe und Beschleunigung ohne Verlust an Genauigkeit erreicht werden kann. Ergebnisse:

  • Verringerung der GrĂ¶ĂŸe
  • Beschleunigung des Durchlaufs
  • Verringerung der CUDA-Last
  • Als Folge davon ist die Umweltfreundlichkeit (Wir optimieren die zukĂŒnftige Nutzung von Rechenressourcen. Irgendwo freut sich eine Greta Thunberg)

Anhang

  • Nach dem Pruning-Schritt kann noch Quantisierung hinzugefĂŒgt werden (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

60GB SSD 8Gb DDR4