MATLAB zlehka - Transfer learning
V předchozím díle jsme si vyzkoušeli, jak je snadné v MATLAB využívat netriviální algoritmy hlubokých neuronových sítí pro klasifikaci obrazů do různých tříd. Pro řádné pochopení tohoto navazujícího dílu doporučuji nejdříve přečíst ten první.
Dnešním cílem bude vytvořit neuronovou síť, která je schopná rozeznávat mezi čtvercem a kruhem. Není to žádný dechberoucí výsledek a tento problém lze celkem snadno řešit i pomocí klasických algoritmů zpracování obrazu. Důvodem takto jednoduchého příkladu je snadná demonstrace a také snadný způsob, jak vygenerovat trénovací data (obrázky s kruhem a čtvercem). Rozšířit možnosti klasifikace o další tvary (trojúhelník, hvězda, ...) je následně velice triviální. S použitím klasických algoritmů by to už tak snadné nebylo.
Transfer Learning
Při řešení dnešní úlohy si pomůžeme pomocí tzv. Transfer Learning. Česky bychom mohli říci přeneseného naučení (znalosti). Myšlenka je taková, že začínáme již s nějakou natrénovanou neuronovou sítí (například Alexnet, viz minulý díl), která již chápe "jak vypadá svět". Konkrétně Alexnet je naučena na milionech obrázků z tisíce různých tříd. Tuto znalost můžeme využít pro klasifikaci jiných obrazů a nemusíme tak síť učit od píky.
Alexnet je složena z 25 vrstev. Prvních 22 slouží jako feature extractor (to, co "chápe jak vypadá svět") a zbylé 3 slouží jako klasifikátor (řekne nám do které z 1000 tříd patří obrázek s danými vlastnostmi, které byly získány z extraktoru). Při transfer learningu si ponecháme extractor, zatímco klasifikační vrstvy nahradíme tak, aby rozeznávaly obrázky do námi zvolených tříd (obdélník, kruh).
Načtení a přenesení Alexnet
V prvním kroku načteme neuronovou síť příkazem alexnet
. V
případě, že nemáte nainstalovaný patřičný add-on, MATLAB vás upozorní
a poskytne odkaz ke stažení.
net = alexnet; extractorLayers = net.Layers(1:end-3); % všechny vrstvy, kromě třech posledních
Proměnná net
má vlastnost Layers
se všemi
vrstvami, které síť tvoří. Poslední 3 budeme nahrazovat, proto si do
extractorLayers
nakopírujeme všechny kromě těchto 3. Že jsou
to 3 se dá zjisti například příkazem analyzeNetwork(net)
, kde
si můžeme prohlédnout i vlastnosti jednotlivých vrstev. K vrstvám, které
jsme odňali z původní Alexnet, přiděláme právě ty tři zodpovědné za
klasifikaci. fullyConnectedLayer
je vrstva, ve které jsou všechny
neurony propojené (ty co přicházejí z extraktoru a ty co jdou dále do
softmaxLayer
). Zvýšíme learning rate factor
,
čímž zajistíme rychlejší učení oproti ostatním vrstvám
(extraktoru):
layers = [ extractorLayers fullyConnectedLayer(2, 'WeightLearnRateFactor', 20, 'BiasLearnRateFactor', 20) softmaxLayer classificationLayer];
Dvojka v prvním parametru značí 2
třídy, do kterých budeme
klasifikovat. softmaxLayer
zařídí, že klasifikace se bude
odehrávat v hodnotách od 0
do 1
a součet všech
hodnot (v našem případě dvou) bude také 1
. To bude výstup
poslední klasifikační vrstvy. Výstup je tedy zároveň i pravděpodobností,
že klasifikace patří do dané třídy.
Data pro učení
Databázi obrázků, na kterých naučíme neuronovou síť rozeznávat kruhy
a obdélníky, si můžeme jednoduše vytvořit. V přibaleném
.zip
souboru je to skript s názvem
tvorba_nahodnych_tvaru.m
. Po jeho spuštění dojde k
vygenerování 10 obrázků od každého tvaru s náhodnou velikostí, barvou a
pozicí.
Skript vytvoří následující složkovou strukturu, každá složka obsahuje 10 tvarů:
Nyní si vytvoříme proměnnou imdsTrain
, kterou využijeme
při trénovaní. imdsTrain
obsahuje informace o souborech (jejich
cestu) a třídu správné klasifikace (kruh nebo obdélník):
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
Funkce imageDatastore()
je dostatečně schopná, aby pochopila
(řekneme jí to v parametrech), že názvy složek jsou zároveň názvem
třídy pro klasifikaci.
Trénink neuronové sítě
Nyní máme architekturu sítě, včetně vah, které jsme převzali z Alexnet. Jsme krůček od toho, abychom mohli spustit trénink. Potřebujeme ještě specifikovat nastavení pro učení:
options = trainingOptions('sgdm','InitialLearnRate',1e-4);
Možností, jak upravit parametry učení, je spousta. Toto je velice zjednodušená varianta s defaultním nastavením většiny parametrů.
Celé učení zařídí funkce trainNetwork()
. První parametr
je výše vytvořená databáze obrázků. Druhý parametr je poskládaná
architektura sítě, kterou jsme definovali dříve (layers
).
Třetím parametrem jsou specifikované options
.
netTransfer = trainNetwork(imdsTrain,layers,options);
Síť se trénuje na dvaceti obrázcích. Ve výstupu uvidíte relativně krátký postup tréninku:
Během 13 sekund se síť natrénovala na grafické kartě (ta je rychlejší než CPU. Pokud ji máte, MATLAB na ni sám přepne) se 100% přesností. Tréninkový proces prohnal během těch pár sekund každý z 20 obrázků neuronkou 30x. Většinou, když je něco s přesností 100 %, je to podezřelé. Přesnost je navíc na datech, na kterých trénujeme. Toho se snažíme většinou vyvarovat, abychom síť nepřeučili (přeučená síť funguje dobře jen na tréninkových datech). Validační data jsme z postupu kvůli jednoduchosti však vypustili. Jestli je síť naučená správně, zjistíme následovně.
Testování sítě
Z trénovacího procesu nám vznikla proměnná netTransfer
,
která umí rozeznávat dva tvary. S pomocí funkce classify()
jí
otestujeme na několika obrázcích. 16 útvarů je naskládáno v jednom
.png
souboru (také přiložen v .zip
archivu).
Prvních 8 jsou prosté kruhy a obdélníky, další tvary už tak jasné
nejsou:
Následující kód, rozseká .png
soubor na 16 obrázků,
každý zvlášť klasifikuje a připíše k němu výsledek, včetně
pravděpodobnosti:
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ýsledek:
Kruhy a obdélníky rozeznala neuronová síť s přehledem (a 100% přesností). Jelikož síť není natrénovaná na nic jiného než právě na kruh a obdélník, není třeba schopná rozeznat smajlík (prý kruh), ani prázdnotu (prý obdélník). Ručně kreslený "kruh" a obdélník rozeznala správně. Pokud má obdélník zakulacené rohy příliš, klasifikuje to již jako kruh.
Závěr a pár poznámek
Využili jsme naučenou neuronovou síť Alexnet k tvorbě vlastní neuronové sítě, jež je schopna rozeznat čtverce od koleček. Článek je sepsán a kód konstruován tak, aby co nejrychlejším způsobem demonstroval danou problematiku a ukázal vše jednoduše a bez zevrubných popisných částí. Téměř každému vysvětlení by mohlo předcházet "zjednodušeně řečeno".
Čas učícího procesu je závislý na velikosti učící databáze, zvolených parametrech a železe, na kterém se učení provádí. Zde to zabralo 13 sekund, což se řádově liší od scénářů skutečného světa (třeba i dny, týdny).
Vzniklá neuronová síť se dá snadno rozšířit. V prvním kroku třeba o
více tvarů. Pouze se přidá další složka s předpřipravenými obrázky a
změní se počet neuronů ve fullyConnectedLayer
.
Obrázky tvarů se dají snadno nahradit jinými (například jablko vs hruška vs pomeranč nebo zatažená obloha vs slunečno vs déšť), což nás už přibližuje k nějakému skutečnému scénáři.
Další podstatnou část, která si zaslouží úpravu, je tréninkový
proces. options
mají spoustu možností,
mezi něž patří třeba databáze validačních dat, počet opakování,
změna algoritmu, atd.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 10x (42.91 kB)
Aplikace je včetně zdrojových kódů