Jedi-Technik zur Reduzierung von Faltungsnetzwerken – Pruning

Jedi-Technik zur Reduzierung von Faltungsnetzwerken – Pruning

Vor Ihnen liegt erneut die Aufgabe, Objekte zu erkennen. Die Priorität liegt auf der Arbeitsgeschwindigkeit mit akzeptabler Genauigkeit. Sie nehmen die YOLOv3-Architektur und trainieren sie weiter. Die Genauigkeit (mAp75) ist größer als 0.95. Aber die Run-Rate ist immer noch niedrig. Mist.

Heute werden wir die Quantisierung umgehen. Und unter den Schnitt schauen wir Modellschnitt – Beschneiden redundanter Teile des Netzwerks, um die Inferenz ohne Genauigkeitsverlust zu beschleunigen. Es ist klar, wo, wie viel und wie geschnitten werden muss. Lassen Sie uns herausfinden, wie Sie dies manuell tun und wo Sie es automatisieren können. Am Ende gibt es ein Repository zu Keras.

Einführung

An meinem vorherigen Arbeitsplatz, Macroscop in Perm, habe ich mir eine Angewohnheit angeeignet – die Ausführungszeit von Algorithmen immer zu überwachen. Und überprüfen Sie die Netzwerklaufzeit immer durch einen Angemessenheitsfilter. Normalerweise besteht der Stand der Technik in der Produktion diesen Filter nicht, was mich zu Pruning geführt hat.

Beschneiden ist ein altes Thema, das in diskutiert wurde Stanford-Vorlesungen im Jahr 2017. Die Hauptidee besteht darin, die Größe des trainierten Netzwerks zu reduzieren, ohne an Genauigkeit zu verlieren, indem verschiedene Knoten entfernt werden. Es klingt cool, aber ich höre selten von seiner Verwendung. Wahrscheinlich gibt es nicht genügend Implementierungen, es gibt keine russischsprachigen Artikel, oder einfach jeder hält es für Beschneidungs-Know-how und schweigt.
Aber nehmen wir es auseinander

Ein Einblick in die Biologie

Ich liebe es, wenn Deep Learning Ideen betrachtet, die aus der Biologie stammen. Man kann ihnen, wie auch der Evolution, vertrauen (wussten Sie, dass ReLU sehr ähnlich ist). Funktion der Neuronenaktivierung im Gehirn?)

Der Model Pruning-Prozess steht auch in der Nähe der Biologie. Die Reaktion des Netzwerks kann hier mit der Plastizität des Gehirns verglichen werden. Das Buch enthält einige interessante Beispiele. Norman Doidge:

  1. Das Gehirn einer Frau, die nur mit einer Hälfte geboren wurde, hat sich neu programmiert, um die Funktionen der fehlenden Hälfte auszuführen.
  2. Der Typ hat den Teil seines Gehirns abgeschossen, der für das Sehen verantwortlich ist. Mit der Zeit übernahmen andere Teile des Gehirns diese Funktionen. (Wir versuchen nicht, es zu wiederholen)

Ebenso können Sie einige der schwachen Faltungen aus Ihrem Modell herausschneiden. Als letzten Ausweg helfen die verbleibenden Bündel dabei, die abgeschnittenen Bündel zu ersetzen.

Lieben Sie Transfer Learning oder lernen Sie von Grund auf?

Option Nummer eins. Sie verwenden Transfer Learning auf Yolov3. Retina, Mask-RCNN oder U-Net. Aber meistens müssen wir nicht wie in COCO 80 Objektklassen erkennen. In meiner Praxis ist alles auf die Klassen 1-2 beschränkt. Man könnte annehmen, dass die Architektur für 80 Klassen hier redundant ist. Dies legt nahe, dass die Architektur kleiner gemacht werden muss. Darüber hinaus möchte ich dies tun, ohne die vorhandenen vortrainierten Gewichte zu verlieren.

Option Nummer zwei. Vielleicht verfügen Sie über viele Daten- und Rechenressourcen oder benötigen einfach eine ganz individuelle Architektur. Nicht wichtig. Aber Sie lernen das Netzwerk von der Pike auf. Das übliche Verfahren besteht darin, sich die Datenstruktur anzusehen, eine Architektur auszuwählen, die ÜBERMÄSSIG leistungsstark ist, und Abbrecher aus der Umschulung zu verdrängen. Ich habe 0.6 Aussteiger gesehen, Karl.

In beiden Fällen kann das Netzwerk reduziert werden. Motiviert. Lassen Sie uns nun herausfinden, was für eine Art Beschneidungsschnitt es ist

Allgemeiner Algorithmus

Wir beschlossen, dass wir die Bündel entfernen könnten. Es sieht ganz einfach aus:

Jedi-Technik zur Reduzierung von Faltungsnetzwerken – Pruning

Das Entfernen jeglicher Faltung stellt eine Belastung für das Netzwerk dar, was in der Regel zu einer gewissen Erhöhung der Fehlerquote führt. Einerseits ist dieser Anstieg des Fehlers ein Indikator dafür, wie korrekt wir Faltungen entfernen (ein großer Anstieg zeigt beispielsweise an, dass wir etwas falsch machen). Eine kleine Steigerung ist aber durchaus akzeptabel und wird oft durch anschließendes leichtes Zusatztraining mit kleinem LR wieder eliminiert. Fügen Sie einen zusätzlichen Trainingsschritt hinzu:

Jedi-Technik zur Reduzierung von Faltungsnetzwerken – Pruning

Jetzt müssen wir herausfinden, wann wir unsere Learning<->Pruning-Schleife beenden wollen. Hier kann es exotische Optionen geben, wenn wir das Netzwerk auf eine bestimmte Größe und Geschwindigkeit reduzieren müssen (z. B. für mobile Geräte). Die häufigste Option besteht jedoch darin, den Zyklus fortzusetzen, bis der Fehler größer als akzeptabel wird. Fügen Sie eine Bedingung hinzu:

Jedi-Technik zur Reduzierung von Faltungsnetzwerken – Pruning

Damit wird der Algorithmus klar. Es bleibt herauszufinden, wie die gelöschten Windungen ermittelt werden können.

Suchen Sie nach gelöschten Paketen

Wir müssen einige Windungen entfernen. Vorzustürmen und jemanden zu „erschießen“ ist eine schlechte Idee, obwohl es funktionieren wird. Aber da Sie einen Kopf haben, können Sie nachdenken und versuchen, „schwache“ Windungen zum Entfernen auszuwählen. Es gibt mehrere Möglichkeiten:

  1. Kleinstes L1-Maß oder low_magnitude_pruning. Die Idee, dass Faltungen mit kleinen Gewichten kaum zur endgültigen Entscheidung beitragen
  2. Kleinstes L1-Maß unter Berücksichtigung von Mittelwert und Standardabweichung. Wir ergänzen mit einer Einschätzung der Art der Verteilung.
  3. Faltungen werden maskiert und diejenigen ausgeschlossen, die die endgültige Genauigkeit am wenigsten beeinflussen. Genauere Bestimmung unbedeutender Windungen, aber sehr zeit- und ressourcenintensiv.
  4. Andere

Jede der Optionen hat das Recht auf Leben und ihre eigenen Implementierungsmerkmale. Hier betrachten wir die Option mit dem kleinsten L1-Maß

Manueller Prozess für YOLOv3

Die ursprüngliche Architektur enthält Restblöcke. Aber egal wie cool sie für tiefe Netzwerke sind, sie werden uns etwas behindern. Die Schwierigkeit besteht darin, dass Sie Abstimmungen mit unterschiedlichen Indizes in diesen Ebenen nicht löschen können:

Jedi-Technik zur Reduzierung von Faltungsnetzwerken – Pruning

Wählen wir daher Ebenen aus, aus denen wir Abstimmungen frei löschen können:

Jedi-Technik zur Reduzierung von Faltungsnetzwerken – Pruning

Lassen Sie uns nun einen Arbeitszyklus erstellen:

  1. Aktivierungen hochladen
  2. Herausfinden, wie viel man schneiden muss
  3. Schneide es aus
  4. Lernen von 10 Epochen mit LR=1e-4
  5. Testen

Das Entladen von Windungen ist nützlich, um abzuschätzen, welchen Teil wir in einem bestimmten Schritt entfernen können. Entladebeispiele:

Jedi-Technik zur Reduzierung von Faltungsnetzwerken – Pruning

Wir sehen, dass fast überall 5 % der Faltungen eine sehr niedrige L1-Norm haben und wir können sie entfernen. Bei jedem Schritt wurde dieses Entladen wiederholt und beurteilt, welche Schichten und wie viele herausgeschnitten werden konnten.

Der gesamte Prozess wurde in 4 Schritten abgeschlossen (Zahlen hier und überall für die RTX 2060 Super):

Schritt Karte75 Anzahl der Parameter, Millionen Netzwerkgröße, MB Vom Anfang, % Laufzeit, ms Beschneidungszustand
0 0.9656 60 241 100 180 -
1 0.9622 55 218 91 175 5 % von allen
2 0.9625 50 197 83 168 5 % von allen
3 0.9633 39 155 64 155 15 % für Schichten mit mehr als 400 Windungen
4 0.9555 31 124 51 146 10 % für Schichten mit mehr als 100 Windungen

Ein positiver Effekt wurde zu Schritt 2 hinzugefügt – Chargengröße 4 passte in den Speicher, was den Prozess des zusätzlichen Trainings erheblich beschleunigte.
Bei Schritt 4 wurde der Prozess gestoppt, weil Selbst langfristiges zusätzliches Training erhöhte MAP75 nicht auf die alten Werte.
Dadurch ist es uns gelungen, die Schlussfolgerung um zu beschleunigen 15%, verkleinern Sie die Größe um 35% und nicht genau verlieren.

Automatisierung für einfachere Architekturen

Bei einfacheren Netzwerkarchitekturen (ohne bedingtes Hinzufügen, Verketten und Restblöcke) ist es durchaus möglich, sich auf die Verarbeitung aller Faltungsschichten zu konzentrieren und den Prozess des Ausschneidens von Faltungen zu automatisieren.

Ich habe diese Option implementiert hier.
Es ist ganz einfach: Sie benötigen lediglich eine Verlustfunktion, einen Optimierer und 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 Sie die Konfigurationsparameter ändern:

{
    "input_model_path": "model.h5",
    "output_model_path": "model_pruned.h5",
    "finetuning_epochs": 10, # the number of epochs for train between pruning steps
    "stop_loss": 0.1, # loss for stopping process
    "pruning_percent_step": 0.05, # part of convs for delete on every pruning step
    "pruning_standart_deviation_part": 0.2 # shift for limit pruning part
}

Zusätzlich ist eine Begrenzung auf Basis der Standardabweichung implementiert. Das Ziel besteht darin, den entfernten Teil zu begrenzen und Faltungen mit bereits „ausreichenden“ L1-Maßen auszuschließen:

Jedi-Technik zur Reduzierung von Faltungsnetzwerken – Pruning

Somit ermöglichen wir Ihnen, nur schwache Faltungen aus Verteilungen zu entfernen, die der rechten ähneln, und haben keinen Einfluss auf die Entfernung aus Verteilungen, die der linken ähneln:

Jedi-Technik zur Reduzierung von Faltungsnetzwerken – Pruning

Wenn sich die Verteilung dem Normalzustand nähert, kann der Koeffizient pruning_standart_deviation_part ausgewählt werden aus:

Jedi-Technik zur Reduzierung von Faltungsnetzwerken – Pruning
Ich empfehle eine Annahme von 2 Sigma. Sie können diese Funktion auch ignorieren und den Wert < 1.0 belassen.

Die Ausgabe ist ein Diagramm der Netzwerkgröße, des Verlusts und der Netzwerklaufzeit für den gesamten Test, normalisiert auf 1.0. Hier wurde beispielsweise die Netzwerkgröße ohne Qualitätsverlust um fast das Zweifache reduziert (kleines Faltungsnetzwerk mit 2 Gewichten):

Jedi-Technik zur Reduzierung von Faltungsnetzwerken – Pruning

Die Laufgeschwindigkeit unterliegt normalen Schwankungen und bleibt nahezu unverändert. Dafür gibt es eine Erklärung:

  1. Die Anzahl der Windungen ändert sich von praktisch (32, 64, 128) zu nicht gerade praktisch für Grafikkarten – 27, 51 usw. Ich könnte mich hier irren, aber höchstwahrscheinlich hat es einen Effekt.
  2. Die Architektur ist nicht breit, aber konsistent. Durch die Reduzierung der Breite haben wir keinen Einfluss auf die Tiefe. Somit reduzieren wir die Belastung, verändern aber nicht die Geschwindigkeit.

Daher äußerte sich die Verbesserung in einer Reduzierung der CUDA-Belastung während des Laufs um 20–30 %, nicht jedoch in einer Reduzierung der Laufzeit

Ergebnisse

Lasst uns nachdenken. Wir haben zwei Optionen zum Beschneiden in Betracht gezogen – für YOLOv2 (wenn Sie mit Ihren Händen arbeiten müssen) und für Netzwerke mit einfacheren Architekturen. Es ist ersichtlich, dass in beiden Fällen eine Reduzierung der Netzwerkgröße und eine Beschleunigung ohne Genauigkeitsverlust möglich ist. Ergebnisse:

  • Reduzierung der Größe
  • Beschleunigungslauf
  • Reduzierung der CUDA-Last
  • Dadurch Umweltfreundlichkeit (Wir optimieren die zukünftige Nutzung von Rechenressourcen. Irgendwo ist man glücklich Greta Tunberg)

Anhang

  • Nach dem Bereinigungsschritt können Sie eine Quantisierung hinzufügen (z. B. mit TensorRT).
  • Tensorflow bietet Funktionen für low_magnitude_pruning. Funktioniert.
  • Repository Ich möchte mich weiterentwickeln und helfe gerne dabei

Source: habr.com

Kommentar hinzufügen