6
\$\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\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.