Wsteczna propagacja błędów na przykładzie XOR

0

Witam
staram się zaimplementować sieć neuronową(pseudoneuronową jak kto woli) w C++ , ale zanim przejde do jej napisania chciałbym napisać algorytm wstecznej propagacji błędu dla funkcji XOR.
I tu chciałbym poskładać swoje informacje , aby je zweryfikować,
A więc:
1.Buduje sieć z 3 neuronów: 2 wejściowe i 1 jeden wyjściowy, (plus 2 wejscia przed warstwa wejsciowa)
2.Obliczam wyjścia wszystkich neuronów i wyjście całej sieci
3.Obliczam błąd sieci czyli wartosc oczekiwana - wartość otrzymana (Dla warstwy wyjsciowej)
4.Dla warstwy wczesniejszej błędy dla każdego neuronu obliczam tak: waga wyjścia neuronu * błąd neuronu z którym jest połączyny
5.Później modyfikuje wagi w += || wsp.uczenia * błąd neuronu * wartosc sygnalu|| ( klamra || xxxx|| oznacza zaokrąglenie
6.modyfikacja progu funkcji aktywacji dla kazdego neuronu (prog -= || błąd.neuronu * wsp.uczenia||)

A co do samej nauki : tworzę wcześniej tablice prawdy np. dla tego przypadku tablice prawdy XOR:
Z niej wstawiam wejscia i podstawiam wartosc oczekiwana , a gdy przelece całą tablice czyli 4 pozycje to koniec nauki?

0

W sieci jest pierdyliard opisów tego algorytmu. Generalnie aby obliczyć błędy w warstwach innych niż ostatnia "propagujesz" błędy wstecz i dopiero jak masz błędy wszystkich neuronów możesz wziąć się za aktualizację wag.

Microb napisał(a)

A co do samej nauki : tworzę wcześniej tablice prawdy np. dla tego przypadku tablice prawdy XOR:
Z niej wstawiam wejscia i podstawiam wartosc oczekiwana , a gdy przelece całą tablice czyli 4 pozycje to koniec nauki?

uczysz dopóki wartość funkcji błędu nie będzie odpowiednio mała(zadowalająca Cię) lub dopóki nie przekroczysz jakiejś liczby iteracji.

Od siebie dodam, że w zależności od tego jaki będziesz miał zbiór uczący powinieneś wybrać odpowiednie funkcje aktywacji, szczególnie dotyczy to neuronu w ostatniej warstwie(jak w zbiorze uczącym będziesz miał wartość wzorcową -1, a ostatni neuron będzie miał funkcję aktywacji sigmoidalną unipolarną to błąd nigdy nie spadnie.)

0

Witam

No więc zapoznałem się z dostępnymi materiałami, a chciałem zweryfikować czy mój tok rozumowania jest dobry. Xor buduje dla progowej funkcji aktywacji

próg aktywacji zmieniam tak:

prog -= błąd_neuronu ; 

a wagę tak:

waga += błąd_neuronu * wartość_wejscia 

dalsze pytania:
-czy mogę pominąć użycie wsp.uczenia?
-czy jeżeli przez trening przepuszcze wektor [x1:0 x2:0 y:0] na wyjsciu dostane oczekiwane 0 to przechodze do wektora[x1:0 x2:1 y:1] i znowu dostaje blad wyjscia rowny 0 czyli zmiana wag spowodowała że mam 1 na wyjsciu, i wtedy jeżeli wszystkie wektory już zostały użyte czyli dla xor będzie ich 4 to trenowanie jest skonczone? czy jest warunek który mówi o ponownym przepuszczeniu tych wektorów?

pytam na forum , bo nie wszystko jest dla mnie jasne , było by dziwnie gdybym wiedział i pisał na forum wydaje mi się że do tego ono służy(oczywiście gdy skończe udostępnie kod , może ktoś kiedyś go znajdzie i mu pomoże)

Pozdrawiam i dziękuje za pomoc oraz zainteresowanie

0
  1. Nie możesz użyć progowej funkcji aktywacji przy uczeniu wielowarstwowej sieci neuronowej metodą wstecznej propagacji błędów - funkcja aktywacji musi mieć ciągłą pochodną. radzę użyć funkcji sigmoidalnych(np. tangensa hiperbolicznego). Progowa funkcja aktywacji(perceptron) nie nadaje się do realizacji problemu XOR, ponieważ przecina Twoją przestrzeń(2D) linią(1D). Spróbuj oddzielić graficznie jedną linią na płaszczyźnie tablicę prawdy dla XOR - nie da się, bo wyjdzie Ci np. elipsa

  2. Co znaczy pominąć współczynnik uczenia? Wstawić 0? Wówczas wagi się nie zmienią.

  3. Podajesz na wejścia ciągi uczące(w losowej kolejności najlepiej), dla każdego ciągu aktualizujesz wagi w zależności od błędu oraz liczysz błąd średniokwadratowy dla wszystkich(4) ciągów uczących. Zastanów się, jaki miałoby sens uczenie sieci jednego ciągu aż błąd osiągnie 0, a później zmiana wag(co za tym idzie zwiększenie tego błędu)

  4. Powtarzam kolejny raz - Uczysz sieć aż błąd średniokwadratowy sieci będzie mniejszy od zadanego progu.

0

Ale jedna linia dzieląca powstaje dla jednego perceptronu, dla sieci już ponoć można stworzyć XOR

0

Ale nie dla progowej funkcji aktywacji w ostatniej(lub dowolnej innej) warstwie. Chyba, że nie wiem o czymś i wsteczną propagację można zastosować dla tej funkcji.

0

Mojim zadaniem jest implementacja sieci która dostanie macierz 3x3 i w zaleznosci od ilosci "1" w macierzy rozpozna cyfre od 0 do 9 np. dla wektora powstałego z przykładowej macierzy 0 0 0 0 0 1 0 0 0 ma rozpoznać 1 podobnie jak i dla 0 0 0 1 0 0 0 0 0 , i mam to zaimplementować dla różnych funkcji aktywacji w punkcie a) mam żądaną progową funkcję aktywacji. Pomyślałem więc że napisze algorytmy uczące najpierw dla czegoś prostego jak XOR sprawdze czy wszystko ok a później stworze tylko inną sieć, dlatego rozpocząłem XORa z progową f.aktywacji.

0

Nie radzę sobie z tym XORem , nie wiem co jest nie tak że sieć się nie uczy, może chciałby ktoś spojrzeć na kod??

0

zainteresowanym pomocą wyśle kod na pw, wciąż sieć nie uczy się , sprawdzałem też by wagi nie były symetryczne i wciąż nie wiem .....spodziewam się prostego przeoczenia w teorii bądź implementacji. Śpiesze się więc bardzo proszę o pomoc
Pozdrawiam

0
 
#include <cstdlib>
#include <iostream>
#include <typeinfo>
#include <vector>
#include <cmath>

using namespace std;


int main(int argc, char *argv[])
{   
    int ile_krokow = 50000;
    //wektory uczace
    double wu[4][3] = { {0,0,0}, {0,1,1}, {1,0,1}, {1,1,0} };
    //pochodne funkcji
    double f[3];
    //Wagi
    double w[9];
    //Sumy ważone
    double s[3];
    //funkcje aktywacji
    double u[3];
    //bledy
    double d[3];
    //wsp.bledu
    double eta = 0.1;
    //wektor trenujacy
    int wk = 0;
    
    
    //losowa inicjalizacja wag początkowych//////////////////////////////
    srand( (unsigned)time( NULL ) );
    for(int i=0 ; i < 9 ; i++){  
        w[i] = ((rand() % 9)-4); // lusuj liczbe z przedzialu od -4 do 5
        cout <<"Waga: " << w[i] << " "; //wypisz na ekran
        }
        cout<<endl;
    ////////////////////////////////////////////////////////////////////
    
    for(int iteracja=0 ; iteracja<ile_krokow ; iteracja++){
            //losowe wybieranie wektora trenującego/////////////////////
            srand( (unsigned)time( NULL ) );
            wk = (rand() % 4);
            ////////////////////////////////////////////////////////////
            //Faza propagacji w przód - warstwa posrednia
            s[0] = w[0] * wu[wk][0] + w[1] * wu[wk][1] + w[2] * 1;
            s[1] = w[3] * wu[wk][0] + w[4] * wu[wk][1] + w[5] * 1;
            
            u[0] = 1/( 1+ ( pow(2.718,(-s[0]) ) ) ) ;
            u[1] = 1/( 1+ ( pow(2.718,(-s[1]) ) ) ) ;
            //Faza propagacji w przód - warstwa wyjsciowa///////////////
            s[2] = w[6] * u[0] + w[7] * u[1] + w[8] * 1;
            u[2] = 1/( 1+ ( pow(2.718,(-s[2]) ) ) ) ;
            ////////////////////////////////////////////////////////////
            //Faza propagacji wstecz - warstwa wyjsciowa////////////////
            f[2] = u[2] * ( 1 - u[2] );
            d[2] = ( wu[wk][2] - u[2] ) * f[2];
            //Faza propagacji wstecz - warstwa posrednia////////////////
            f[0] = u[0] * ( 1 - u[0] );
            d[0] = w[6] * d[2] * f[0];
            f[1] = u[1] * ( 1 - u[1] );
            d[1] = w[7] * d[2] * f[1];
            ////////////////////////////////////////////////////////////
            //Uaktualnienie wag - warstwa wyjściowa/////////////////////
            w[6] = w[6] + ( eta * d[2] * u[0] );
            w[7] = w[7] + ( eta * d[2] * u[1] );
            w[8] = w[8] + ( eta * d[2] * 1 );
            ////////////////////////////////////////////////////////////
            //Uaktualnienie wag - warstwa posrednia///////////////////// 
            w[0] = w[0] + ( eta * d[0] * wu[wk][0] ) ;    
            w[1] = w[1] + ( eta * d[0] * wu[wk][1] ) ;           
            w[2] = w[2] + ( eta * d[0] * 1 ) ; 
            
            w[3] = w[3] + ( eta * d[1] * wu[wk][0] ) ;    
            w[4] = w[4] + ( eta * d[1] * wu[wk][1] ) ;           
            w[5] = w[5] + ( eta * d[1] * 1 ) ; 
            ////////////////////////////////////////////////////////////
            
            
            }
            
    /////////////Test////////////////////////////////////////////////////
            int a;
            int b;
            int i = 0;
            while(i<10){
            
            cout << "Podaj a: ";
            cin>> a;
            cout << "Podaj b: ";
            cin>> b;
            
            s[0] = w[0] * a + w[1] * b + w[2] * 1;
            s[1] = w[3] * a + w[4] * b + w[5] * 1;
            u[0] = 1/( 1+ ( pow(2.718,(-s[0]) ) ) ) ;
            u[1] = 1/( 1+ ( pow(2.718,(-s[1]) ) ) ) ;
            //Faza propagacji w przód - warstwa wyjsciowa///////////////
            s[2] = w[6] * u[0] + w[7] * u[1] + w[8] * 1;
            u[2] = 1/( 1+ ( pow(2.718,(-s[2]) ) ) ) ;        
            
            cout << "Na wyjsciu mamy: " << u[2] <<endl;
            }
                               
                               
    system("PAUSE");
    return EXIT_SUCCESS;
}
0

Mam ten sam problem zaimplementowalem cala sieć zgodnie z algorytmem naszych materialów i siec sie nie uczy. JA swojej podaje dobre i złe przykłady i niestety jej to nie idzie. Czasami wagi rosna mi do 60 000 itp a czasami male 13 itp.

0
    //pochodne funkcji
    double f[3];
    //Wagi
    double w[9];
    //Sumy ważone
    double s[3];
    //funkcje aktywacji
    double u[3];
    //bledy
    double d[3];

a dlaczego by nie:

double pochodne_funkcji[3];
double wagi[9];
double sumy_wazone[3];
double funkcje_aktywacji[3];
double bledy[3];

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