Jedi-techniek voor het verminderen van convolutionele netwerken - snoeien

Jedi-techniek voor het verminderen van convolutionele netwerken - snoeien

Voordat je weer de taak hebt om objecten te detecteren. De prioriteit is snelheid van werken met aanvaardbare nauwkeurigheid. Je neemt de YOLOv3-architectuur en traint deze verder. Nauwkeurigheid (mAp75) is groter dan 0.95. Maar de runrate is nog steeds laag. Stront.

Vandaag zullen we de kwantisering omzeilen. En onder de snee zullen we kijken Model snoeien — het inkorten van overtollige delen van het netwerk om Inference te versnellen zonder verlies van nauwkeurigheid. Het is duidelijk waar, hoeveel en hoe je moet snijden. Laten we eens kijken hoe u dit handmatig kunt doen en waar u het kunt automatiseren. Aan het einde is er een repository op keras.

Introductie

Op mijn vorige werkplek, Macroscop in Perm, heb ik één gewoonte verworven: altijd de uitvoeringstijd van algoritmen controleren. En controleer altijd de netwerkruntime via een geschiktheidsfilter. Meestal passeert de modernste productie dit filter niet, wat mij naar Snoeien leidde.

Snoeien is een oud onderwerp waar in werd gesproken Stanford-lezingen in 2017. Het belangrijkste idee is om de omvang van het getrainde netwerk te verkleinen zonder de nauwkeurigheid te verliezen door verschillende knooppunten te verwijderen. Het klinkt cool, maar ik hoor zelden over het gebruik ervan. Waarschijnlijk zijn er niet genoeg implementaties, zijn er geen Russischtalige artikelen, of beschouwt iedereen het gewoon als snoeiharde kennis en zwijgt.
Maar laten we het uit elkaar halen

Een kijkje in de biologie

Ik vind het geweldig als Deep Learning kijkt naar ideeën die uit de biologie komen. Ze zijn, net als evolutie, te vertrouwen (wist je dat ReLU erg op functie van neuronactivatie in de hersenen?)

Het Model Pruning-proces ligt ook dicht bij de biologie. De reactie van het netwerk hier kan worden vergeleken met de plasticiteit van de hersenen. Er staan ​​een paar interessante voorbeelden in het boek. Norman Doidge:

  1. De hersenen van een vrouw die met slechts één helft is geboren, hebben zichzelf opnieuw geprogrammeerd om de functies van de ontbrekende helft uit te voeren.
  2. De man schoot het deel van zijn hersenen af ​​dat verantwoordelijk is voor het gezichtsvermogen. Na verloop van tijd namen andere delen van de hersenen deze functies over. (we proberen niet te herhalen)

Op dezelfde manier kunt u enkele van de zwakke convoluties uit uw model verwijderen. Als laatste redmiddel zullen de resterende bundels de afgeknipte bundels helpen vervangen.

Houd je van Transfer Learning of leer je helemaal opnieuw?

Optie nummer één. Je maakt gebruik van Transfer Learning op Yolov3. Retina, Masker-RCNN of U-Net. Maar meestal hoeven we geen 80 objectklassen te herkennen, zoals in COCO. In mijn praktijk beperkt alles zich tot groep 1-2. Je zou kunnen aannemen dat de architectuur voor 80 klassen hier overbodig is. Het idee doet zich voor dat de architectuur kleiner gemaakt moet worden. Bovendien zou ik dit graag willen doen zonder de bestaande vooraf getrainde gewichten te verliezen.

Optie nummer twee. Misschien beschikt u over veel gegevens- en computerbronnen, of heeft u gewoon een super-aangepaste architectuur nodig. Maakt niet uit. Maar je leert het netwerk helemaal opnieuw kennen. De gebruikelijke procedure is om naar de datastructuur te kijken, een architectuur te selecteren die buitensporig krachtig is, en uitvallers uit de omscholing te weren. Ik zag 0.6 uitvallers, Karl.

In beide gevallen kan het netwerk worden verkleind. Gemotiveerd. Laten we nu eens kijken wat voor soort besnijdenissnoei is

Algemeen algoritme

We besloten dat we de bundels konden verwijderen. Het ziet er vrij eenvoudig uit:

Jedi-techniek voor het verminderen van convolutionele netwerken - snoeien

Het verwijderen van eventuele convoluties is stressvol voor het netwerk, wat meestal leidt tot enige toename van het aantal fouten. Aan de ene kant is deze toename in fouten een indicator van hoe correct we convoluties verwijderen (een grote toename geeft bijvoorbeeld aan dat we iets verkeerd doen). Maar een kleine toename is heel acceptabel en wordt vaak geëlimineerd door daaropvolgende lichte aanvullende training met een kleine LR. Voeg een extra trainingsstap toe:

Jedi-techniek voor het verminderen van convolutionele netwerken - snoeien

Nu moeten we uitzoeken wanneer we onze leer<->snoei-lus willen stoppen. Er kunnen hier exotische opties zijn wanneer we het netwerk moeten terugbrengen tot een bepaalde omvang en snelheid (bijvoorbeeld voor mobiele apparaten). De meest gebruikelijke optie is echter om de cyclus voort te zetten totdat de fout groter wordt dan acceptabel. Een voorwaarde toevoegen:

Jedi-techniek voor het verminderen van convolutionele netwerken - snoeien

Het algoritme wordt dus duidelijk. Het blijft de vraag hoe de verwijderde convoluties kunnen worden bepaald.

Zoek naar verwijderde bundels

We moeten enkele convoluties verwijderen. Vooruit rennen en iemand ‘neerschieten’ is een slecht idee, hoewel het wel zal werken. Maar omdat je een hoofd hebt, kun je nadenken en proberen 'zwakke' convoluties te selecteren om te verwijderen. Er zijn verschillende opties:

  1. Kleinste L1-maat of low_magnitude_pruning. Het idee dat convoluties met kleine gewichten weinig bijdragen aan de uiteindelijke beslissing
  2. Kleinste L1-maatstaf, rekening houdend met gemiddelde en standaarddeviatie. Wij vullen aan met een beoordeling van de aard van de uitkering.
  3. Het maskeren van convoluties en het uitsluiten van convoluties die de uiteindelijke nauwkeurigheid het minst beïnvloeden. Nauwkeurigere bepaling van onbeduidende convoluties, maar zeer tijdrovend en middelenrovend.
  4. Anderen

Elk van de opties heeft recht op leven en zijn eigen implementatiekenmerken. Hier beschouwen we de optie met de kleinste L1-maat

Handmatig proces voor YOLOv3

De oorspronkelijke architectuur bevat restblokken. Maar hoe cool ze ook zijn voor diepe netwerken, ze zullen ons enigszins hinderen. De moeilijkheid is dat je afstemmingen met verschillende indexen in deze lagen niet kunt verwijderen:

Jedi-techniek voor het verminderen van convolutionele netwerken - snoeien

Laten we daarom lagen selecteren waaruit we vrijelijk afstemmingen kunnen verwijderen:

Jedi-techniek voor het verminderen van convolutionele netwerken - snoeien

Laten we nu een werkcyclus bouwen:

  1. Activeringen uploaden
  2. Uitzoeken hoeveel er moet worden gesneden
  3. Stop ermee
  4. 10 tijdperken leren met LR=1e-4
  5. Testen

Het ontladen van convoluties is handig om in te schatten hoeveel onderdeel we bij een bepaalde stap kunnen verwijderen. Voorbeelden van lossen:

Jedi-techniek voor het verminderen van convolutionele netwerken - snoeien

We zien dat bijna overal 5% van de convoluties een zeer lage L1-norm heeft en we kunnen deze verwijderen. Bij elke stap werd dit lossen herhaald en werd beoordeeld welke lagen en hoeveel er uitgesneden konden worden.

Het hele proces werd in 4 stappen voltooid (nummers hier en overal voor de RTX 2060 Super):

stap mAp75 Aantal parameters, miljoen Netwerkgrootte, mb Vanaf aanvankelijk, % Looptijd, mevrouw Besnijdenis toestand
0 0.9656 60 241 100 180 -
1 0.9622 55 218 91 175 5% van alles
2 0.9625 50 197 83 168 5% van alles
3 0.9633 39 155 64 155 15% voor lagen met meer dan 400 windingen
4 0.9555 31 124 51 146 10% voor lagen met meer dan 100 windingen

Er werd één positief effect toegevoegd aan stap 2: batchgrootte 4 paste in het geheugen, wat het proces van aanvullende training enorm versnelde.
Bij stap 4 werd het proces gestopt omdat zelfs langdurige aanvullende training bracht mAp75 niet naar de oude waarden.
Als gevolg hiervan zijn we erin geslaagd de gevolgtrekking te versnellen 15%, verklein de grootte met 35% en niet precies verliezen.

Automatisering voor eenvoudigere architecturen

Voor eenvoudigere netwerkarchitecturen (zonder voorwaardelijke optel-, aaneengeschakelde en restblokken) is het heel goed mogelijk om je te concentreren op het verwerken van alle convolutielagen en het proces van het verwijderen van convoluties te automatiseren.

Ik heb deze optie geïmplementeerd hier.
Het is simpel: je hebt alleen een verliesfunctie, een optimizer en batchgeneratoren nodig:

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)

Indien nodig kunt u de configuratieparameters wijzigen:

{
    "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
}

Bovendien wordt een beperking op basis van de standaarddeviatie geïmplementeerd. Het doel is om het deel dat wordt verwijderd te beperken, met uitsluiting van convoluties met reeds “voldoende” L1-maten:

Jedi-techniek voor het verminderen van convolutionele netwerken - snoeien

We staan ​​u dus toe alleen zwakke convoluties te verwijderen uit distributies die vergelijkbaar zijn met de rechter en hebben geen invloed op de verwijdering uit distributies die vergelijkbaar zijn met de linker:

Jedi-techniek voor het verminderen van convolutionele netwerken - snoeien

Wanneer de verdeling normaal benadert, kan de pruning_standart_deviation_part-coëfficiënt worden geselecteerd uit:

Jedi-techniek voor het verminderen van convolutionele netwerken - snoeien
Ik adviseer een aanname van 2 sigma. Of u kunt deze functie negeren en de waarde < 1.0 laten.

De uitvoer is een grafiek van de netwerkgrootte, het verlies en de netwerkruntime voor de gehele test, genormaliseerd naar 1.0. Hier werd de netwerkgrootte bijvoorbeeld bijna 2 keer verkleind zonder kwaliteitsverlies (klein convolutioneel netwerk met 100k-gewichten):

Jedi-techniek voor het verminderen van convolutionele netwerken - snoeien

De loopsnelheid is onderhevig aan normale schommelingen en blijft vrijwel onveranderd. Hier is een verklaring voor:

  1. Het aantal convoluties verandert van handig (32, 64, 128) naar niet het meest handig voor videokaarten - 27, 51, enz. Ik kan het mis hebben, maar hoogstwaarschijnlijk heeft het een effect.
  2. De architectuur is niet breed, maar consistent. Door de breedte te verkleinen, hebben we geen invloed op de diepte. We verminderen dus de belasting, maar veranderen de snelheid niet.

Daarom werd de verbetering uitgedrukt in een vermindering van de CUDA-belasting tijdens de run met 20-30%, maar niet in een vermindering van de runtime

Resultaten van

Laten we reflecteren. We hebben twee opties voor snoeien overwogen: voor YOLOv2 (wanneer je met je handen moet werken) en voor netwerken met eenvoudigere architecturen. Het blijkt dat het in beide gevallen mogelijk is om de netwerkgrootte te verkleinen en te versnellen zonder verlies van nauwkeurigheid. Resultaten:

  • Verkleinen
  • Acceleratie uitgevoerd
  • CUDA-belasting verminderen
  • Als gevolg hiervan, milieuvriendelijkheid (we optimaliseren het toekomstige gebruik van computerbronnen. Ergens ben je gelukkig Greta Thunberg)

Bijlage

  • Na de snoeistap kunt u kwantisering toevoegen (bijvoorbeeld met TensorRT)
  • Tensorflow biedt mogelijkheden voor lage_magnitude_pruning. Werken.
  • opslagplaats Ik wil me ontwikkelen en help je graag

Bron: www.habr.com

Voeg een reactie