4
0

feat: Create Neural network for XOR

This commit is contained in:
Louis Gallet 2024-09-27 18:00:01 +02:00
commit 82b65ae2a1
Signed by: lgallet
GPG Key ID: 84D3DF1528A84511
7 changed files with 698 additions and 0 deletions

297
.gitignore vendored Normal file
View File

@ -0,0 +1,297 @@
*.csv
# Created by https://www.toptal.com/developers/gitignore/api/c,cmake,jetbrains,clion,macos
# Edit at https://www.toptal.com/developers/gitignore?templates=c,cmake,jetbrains,clion,macos
### C ###
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
### CLion ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### CLion Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
# https://plugins.jetbrains.com/plugin/7973-sonarlint
.idea/**/sonarlint/
# SonarQube Plugin
# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
.idea/**/sonarIssues.xml
# Markdown Navigator plugin
# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
.idea/**/markdown-navigator.xml
.idea/**/markdown-navigator-enh.xml
.idea/**/markdown-navigator/
# Cache file creation bug
# See https://youtrack.jetbrains.com/issue/JBR-2257
.idea/$CACHE_FILE$
# CodeStream plugin
# https://plugins.jetbrains.com/plugin/12206-codestream
.idea/codestream.xml
# Azure Toolkit for IntelliJ plugin
# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij
.idea/**/azureSettings.xml
### CMake ###
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
### CMake Patch ###
# External projects
*-prefix/
### JetBrains ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
# AWS User-specific
# Generated files
# Sensitive or high-churn files
# Gradle
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
# Mongo Explorer plugin
# File-based project format
# IntelliJ
# mpeltonen/sbt-idea plugin
# JIRA plugin
# Cursive Clojure plugin
# SonarLint plugin
# Crashlytics plugin (for Android Studio and IntelliJ)
# Editor-based Rest Client
# Android studio 3.1+ serialized cache file
### JetBrains Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
# https://plugins.jetbrains.com/plugin/7973-sonarlint
# SonarQube Plugin
# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
# Markdown Navigator plugin
# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
# Cache file creation bug
# See https://youtrack.jetbrains.com/issue/JBR-2257
# CodeStream plugin
# https://plugins.jetbrains.com/plugin/12206-codestream
# Azure Toolkit for IntelliJ plugin
# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### macOS Patch ###
# iCloud generated files
*.icloud
# End of https://www.toptal.com/developers/gitignore/api/c,cmake,jetbrains,clion,macos

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Louis Gallet
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1
README.md Normal file
View File

@ -0,0 +1 @@
# XOR Neural Network

237
main.c Normal file
View File

@ -0,0 +1,237 @@
//
// Created by Louis Gallet on 24/09/2024.
//
#include "main.h"
#include <stdio.h>
double sigmoid(double x)
{
if(x > 20) return 1.0;
if(x < -20) return 0.0;
double z = exp(-x);
return 1.0 / (1.0 + z);
}
double sigmoid_derivative(double x)
{
return x * (1.0 - x);
}
double init_weights()
{
double placeholder = ((double) rand()) / ((double) RAND_MAX);
return placeholder;
}
void shuffle(int *array, size_t n){
if(n > 1) {
size_t i;
for(i = 0; i < n - 1; i++) {
size_t j = i + rand() / (RAND_MAX / (n - i) + 1);
int t = array[j];
array[j] = array[i];
array[i] = t;
}
}
}
void backup_weights(char filename[], double hiddenWeights[NUM_INPUTS][NUM_HIDDEN], double hiddenLayerBias[NUM_HIDDEN],
double outputWeights[NUM_HIDDEN][NUM_OUTPUTS], double outputLayerBias[NUM_OUTPUTS])
{
FILE *fpt;
fpt = fopen(filename, "w+");
for(int j = 0; j < NUM_HIDDEN; j++){
for(int k = 0; k < NUM_INPUTS; k++){
fprintf(fpt, "%f,", hiddenWeights[k][j]);
}
}
printf("Final Hidden Biases: \n");
for(int j = 0; j < NUM_HIDDEN; j++){
fprintf(fpt, "%f,", hiddenLayerBias[j]);
}
printf("Final Output Weights: \n");
for(int j = 0; j < NUM_OUTPUTS; j++){
for(int k = 0; k < NUM_HIDDEN; k++){
fprintf(fpt, "%f,", outputWeights[k][j]);
}
}
printf("Final Output Biases: \n");
for(int j = 0; j < NUM_OUTPUTS; j++){
fprintf(fpt, "%f,", outputLayerBias[j]);
}
}
int main(){
//learning rate
const double lr = 0.1f;
double hiddenLayer[NUM_HIDDEN];
double outputLayer[NUM_OUTPUTS];
double hiddenLayerBias[NUM_HIDDEN];
double outputLayerBias[NUM_OUTPUTS];
double hiddenWeights[NUM_INPUTS][NUM_HIDDEN];
double outputWeights[NUM_HIDDEN][NUM_OUTPUTS];
double trainingInputs[NUM_TRAINING_SETS][NUM_INPUTS] = {{0.0f, 0.0f}, {1.0f, 0.0f},
{0.0f, 1.0f}, {1.0f, 1.0f}};
double trainingOutputs[NUM_TRAINING_SETS][NUM_OUTPUTS] = {{0.0f}, {1.0f}, {1.0f}, {0.0f}};
for(int i = 0; i < NUM_INPUTS; i++){
for(int j = 0; j < NUM_HIDDEN; j++){
hiddenWeights[i][j] = init_weights();
}
}
for(int i = 0; i < NUM_HIDDEN; i++){
for(int j = 0; j < NUM_OUTPUTS; j++){
outputWeights[i][j] = init_weights();
}
}
for(int i = 0; i < NUM_OUTPUTS; i++){
outputLayerBias[i] = init_weights();
}
for(int i = 0; i < NUM_HIDDEN; i++){
hiddenLayerBias[i] = init_weights();
}
int trainingSetOrder[] = {0,1,2,3};
int numEpochs = 100000;
//training loop
for(int epoch = 0; epoch < numEpochs; epoch++){
shuffle(trainingSetOrder, NUM_TRAINING_SETS);
for(int x = 0; x < NUM_TRAINING_SETS; x++){
int i = trainingSetOrder[x];
//forward pass
//compute hidden layer activation
for(int j = 0; j < NUM_HIDDEN; j++){
double activation = hiddenLayerBias[j];
for(int k = 0; k < NUM_INPUTS; k++){
activation += trainingInputs[i][k] * hiddenWeights[k][j];
}
//printf("Hidden layer activation[%d] = %f\n", j, activation);
hiddenLayer[j] = sigmoid(activation);
if(isnan(hiddenLayer[j])){
//printf("NaN in hidden layer at index %d\n", j);
}
}
//compute output layer activation
for(int j = 0; j < NUM_OUTPUTS; j++){
double activation = outputLayerBias[j];
for(int k = 0; k < NUM_HIDDEN; k++){
activation += hiddenLayer[k] * outputWeights[k][j];
}
//printf("Output layer activation[%d] = %f\n", j, activation);
outputLayer[j] = sigmoid(activation);
if(isnan(outputLayer[j])){
//printf("NaN in output layer at index %d\n", j);
}
}
printf("Input: %g %g Output: %g Predicted Output: %g\n", trainingInputs[i][0], trainingInputs[i][1], outputLayer[0], trainingOutputs[i][0]);
//Back prop
//compute change in output weights
double deltaOutput[NUM_OUTPUTS];
for(int j = 0; j < NUM_OUTPUTS; j++){
double error = (trainingOutputs[i][j] - outputLayer[j]);
deltaOutput[j] = error * sigmoid_derivative(outputLayer[j]);
if(isnan(deltaOutput[j])){
//printf("NaN in deltaOutput at index %d\n", j);
}
}
//compute change in hidden weights
double deltaHidden[NUM_HIDDEN];
for(int j = 0; j < NUM_HIDDEN; j++){
double error = 0.0f;
for(int k = 0; k < NUM_OUTPUTS; k++){
error += deltaOutput[k] * outputWeights[j][k];
}
deltaHidden[j] = error * sigmoid_derivative(hiddenLayer[j]);
if(isnan(deltaHidden[j])){
//printf("NaN in deltaHidden at index %d\n", j);
}
}
//apply change in output weights
for(int j = 0; j < NUM_OUTPUTS; j++){
outputLayerBias[j] += deltaOutput[j] * lr;
for(int k = 0; k < NUM_HIDDEN; k++){
outputWeights[k][j] += hiddenLayer[k] * deltaOutput[j] * lr;
if(isnan(outputWeights[k][j])){
//printf("NaN in outputWeights at [%d][%d]\n", k, j);
}
}
}
for(int j = 0; j < NUM_HIDDEN; j++){
hiddenLayerBias[j] += deltaHidden[j] * lr;
for(int k = 0; k < NUM_INPUTS; k++){
hiddenWeights[k][j] += trainingInputs[i][k] * deltaHidden[j] * lr;
if(isnan(hiddenWeights[k][j])){
//printf("NaN in hiddenWeights at [%d][%d]\n", k, j);
}
}
}
}
}
//print final weights after training
printf("\nFinal Hidden Weights: \n");
for(int j = 0; j < NUM_HIDDEN; j++){
for(int k = 0; k < NUM_INPUTS; k++){
printf("%f ", hiddenWeights[k][j]);
}
}
printf("\n");
printf("Final Hidden Biases: \n");
for(int j = 0; j < NUM_HIDDEN; j++){
printf("%f ", hiddenLayerBias[j]);
}
printf("\n\n\n");
printf("Final Output Weights: \n");
for(int j = 0; j < NUM_OUTPUTS; j++){
for(int k = 0; k < NUM_HIDDEN; k++){
printf("%f ", outputWeights[k][j]);
}
}
printf("\n");
printf("Final Output Biases: \n");
for(int j = 0; j < NUM_OUTPUTS; j++){
printf("%f ", outputLayerBias[j]);
}
backup_weights("weights.csv", hiddenWeights, hiddenLayerBias, outputWeights, outputLayerBias);
return 0;
}

26
main.h Normal file
View File

@ -0,0 +1,26 @@
//
// Created by Louis Gallet on 24/09/2024.
//
#ifndef MAIN_H
#define MAIN_H
#include <sys/_types/_size_t.h>
#include <math.h>
#include <stdlib.h>
#define NUM_INPUTS 2
#define NUM_HIDDEN 2
#define NUM_OUTPUTS 1
#define NUM_TRAINING_SETS 4
double sigmoid(double x);
double sigmoid_derivative(double x);
double init_weights();
void shuffle(int *array, size_t n);
void backup_weights(char filename[], double hiddenWeights[NUM_INPUTS][NUM_HIDDEN], double hiddenLayerBias[NUM_HIDDEN],
double outputWeights[NUM_HIDDEN][NUM_OUTPUTS], double outputLayerBias[NUM_OUTPUTS]);
int main();
#endif //MAIN_H

98
use.c Normal file
View File

@ -0,0 +1,98 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <err.h>
// Function prototypes
void getvalues(char filename[], double **HiddenWeights, double *HiddenBiases, double **FinalOutputWeights, double *FinalOutputBiases);
double sigmoid(double x);
double sigmoid_derivative(double x);
int main() {
// Allocate memory for weights and biases
double **HiddenWeights = (double **)malloc(2 * sizeof(double *));
for (int i = 0; i < 2; i++) {
HiddenWeights[i] = (double *)malloc(2 * sizeof(double));
}
double *HiddenBiases = (double *)malloc(2 * sizeof(double));
double **FinalOutputWeights = (double **)malloc(2 * sizeof(double *));
for (int i = 0; i < 2; i++) {
FinalOutputWeights[i] = (double *)malloc(1 * sizeof(double));
}
double *FinalOutputBiases = (double *)malloc(1 * sizeof(double));
// Load weights and biases from CSV file
getvalues("weights.csv", HiddenWeights, HiddenBiases, FinalOutputWeights, FinalOutputBiases);
// Prompt user for input values
double input1, input2;
printf("Enter two values to compute XOR: ");
scanf("%lf %lf", &input1, &input2);
// Perform forward pass
double hiddenLayer[2];
double outputLayer[1];
// Compute hidden layer activation
for (int j = 0; j < 2; j++) {
double activation = HiddenBiases[j];
activation += input1 * HiddenWeights[0][j];
activation += input2 * HiddenWeights[1][j];
hiddenLayer[j] = sigmoid(activation);
}
// Compute output layer activation
for (int j = 0; j < 1; j++) {
double activation = FinalOutputBiases[j];
activation += hiddenLayer[0] * FinalOutputWeights[0][j];
activation += hiddenLayer[1] * FinalOutputWeights[1][j];
outputLayer[j] = sigmoid(activation);
}
// Print the result
printf("Input: %g %g Predicted Output: %g Output round: %g\n", input1, input2, outputLayer[0], round(outputLayer[0]));
// Free allocated memory
for (int i = 0; i < 2; i++) {
free(HiddenWeights[i]);
free(FinalOutputWeights[i]);
}
free(HiddenWeights);
free(HiddenBiases);
free(FinalOutputWeights);
free(FinalOutputBiases);
return 0;
}
void getvalues(char filename[], double **HiddenWeights, double *HiddenBiases, double **FinalOutputWeights, double *FinalOutputBiases) {
FILE *fp = fopen(filename, "r");
if (fp == NULL) {
errx(EXIT_FAILURE, "Could not open file %s", filename);
}
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
fscanf(fp, "%lf,", &HiddenWeights[i][j]);
}
}
for (int i = 0; i < 2; i++) {
fscanf(fp, "%lf,", &HiddenBiases[i]);
}
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 1; j++) {
fscanf(fp, "%lf,", &FinalOutputWeights[i][j]);
}
}
for (int i = 0; i < 1; i++) {
fscanf(fp, "%lf,", &FinalOutputBiases[i]);
}
fclose(fp);
}
double sigmoid(double x) {
return 1.0 / (1.0 + exp(-x));
}
double sigmoid_derivative(double x) {
return x * (1.0 - x);
}

18
use.h Normal file
View File

@ -0,0 +1,18 @@
//
// Created by Louis Gallet on 27/09/2024.
//
#ifndef USE_H
#define USE_H
#define NUM_INPUTS 2
#define NUM_HIDDEN 2
#define NUM_OUTPUTS 1
#define NUM_TRAINING_SETS 4
void getvalues(char filename[], double **HiddenWeights, double *HiddenBiases, double **FinalOutputWeights, double *FinalOutputBiases);
int main();
#endif //USE_H