2
\$\begingroup\$

A neural network is a structure of connections and nodes that takes input and generates an output. It can be "taught"(adjusting weights and biases of connections) from a teacher data set with acceptable outputs and inputs. See https://en.wikipedia.org/wiki/Neural_network for more details.

nn.c:

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>

#define INPUT_COUNT 3
#define HIDDEN_COUNT 3
#define OUTPUT_COUNT 1

#define LEARNING_RATE 0.15

typedef struct
{
  float in_Weights[INPUT_COUNT];
  float inBias;
  float value;
  float out_Weights[OUTPUT_COUNT];
}Neuron;

typedef struct
{
  float value;
}IO_Neuron;

typedef struct
{
  int sucess;
  IO_Neuron** training_in;
  IO_Neuron** training_out;
  int examples;
}TData;
//loads training data from a file like format below
/*
  #inputs,outputs,count
  input1,input2,input3 output1,output2
*/
TData tData(const char* filename)
{
  FILE* fp = fopen(filename,"r");
  int ins,outs,count;
  fscanf(fp,"#%i,%i,%i",&ins,&outs,&count);
  TData ret;
  ret.sucess = 1;
  if (ins != INPUT_COUNT || outs != OUTPUT_COUNT)
  {
    printf("%s\n","File will not fit into network!" );
    ret.sucess = 0;
    return ret;
  }


  int i,j;
  ret.training_in = malloc(sizeof(IO_Neuron*)*count);
  ret.training_out = malloc(sizeof(IO_Neuron*)*count);
  ret.examples = count;
  for (i =0; i< count;i++)
  {
    ret.training_in[i] = malloc(sizeof(IO_Neuron)*INPUT_COUNT);
  }

  for (i =0; i< count;i++)
  {
    ret.training_out[i] = malloc(sizeof(IO_Neuron)*OUTPUT_COUNT);
  }

  for (i =0 ; i < count;i++)
  {
    int inIndex = 0;
    int outIndex = 0;
    for (j =0; j < (INPUT_COUNT*2 - 1);j++)
    {
      if (j % 2 == 1)
      {
        fscanf(fp,",");
      }
      else
      {
        fscanf(fp,"%f",&ret.training_in[i][inIndex]);
        inIndex += 1;
      }
    }
    fscanf(fp," ");
    for (j =0; j < (OUTPUT_COUNT*2 - 1);j++)
    {
      if (j % 2 == 1)
      {
        fscanf(fp,",");
      }
      else
      {
        fscanf(fp,"%f",&ret.training_out[i][outIndex]);
        outIndex += 1;
      }
    }
  }
  printf("%s\n","File Read Sucessfully!" );
  return ret;

}

float genRandRange(float min,float max)
{
  if (min == max)
  return min;
  float scale = rand() / (float) RAND_MAX; /* [0, 1.0] */
  return min + scale * ( max - min );      /* [min, max] */
}

//activation function
float sigmoid(float x)
{
  return 1 / (1 + exp(-x));
}

float sigmoid_derivative(float x)
{
  return sigmoid(x) * (1 - sigmoid(x));
}

//computes weighted sum
float dot_summation(float* in,float* weights,int count)
{
  int i;
  float result = 0;
  for (i =0;i < count;i++)
  {
    result += in[i]*weights[i];
  }
  return result;
}

//these functions extract data into an easier to handle format
float* ioValues(IO_Neuron* hidden_layer)
{
  float* ret = malloc(sizeof(float)*INPUT_COUNT);
  int i;
  for (i =0; i < INPUT_COUNT;i++)
  {
    ret[i] = hidden_layer[i].value;
  }
  return ret;
}

float* values(Neuron* hidden_layer)
{
  float* ret = malloc(sizeof(float)*HIDDEN_COUNT);
  int i;
  for (i =0; i < HIDDEN_COUNT;i++)
  {
    ret[i] = hidden_layer[i].value;
  }
  return ret;
}

float* outWeights(Neuron* hidden_layer,int index)
{
  float* ret = malloc(sizeof(float)*HIDDEN_COUNT);
  int i;
  for (i =0; i < HIDDEN_COUNT;i++)
  {
    ret[i] = hidden_layer[i].out_Weights[index];
  }
  return ret;
}

//pass values through the neural network
void think(IO_Neuron* input_layer,Neuron* hidden_layer,IO_Neuron* output_layer)
{
  int i;
  float* io_values = ioValues(input_layer);
  for (i =0; i < HIDDEN_COUNT;i++)
  {
    hidden_layer[i].value = sigmoid(dot_summation(io_values,hidden_layer[i].in_Weights,INPUT_COUNT) + hidden_layer[i].inBias);
  }
  free(io_values);

  float* hidden_values = values(hidden_layer);
  for (i =0; i < OUTPUT_COUNT;i++)
  {
    float* out_weights = outWeights(hidden_layer,i);
    output_layer[i].value = sigmoid(dot_summation(hidden_values,out_weights,HIDDEN_COUNT));
    free(out_weights);
  }
  free(hidden_values);

}

//adjust the neural network's connection weights and biases based upon training data
void train(IO_Neuron* input_layer,Neuron* hidden_layer,IO_Neuron* output_layer,IO_Neuron** input_training,IO_Neuron** output_training,int training_samples,int iterations)
{
  int i,j,k,l;
  IO_Neuron recorded_outputs[training_samples][OUTPUT_COUNT];
  Neuron recorded_hidden[training_samples][HIDDEN_COUNT];
  float error_output[training_samples][OUTPUT_COUNT];//contains output node's delta
  float error_hidden[training_samples][HIDDEN_COUNT];
  for (i =0; i < iterations;i++)
  {
    for (j =0; j < training_samples;j++)
    {
      think(input_training[j],hidden_layer,output_layer);
      memcpy(recorded_outputs[j],output_layer,sizeof(IO_Neuron)*OUTPUT_COUNT);
      memcpy(recorded_hidden[j],hidden_layer,sizeof(Neuron)*HIDDEN_COUNT);
    }

    for (j =0; j < training_samples;j++)
    {
      for (k =0; k < OUTPUT_COUNT;k++)
      {
        error_output[j][k] = recorded_outputs[j][k].value*(1 - recorded_outputs[j][k].value) * (output_training[j][k].value - recorded_outputs[j][k].value);
      }
    }
    for (j =0; j < training_samples;j++)
    {
      for (k =0; k < HIDDEN_COUNT;k++)
      {
        float errorFactor = 0;
        for (l =0;l < OUTPUT_COUNT;l++)
        {
          errorFactor += (error_output[j][l]*hidden_layer[k].out_Weights[l]);
        }
        error_hidden[j][k] = recorded_hidden[j][k].value*(1 - recorded_hidden[j][k].value) * errorFactor;
      }
    }

    for (j =0; j < training_samples;j++)
    {
      for (k =0; k < HIDDEN_COUNT;k++)
      {//TODO update biases
        hidden_layer[k].inBias = hidden_layer[k].inBias + LEARNING_RATE *error_hidden[j][k];
        for (l = 0;l < INPUT_COUNT;l++)
        {
          hidden_layer[k].in_Weights[l] = hidden_layer[k].in_Weights[l] + (LEARNING_RATE*error_hidden[j][k]*input_training[j][l].value)/training_samples;
        }
      }
    }

    for (j =0; j < training_samples;j++)
    {
      for (k =0; k < HIDDEN_COUNT;k++)
      {
        for (l = 0;l < OUTPUT_COUNT;l++)
        {
          hidden_layer[k].out_Weights[l] = hidden_layer[k].out_Weights[l] + (LEARNING_RATE*error_output[j][k]*recorded_hidden[j][k].value)/training_samples;
        }
      }
    }



  }
}

//assign random weights to the neural network's connections
void randweights(Neuron* neurons)
{
  int i;
  for (i =0;i< HIDDEN_COUNT;i++)
  {
    neurons[i].in_Weights[0] = 2*genRandRange(0,1) - 1;
    neurons[i].in_Weights[1] = 2*genRandRange(0,1) - 1;
    neurons[i].in_Weights[2] = 2*genRandRange(0,1) - 1;

    neurons[i].out_Weights[2] = 2*genRandRange(0,1) - 1;

    neurons[i].inBias = 2*genRandRange(0,1) - 1;
  }
}

int main()
{
  srand(1);
  int i,j;
  //aquire training data
  TData t_data = tData("training.txt");
  if (!t_data.sucess)
  {
    return 0;
  }
  IO_Neuron** training_in = t_data.training_in;
  IO_Neuron** training_out = t_data.training_out;

  //allocate neural network
  IO_Neuron* input_layer = malloc(sizeof(IO_Neuron)*INPUT_COUNT);
  Neuron* hidden_layer = malloc(sizeof(Neuron)*HIDDEN_COUNT);
  IO_Neuron* output_layer = malloc(sizeof(IO_Neuron)*OUTPUT_COUNT);

  randweights(hidden_layer);

  //train with training data
  train(input_layer,hidden_layer,output_layer,training_in,training_out,t_data.examples,10000);

  //test out the learned pattern
  input_layer[0].value = 0;
  input_layer[1].value = 0;
  input_layer[2].value = 0;
  //generates the output
  think(input_layer,hidden_layer,output_layer);

  for (i =0; i < OUTPUT_COUNT;i++)
  {
    printf("%f\n",output_layer[i].value );
  }
  return 0;
}

Here is a sample file of training data that can be read by the program.

training.txt:

#3,1,7
1,0,1 0
1,0,0 0
1,1,0 1
1,1,1 1
0,1,0 1
0,1,1 1
0,0,1 0
0,0,0 0

In case you have not noticed, the pattern present within the data is that it simply outputs the first number of the input.

The neural network has 3 inputs,3 hidden nodes, and 1 output, these can be changed easily by modifying training data and the constants at the beginning of the code.

I know pure C is not often used for neural networks due to its lack of true object orientation and strict typing but I prefer its simplicity and readability.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.