Witam serdecznie. Jest to mój twór, który napisałem po obejrzeniu tego http://vimeo.com/19569529 .
Jednak sam nie jestem pewien czy napisałem to dobrze. Wydaje się, że sieć działa, ale nie do końca.
To znaczy, dla przykładu chciałem nauczyć ją potęgować. Uczę ją na wartościach z zakresu 0.1-0.6. W tym zakresie daje sobie rade, ale dla wartości > 0.6 wyniki są bardzo niepoprawne.
class Neuron;
typedef vector<Neuron> Layer;
// **************** class Neuron ****************
struct Connection
{
double weight;
double deltaWeight;
};
class Neuron
{
public:
Neuron(unsigned numConnections, unsigned index);
void setOutput(double o) { output = o; }
double getOutput(void) const { return output; }
void feedForward(const Layer &prevLayer);
void calcOutputGradients(double x);
void calcHiddenGradients(const Layer &nextLayer);
void updateInputWeights(Layer &prevLayer);
private:
unsigned myIndex;
vector<Connection> outputWeights;
double output;
double gradient;
static double eta;
static double alpha;
static double RandomNumber(double Min, double Max) { return ((double(rand()) / double(RAND_MAX)) * (Max - Min)) + Min; }
// Funckja aktywacji
static double transferFunction(double x) { return tanh(x); }
// Pochodna funkcji aktywacji
static double transferFunctionDerivative(double x) { return 1.0 - tanh(x) * tanh(x); }
};
// Wspolczynniki uczenia sieci
double Neuron::eta = 0.15;
double Neuron::alpha = 0.5;
Neuron::Neuron(unsigned numConnections, unsigned index)
{
// Tworzenie neuronu, polaczen z innymi neuronami,
// ustawienie losowych wag
for(unsigned i = 0; i < numConnections; i++)
{
outputWeights.push_back(Connection());
outputWeights.back().weight = RandomNumber(0.0, 1.0);
outputWeights.back().deltaWeight = 0.0;
}
myIndex = index;
}
void Neuron::feedForward(const Layer &prevLayer)
{
double sum = 0.0;
// Obliczanie wartosci wyjscia neuronu
// dla kazdego wejscia: wyjscie += wejscie * waga
for(unsigned n = 0; n < prevLayer.size(); n++) {
sum += prevLayer[n].getOutput() * prevLayer[n].outputWeights[myIndex].weight;
}
// Funkcja aktywacji
output = transferFunction(sum);
}
void Neuron::calcOutputGradients(double x)
{
// Obliczanie gradientu warstwy wyjsciowej
double delta = x - output;
gradient = delta * transferFunctionDerivative(x);
}
void Neuron::calcHiddenGradients(const Layer &nextLayer)
{
// Obliczanie gradientu warstwy ukrytej
double dow = 0.0;
for(unsigned n = 0; n < nextLayer.size(); n++) {
dow += outputWeights[n].weight * nextLayer[n].gradient;
}
gradient = dow * transferFunctionDerivative(output);
}
void Neuron::updateInputWeights(Layer &prevLayer)
{
// Aktualizacja wag na wejsciach neuronu (wyjsciach neuronow z poprzedniej warstwy)
for(unsigned n = 0; n < prevLayer.size(); n++)
{
Neuron &neuron = prevLayer[n];
double oldDelta = neuron.outputWeights[myIndex].deltaWeight;
// Obliczanie nowej wartosci wagi
double newDelta = eta * neuron.getOutput() * gradient + alpha * oldDelta;
neuron.outputWeights[myIndex].weight += newDelta;
neuron.outputWeights[myIndex].deltaWeight = newDelta;
}
}
// **************** class Net ****************
class Net
{
public:
Net(const vector<unsigned> &topology);
void feedForward(const vector<double> &input);
void backProp(const vector<double> &targetVal);
void getResult(vector<double> &result);
private:
vector<Layer> layers;
};
Net::Net(const vector<unsigned> &topology)
{
// Tworzenie sieci na podstawie danych w kontenerze topology
// Liczba warstw
unsigned numLayers = topology.size();
for(unsigned l = 0; l < numLayers; l++)
{
// Tworzenie nowej warstwy
layers.push_back(Layer());
// Jesli warstwa nie jest ostatnia warstwa, posiada wyjscia do innych warstw
unsigned numConns = l == topology.size() - 1 ? 0 : topology[l + 1];
// Tworzenie neuronow w warstwie
for(unsigned n = 0; n < topology[l]; n++) {
layers[l].push_back(Neuron(numConns, n));
}
}
}
void Net::feedForward(const vector<double> &input)
{
// Jesli ilosc danych wejsciowych nie rowna sie ilosci neuronow w warstwie wejsciowej- zakoncz dzialanie
if(input.size() != layers[0].size())
return;
// Ustawienie danych wejsciowych jako wartosc wyjsciowa neuronow w pierwszej warstwie
for(unsigned i = 0; i < input.size(); i++) {
layers[0][i].setOutput(input[i]);
}
// Dla kazdego neuronu w warstwach ukrytych i warstwie wyjsciowej wykonaj obliczenia
for(unsigned l = 1; l < layers.size(); l++) {
Layer &prevLayer = layers[l - 1];
for(unsigned n = 0; n < layers[l].size(); n++) {
layers[l][n].feedForward(prevLayer);
}
}
}
void Net::backProp(const vector<double> &targetVal)
{
// Korekcja bledu sieci
Layer &outputLayer = layers.back();
// Obliczanie gradientow warstwy wyjsciowej
for(unsigned n = 0; n < outputLayer.size(); n++) {
outputLayer[n].calcOutputGradients(targetVal[n]);
}
// Obliczanie gradientow warstw ukrytych
for(unsigned l = layers.size() - 2; l > 0; l--)
{
Layer &hidden = layers[l];
Layer &next = layers[l + 1];
for(unsigned n = 0; n < hidden.size(); n++) {
hidden[n].calcHiddenGradients(next);
}
}
// Aktualizacja wag w warstwach ukrytych i warstwie wyjsciowej
for(unsigned l = layers.size() - 1; l > 0; l--)
{
Layer &layer = layers[l];
Layer &prevLayer = layers[l - 1];
for(unsigned n = 0; n < layer.size(); n++) {
layer[n].updateInputWeights(prevLayer);
}
}
}
void Net::getResult(vector<double> &result)
{
Layer &output = layers.back();
for(unsigned n = 0; n < output.size(); n++) {
result.push_back(output[n].getOutput());
}
}