Áttörés (de nem a pofáján)

     Ezt a bejegyzést Brassai tanárúrnak dedikálom, aki ráébresztett arra, hogy egyedül is meg tudom csinálni és vissza tudom fejteni a megtanított neuronháló kódját egy olyan kóddá, amiből megírhatom a saját arcfelismerő algoritmusomat.

     Ez az egész bejegyzés ezt az arcfelismerő módszert fogja végigtárgyalni, egészen a tanítási folyamat kezdetétől a tanítást ellenőrző hisztogramig.

     Szükséges eszközök : MatlabR2010a meg egy képadatbázis. Az én képadatbázisom úgy néz ki, hogy 4533 arc, és 96781 nem arc. Pár bejegyzéssel korábban raktam fel képeket matlabból, ahol eloszlások (zöld-piros kis keresztek) illetve hisztogramok szerepelnek. Eddig összegyűlt 42 ilyen eloszlásom, meg hisztogramom. Ebből 10et használtam fel a mostani tanításhoz. Ezeknek elrendezését a "Neuronháló" bejegyzésben tárgyaltam részletesebben.

     Elindítjuk a Matlabot, s bepötyögjük, hogy nprtool, amire megjelenik a már jól ismert "Welcome to the Neural Network Pattern Recognition Tool." Itt nyomunk egy Next-et, majd az Inputs és Targets résznél beállítjuk a bemeneti és a kategóriaeldöntő változóinkat. Nálam ez a következőképpen nézett ki:


Nyomjuk a Next gombot, itt nagyon nincs amit állítani, a szokásos 70%, 15%, 15% tökéletesen megfelel bármilyen célnak. Itt egy újabb Next gombnyomásra megjelenik a legfontosabb beállítási lehetőség, vagyis, hogy hány rejtett réteg legyen a rendszerben. Az egyszerűség és könnyebb megértés kedvéért ezt 1-re állítjuk. (Nomeg azért is állítjuk egyre, mert egyelőre csak annak a működését értettem meg...)

      Itt határozhatjuk meg a neuronhálónk kinézetét. (Sajnos azt nem tudom, hogy ha valami bonyolultabb elrendezést akarunk, azt hogyan tudjuk elérni, viszont egyelőre elégedjünk meg azzal, hogy sok párhuzamosan kötött neuront be tudunk rakni, ha a program nem fut ki a memóriából...) Ugyancsak Next gomb megnyomása után a következő ablakot látjuk :

     Itt a pirossal bekeretezett Train gombra kattintva elindul a vonat, :P  vagyis elkezdődik a neuronháló súlyzóinak a behangolása a lehető legjobb osztályozási eredmény elérése érdekében. A tanítás a beadott paraméterek függvényében több-kevesebb ideig tarthat, nomeg a végeredmény is eltérhet az egyes tanítási ciklusok között. Ennek az az oka, hogy a súlyzókat a program véletlenszámokkal tölti fel, annak érdekében, hogy legyen olyan, aki kisebb eséllyel indul a tanulás során, illetve legyen olyan is, akinek nagyobbak az esélyei.

A tanulási folyamat során elért pontosságot is figyelemmel kisérhetjük, ha a Performance gombra kattintunk. Mivel nem voltam elég gyors, így csak a végét tudom megmutatni :

     Ezen a képen az látható, hogy hogyan változott a háló osztályozási pontossága a tanítási ciklusok alatt. Persze ez a 10^-7.en pontosság nekem sem elsőre jött össze... legtöbbször leragad a 10^-1 és 10^-2 között. Viszont az ideális bemeneti konfigurációval, illetve egy ideális kezdőértékkel nagyon szép eredményeket lehet elérni. A következő ábra, amit érdemes tárgyalni, az a találati pontossága ennek a hálónak:
A jobb alsó sarokban megjelenő egyik zöld négyzetben írja a 4533 arcot, a másikban az összes nem-arcot. Ez azt jelenti, hogy a találati arány 100 %, ami bizakodásra adhat okot. Most jön a nehezebb része, mégpedig a neuronháló megértése és visszafejtése.

     A legelső lépés a megértés felé a Simulink diagram kigenerálása, amit két Next gomb nyomás után meg is tehetünk a Generate Simulink Diagram gombra kattintva. Ha mindent jól csináltunk, akkor a következő képet látjuk a Simulink ablakban:
Itt a kék Neural Network négyzet rejti az igaz tudást, melynek bírtokában már Szarumán sem állíthat meg a vulkán felé vezető úton... :P
Ha az előző körben figyelmesek voltunk észrevehetjük, hogy egy hasonló ábrát már láttunk... Amikor beállítottuk, hogy hány rejtett réteg legyen a hálónkban. Ezt is tovább fogjuk boncolni. Legelső amit megnézünk, az az adatok előfeldolgozása. Ezt a legelső rózsaszín négyzetre kattintva tehetjük meg.
Itt a legelső kék négyzet azt mondja, hogy ő nem csinál semmit: "This processing function is not yet supported in Simulink. It currently returns its input without any change." vagyis ami a bemeneten, az a kimeneten. A középső kék négyzet "Removes rows from a vector in positions where the values are always constant." magyarul eltávolítja azokat a sorokat, ahol mindíg konstans értékek vannak, valószínűleg azért, mert az úgysem befolyásolná a tanítás eredményét. A harmadik pedig átlagolja a táblázatban szereplő értékeket -1 és 1 közé. Ezt pont nem tudom, hogy miért csinálja. Viszont azt tudom, hogy hogyan csinálja :

     megkeresi az első sorból a legnagyobb és legkisebb elemet, ezek lesznek az xmax és xmin. A -1 lesz az ymin, a +1 lesz az ymax. Tudjuk, hogy melyik értékünket akarjuk beállítani -1 és +1 közé, az lesz az X, a neki megfelelő kimeneti érték lesz az Y. A képlet pedig a következő:


y = (ymax-ymin)*(x-xmin)/(xmax-xmin) + ymin;

     Ezzel kiszámoljuk minden egyes bemeneti értékrea -1 és 1 közé eső átlagát. Ha azzal végeztünk, belépünk az első neuronháló rétegbe.
    Itt van egy Delay tagunk, amivel most nem foglalkozunk, majd egy weight, amiben vannak a behangolt súlyzóink.Ahhoz, hogy azokat a súlyzókat elérjük, duplaklikk a weight feliratra. Ez a belső felépítése:
Ez a bemenetére érkező tíz értéket skalárisan megszorozza a weights-ben lévő értékekkel, majd összeadja őket. A dotprod1 kimenetén a következő értéket fogjuk látni: sum(pd.*weights). Az én súlyzóim a következők:

[-1.9841331421273383;-1.3585126958692031;
-1.4351172518964324;-1.4932715678081847;
0.015225782582406353;7.1424511152721832;
6.8843449087072308;0.89608401038087471;
0.56418414319632859;0.24452191060179679]

Pontosan 10 darab majdnem tökéletesen behangolt súlyzó. Ha ezzel a szorzás-összeadás kombóval megvagyunk, akkor a következő lépés a Bias, vagy magyarul eltolás hozzáadása ehhez a sum(pd.*weights) értékhez. Az én Bias értékem = -0.12710590228495711.

Ezt az összeadást fogja elvégezni a netsum nevű kicsi kék négyzet, aminek a kimenete egy csúnya tangens hiperbolikusz szigmoid átviteli függvénynek a bemenetére van kötve. Ennek az a lényege, mint a komparátornak, vagyis egy bizonyos értéknél kapcsol, viszont itt a kapcsolás elég lágy. Egy kis programrészlettel szemléltetném a függvény kinézetét :

n = -5:0.1:5;
a = tansig(n);
plot(n,a) 
%a = tansig(n) = 2./(1+exp(-2.*n))-1 

A tangens-szigmoidnak az átviteli függvénye, fel van tüntetve a forráskódban, a kikommentált részben. A tangent-szigmoidnak a kimenetét nevezzük el az egyszerűség kedvéért "a"-nak. Ez az a lesz az első neuronháló rétegnek a kimenete, és ez szolgálja a bemenetet a második neuronháló rétegnek, a Layer2-nek.
A Layer2 láttán már meg sem ijedünk, mert majdnem ugyanúgy néz ki, mint a Layer1. Egyetlen lényeges különbség ott van, hogy a weight-re kattintva egy dupla súlyzós izét látunk. Franc essen bele... próbálok szépen fogalmazni, de még csak azt sem tudom, hogy hívják ezeket...
Itt a felső súlyzó értéke 5.9364589656690185, az alsó súlyzó értéke pedig -8.7710742682750453. Azért van itt két súlyzónk, mert két kimeneti kategóriánk van. A végigvitt példánál megtárgyaljuk a két kimeneti eredményt. Lényeg, hogy az "a" változónkat megszorozzuk mindkét számmal, ebből lesz az "a[1]" és az "a[2]". Ez lesz a weight kimenete, illetve a netsum egyik bemenete. A másik bemenete a már korábban megismert Bias, csak más értékekkel. A jelenlegi értékeink a következők : [-0.51040076648123345;1.4045288690576592] Itt is két értékünk van, mert a két kategóriába kell besorolni a bejövő adatot. A netsumnak a kimenete a tansig() bemenete lesz, vagyis ezt az értéket megint hasonlítjuk egy kapcsolási küszöbszinthez.

      Ennek a tansig()-nek a kimenete megy a "posztprocesszálló" blokkba, ahol visszalakítják az eredeti értékére (mert mint tudjuk, behangoltuk -1 és +1 közé).

Itt már megkapjuk, hogy melyik arc illetve melyik nem arc. A lényeg, hogy amelyik oldalon van az 1-es ahhoz a csoporthoz tartozik az illető kép. Nálam ez úgy nézett ki, hogy az arcokat az [1 0] transzponált vektor kategorizálta, a nem arcokat pedig a [0 1]. Ennek megfelelően, ha a tansig kimenetén azt az eredmenyt kapjuk, hogy

eredmeny =

    0.9990
   -0.9999

akkor azt mondhatjuk, hogy az egy arc. Ha azt az eredményt kapjuk, hogy

eredmeny =

   -0.9999
    1.0000

vagyis felül van a -1 és alul a +1, akkor az nem arc.

clear all
close all
clc

load neural_net.mat %itt vannak tarolva a kepek
for j=4500:4600 %4533-ig vannak az arcaim
            bemenet=zeros(10,1);
            bemenet = f_and_nf_proba(:,j); %beallitjuk a bemenetet
            %minimum es maximum ertekek meghatarozasa
            ymax=1;
            ymin=-1;
            xmin=zeros(10,1);
            for i=1:10
            xmin(i,1)=min(f_and_nf_proba(i,:));
            end
            xmax=zeros(10,1);
            for i=1:10
            xmax(i,1)=max(f_and_nf_proba(i,:));
            end
            y=zeros(10,1);%ide kerulnek a normalizalt bemeneti ertekek
            for i=1:10
            y(i,1) = (ymax-ymin)*(bemenet(i,1)-xmin(i,1))/(xmax(i,1)-xmin(i,1)) + ymin;
            end
            L1_w1=[-1.9841331421273383;-1.3585126958692031;-1.4351172518964324;-1.4932715678081847;0.015225782582406353;7.1424511152721832;6.8843449087072308;0.89608401038087471;0.56418414319632859;0.24452191060179679];
            %L1_w1 tartalmazza a Layer1-ben levo sulyzo ertekeit.
            L1_w1_ki=sum(L1_w1.*y);%ehhez hozzaadjuk a sulyzot
            eltolas_1=-0.12710590228495711;
            netsum_1_ki=eltolas_1+L1_w1_ki;%ez megy be a tangens hiperbolikuszba
            tansig_kimenet = tansig(netsum_1_ki);
            %ezt a tansig_kimenet-et neveztuk mi "a"-nak az egyszeruseg vegett
            %ez a tansig_kimenet megy be a Layer2-be. 
            sulyzo_2_1=5.9364589656690185;
            sulyzo_2_2=-8.7710742682750453;
            %ez a sulyzo_2_* a layer2-es ket sulyzoja
            arc_e=zeros(2,1);
            arc_e(1)=tansig_kimenet*sulyzo_2_1;
            arc_e(2)=tansig_kimenet*sulyzo_2_2;
            %ezt meg el kell toljuk a Layer2-Bias ertekevel
            eltolas_2=[-0.51040076648123345;1.4045288690576592];
            majdnemvege = arc_e+eltolas_2;
            eredmeny = tansig(majdnemvege);
            j
            eredmeny
            if(eredmeny(1)>0)display('Ez arc.');end
            if(eredmeny(2)>0)display('Ez nem arc.');end
end

     Remélem elég világos voltam, illetve azt is remélem, hogy a leírásban nincsenek szarvashibák. Ha mégis, akkor kell írni a szerkesztőség címére, s kijavítjuk.

     Ennyi mára.

0 megjegyzés:

Megjegyzés küldése

Return top