E
Ja, das stimmt. Einfaches NN ist easy.
constexpr int INPUT_SIZE = 4;
constexpr int OUTPUT_SIZE = 2;
constexpr double INPUT_3 = 0.0;
constexpr double INPUT_4 = 0.0;
constexpr int NUM_HIDDEN_LAYERS = 2;
constexpr int HIDDEN_LAYER_SIZE = 8;
constexpr bool RELU_FOR_HIDDEN = false;
constexpr bool SIGMOID_FOR_OUTPUT = false;
#pragma once
#include <vector>
class NeuralNetwork
{
public:
NeuralNetwork(int inputSize, int outputSize, int numHiddenLayers, int hiddenLayerSize);
std::vector<double> FeedForward(const std::vector<double>& inputs);
std::vector<double> GetWeights() const;
void SetWeights(const std::vector<double>& newWeights);
double Sigmoid(double x);
double ReLU(double x);
private:
std::vector<std::vector<double>> weights;
int inputSize;
int outputSize;
int numHiddenLayers;
int hiddenLayerSize;
void InitializeWeights();
};
#include <random>
#include <algorithm>
#include <vector>
#include <cassert>
#include <sstream>
#include "NeuralNetwork.h"
#include "DebugOutput.h"
#include "ConstantData.h"
NeuralNetwork::NeuralNetwork(int inputSize, int outputSize, int numHiddenLayers, int hiddenLayerSize)
: inputSize(inputSize), outputSize(outputSize), numHiddenLayers(numHiddenLayers), hiddenLayerSize(hiddenLayerSize)
{
InitializeWeights();
}
void NeuralNetwork::InitializeWeights()
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<> dis(-1.0, 1.0);
// Initialisiere die Gewichte für die Eingabe zur ersten versteckten Schicht
weights.push_back(std::vector<double>(inputSize * hiddenLayerSize));
std::generate(weights.back().begin(), weights.back().end(), [&]() { return dis(gen); });
// Initialisiere die Gewichte für die versteckten Schichten
for (int i = 0; i < numHiddenLayers - 1; ++i)
{
weights.push_back(std::vector<double>(hiddenLayerSize * hiddenLayerSize));
std::generate(weights.back().begin(), weights.back().end(), [&]() { return dis(gen); });
}
// Initialisiere die Gewichte für die letzte versteckte Schicht zur Ausgabeschicht
weights.push_back(std::vector<double>(hiddenLayerSize * outputSize));
std::generate(weights.back().begin(), weights.back().end(), [&]() { return dis(gen); });
}
std::vector<double> NeuralNetwork::FeedForward(const std::vector<double>& inputs)
{
assert(inputs.size() == inputSize);
std::vector<double> outputs(hiddenLayerSize, 0.0);
std::vector<double> currentInputs = inputs;
// Berechnung der versteckten Schichten
for (int l = 0; l < numHiddenLayers; ++l)
{
outputs.assign(hiddenLayerSize, 0.0);
for (int j = 0; j < hiddenLayerSize; ++j)
{
for (int i = 0; i < currentInputs.size(); ++i)
{
outputs[j] += currentInputs[i] * weights[l][j * currentInputs.size() + i];
}
if (RELU_FOR_HIDDEN)
{
outputs[j] = ReLU(outputs[j]); // ReLU Aktivierungsfunktion
}
}
currentInputs = outputs;
}
// Berechnung der Ausgabeschicht
outputs.assign(outputSize, 0.0);
for (int j = 0; j < outputSize; ++j)
{
for (int i = 0; i < currentInputs.size(); ++i)
{
outputs[j] += currentInputs[i] * weights.back()[j * currentInputs.size() + i];
std::ostringstream oss;
oss << "\noutput[j]: " << outputs[j];
DebugOutput(oss.str());
}
if (SIGMOID_FOR_OUTPUT)
{
outputs[j] = Sigmoid(outputs[j]) * 600 - 300; // Skalieren, um den Bereich [-300, 300] zu haben --> Käfer bewegen sich seltsam!
}
}
return outputs;
}
std::vector<double> NeuralNetwork::GetWeights() const
{
// Alle Gewichtsmatrizen in einen einzelnen Vektor zusammenführen
std::vector<double> allWeights;
for (const auto& layerWeights : weights)
{
allWeights.insert(allWeights.end(), layerWeights.begin(), layerWeights.end());
}
return allWeights;
}
void NeuralNetwork::SetWeights(const std::vector<double>& newWeights)
{
size_t offset = 0;
for (auto& layerWeights : weights)
{
std::copy(newWeights.begin() + offset, newWeights.begin() + offset + layerWeights.size(), layerWeights.begin());
offset += layerWeights.size();
}
}
double NeuralNetwork::Sigmoid(double x)
{
return 1.0 / (1.0 + exp(-x));
}
double NeuralNetwork::ReLU(double x)
{
return std::max(0.0, x);
}