sztuczne sieci neuronowe - błędne rozpoznawanie liter

0

Witam , napisałem program do rozpoznawanie liter w javie zgodnie z podpowiedziami jakie znalazłem w Internecie. Wygląda on mniej więcej tak:
Są 3 warstwy:
1: 35 neuronów z których każdy reprezentuje 1 piksel "matrycy". ( jest to plansza 5x7 na której znajduje się konkretna litera)
2:(ukryta) 10 neuronów ;
3: wyjściowa 26 neuronów z których każdy reprezentuje konkretną literę.

I program wygląda następująco:
Losuję wagi dla każdego neuronu
Uzupełniam wektory uczące - które służą do uczeni a sieci ( np. dla litera A wektor wygląda: 1 0 0 0 .... (25 zer) dla B (0 1 0 0 .....0) itd.

{
-następnie wczytuję pierwszy obrazek na którym jest litera A i zamieniam go na binarke

  • wypełniam 1 warstwę tą binarką
  • .... Potem poprzez odpowiednie sumy iloczynów i funkcję uzupełniam kolejne neurony w kolejnych warstwach
  • sprawdzam co powinno być na wyjściu a co jest ( porównuję z wektorem uczącym
    -obliczam błąd i uwzględniając ten bład modyfikuję wagi
    } to wszystko w pętli dopóki błąd nie będzie mniejszy od 0.01

i w tym momencie mam nauczoną sieć dla litery A
powtarzam te instrukcje dla kolejnych liter

Mój problem polega na tym że jak nauczę sieć ( na razie uczyłem dla liter A-E) to po wczytaniu dowolnej litery zawsze pokazuje że jest to E ( czyli ostatnia litera nauczona) . Samo uczenie na pewno działa dobrze. Ale mam wątpliwości czegoś mi nie brakuje.
Ogólnie wygląda to tak:

while(błąd>0.01)
uczę litere A
Jeżeli nauczona robię to z kolejnymi

Następnie Proces uczenia od A-E powtarzam na razie dałem w pętli 100 razy ( bo mi stos przepełniało) ale nie zmienia to faktu że nawet po setnym razie wyniki całkowicie nie są zbliżone do tych jakie powinny być i pokazuje że w 99% jest to litera E ( przy dowolnie wczytanej ).

0

Uczysz to w zły sposób. Musisz na wejście podawać wszystkie potencjalne dane żeby sieć sie nauczyła rozpoznawać wszystkie! Najprościej będzie jak będziesz na wejście pchał po kolei cały alfabet. Tzn dajesz literkę 'A' testujesz, modyfikujesz wagi, dajesz literkę 'B' ... potem 'Z' a potem znów 'A'.
Reszta wygląda ok, o ile ten twój back-propagation jest dobrze napisany.
BTW "nawet po setnym razie"? Sieci neuronowe uczy się dziesiątki tysięcy epok...

0

Poza tym while(bląd>0.01) może nie być dobrym pomysłem, gdy wewnątrz nie masz innej możliwości przerwania pętli (np. uzależnionej od wielkości normy, czasu i/lub ilości iteracji)

0

To ja dokładnie tak robię tylko że jak na razie nauczyłem litery od A-E . Tylko czy po jednej epoce jest możliwe że zawsze pokazuje E ? Jeśli chodzi o funkcje poprawianie błędów czy wyznaczania kolejnych neuronów są one wzięte z przykładów ,a nie wymyślane przez mnie więc to raczej jest w porządku.

Liczbę epok ustawiłem ręcznie. I wygląda to tak:
1 epoka:
A-uczy się ok.2-5min (zależy od wylosowanych wag początkowych) osiąga błąd <0.01
B uczy się kilkanaście sekund do minuty
C <1s ( około 10 przejść)
... <1s

2 epoka:
w przypadku każdej litery błąd początkowy wynosi mniej więcej 22-24% - co mnie trochę dziwi że każdy ma zbliżoną wartość

W kolejnych epokach błąd kolejnych liter się zmniejsza. Więc na początku przyjmuje wartości nie 23%
tylko powiedzmy 22,5% i z każdą epoką mniej aż w jakiejś z kolei rośnie. Tak jak to widać tu:
Dla litery A (kolejny wiersz - kolejna epoka)
Po 1 epoce (nauczone litery od A-E) 0.19611613513818404
0.21976570197556042 2 epoka
0.22667217322354122
0.2196012853640355
0.22936017240056922
0.22883798129217828
0.22885483798469936
0.2288542766422194
0.2288542953123754
0.22885429468691823
0.228854294703401
0.22885429469852794
0.22885429469436613
0.22885429469018145
0.2288542946859987
0.22885429468181634
0.22885429467763518
...

Czy jest możliwe że w kolejnych epokach błąd rośnie czy jest to błąd gdzieś w implementacji ?

0

PS jak na razie nie mogę sprawdzić jak by wyglądała nauka sieci dla większej ilości epok gdyżjak już napisałem mam problem z przepełnieniem stosu:
Wygląda to tak:

void kolejneObrazki(){
      
      if((getNextPicture()!=null)&&(o==0))
        siecN.teachNetwork2(getNextPicture(),jakaLitera(paths[nrObrazka++]));   // tutaj przesyłam refernecje do tablicy binarnej reprezentującej literę oraz nazwę litery(dla wektora sprawdzającego)
     

 else if(o<800){                                                              
          o++;
          if(getNextPicture()==null)
              nrObrazka=0;
          
          System.out.println("o= "+o);
          siecN.teachNetwork2(getNextPicture(),jakaLitera(paths[nrObrazka++]));
      }
  } 
0

Przepraszam ale nie mogę edytować jeszcze raz przesyłam

 	
PS jak na razie nie mogę sprawdzić jak by wyglądała nauka sieci dla większej ilości epok gdyżjak już napisałem mam problem z przepełnieniem stosu:
Wygląda to tak:

void kolejneObrazki(){
 
      if((getNextPicture()!=null)&&(o==0))
        siecN.teachNetwork2(getNextPicture(),jakaLitera(paths[nrObrazka++]));   // tutaj przesyłam refernecje do tablicy binarnej reprezentującej
                                                                                                            // literę oraz nazwę litery(dla wektora sprawdzającego)

 else if((o++)<800){                                                              
          
          if(getNextPicture()==null)
              nrObrazka=0;

          siecN.teachNetwork2(getNextPicture(),jakaLitera(paths[nrObrazka++]));
      }
  }  

Następnie po nauczeniu sieci dla konkretnej litery z klasy sieciN wywołuję tą (kolejneObrazki()) funkcję która przesyła tamtej klasie kolejną literę.
Gdy nauczy się wszystkich liter powtarzam cały proces (800/ilosc liter) razy , przy większej ilości pojawia się wyjątek stack overflow.
Więc innymi słowy funkcja1->funkcja2->funkcja1->funkcja2 wykonują się po kolei a nie w jednym czasie gdy 1 funkcja "skończy pracę" rozpoczyna ją druga funkcja.
Jak mógłbym to rozwiązać żeby nie było przepełnienia stosu ?

0

Nic nie rozumiem z tego co tutaj napisałeś, ale mam wrażenie że praktycznie nic nie robisz tu dobrze.

  1. Uczyć sieć musisz NA RÓŻNYCH PRZYKŁADACH. Nie moze tak zrobić że najpierw uczysz literki A, potem literki B, potem C, bo TO NIE MA SENSU! POMYŚL trochę jak to działa! Modyfikując wagi dla literki B popsujesz wagi które rozpoznawały literkę A! Jedyny sposób jest taki że uczysz literki A jeden raz (tzn tylko RAZ poprawiasz wagi), następnie uczysz B, następnie C i tak w kółko.
  2. Nie rozumiem gdzie tu niby możesz wykańczać stos. Przecież sam algorytm nauki jest prosty jak budowa cepa...
for i in liczba_epok
  for obrazek in dane_uczace
    wynik = wylicz_odpowiedź_sieci(obrazek)
    zaktualizuj_wagi_sieci(wynik, obrazek)

I tyle, nic więcej. Gdzie tu się może skończyć stos?

Mam wrażenie że ty bardzo słabo ogarniasz programowanie i nie wiadomo czemu postanowiłeś sie zabrać za nie-aż-taki-trywialny problem. To tak jakbyś nie umiał chodzić a postanowił startować w zawodach w bieganiu...

0

Nie wiem czy dobrze zrozumiałem:
u mnie wygląda to tak że mam literką A uczę się jej. potem B ,C ... aż do E i następnie powtarzam na tych samych danych uczących (kolejne epokę) i rzeczywiście kolejne litery zmieniają wagi " pod siebie" więc dlatego nie do końca jestem pewny czy dobrze zrozumiałem idee.
W takim razie powinienem mieć klika różnych wersji każdej litery ? i w kolejnych epokach je uwzględniać ?

Nie moze tak zrobić że najpierw uczysz literki A, potem literki B, potem C, bo TO NIE MA SENSU! POMYŚL trochę jak to działa! Modyfikując wagi dla literki B popsujesz wagi które rozpoznawały literkę A! Jedyny sposób jest taki że uczysz literki A jeden raz (tzn tylko RAZ poprawiasz wagi), następnie uczysz B, następnie C i tak w kółko.

Nie rozumiem za bardzo tego co tu napisałeś 1 zdanie zaprzecza drugiemu. Chyba że chodzi CI iż całkowicie osobno uczę literę A przez wszystkie epoki potem B itd...
Nie, ja robię tak że cały alfabet to jest jedna epoka.
A jeśli chodzi o kwestię przepełnienia stosu to również nie mam pojęcia dlaczego się stos przepełnia zrobiłem to tak samo jak napisałeś lecz używając dodatkowej klasy.

0

Nic tu sobie nie zaprzecza, po prostu ty nic na ten temat wcześniej nie przeczytałeś i nie rozumiesz jak SSN działa...
Jeśli twoja główna pętla ucząca wygląda tak jak napisałem wyżej to musisz mieć błąd w metodzie wstecznej propagacji. Bo w normalnej sytuacji modyfikacja wag jest "niewielka", więc nie jest możliwe żeby nagle sieć rozpoznawała ci tylko ostatni symbol, chyba że uczysz tylko tym ostatnim symbolem przez długi czas.
Algorytm wygląda tak:

  • podajesz na wejście A, liczysz jak sieć to sklasyfikowała, wyliczasz sobie jak należałoby zmodyfikować wagi żeby być bliżej poprawnej odpowiedzi (google: back propagation), modyfikujesz wagi zgodnie z algorytmem (tzn dokonujesz bardzo niewielkiej zmiany wag w odpowiednim "kierunku"
  • podajesz na wejście B, postępujesz jw.
    ...
  • podajesz na wejście Z, postępujesz jw.
  • podajesz na wejście A postępujesz jw.
    ...
    i tak dalej aż sieć w całej jednej serii A-Z będzie miała zadaną dokładność.

Problemy ktore opisałes mogą wynikać z 2 powodów:

  1. Niepoprawnie uczysz sieć, podając wielokrotnie pod rząd na wejście ten sam symbol
  2. Niepoprawnie modyfikujesz wagi neuronów zmieniając je bardzo mocno w kierunku aktualnie testowanego symbolu

Popatrz sobie na ten kod: https://github.com/Pharisaeus/Neural co prawda to python, ale kod jest w miarę czytelny.

0
Łukasz6565 napisał(a):

Nie wiem czy dobrze zrozumiałem:
u mnie wygląda to tak że mam literką A uczę się jej. potem B ,C ... aż do E i następnie powtarzam na tych samych danych uczących (kolejne epokę) i rzeczywiście kolejne litery zmieniają wagi " pod siebie" więc dlatego nie do końca jestem pewny czy dobrze zrozumiałem idee.
W takim razie powinienem mieć klika różnych wersji każdej litery ? i w kolejnych epokach je uwzględniać ?

Tu masz główny błąd. Tak jak napisał Shalom, nie uczysz (korekta sieci do osiągnięcia dobrego wyniku dla jednej literki) a prezentujesz literki (jedna korekta per literka per epoka).
Raczej nie potrafię napisać tego jaśniej niż on to zrobił.

1 użytkowników online, w tym zalogowanych: 0, gości: 1