MATLAB zľahka - Transfer learning
V predchádzajúcom diele sme si vyskúšali, ako je ľahké v MATLAB využívať netriviálne algoritmy hlbokých neurónových sietí pre klasifikáciu obrazov do rôznych tried. Pre riadne pochopenie tohto nadväzujúceho dielu odporúčam najskôr prečítať ten prvý.
Dnešným cieľom bude vytvoriť neurónovou sieť, ktorá je schopná rozoznávať medzi štvorcom a kruhom. Nie je to žiadny dychberúci výsledok a tento problém sa dá celkom ľahko riešiť aj pomocou klasických algoritmov spracovania obrazu. Dôvodom takto jednoduchého príkladu je ľahká demonštrácie a tiež jednoduchý spôsob, ako vygenerovať trénovací dáta (obrázky s kruhom a štvorcom). Rozšíriť možnosti klasifikácie o ďalšie tvary (trojuholník, hviezda, ...) je následne veľmi triviálne. S použitím klasických algoritmov by to už tak jednoduché nebolo.
Transfer Learning
Pri riešení dnešnej úlohy si pomôžeme pomocou tzv. Transfer Learning. Slovensky by sme mohli povedať preneseného naučenia (znalosti). Myšlienka je taká, že začíname už s nejakou natrénované neurónovou sietí (napríklad Alexnet, pozri minulý diel), ktorá už chápe "ako vyzerá svet". Konkrétne Alexnet je naučená na miliónoch obrázkov z tisíc rôznych tried. Túto znalosť môžeme využiť pre klasifikáciu iných obrazov a nemusíme tak sieť učiť od piky.
Alexnet je zložená z 25 vrstiev. Prvých 22 slúži ako feature extractor (to, čo "chápe ako vyzerá svet") a zvyšné 3 slúži ako klasifikátor (povie nám do ktorej z 1000 tried patrí obrázok s danými vlastnosťami, ktoré boli získané z extraktora). Pri transfer learningu si ponecháme extractor, zatiaľ čo klasifikačné vrstvy nahradíme tak, aby musia rozpoznať obrázky do nami zvolených tried (obdĺžnik, kruh).
Načítanie a prenesenie Alexnet
V prvom kroku načítame neurónovou sieť príkazom alexnet
. V
prípade, že nemáte nainštalovaný patričný add-on, MATLAB vás upozorní a
poskytne odkaz na stiahnutie.
net = alexnet; extractorLayers = net.Layers(1:end-3); % všechny vrstvy, kromě třech posledních
Premenná net
má vlastnosť Layers
so všetkými
vrstvami, ktoré sieť tvoria. Posledné 3 budeme nahrádzať, preto si do
extractorLayers
nakopírujeme všetky okrem týchto 3. Že sú to 3
sa dá zisti napríklad príkazom analyzeNetwork(net)
, kde si
môžeme prezrieť aj vlastnosti jednotlivých vrstiev. K vrstvám, ktoré sme
odňali z pôvodnej Alexnet, prirobíme práve tie tri zodpovedné za
klasifikáciu. fullyConnectedLayer
je vrstva, v ktorej sú všetky
neuróny prepojené (tých čo prichádzajú z extraktora a tých čo idú
ďalej do softmaxLayer
). Zvýšime
learning rate factor
, čím zabezpečíme rýchlejší učenia
oproti ostatným vrstvám (extraktore):
layers = [ extractorLayers fullyConnectedLayer(2, 'WeightLearnRateFactor', 20, 'BiasLearnRateFactor', 20) softmaxLayer classificationLayer];
Dvojka v prvom parametri značí 2
triedy, do ktorých budeme
klasifikovať. softmaxLayer
zariadi, že klasifikácia sa bude
odohrávať v hodnotách od 0
do 1
a súčet
všetkých hodnôt (v našom prípade dvoch) bude tiež 1
. To bude
výstup poslednej klasifikačnej vrstvy. Výstup je teda zároveň aj
pravdepodobnosťou, že klasifikácia patrí do danej triedy.
Dáta pre učenie
Databázu obrázkov, na ktorých naučíme neurónovou sieť rozoznávať
kruhy a obdĺžniky, si môžeme jednoducho vytvoriť. V pribalenom
.zip
súboru je to skript s názvom
tvorba_nahodnych_tvaru.m
. Po jeho spustení dôjde k vygenerovaniu
10 obrázkov od každého tvaru s náhodnou veľkosťou, farbou a pozíciou.
Skript vytvorí nasledujúce zložkovú štruktúru, každá zložka obsahuje 10 tvarov:
Teraz si vytvoríme premennú imdsTrain
, ktorú využijeme pri
trénovaní. imdsTrain
obsahuje informácie o súboroch (ich cestu)
a triedu správnej klasifikácie (kruh alebo obdĺžnik):
imdsTrain = imageDatastore('imgs_shapes', ... % Obrázky z této složky 'IncludeSubfolders', true, ... % Ber to z podsložek 'LabelSource', 'foldernames'); % jako název třídy použij název složky
Funkcia imageDatastore()
je dostatočne schopná, aby pochopila
(povieme jej to v parametroch), že názvy zložiek sú zároveň názvom triedy
pre klasifikáciu.
Tréning neurónové siete
Teraz máme architektúru siete, vrátane váh, ktoré sme prevzali z Alexnet. Sme krôčik od toho, aby sme mohli spustiť tréning. Potrebujeme ešte špecifikovať nastavenie pre učenie:
options = trainingOptions('sgdm','InitialLearnRate',1e-4);
Možností, ako upraviť parametre učenia, je veľa. Toto je veľmi zjednodušená varianta s defaultným nastavením väčšiny parametrov.
Celé učenie zariadi funkcie trainNetwork()
. Prvý parameter je
vyššie vytvorená databáza obrázkov. Druhý parameter je poskladaná
architektúra siete, ktorú sme definovali skôr (layers
). Tretím
parametrom sú špecifikované options
.
netTransfer = trainNetwork(imdsTrain,layers,options);
Sieť sa trénuje na dvadsiatich obrázkoch. Vo výstupe uvidíte relatívne krátky postup tréningu:
Počas 13 sekúnd sa sieť natrénovali na grafickej karte (tá je rýchlejší ako CPU. Ak ju máte, MATLAB na ňu sám prepne) so 100% presnosťou. Tréningový proces prehnal počas tých pár sekúnd každý z 20 obrázkov neuronkou 30x. Väčšinou, keď je niečo s presnosťou 100%, je to podozrivé. Presnosť je navyše na dátach, na ktorých trénujeme. Toho sa snažíme väčšinou vyvarovať, aby sme sieť nepřeučili (preučené sieť funguje dobre len na tréningových dátach). Validačný dáta sme z postupu kvôli jednoduchosti však vypustili. Ak je sieť naučená správne, zistíme nasledovne.
Testovanie siete
Z trénovacího procesu nám vznikla premenná netTransfer
,
ktorá vie rozoznávať dva tvary. S pomocou funkcie classify()
jej
otestujeme na niekoľkých obrázkoch. 16 útvarov je naskladaný v jednom
.png
súboru (tiež priložený v .zip
archíve).
Prvých 8 sú prosté kruhy a obdĺžniky, ďalšie tvary už tak jasné nie
sú:
Nasledujúci kód, rozseká .png
súbor na 16 obrázkov, každý
zvlášť klasifikuje a pripíše k nemu výsledok, vrátane
pravdepodobnosti:
A = imread('shapes16b.png'); % načti obrázek s 16 tvary % překonvertovat na buňky o velikosti 227x227x3 A_in_cells = mat2cell(A, [227 227 227 227], [227 227 227 227], [1 1 1]); ind = 1; for ii = 1:4 % projít všech 16 obrázků for yy = 1:4 % v těchto 2 cyklech % složit obrázek z buněk... one_shape = cat(3, A_in_cells{ii, yy, 1}, A_in_cells{ii, yy, 2}, A_in_cells{ii, yy, 3}); [label,score] = classify(netTransfer,one_shape); % vlastní klasifikace Classified_images{ind} = insertText(one_shape,[1 1],... % přidání výsledku klasifikace cellstr(string(label) + " " + num2str(max(score) * 100) + " %"), 'FontSize', 26); ind = ind + 1; end end montage(Classified_images) % zobrazení několika obrázků najednou.
výsledok:
Kruhy a obdĺžniky rozoznala neurónové siete s prehľadom (a 100% presnosťou). Keďže sieť nie je natrénovaná na nič iné ako práve na kruh a obdĺžnik, nie je potrebné schopná rozoznať smajlík (vraj kruh), ani prázdnotu (vraj obdĺžnik). Ručne kreslený "kruh" a obdĺžnik rozoznala správne. Pokiaľ má obdĺžnik zaoblené rohy príliš, klasifikuje to už ako kruh.
Záver a pár poznámok
Využili sme naučenú neurónovou sieť Alexnet k tvorbe vlastnej neurónové siete, ktorá je schopná rozoznať štvorca od koliesok. Článok je spísaný a kód konštruovaný tak, aby čo najrýchlejším spôsobom demonštroval danú problematiku a ukázal všetko jednoducho a bez komplexných opisných častí. Takmer každému vysvetlenie by mohlo predchádzať "zjednodušene povedané".
Čas učiaceho procesu je závislý od veľkosti učiacej databázy, zvolených parametroch a železe, na ktorom sa učenie uskutočňuje. Tu to zabralo 13 sekúnd, čo sa rádovo líši od scenárov skutočného sveta (napríklad aj dni, týždne).
Vzniknutá neurónové siete sa dá ľahko rozšíriť. V prvom kroku treba o
viac tvarov. Iba sa pridá ďalšia zložka s predpripravenými obrázkami a
zmení sa počet neurónov vo fullyConnectedLayer
.
Obrázky tvarov sa dajú ľahko nahradiť inými (napríklad jablko vs hruška vs pomaranč alebo zatiahnutá obloha vs slnečno vs dážď), čo nás už približuje k nejakému skutočnému scenári.
Ďalšie podstatnú časť, ktorá si zaslúži úpravu, je tréningový
proces. options
majú veľa možností,
medzi ktoré patrí napríklad databázy validačných dát, počet opakovaní,
zmena algoritmu, atď.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 10x (42.91 kB)
Aplikácia je vrátane zdrojových kódov