Autor Wątek: Sterowanie armatką (ssn-ann: algorytm wstecznej propagacji błędów)  (Przeczytany 6592 razy)

Offline grrr

  • Użytkownik

# Styczeń 15, 2009, 23:19:49
A tak serio, algorytmy genetyczne możemy stosować dla każdego problemu, w którym:
- potrafimy wydzielić zbiór danych wejściowych,
- potrafimy oceniać wyniki produkowane przez ten zbiór (wystarczy porównanie które lepsze, które gorsze),
- potrafimy przeprowadzić operację krzyżowania danych wejściowych (opcjonalnie - jeżeli nie potrafimy, to możemy zawsze jeszcze zrobić błądzenie losowe),
- potrafimy przeprowadzić mutacje danych wejściowych,
Tak na serio to problem w tym, że ktoś albo zrezygnował z konta, albo nie, ciężko nam porównywać, czy ktoś wystąpił bardziej, albo mniej. Nie umiemy tego ocenić. Sieci neuronowe działają na logice binarnej (wejście i wyjście).
Mógłbyś wyjasnić? Jak dla mnie działanie sieci wielowarstwowych jest typowym działaniem "black box" - mamy listę wag i połączeń, ale nic a nic nam to nie mówi o samym algorytmie wnioskowania.
Algorytmy można znaleźć w googlach "ekstrakcja reguł z sieci neuronowej". To nie jest żaden "black-box".
Dla autora wątku: poszuka haseł "prof. Tadeusiewicz sieci neuronowe". Pozycja udostępniona na stronach AGH.
Cytuj
Mi niezalezy na sugestiach co dac na wejscie - wyjscie, ile powinno byc epok (po semestrze metod sztucznych inteligencji nabralem nieco wprawy). Korzystajac z gotowych rozwiazan (np Matlab) latwo ustawic funkcje wyjscia jako linowa - jeden parametr i po sprawie.
Jak natomist tego dokonac implementujac algorytm wstecznej propagacji bledow na wlasna reke?
Podobno miałeś poprany algorytm zaimplementowany :) W uproszczeniu: losujesz sieć i dla każdego wektora wejściowego przepuszczasz po kolei przez neurony w poszczególnych warstwach. Tutaj używasz oczywiście funkcji aktywacji. Porównujesz to co masz na wyjściu, z tym co ma być. Masz różnicę. Coś, co powinien dawać dany neuron. I uważaj: zakładasz, że to co dany neuron a na wejściu jest poprawne (a w sieci wielowarstwowej pewnie nie będzie) i możesz łatwo znaleźć poszczególne wagi dla tego neuronu tak, jakby ten warunek był spełniony (tutaj potrzebna Ci jest pochodna funkcji aktywacji). Każdą wagę wiemy o ile zmodyfikować, mnożymy przez współczynnik uczenia, dodajemy dodatkowo jakiś szum. Neuron się trochę poduczy dla tego wektora danych. Robimy to samo z wszyskimi wektorami neuronami od warstwy wyjściowej do wejściowej (najpierw warstwa ostatnia, potem przedostatnia... nie modyfikując wag biasu, bo on nie ma wejścia). Ale tu też uważaj, nie masz poprawnych danych wyjściowych dla warstw innych niż wyjściowa (bo masz to, co wysłałeś do warstwy niżej). Wartości te dostajesz zakładając, że teraz (ta poduczona warstwa niższa) jest poprawna... Wykonaliśmy uczenie dla jednego wektora danych. Robimy to samo dla pozostałych (oczywiście sieci już nie będziemy nigdy losować). Jak zrobimy epokę, to wiesz, przelosowujemy wektory i od nowa wszystkie... Wytłumaczyłem swoimi słowami...
Cytuj
Czy czujesz się funkcją analityczną? Nie? A jakoś "algorytm genetyczny" Ciebie znalazł. Wink
Fajnie, możemy sobie symulować ewolucję człowieka, musimy "tylko" znać funkcję przystosowania do środowiska, albo chociaż wiedzieć, który człowiek jest "lepsiejszy". Wolę nie pytać mojego syna, co on na to...
Cytuj
wprowadzenie czegos na warstwie ostatniej niszczy dzialanie algorytmu uczacego.
Co to znaczy że niszczy? Napisałem to, co jest w książkach, będziesz miał problem, to pisz... Jak zrozumiesz zasadę, to spokojnie dasz sobie radę... Cały myk polega na tym, że za każdym razem lekko modyfikujemy wszystkie wagi tak, żeby wyjście było bliższe zadanemu. Koniec filozofii. Są też gotowe (otwarte) implementacje, możesz się podeprzeć. A od tych wektorków ze strony to można ewentualnie oczopląsu dostać.
Cytuj
Dlatego modele często opracowuje się z udziałem ekspertów z danej dziedziny... Smiley
Którzy często robią odkrycia przy pomocy tych okropnych sieci neuronowych....
Cytuj
Takie tylko sprawdzenie to podstawowy błąd, który nie gwarantuje żadnej sensowności działania sieci
Podstawowe błędy są takie:
- uczenie sieci wartościami "kosmicznymi". Neurony powinny być albo "on", albo "off".
- zła funkcja stopu - sprawdzanie, czy nauczyliśmy sieć licząc błędy dla danych testowych. Dane testowe są dla weryfikacji, a nie jako funkcja stopu.
Zbyt duża sieć to nie jest straszny błąd, "uśmierca" się małopobudzone neurony w nauczonej sieci i sprawdzanie, czy sieć straci zdolność samouczenia. Wtedy dostajemy sieć bardziej odporną na błędy...

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Marcin Baszcz...

  • Użytkownik

# Styczeń 16, 2009, 02:07:29
Grr czytaj uwaznie ;) chodzi mi wylacznie o funkcje aktywacji dla warstwy wyjsciowej, ktora bedzie inna niz w warstawach ukrytych; zamieszczam kod:

neuralnetwork.h
Kod: (cpp) [Zaznacz]
/***********************************************************************************
* neuralnetwork
* Copyright (C) 2009 Marcin Baszczewski <marcin.baszczewski@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*
***********************************************************************************/

#ifndef NEURALNETWORK_H
#define NEURALNETWORK_H

//qt
#include <QApplication>

class Neuron : public QObject
{
    Q_OBJECT
    public:
Neuron();
double n_value;
double n_bias;
double n_delta;
QList<double> n_weights;
};

class Layer : public QObject
{
    Q_OBJECT
    public:
Layer(int size);
QList<Neuron*> n_neurons;
};

enum activationFunction{DEFAULT,TANSIG,LOGSIG};
class NeuralNetwork : public QObject
{
    Q_OBJECT

    public:
NeuralNetwork(QList<int>layers,double learningRate=0.1,bool bias=false,activationFunction function=DEFAULT);
~NeuralNetwork();
void train(QList<double> inputs,QList<double> outputs);
QList<double> getOutput(QList<double> inputs);
void randomize();
    //private:
double random();
double function(double value);
double derivative(double value);
double n_learningRate;
bool n_bias;
QList<int> n_layers;
QList<Layer*> n_layer;
activationFunction n_function;
};

#endif

neuralnetwork.cpp
Kod: (cpp) [Zaznacz]

/***********************************************************************************
* neuralnetwork
* Copyright (C) 2009 Marcin Baszczewski <marcin.baszczewski@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*
***********************************************************************************/

//my
#include "neuralnetwork.h"
//qt
#include <QTime>
//other
#include <math.h>


Neuron::Neuron()
{
    n_value = 1.00;
    n_delta = 0.00;
    n_bias = 0.00;
}

Layer::Layer(int size)
{
    for (int i=0;i<size;i++)
    n_neurons.append(new Neuron());
}

NeuralNetwork::NeuralNetwork(QList<int>layers,double learningRate,bool bias,activationFunction function)
{
    n_learningRate = learningRate;
    n_layers = layers;
    n_bias = bias;
    n_function = function;

    for (int i=0;i<n_layers.size();i++)
    n_layer.append(new Layer(n_layers[i]));
    randomize();
}
NeuralNetwork::~NeuralNetwork()
{
}
void NeuralNetwork::train(QList<double> inputs,QList<double> outputs)
{
    QList<double> actual = getOutput(inputs);

    for(int i=n_layers.size()-1;i>0;i--)
    {
for(int j=0;j<n_layers[i];j++)
{
    //delta
    if (i==n_layers.size()-1)
    n_layer[i]->n_neurons[j]->n_delta = outputs[j] - actual[j];
    else
    {
n_layer[i]->n_neurons[j]->n_delta = 0;
for (int k=0;k<n_layers[i+1];k++)
n_layer[i]->n_neurons[j]->n_delta = n_layer[i+1]->n_neurons[k]->n_weights[j]*n_layer[i+1]->n_neurons[k]->n_delta;
    }
    //training - change weights
    for (int k=0;k<n_layers[i-1];k++)
    n_layer[i]->n_neurons[j]->n_weights[k] += n_learningRate * n_layer[i]->n_neurons[j]->n_delta * derivative(n_layer[i]->n_neurons[j]->n_value) * n_layer[i-1]->n_neurons[k]->n_value;
    if (n_bias==true)
    n_layer[i]->n_neurons[j]->n_bias += n_learningRate * n_layer[i]->n_neurons[j]->n_delta * derivative(n_layer[i]->n_neurons[j]->n_value) * 1.0;
}
    }
}
QList<double> NeuralNetwork::getOutput(QList<double> inputs)
{
    //inputs
    for (int i=0;i<inputs.size();i++)
    n_layer.first()->n_neurons[i]->n_value = inputs[i];

    for (int i=1;i<n_layers.size();i++)
    {
for (int j=0;j<n_layers[i];j++)
{
    n_layer[i]->n_neurons[j]->n_value = 0;
    for (int k=0;k<n_layers[i-1];k++)
    n_layer[i]->n_neurons[j]->n_value += n_layer[i-1]->n_neurons[k]->n_value*n_layer[i]->n_neurons[j]->n_weights[k];
    if (n_bias==true)
    n_layer[i]->n_neurons[j]->n_value += 1.0*n_layer[i]->n_neurons[j]->n_bias;
    n_layer[i]->n_neurons[j]->n_value = function(n_layer[i]->n_neurons[j]->n_value);
}
    }
   
    //output
    QList<double> output;
    for (int i=0;i<n_layers.last();i++)
    output.append(n_layer.last()->n_neurons[i]->n_value);
    return output;
}
void NeuralNetwork::randomize()
{
    for (int i=0;i<n_layers.size();i++)
    {
int weights=0;
if (i!=0) weights = n_layers[i-1];
for (int j=0;j<n_layers[i];j++)
{
    if ((n_bias==true) && (i!=0))
    n_layer[i]->n_neurons[j]->n_bias = random();

    n_layer[i]->n_neurons[j]->n_weights.clear();
    for (int k=0;k<weights;k++)
    n_layer[i]->n_neurons[j]->n_weights.append(random());
}
    }
}
double NeuralNetwork::random()
{
    QTime sometime(0, 0, 0);
    qsrand(qrand() % sometime.secsTo(QTime::currentTime()));
    float temp =( (qrand() % 100)-(qrand() % 100) ) *0.01;
    return temp;
}
double NeuralNetwork::derivative(double value)
{
    switch(n_function)
    {
case TANSIG:
     return 1-(tanh(value)*tanh(value));
case LOGSIG:
     return value * (1.0 - value);
default:
    qDebug("[derivative = 0]");
    return 0;
    }
}
double NeuralNetwork::function(double value)
{
    switch(n_function)
    {
case TANSIG:
    return tanh(value);
    break;
case LOGSIG:
    return (1.0/(1+exp(-value)));
    break;
default:
    return value;
    }
}





Moj projekt:


Na jednym screenie widac malutki zalazek GUI - byc moze kiedys wszystko rozbuduje i stworze kreator sieci neuronowych... (mam duuuzo pomyslow - ale jesli juz to po sesji)
« Ostatnia zmiana: Styczeń 16, 2009, 02:12:22 wysłana przez Marcin Baszczewski »

Offline grrr

  • Użytkownik

# Styczeń 16, 2009, 10:18:02
Grr czytaj uwaznie ;) chodzi mi wylacznie o funkcje aktywacji dla warstwy wyjsciowej, ktora bedzie inna niz w warstawach ukrytych; zamieszczam kod:
Powiedz mi, po co? Może dlatego, że chcesz na wyjściu liczby w stylu 20,30 itp? Na razie inny powód do głowy mi nie przychodzi... Chciałbym pomóc, ale nie wiem, w czym...
Nie chce mi się całego kodu przerabiać, ale tak jak spojrzałem w nagłówki i...
void train(QList<double> inputs,QList<double> outputs);
Zmień od razu na:
void train(QList<bool> inputs,QList<bool> outputs);
Dlaczego? Pisałem już o tym kilka razy w tym wątku...

Offline shyha

  • Użytkownik
    • Shyha@Flickr

# Styczeń 16, 2009, 10:52:01
Sorki, nie chce mi się czytać całości, ale dobrym podejściem jest normalizacja danych wejściowych i denormalizacja wyniku. To powinno załatwić sprawę.

Offline Marcin Baszcz...

  • Użytkownik

# Styczeń 16, 2009, 11:46:08
Bez urazu Grr ale nie zamiana z Double na Bool jest tu rozwiazaniem... Tylko stosowna normalizacja / zamiana funkcji aktywacji umozliwi mi poradzenie sobie z tym problemem...
Probowalem to robic w prymitywny sposob (wszystkie wartosci przed treningiem dzielilem przez 100 by miescily sie w przedziale 0-1 a na wyjsciu je wymnazalem... niestety taka metoda nic nie daje...)

Niestety o ile o normalizacji troszke czytalem o denormalizacji jako takiej nie... jakie wystepuja techniki? Sa moze jakies przyklady w kodzie?
« Ostatnia zmiana: Styczeń 16, 2009, 11:59:20 wysłana przez Marcin Baszczewski »

Offline grrr

  • Użytkownik

# Styczeń 16, 2009, 12:28:29
Bez urazu Grr ale Ty mnie niepomorzesz ;)

Kolega mial pomysl z normalizacja - denormalizacja... znasz jakies konkretne techniki? Zalezy mi glownie na denormalizacji bo o tych pierwszych czytalem...
Nie unoś się honorem...
Jak przeprowadzić normalizację pisałem Ci już w pierwszym poście, proszę, szanuj mój czas... Opisałem też wtedy prostą denormalizację...
Cytuj
- skwantyzować wejście: przykładowo zrobić 10 neuronów wejściowych dla 10 różnych zakresów Y i odpowiedni ustawiać na 1, reszta na zero (w uproszczeniu)
(...)
-skwantyzować wyjście: zrobić n neuronów wyjściowych dla siły(to samo, kwantyzacja) i wybierać odpowiedni neuron wyjściowy (np o największej wartości); to samo dla kąta (neurony wyjściowe dla siły i dla kąta mogą być w tej samej sieci, oczywiście w tej samej warstwie, tylko to nie mogą być te same neurony, ale to oczywiste).
I wierz mi, normalizacja do wartości pośrednich jest błędna (poszukaj na tym forum, pisałem dlaczego)... Ale jak nie chcesz, to mogem nipomagywać....
Cytuj
niestety taka metoda nic nie daje...)
Tak jak mówiłem, pisałem już o tym na tym forum. Właśnie Boole JEST rozwiązaniem...Po prostu dzielisz tę liczbę na zakresy którym przydzielasz neurony, tworzysz N neuronów dla N zakresów i zapalasz odpowiedni... Ból wymusza takie rozwiązanie... I  doświadczenia: jeśli nie popełniłeś głupiego błędu we wzorkach, to możesz olać zmianę funkcji aktywacji, bo to nic Ci nie da, skup się na normalizacji.
« Ostatnia zmiana: Styczeń 16, 2009, 13:00:27 wysłana przez grrr »

Offline Marcin Baszcz...

  • Użytkownik

# Styczeń 16, 2009, 13:02:01
Grr: Zalezy mi na uzyciu MLP i podanego przezemnie algorytmu uczenia  (nie chce - nie moge) korzystac z innej techniki.
To co proponujesz(mozliwe ze poprawne) nie wyglada mi na cos wspolnego z BP. Pewnie wroce do tego problemu jeszcze Twoja metodyka (cos doczytam) ale w chwili obecnej musze sie ograniczyc do juz napisanych rzeczy...

Offline grrr

  • Użytkownik

# Styczeń 16, 2009, 13:20:05
Grr: Zalezy mi na uzyciu MLP i podanego przezemnie algorytmu uczenia  (nie chce - nie moge) korzystac z innej techniki.
To co proponujesz(mozliwe ze poprawne) nie wyglada mi na cos wspolnego z BP. Pewnie wroce do tego problemu jeszcze Twoja metodyka (cos doczytam) ale w chwili obecnej musze sie ograniczyc do juz napisanych rzeczy...
Wierz mi, mówimy o tym samym... Po prostu pamiętaj, że taka sieć działa na wartościach treningowych, które są wartościami LOGICZNYM. Mimo że w środku latają float, sieć uczy się wartościami logicznym. Zastanów się, dlaczego standardowym przykładem konieczności zastosowania sieci wielowarstwowej, a nie jednowarstwowej, jest XOR, a nie dajmy na to funkcja liniowa czy kwadratowa... Z drugiej strony jest na sieci tutorial, który rozpoznaje funkcje sin(ax) i cos(ax), gdzie a jest zmienne. Autor chwali się, że sieć mu się uczy (a uczy z tego co pamiętam wartościami pośrednimi), ale przemilcza fakt, że wyjście zależy tylko od pierwszego neuronu, resztę możemy śmiało olać...Po prostu znalazł przykład, który mu zadziałał...

Cały algorytm jest niezmienny, za to dane do uczenia MUSZ¡  być 0 lub 1. Chcesz, mogę Ci dowieść (bardziej matematycznie), że to jedyny sposób na SN i BP... Dzielenie prze 100 marnuje tylko Twój czas...
« Ostatnia zmiana: Styczeń 16, 2009, 13:48:10 wysłana przez grrr »

Offline głos

  • Użytkownik

# Styczeń 16, 2009, 14:56:06
Przeczytałem materiały z AGH, zupełnie przystępne wyjaśnienie sieci neuronowych
:) poeksperymentuję z tym trochę
ale do rzeczy:
wygląda że rozwiązaniem Twojego problemu jest znormalizowanie danych wejściowych wg
zasady opisanej na stronie AGH.
Chcąc znormalizować wartość wejściową X1 musisz (zakładamy że mamy do czynienia z wektorem wejściowym dwuwymiarowym czyli jest X1 i X2) podzielić ją (X1) przez pierwiastek z sumy kwadratów X1 i X2
tak samo postępujemy z wartością X2

W implementacji musisz też użyć biasu (np. równego 1) a funkcji przejścia np: sinh lub tgh i powinno zadziałać tj sieć powinna znaleźć rozwiązanie.

Offline Marcin Baszcz...

  • Użytkownik

# Styczeń 16, 2009, 15:33:39
Przemysle to i postaram sie w ten weekend z tym popracowac... Dziekuje wszystkim za uwage:)