Teknika Jedi për reduktimin e rrjeteve konvolucionale - krasitje
Para jush përsëri është detyra e zbulimit të objekteve. Prioriteti është shpejtësia e funksionimit me saktësi të pranueshme. Ju merrni arkitekturën YOLOv3 dhe e trajnoni më tej. Saktësia (mAp75) është më e madhe se 0.95. Por shkalla e vrapimit është ende e ulët. Katrahurë.
Sot do të anashkalojmë kuantizimin. Dhe nën prerje ne do të shikojmë Krasitja e modelit — shkurtimi i pjesëve të tepërta të rrjetit për të shpejtuar përfundimin pa humbje të saktësisë. Është e qartë se ku, sa dhe si të pritet. Le të kuptojmë se si ta bëjmë këtë me dorë dhe ku mund ta automatizoni atë. Në fund ka një depo për keras.
Paraqitje
Në vendin tim të mëparshëm të punës, Macroscop në Perm, fitova një zakon - të monitoroja gjithmonë kohën e ekzekutimit të algoritmeve. Dhe gjithmonë kontrolloni kohën e funksionimit të rrjetit përmes një filtri të përshtatshmërisë. Zakonisht teknologjia moderne në prodhim nuk e kalon këtë filtër, gjë që më çoi në Krasitje.
Krasitja është një temë e vjetër që është diskutuar në Leksione në Stanford në vitin 2017. Ideja kryesore është zvogëlimi i madhësisë së rrjetit të trajnuar pa humbur saktësinë duke hequr nyjet e ndryshme. Tingëllon bukur, por rrallë dëgjoj për përdorimin e tij. Ndoshta, nuk ka zbatime të mjaftueshme, nuk ka artikuj në gjuhën ruse, ose thjesht të gjithë e konsiderojnë atë si ekspertizë dhe heshtin.
Por le ta ndajmë atë
Një vështrim në biologji
Më pëlqen kur Deep Learning shikon idetë që vijnë nga biologjia. Atyre, si evolucioni, mund t'u besohet (a e dini se ReLU është shumë e ngjashme me funksioni i aktivizimit të neuroneve në tru?)
Procesi i krasitjes model është gjithashtu i afërt me biologjinë. Përgjigja e rrjetit këtu mund të krahasohet me plasticitetin e trurit. Ka disa shembuj interesantë në libër. Norman Doidge:
Truri i një gruaje që ka lindur vetëm me një gjysmë, është riprogramuar për të kryer funksionet e gjysmës që mungon.
Djali qëlloi pjesën e trurit të tij përgjegjëse për shikimin. Me kalimin e kohës, pjesë të tjera të trurit morën përsipër këto funksione. (nuk po përpiqemi të përsërisim)
Po kështu, ju mund të hiqni disa nga konvolucionet e dobëta nga modeli juaj. Si mjet i fundit, tufat e mbetura do të ndihmojnë në zëvendësimin e atyre të prera.
A ju pëlqen Transfer Learning apo po mësoni nga e para?
Opsioni numër një. Ju përdorni Transfer Learning në Yolov3. Retina, Mask-RCNN ose U-Net. Por shumicën e kohës ne nuk kemi nevojë të njohim 80 klasa objektesh si në COCO. Në praktikën time, gjithçka është e kufizuar në klasat 1-2. Mund të supozohet se arkitektura për 80 klasa është e tepërt këtu. Kjo sugjeron që arkitektura duhet të bëhet më e vogël. Për më tepër, unë do të doja ta bëja këtë pa humbur peshat ekzistuese të para-stërvitura.
Opsioni numër dy. Ndoshta keni shumë të dhëna dhe burime kompjuterike, ose thjesht keni nevojë për një arkitekturë super të personalizuar. Nuk ka rëndësi. Por ju po mësoni rrjetin nga e para. Procedura e zakonshme është të shikoni strukturën e të dhënave, të zgjidhni një arkitekturë me fuqi të tepërt dhe të shtyni braktisjen nga rikualifikimi. Pashë 0.6 braktisje, Karl.
Në të dyja rastet, rrjeti mund të reduktohet. I motivuar. Tani le të kuptojmë se çfarë lloj krasitjeje rrethprerjeje është
Algoritmi i përgjithshëm
Ne vendosëm që mund të hiqnim paketat. Duket mjaft e thjeshtë:
Heqja e çdo konvolucioni është stresuese për rrjetin, gjë që zakonisht çon në një rritje të gabimit. Nga njëra anë, kjo rritje e gabimit është një tregues se sa saktë i heqim konvolucionet (për shembull, një rritje e madhe tregon se po bëjmë diçka të gabuar). Por një rritje e vogël është mjaft e pranueshme dhe shpesh eliminohet nga trajnimi shtesë i lehtë pasues me një LR të vogël. Shtoni një hap shtesë trajnimi:
Tani duhet të kuptojmë se kur duam të ndalojmë ciklin tonë të të mësuarit<->Pruning. Mund të ketë opsione ekzotike këtu kur duhet të zvogëlojmë rrjetin në një madhësi dhe shpejtësi të caktuar (për shembull, për pajisjet celulare). Megjithatë, opsioni më i zakonshëm është të vazhdohet cikli derisa gabimi të bëhet më i lartë se i pranueshëm. Shtoni një kusht:
Pra, algoritmi bëhet i qartë. Mbetet për të kuptuar se si të përcaktohen konvolucionet e fshira.
Kërkoni për paketa të fshira
Duhet të heqim disa konvolucione. Të nxitosh përpara dhe "të qëllosh" këdo është një ide e keqe, megjithëse do të funksionojë. Por meqenëse keni një kokë, mund të mendoni dhe të përpiqeni të zgjidhni konvolucione "të dobëta" për heqje. Ka disa opsione:
Secila prej opsioneve ka të drejtën e jetës dhe veçoritë e veta të zbatimit. Këtu shqyrtojmë opsionin me masën më të vogël L1
Procesi manual për YOLOv3
Arkitektura origjinale përmban blloqe të mbetura. Por sado të lezetshëm të jenë për rrjete të thella, do të na pengojnë disi. Vështirësia është se nuk mund të fshini rakordimet me indekse të ndryshme në këto shtresa:
Prandaj, le të zgjedhim shtresat nga të cilat mund të fshijmë lirisht rakordimet:
Tani le të ndërtojmë një cikël pune:
Ngarkimi i aktivizimeve
Duke kuptuar se sa për të prerë
Prerë jashtë
Mësimi i 10 epokave me LR=1e-4
Duke testuar
Shkarkimi i konvolucioneve është i dobishëm për të vlerësuar se sa pjesë mund të heqim në një hap të caktuar. Shembuj të shkarkimit:
Ne shohim se pothuajse kudo 5% e konvolucioneve kanë një normë L1 shumë të ulët dhe ne mund t'i heqim ato. Në çdo hap, ky shkarkim përsëritej dhe bëhej një vlerësim se cilat shtresa dhe sa mund të priheshin.
I gjithë procesi u përfundua në 4 hapa (numrat këtu dhe kudo për RTX 2060 Super):
hap
mAp75
Numri i parametrave, milion
Madhësia e rrjetit, mb
Nga fillimi, %
Koha e ekzekutimit, ms
Gjendja e synetisë
0
0.9656
60
241
100
180
-
1
0.9622
55
218
91
175
5% e të gjithëve
2
0.9625
50
197
83
168
5% e të gjithëve
3
0.9633
39
155
64
155
15% për shtresat me 400+ konvolucione
4
0.9555
31
124
51
146
10% për shtresat me 100+ konvolucione
Një efekt pozitiv iu shtua hapit 2 - madhësia e grupit 4 përshtatet në kujtesë, gjë që përshpejtoi shumë procesin e trajnimit shtesë.
Në hapin 4, procesi u ndërpre sepse edhe trajnimi shtesë afatgjatë nuk e ngriti mAp75 në vlerat e vjetra.
Si rezultat, ne arritëm të përshpejtonim përfundimin me 15%, zvogëloni madhësinë me 35% dhe të mos humbasë saktësisht.
Automatizimi për arkitektura më të thjeshta
Për arkitekturat më të thjeshta të rrjetit (pa blloqe shtesë të kushtëzuara, të lidhura dhe të mbetura), është mjaft e mundur të përqendroheni në përpunimin e të gjitha shtresave konvolucionale dhe të automatizoni procesin e prerjes së konvolucioneve.
Unë e zbatova këtë opsion këtu.
Është e thjeshtë: ju nevojitet vetëm një funksion humbjeje, një optimizues dhe gjeneratorë të grupeve:
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)
Nëse është e nevojshme, mund të ndryshoni parametrat e konfigurimit:
{
"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
}
Për më tepër, zbatohet një kufizim i bazuar në devijimin standard. Qëllimi është të kufizohet pjesa që hiqet, duke përjashtuar konvolucionet me masa tashmë "të mjaftueshme" L1:
Kështu, ne ju lejojmë të hiqni vetëm konvolucione të dobëta nga shpërndarjet e ngjashme me atë të djathtën dhe të mos ndikoni në heqjen nga shpërndarjet e ngjashme me atë të majtë:
Kur shpërndarja i afrohet normales, koeficienti pruning_standart_deviation_part mund të zgjidhet nga:
Unë rekomandoj një supozim prej 2 sigma. Ose mund ta shpërfillni këtë veçori, duke lënë vlerën < 1.0.
Dalja është një grafik i madhësisë së rrjetit, humbjes dhe kohës së funksionimit të rrjetit për të gjithë testin, i normalizuar në 1.0. Për shembull, këtu madhësia e rrjetit u zvogëlua pothuajse 2 herë pa humbje të cilësisë (rrjet i vogël konvolucional me 100k pesha):
Shpejtësia e drejtimit është subjekt i luhatjeve normale dhe mbetet praktikisht e pandryshuar. Ka një shpjegim për këtë:
Numri i konvolucioneve ndryshon nga i përshtatshëm (32, 64, 128) në jo më të përshtatshëm për kartat video - 27, 51, etj. Mund të jem i gabuar këtu, por ka shumë të ngjarë që ka një efekt.
Arkitektura nuk është e gjerë, por konsistente. Duke ulur gjerësinë, ne nuk ndikojmë në thellësi. Kështu, ne zvogëlojmë ngarkesën, por nuk e ndryshojmë shpejtësinë.
Prandaj, përmirësimi u shpreh në një ulje të ngarkesës CUDA gjatë drejtimit me 20-30%, por jo në një ulje të kohës së funksionimit.
Rezultatet e
Le të reflektojmë. Ne shqyrtuam 2 opsione për krasitjen - për YOLOv3 (kur duhet të punoni me duart tuaja) dhe për rrjetet me arkitektura më të thjeshta. Mund të shihet se në të dyja rastet është e mundur të arrihet zvogëlimi dhe shpejtësia e madhësisë së rrjetit pa humbje të saktësisë. Rezultatet:
Reduktimi i madhësisë
Vrapimi i përshpejtimit
Reduktimi i ngarkesës CUDA
Si rezultat, mirëdashësi mjedisore (Ne optimizojmë përdorimin e ardhshëm të burimeve kompjuterike. Diku dikush është i lumtur Greta Thunberg)
Shtojcë
Pas hapit të krasitjes, mund të shtoni kuantizimin (për shembull, me TensorRT)