Single Neuron Perceptron (C++)
This is a basic implementation of a single neuron perceptron that can learn to act as AND, OR, and NAND gates in C++.
neuron.h
#ifndef __NEURON_H__
#define __NEURON_H__
#include <vector>
#include <tuple>
using namespace std;
class Neuron
{
public:
Neuron(int numberOfInputs);
~Neuron() {};
double forward(vector<bool> inputs);
bool forwardBinary(vector<bool> inputs);
bool forwardBinaryCheck(vector<bool> inputs, bool answer);
bool train(vector<bool> inputs, bool answers);
private:
vector<double> weights; // input weights
vector<Neuron> inputs; // for future backpropagation
vector<Neuron> outputs; // for futude backpropagation
double threshold; // trigger threshold
double learningRate; // how aggressive to tune the above values
};
#endif // __NEURON_H__
neuron.cpp
#include <iostream>
#include <math.h>
#include "neuron.h"
using namespace std;
Neuron::Neuron(int numberOfInputs)
{
for (int i = 0; i < numberOfInputs; i++)
weights.push_back(1.0 / numberOfInputs);
threshold = 0.9;
learningRate = 0.01;
}
double Neuron::forward(vector<bool> inputs)
{
if (inputs.size() == weights.size())
{
double sum = 0.0;
for (int i = 0; i < inputs.size(); i++)
sum += inputs[i] * weights[i];
return tanh(sum);
}
return -1;
}
bool Neuron::forwardBinary(vector<bool> inputs)
{
if (inputs.size() == weights.size())
{
if (forward(inputs) > threshold)
return true;
}
return false;
}
bool Neuron::forwardBinaryCheck(vector<bool> inputs, bool answer)
{
if (inputs.size() == weights.size())
{
bool result = forwardBinary(inputs);
if (result == answer)
return true;
}
return false;
}
bool Neuron::train(vector<bool> inputs, bool answer)
{
if (inputs.size() == weights.size())
{
bool result = forwardBinary(inputs);
for (int i = 0; i < inputs.size(); i++)
weights[i] += learningRate * ((double)answer - (double)result) * (double)inputs[i];
threshold -= learningRate * ((double)answer - (double)result);
/*for (int i = 0; i < weights.size(); i++)
cout << "Weight " << i << ": " << weights[i] << "\n";
cout << "Threshold: " << threshold << "\n";*/
if (result == answer)
return true;
}
return false;
}
main.cpp
#include "neuron.h"
#include "json.hpp"
#include <iostream>
#include <fstream>
#include <chrono>
#include <random>
using namespace std;
using json = nlohmann::json;
int main(int argc, char** argv)
{
ifstream file("Answers.json");
json answers;
file >> answers;
unsigned int seed = (unsigned int)chrono::system_clock::now().time_since_epoch().count();
default_random_engine engine(seed);
uniform_int_distribution<unsigned int> rand(0, 1);
int numInputs = 3;
unsigned int reps = 1000;
Neuron neuron(numInputs);
vector<bool> inputs;
inputs.reserve(numInputs);
vector<bool> results;
inputs.reserve(reps);
for (unsigned int i = 0; i < reps; i++)
{
for (int i = 0; i < numInputs; i++)
inputs.push_back(rand(engine) != 0);
bool answer = inputs[0];
if (numInputs > 0)
{
for (int i = 1; i < numInputs; i++)
//answer = answer || inputs[i]; // learn to be an OR gate
answer = answer && inputs[i]; // learn to be an AND gate
answer = !answer; // learn to be a NAND gate
}
results.push_back(neuron.train(inputs, answer));
inputs.clear();
}
for (auto& result : results)
cout << "result: " << result << "\n";
cout << "---------------------------------" << endl;
// OR gate
//cout << "correct: " << neuron.forwardBinaryCheck({ 0, 0, 0 }, false) << endl;
//cout << "correct: " << neuron.forwardBinaryCheck({ 0, 0, 1 }, true) << endl;
//cout << "correct: " << neuron.forwardBinaryCheck({ 0, 1, 0 }, true) << endl;
//cout << "correct: " << neuron.forwardBinaryCheck({ 0, 1, 1 }, true) << endl;
//cout << "correct: " << neuron.forwardBinaryCheck({ 1, 0, 0 }, true) << endl;
//cout << "correct: " << neuron.forwardBinaryCheck({ 1, 0, 1 }, true) << endl;
//cout << "correct: " << neuron.forwardBinaryCheck({ 1, 1, 0 }, true) << endl;
//cout << "correct: " << neuron.forwardBinaryCheck({ 1, 1, 1 }, true) << endl;
// AND gate
//cout << "correct: " << neuron.forwardBinaryCheck({ 0, 0, 0 }, false) << endl;
//cout << "correct: " << neuron.forwardBinaryCheck({ 0, 0, 1 }, false) << endl;
//cout << "correct: " << neuron.forwardBinaryCheck({ 0, 1, 0 }, false) << endl;
//cout << "correct: " << neuron.forwardBinaryCheck({ 0, 1, 1 }, false) << endl;
//cout << "correct: " << neuron.forwardBinaryCheck({ 1, 0, 0 }, false) << endl;
//cout << "correct: " << neuron.forwardBinaryCheck({ 1, 0, 1 }, false) << endl;
//cout << "correct: " << neuron.forwardBinaryCheck({ 1, 1, 0 }, false) << endl;
//cout << "correct: " << neuron.forwardBinaryCheck({ 1, 1, 1 }, true) << endl;
// NAND gate
cout << "check: " << neuron.forwardBinaryCheck({ 0, 0, 0 }, true) << endl;
cout << "check: " << neuron.forwardBinaryCheck({ 0, 0, 1 }, true) << endl;
cout << "check: " << neuron.forwardBinaryCheck({ 0, 1, 0 }, true) << endl;
cout << "check: " << neuron.forwardBinaryCheck({ 0, 1, 1 }, true) << endl;
cout << "check: " << neuron.forwardBinaryCheck({ 1, 0, 0 }, true) << endl;
cout << "check: " << neuron.forwardBinaryCheck({ 1, 0, 1 }, true) << endl;
cout << "check: " << neuron.forwardBinaryCheck({ 1, 1, 0 }, true) << endl;
cout << "check: " << neuron.forwardBinaryCheck({ 1, 1, 1 }, false) << endl;
return 0;
}