Jedi-tekniko por redukti konvoluciajn retojn - pritondado
Antaŭ vi denove estas la tasko detekti objektojn. La prioritato estas rapideco de operacio kun akceptebla precizeco. Vi prenas la arkitekturon YOLOv3 kaj plu trejnas ĝin. Precizeco (mAp75) estas pli granda ol 0.95. Sed la kurofteco estas ankoraŭ malalta. Crap.
Hodiaŭ ni preteriros kvantigon. Kaj sub la tranĉo ni rigardos Modelo Pritondado — tondado de redundaj partoj de la reto por akceli Inferencon sen perdo de precizeco. Estas klare kie, kiom kaj kiel tranĉi. Ni eltrovu kiel fari tion permane kaj kie vi povas aŭtomatigi ĝin. Fine estas deponejo pri keras.
Enkonduko
Ĉe mia antaŭa laborloko, Macroscop en Perm, mi akiris unu kutimon - ĉiam kontroli la ekzekuttempon de algoritmoj. Kaj ĉiam kontrolu la retan rultempon per taŭgeca filtrilo. Kutime pintnivela en produktado ne pasas ĉi tiun filtrilon, kiu kondukis min al Tondado.
Pritondado estas malnova temo pri kiu oni diskutis Stanfordaj prelegoj en 2017. La ĉefa ideo estas redukti la grandecon de la trejnita reto sen perdi precizecon forigante diversajn nodojn. Ĝi sonas mojosa, sed mi malofte aŭdas pri ĝia uzo. Verŝajne, ne estas sufiĉe da efektivigoj, mankas ruslingvaj artikoloj, aŭ simple ĉiuj konsideras ĝin pritondi scipovon kaj silentas.
Sed ni disigu ĝin
Ekrigardo en biologion
Mi amas ĝin kiam Deep Learning rigardas ideojn kiuj venas de biologio. Ili, kiel evolucio, estas fidindaj (ĉu vi sciis, ke ReLU tre similas al funkcio de neŭrona aktivigo en la cerbo?)
La Modela Pruning-procezo ankaŭ estas proksima al biologio. La respondo de la reto ĉi tie povas esti komparita kun la plastikeco de la cerbo. Estas kelkaj interesaj ekzemploj en la libro. Norman Doidge:
La cerbo de virino, kiu naskiĝis kun nur unu duono, reprogramis sin por plenumi la funkciojn de la mankanta duono.
La ulo pafis la parton de sia cerbo respondeca por vizio. Kun la tempo, aliaj partoj de la cerbo transprenis ĉi tiujn funkciojn. (ni ne provas ripeti)
Same, vi povas eltranĉi kelkajn el la malfortaj cirkloj el via modelo. Kiel lasta rimedo, la ceteraj pakaĵoj helpos anstataŭigi la tranĉitajn.
Ĉu vi amas Transfer Learning aŭ ĉu vi lernas de nulo?
Opcio numero unu. Vi uzas Transfer Learning sur Yolov3. Retino, Mask-RCNN aŭ U-Net. Sed plejofte ni ne bezonas rekoni 80 objektoklasojn kiel en COCO. En mia praktiko, ĉio estas limigita al klasoj 1-2. Oni povus supozi, ke la arkitekturo por 80 klasoj estas superflua ĉi tie. Ĉi tio sugestas, ke la arkitekturo devas fariĝi pli malgranda. Plie, mi ŝatus fari ĉi tion sen perdi la ekzistantajn antaŭtrejnitajn pezojn.
Opcio numero du. Eble vi havas multajn datumojn kaj komputikajn rimedojn, aŭ nur bezonas superpersonan arkitekturon. Ne gravas. Sed vi lernas la reton de nulo. La kutima proceduro estas rigardi la datumstrukturon, elekti arkitekturon kiu estas TROA en potenco, kaj puŝi ĉesulojn de retrejnado. Mi vidis 0.6 ĉesintojn, Karl.
En ambaŭ kazoj, la reto povas esti reduktita. Motivigita. Nun ni iru eltrovi kia cirkumcida pritondado estas
Ĝenerala algoritmo
Ni decidis, ke ni povus forigi la pakaĵojn. Ĝi aspektas sufiĉe simpla:
Forigi ajnan konvolucion estas streĉa por la reto, kiu kutime kondukas al ioma pliiĝo de eraro. Unuflanke, ĉi tiu pliigo de eraro estas indikilo de kiom ĝuste ni forigas cirklaciojn (ekzemple, granda pliiĝo indikas, ke ni faras ion malbone). Sed malgranda pliiĝo estas sufiĉe akceptebla kaj ofte estas forigita per posta malpeza kroma trejnado kun malgranda LR. Aldonu plian trejnan paŝon:
Nun ni devas eltrovi kiam ni volas ĉesigi nian Lernadon<->Eltondan buklon. Eble ekzistas ekzotikaj opcioj ĉi tie kiam ni bezonas redukti la reton al certa grandeco kaj rapideco (ekzemple, por porteblaj aparatoj). Tamen, la plej ofta opcio estas daŭrigi la ciklon ĝis la eraro iĝas pli alta ol akceptebla. Aldonu kondiĉon:
Do, la algoritmo iĝas klara. Restas eltrovi kiel determini la forigitajn cirkvitojn.
Serĉu forigitajn pakaĵojn
Ni devas forigi iujn cirkvitojn. Rapidi antaŭen kaj "pafi" iun ajn estas malbona ideo, kvankam ĝi funkcios. Sed ĉar vi havas kapon, vi povas pensi kaj provi elekti "malfortajn" cirkvitojn por forigo. Estas pluraj ebloj:
Ĉiu el la opcioj havas la rajton al vivo kaj siajn proprajn efektivigajn funkciojn. Ĉi tie ni konsideras la opcion kun la plej malgranda L1-mezuro
Mana procezo por YOLOv3
La origina arkitekturo enhavas restajn blokojn. Sed kiom ajn bonegaj ili estas por profundaj retoj, ili iom malhelpos nin. La malfacilaĵo estas, ke vi ne povas forigi repaciĝon kun malsamaj indeksoj en ĉi tiuj tavoloj:
Sekve, ni elektu tavolojn el kiuj ni povas libere forigi repaciĝon:
Nun ni konstruu laborciklon:
Alŝutante aktivigojn
Eltrovi kiom tranĉi
Eltranĉu ĝin
Lernado de 10 epokoj kun LR=1e-4
Testado
Malŝarĝo de cirkloj estas utila por taksi kiom da parto ni povas forigi je certa paŝo. Malŝarĝaj ekzemploj:
Ni vidas, ke preskaŭ ĉie 5% de konvolucioj havas tre malaltan L1-normon kaj ni povas forigi ilin. Je ĉiu paŝo, ĉi tiu malŝarĝo estis ripetita kaj oni taksis kiuj tavoloj kaj kiom oni povus eltranĉi.
La tuta procezo estis kompletigita en 4 paŝoj (nombroj ĉi tie kaj ĉie por la RTX 2060 Super):
Paŝo
mapo75
Nombro de parametroj, miliono
Reta grandeco, mb
De komenca, %
Runtempo, ms
Kondiĉo de cirkumcido
0
0.9656
60
241
100
180
-
1
0.9622
55
218
91
175
5% de ĉiuj
2
0.9625
50
197
83
168
5% de ĉiuj
3
0.9633
39
155
64
155
15% por tavoloj kun pli ol 400 cirkvitoj
4
0.9555
31
124
51
146
10% por tavoloj kun pli ol 100 cirkvitoj
Unu pozitiva efiko estis aldonita al paŝo 2 - aro grandeco 4 taŭgas en memoro, kiu multe akcelis la procezon de plia trejnado.
Je paŝo 4, la procezo estis ĉesigita ĉar eĉ longdaŭra plia trejnado ne altigis mAp75 al malnovaj valoroj.
Kiel rezulto, ni sukcesis akceli la inferencon per 15%, redukti la grandecon per 35% kaj ne perdi precize.
Aŭtomatigo por pli simplaj arkitekturoj
Por pli simplaj retaj arkitekturoj (sen kondiĉaj aldonaj, kunigitaj kaj restaj blokoj), estas tute eble koncentriĝi pri prilaborado de ĉiuj konvoluciaj tavoloj kaj aŭtomatigi la procezon de eltranĉado de konvolucioj.
Mi efektivigis ĉi tiun opcion tie.
Ĝi estas simpla: vi bezonas nur perdan funkcion, optimumigilon kaj batajn generatorojn:
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)
Se necese, vi povas ŝanĝi la agordajn parametrojn:
{
"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
}
Aldone, limigo bazita sur la norma devio estas efektivigita. La celo estas limigi la forigitan parton, ekskludante cirkonfluojn kun jam "sufiĉaj" L1-mezuroj:
Tiel, ni permesas al vi forigi nur malfortajn konvoluciojn de distribuoj similaj al la dekstra kaj ne influi la forigon de distribuoj similaj al la maldekstra:
Kiam la distribuo proksimiĝas al normalo, la koeficiento pruning_standart_deviation_part povas esti elektita el:
Mi rekomendas supozon de 2 sigma. Aŭ vi povas ignori ĉi tiun funkcion, lasante la valoron < 1.0.
La eligo estas grafeo de retgrandeco, perdo kaj retrultempo por la tuta testo, normaligita al 1.0. Ekzemple, ĉi tie la retograndeco estis reduktita preskaŭ 2 fojojn sen perdo de kvalito (malgranda konvolucia reto kun 100k pezoj):
La kura rapideco estas kondiĉigita de normalaj fluktuoj kaj restas preskaŭ senŝanĝa. Estas klarigo por ĉi tio:
La nombro da konvolucioj ŝanĝiĝas de oportuna (32, 64, 128) al ne la plej oportuna por vidkartoj - 27, 51, ktp. Mi povus erari ĉi tie, sed plej verŝajne ĝi efikas.
La arkitekturo ne estas larĝa, sed konsekvenca. Reduktante la larĝon, ni ne influas la profundon. Tiel, ni reduktas la ŝarĝon, sed ne ŝanĝas la rapidon.
Sekve, la plibonigo estis esprimita en redukto de la CUDA-ŝarĝo dum la kuro je 20-30%, sed ne en redukto de la rultempo.
Rezultoj
Ni pripensu. Ni pripensis 2 eblojn por pritondado - por YOLOv3 (kiam vi devas labori per viaj manoj) kaj por retoj kun pli simplaj arkitekturoj. Videblas, ke en ambaŭ kazoj eblas atingi retan grandeco-redukton kaj plirapidigon sen perdo de precizeco. Rezulto:
Reduktante la grandecon
Akcela kurado
Reduktante CUDA-Ŝarĝon
Rezulte, media amikeco (Ni optimumigas la estontan uzon de komputikaj rimedoj. Ie oni estas feliĉa Greta Thunberg)
apendico
Post la tonda paŝo, vi povas aldoni kvantigon (ekzemple kun TensorRT)