commit 8c7af7c4a65404b96689ffc1ebadcaf2954e6957 Author: hladu357 Date: Thu Jun 20 22:28:40 2024 +0200 first train diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..14d1577 --- /dev/null +++ b/main.cpp @@ -0,0 +1,168 @@ +#include +#include +// #include +// #include +// #include +#include + + +struct Perceptron { + std::vector _weights; + double _bias; + + // random weight between -1 and 1 but not 0 + Perceptron(size_t n) : _weights(n) { + for (size_t i = 0; i < n; ++i) { + _weights[i] = (rand() % 2000 - 1000) / 1000.0; + } + _bias = (rand() % 2000 - 1000) / 1000.0; + } + + float evaluate(const std::vector& inputs, float (*const activation)(float)) { + float sum = 0.0; + + for (size_t i = 0; i < inputs.size(); ++i) { + sum += inputs[i] * _weights[i]; + } + return activation(sum + _bias); + } +}; + +struct Layer { + std::vector _perceptrons; + float (*const _activation)(float); + float (*const _actDeriv)(float); + + Layer(size_t n, size_t i, float (*const activation)(float), float (*const actDeriv)(float)) + : _perceptrons(n, Perceptron(i)), _activation(activation), _actDeriv(actDeriv) { + } + + std::vector evaluate(const std::vector& inputs) { + std::vector outputs; + + for (size_t i = 0; i < _perceptrons.size(); ++i) { + outputs.push_back(_perceptrons[i].evaluate(inputs, _activation)); + } + // std::transform(std::execution::par_unseq, _perceptrons.begin(), _perceptrons.end(), outputs.begin(), [&](Perceptron& perceptron) { + // return perceptron.evaluate(inputs, _activation); + // }); + return outputs; + } + + +}; + + +struct NeuralNetwork { +private: + std::vector _layers; + +public: + NeuralNetwork() { + } + + void addLayer(size_t n, size_t i, float (*const activation)(float), float (*const actDeriv)(float)) { + _layers.emplace_back(n, i, activation, actDeriv); + } + + std::vector eval(const std::vector& inputs) { + std::vector outputs = inputs; + + for (int i = 0; i < _layers.size(); ++i) { + outputs = _layers[i].evaluate(outputs); + } + return outputs; + } + + void train(const std::vector>& inputs, const std::vector>& outputs, float learningRate, int epochs) { + for (int i = 0; i < epochs; ++i) { + float avgError = 0.0; + + for (int sample = 0; sample < inputs.size(); ++sample) { + std::vector> layerOutputs; + std::vector> layerInputs; + + layerInputs.push_back(inputs[sample]); + for (int j = 0; j < _layers.size(); ++j) { + layerOutputs.push_back(_layers[j].evaluate(layerInputs[j])); + layerInputs.push_back(layerOutputs[j]); + } + + std::vector error(outputs[sample].size()); + for (int j = 0; j < outputs[sample].size(); ++j) { + error[j] = outputs[sample][j] - layerOutputs.back()[j]; + } + + for (int j = _layers.size() - 1; j >= 0; --j) { + std::vector newError(_layers[j]._perceptrons.size()); + for (int k = 0; k < _layers[j]._perceptrons.size(); ++k) { + if (j == _layers.size() - 1) { + newError[k] = error[k] * _layers[j]._actDeriv(layerOutputs[j][k]); + } else { + newError[k] = 0.0; + for (int l = 0; l < _layers[j + 1]._perceptrons.size(); ++l) { + newError[k] += _layers[j + 1]._perceptrons[l]._weights[k] * error[l]; + } + newError[k] *= _layers[j]._actDeriv(layerOutputs[j][k]); + } + } + + for (int k = 0; k < _layers[j]._perceptrons.size(); ++k) { + for (int l = 0; l < layerInputs[j].size(); ++l) { + _layers[j]._perceptrons[k]._weights[l] += learningRate * newError[k] * layerInputs[j][l]; + } + _layers[j]._perceptrons[k]._bias += learningRate * newError[k]; + } + error = newError; + } + + for (int j = 0; j < error.size(); ++j) { + avgError += std::abs(error[j]); + } + + } + + avgError /= outputs[0].size() * outputs.size(); + if (i % 1000 == 0) + std::cout << "epoch: " << i << " error: " << avgError << "\n"; + } + } + +}; + +float sigmoid(float x) { + return 1.0 / (1.0 + std::expf(-x)); +} + +float sigmoidDerivative(float x) { + return x * (1 - x); +} + +int main() { + NeuralNetwork nn; + nn.addLayer(2, 2, sigmoid, sigmoidDerivative); + nn.addLayer(1, 1, sigmoid, sigmoidDerivative); + + std::vector> ref = { + {0, 0}, + {0, 1}, + {1, 0}, + {1, 1} + }; + + std::vector> out = { + {0}, + {1}, + {1}, + {0} + }; + + nn.train(ref, out, 0.1, 100000); + + std::cout << "0, 0: " << nn.eval({0, 0})[0] << "\n"; + std::cout << "0, 1: " << nn.eval({0, 1})[0] << "\n"; + std::cout << "1, 0: " << nn.eval({1, 0})[0] << "\n"; + std::cout << "1, 1: " << nn.eval({1, 1})[0] << "\n"; + + return 0; +} \ No newline at end of file