Advertisement
Guest User

A Simple NN

a guest
May 15th, 2024
498
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 12.77 KB | Source Code | 0 0
  1. // A simple neural network framework
  2.  
  3. // TODO: Add back propagation
  4.  
  5. // Basic assumptions:
  6. //  1. Fully connected NN only.
  7. //  2. Each neuron in the output layer has only one output, so output layer has as many neurons as the outputs.
  8.  
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <math.h>
  12. #include <assert.h>
  13.  
  14.  
  15.  
  16. float ti [] = {
  17. 0, 1, 2, 3, 4, 5
  18. };
  19.  
  20. float to [] = {
  21. 0, 2, 4, 6, 8, 10
  22. };
  23.  
  24.  
  25.  
  26. typedef struct {
  27.     int numRows;
  28.     int numCols;
  29.     float *elements;
  30. } Matrix;
  31.  
  32. #define MAT_AT(m, i, j) ((m)->elements[(i)*(m)->numCols+(j)])
  33.  
  34.  
  35.  
  36. typedef struct {
  37.     int numInputs; // Number of inputs to the NN
  38.     int numOutputs; // Number of output neurons in output layer
  39.     int numHiddenLayers; // Number of hidden layers. Can be 0
  40.     int *pnWidth; // Width of each hidden layer saved as a pointer to a list of width. numHiddenLayers is the number of layers.
  41.  
  42.     Matrix matInputs; // Single value for each input i.e. x1, x2.. etc. Can be represented by a single row [x1, x2...]
  43.     Matrix *matLayerWeights; // Weights of layers.
  44.     Matrix *matLayerBiases; // Biases of layers
  45.     Matrix *matLayerOutputs; // Intermediate values from Layers after passing in values from previous layers
  46. } NN;
  47.  
  48.  
  49. void matrix_print(Matrix *m);
  50.  
  51. void matrix_fill_zero(Matrix *m)
  52. {
  53.     for(int i=0; i < m->numRows; i++) {
  54.         for(int j=0; j < m->numCols; ++j){
  55.             int k = i*m->numCols + j;
  56.                 m->elements[k] = 0;
  57.         }
  58.     }
  59. }
  60.  
  61.  
  62. Matrix *matrix_create(int r, int c)
  63. {
  64.     Matrix *m = NULL;
  65.  
  66.     m = malloc(sizeof(m));
  67.     assert(m != NULL);
  68.  
  69.     m->numRows = r;
  70.     m->numCols = c;
  71.     m->elements = NULL;
  72.     m->elements = malloc(sizeof(m->elements)*r*c);
  73.     assert(m->elements != NULL);
  74.  
  75.     matrix_fill_zero(m);
  76.  
  77.     return m;
  78. }
  79.  
  80.  
  81.  
  82. void matrix_dot(Matrix *dst, Matrix *a, Matrix *b)
  83. {
  84.     float n, sum;
  85.  
  86.     assert(a->numCols == b->numRows);
  87.     assert(dst->numRows == a->numRows);
  88.     assert(dst->numCols == b->numCols);
  89.  
  90.     n = a->numCols;
  91.     for(int i=0; i<a->numRows; ++i) {
  92.         for(int j=0; j<b->numCols; ++j) {
  93.             sum = 0;
  94.             for(int k=0; k<n; k++) {
  95.                 sum += MAT_AT(a, i, k) * MAT_AT(b, k, j);
  96.             }
  97.             MAT_AT(dst, i, j) = sum;
  98.         }
  99.     }
  100. }
  101.  
  102.  
  103. void matrix_add(Matrix *dst, Matrix *a, Matrix *b)
  104. {
  105.     float sum;
  106.  
  107.     assert((a->numCols == b->numCols) && (b->numCols == dst->numCols));
  108.     assert((a->numRows == b->numRows) && (b->numRows == dst->numRows));
  109.  
  110.     for(int i=0; i<a->numRows; ++i) {
  111.         for(int j=0; j<a->numCols; ++j) {
  112.             sum = MAT_AT(a, i, j) + MAT_AT(b, i, j);
  113.             MAT_AT(dst, i, j) = sum;
  114.         }
  115.     }
  116. }
  117.  
  118.  
  119.  
  120. void matrix_print(Matrix *m)
  121. {
  122.     printf("matrix_print: Rows: %d Cols: %d\n", m->numRows, m->numCols);
  123.     for(int i=0; i<m->numRows; i++) {
  124.         for(int j=0; j<m->numCols; ++j){
  125.             printf("%f ", MAT_AT(m, i, j));
  126.         }
  127.         printf("\n");
  128.     }
  129. }
  130.  
  131.  
  132.  
  133. void matrix_randomize(Matrix *m, float low, float high)
  134. {
  135.     for(int i=0; i<m->numRows; i++) {
  136.         for(int j=0; j<m->numCols; j++) {
  137.             MAT_AT(m, i, j) = rand()/(float)RAND_MAX;
  138.         }
  139.     }
  140. }
  141.  
  142.  
  143.  
  144. NN NN_create(int inputs, int outputs, int layers, int *width)
  145. {
  146.     NN nn, *tnn;
  147.     Matrix  *mw, *mb, *mo;
  148.  
  149.     nn.numInputs = inputs;
  150.     nn.numOutputs = outputs;
  151.     nn.numHiddenLayers = layers;
  152.     nn.pnWidth = width;
  153.  
  154.     if (layers < 0) { // Can't have negative number of layers
  155.         assert(layers >= 0);
  156.         return nn;
  157.  
  158.     } else if (layers == 0) { // No hidden layer. Only input and output layer. So basically only as many neurons as the output layer.
  159.         nn.matInputs = *matrix_create(1, inputs);
  160.  
  161.         // As there are no hidden layers, there is only going to be one layer i.e. the output layer
  162.         mw = matrix_create(inputs, outputs);
  163.         mb = matrix_create(1, outputs);
  164.         mo = matrix_create(1, outputs);
  165.  
  166.         nn.matLayerWeights = mw;
  167.         nn.matLayerBiases = mb;
  168.         nn.matLayerOutputs = mo;
  169.         nn.pnWidth = NULL; //Width should be NULL for neural networks with no hidden layers
  170.  
  171.     } else { // Hidden layers are 1 or more than 1.
  172.         Matrix *m;
  173.         m = matrix_create(1, inputs);
  174.         nn.matInputs = *m;
  175.  
  176.         if (layers == 1) {
  177.             // Layer 1 is hidden and the next is the output layer. So 2 layers.
  178.             nn.matLayerWeights = malloc(sizeof(nn.matLayerWeights)*(layers + outputs));
  179.             nn.matLayerBiases = malloc(sizeof(nn.matLayerBiases)*(layers + outputs));
  180.             nn.matLayerOutputs = malloc(sizeof(nn.matLayerOutputs)*(layers + outputs));
  181.  
  182.             mw = matrix_create(inputs, *width);
  183.             mb = matrix_create(1, *width);
  184.             mo = matrix_create(1, *width);
  185.             nn.matLayerWeights[0] = *mw;
  186.             nn.matLayerBiases[0] = *mb;
  187.             nn.matLayerOutputs[0] =  *mo;
  188.  
  189.             mw = matrix_create(*width, outputs);
  190.             mb = matrix_create(1, outputs);
  191.             mo = matrix_create(1, outputs);
  192.             nn.matLayerWeights[1] = *mw;
  193.             nn.matLayerBiases[1] = *mb;
  194.             nn.matLayerOutputs[1] =  *mo;
  195.         } else {
  196.  
  197.             nn.matLayerWeights = malloc(sizeof(nn.matLayerWeights)*(layers + 10));
  198.             nn.matLayerBiases = malloc(sizeof(nn.matLayerBiases)*(layers + 10));
  199.             nn.matLayerOutputs = malloc(sizeof(nn.matLayerOutputs)*(layers + 10));
  200.  
  201.  
  202.                 // First hidden layer's number of rows is equal to the number of columns of input layer
  203.                 mw = matrix_create(inputs, *width);
  204.                 mb = matrix_create(1, *width);
  205.                 mo = matrix_create(1, *width);
  206.  
  207.                 nn.matLayerWeights[0] = *mw;
  208.                 nn.matLayerBiases[0] = *mb;
  209.                 nn.matLayerOutputs[0] =  *mo;
  210.  
  211.             for (int i=1; i<layers; i++) {
  212.                 mw = matrix_create(width[i-1], width[i]);
  213.                 mb = matrix_create(1, width[i]);
  214.                 mo = matrix_create(1, width[i]);
  215.  
  216.                 nn.matLayerWeights[i] = *mw;
  217.                 nn.matLayerBiases[i] = *mb;
  218.                 nn.matLayerOutputs[i] =  *mo;
  219.  
  220.             }
  221.  
  222.             // Now the last layer i.e. output layer
  223.             mw = matrix_create(width[layers-1], outputs);
  224.             mb = matrix_create(1, outputs);
  225.             mo = matrix_create(1, outputs);
  226.  
  227.             nn.matLayerWeights[layers] = *mw;
  228.             nn.matLayerBiases[layers] = *mb;
  229.             nn.matLayerOutputs[layers] = *mo;
  230.         }
  231.     }
  232.     return nn;
  233. }
  234.  
  235.  
  236.  
  237. void NN_print(NN nn)
  238. {
  239.     printf("NN_Print: Printing Neural network\nInputs: %d Outputs: %d Layers: %d Width: ", nn.numInputs, nn.numOutputs, nn.numHiddenLayers);
  240.  
  241.     if (nn.numHiddenLayers == 0)
  242.         printf("0\n");
  243.     else
  244.         printf("%d \n", nn.numHiddenLayers);
  245.  
  246.     printf("NN_Print: Input layer: ****\n");
  247.     matrix_print(&nn.matInputs);
  248.  
  249.     for(int i=0; i<nn.numHiddenLayers+1; ++i) {
  250.         printf("NN_Print: Printing layer weights: %d***\n", i);
  251.         matrix_print(&nn.matLayerWeights[i]);
  252.  
  253.         printf("NN_Print: Printing biases: %d***\n", i);
  254.         matrix_print(&nn.matLayerBiases[i]);
  255.  
  256.         printf("NN_Print: Printing intermediate: %d***\n", i);
  257.         matrix_print(&nn.matLayerOutputs[i]);
  258.  
  259.         printf("****************************\n");
  260.     }
  261. }
  262.  
  263.  
  264.  
  265. void NN_randomize(NN nn)
  266. {
  267.     // We randomize weights and biases of the hidden and output layers
  268.     for(int i=0; i<nn.numHiddenLayers+1; i++) {
  269.         matrix_randomize(&nn.matLayerWeights[i], 0, 1);
  270.         matrix_randomize(&nn.matLayerBiases[i], 0, 1);
  271.     }
  272. }
  273.  
  274.  
  275. // Function to feed values to NN. As many inputs as numInputs at a time.
  276. void NN_feed(NN nn, float *inputs, float inputLen)
  277. {
  278.     assert(inputLen == nn.numInputs);
  279.  
  280.     Matrix *m = &nn.matInputs;
  281.  
  282.     for(int i=0; i<inputLen; i++) {
  283.         MAT_AT(m, 0, i) = inputs[i];
  284.     }
  285.  
  286.     if (nn.numHiddenLayers == 0) {
  287.  
  288.         // No hidden layers. Output can be directly computed.
  289.         matrix_dot(nn.matLayerOutputs, &nn.matInputs, nn.matLayerWeights);
  290.         matrix_add(nn.matLayerOutputs, nn.matLayerOutputs, nn.matLayerBiases);
  291.  
  292.     } else if (nn.numHiddenLayers == 1) { // 1 Hidden Layer
  293.  
  294.         matrix_dot(&nn.matLayerOutputs[0], &nn.matInputs, &nn.matLayerWeights[0]);
  295.         matrix_add(&nn.matLayerOutputs[0], &nn.matLayerOutputs[0], &nn.matLayerBiases[0]);
  296.  
  297.         matrix_dot(&nn.matLayerOutputs[1], &nn.matLayerOutputs[0], &nn.matLayerWeights[1]);
  298.         matrix_add(&nn.matLayerOutputs[1], &nn.matLayerOutputs[1], &nn.matLayerBiases[1]);
  299.  
  300.     } else { // More than 1 Hidden Layers
  301.  
  302.         matrix_dot(&nn.matLayerOutputs[0], &nn.matInputs, &nn.matLayerWeights[0]);
  303.         matrix_add(&nn.matLayerOutputs[0], &nn.matLayerOutputs[0], &nn.matLayerBiases[0]);
  304.  
  305.         for(int i=1; i<nn.numHiddenLayers + 1; i++) {
  306.             matrix_dot(&nn.matLayerOutputs[i], &nn.matLayerOutputs[i-1], &nn.matLayerWeights[i]);
  307.             matrix_add(&nn.matLayerOutputs[i], &nn.matLayerOutputs[i], &nn.matLayerBiases[i]);
  308.         }
  309.     }
  310. }
  311.  
  312.  
  313.  
  314. float NN_cost(NN nn)
  315. {
  316.     // Calculate the cost of NN
  317.     // Input all the rows of the input data and compare with output
  318.  
  319.     int n = sizeof(ti)/sizeof(ti[0]);
  320.     float cost = 0.0f, y = 0.0f;
  321.  
  322.  
  323.     for (int i=0; i<n; i++) {
  324.         float d = 0.0f;
  325.         NN_feed(nn, &ti[i], 1);
  326.         for(int j=0; j<nn.numOutputs; j++) {
  327.             y = MAT_AT(&nn.matLayerOutputs[nn.numHiddenLayers], 0, j);
  328.             d = to[i*nn.numOutputs+j] - y;
  329.             cost += d*d;
  330.         }
  331.     }
  332.  
  333.     return cost/n;
  334. }
  335.  
  336.  
  337.  
  338. void NN_finite_difference(NN nn, NN g, float eps)
  339. {
  340.     // Here we calculate the difference in costs of original NN and changing one weight by epsilon.
  341.     // nn is the original neural network, g is the neural network that contains the finite differences
  342.     // Later we'll use NN_learn_with_rate to make changes to the original neural network
  343.  
  344.     // We'll cycle through the layers to the output layer and change one weight at a time and calculate the cost
  345.     // We have to do both weights and biases
  346.  
  347.     float saved = 0;
  348.     float cost = NN_cost(nn); // Save this cost as this will be required later
  349.     for(int i=0; i<nn.numHiddenLayers+1; i++) {
  350.  
  351.         // First for weights
  352.         for(int j=0; j<nn.matLayerWeights[i].numRows; j++) {
  353.             for(int k=0; k<nn.matLayerWeights[i].numRows; k++) {
  354.                 saved = MAT_AT(&nn.matLayerWeights[i], j, k);
  355.                 MAT_AT(&nn.matLayerWeights[i], j, k) += eps;
  356.                 MAT_AT(&g.matLayerWeights[i], j, k) = (NN_cost(nn) - cost)/eps;
  357.                 MAT_AT(&nn.matLayerWeights[i], j, k) = saved;
  358.             }
  359.         }
  360.  
  361.         // Now for biases
  362.         for(int j=0; j<nn.matLayerBiases[i].numRows; j++) {
  363.             for(int k=0; k<nn.matLayerBiases[i].numRows; k++) {
  364.                 saved = MAT_AT(&nn.matLayerBiases[i], j, k);
  365.                 MAT_AT(&nn.matLayerBiases[i], j, k) += eps;
  366.                 MAT_AT(&g.matLayerBiases[i], j, k) = (NN_cost(nn) - cost)/eps;
  367.                 MAT_AT(&nn.matLayerBiases[i], j, k) = saved;
  368.             }
  369.         }
  370.     }
  371. }
  372.  
  373.  
  374.  
  375. void NN_learn_with_rate(NN nn, NN g, float rate)
  376. {
  377.     for(int i=0; i<nn.numHiddenLayers+1; i++) {
  378.  
  379.         // First for weights
  380.         for(int j=0; j<nn.matLayerWeights[i].numRows; j++) {
  381.             for(int k=0; k<nn.matLayerWeights[i].numRows; k++) {
  382.                 MAT_AT(&nn.matLayerWeights[i], j, k) -= rate * MAT_AT(&g.matLayerWeights[i], j, k);
  383.             }
  384.         }
  385.  
  386.         // Now for biases
  387.         for(int j=0; j<nn.matLayerBiases[i].numRows; j++) {
  388.             for(int k=0; k<nn.matLayerBiases[i].numRows; k++) {
  389.                 MAT_AT(&nn.matLayerBiases[i], j, k) -= rate * MAT_AT(&g.matLayerBiases[i], j, k);
  390.             }
  391.         }
  392.     }
  393. }
  394.  
  395.  
  396.  
  397. int main() {
  398.  
  399.     int input = 1;
  400.     int numberOfHiddenLayers = 1;
  401.     int widthOfHiddenLayers[] = {2, 3, 4};
  402.     int output = 1;
  403.     float eps = 1e-3, rate = 1e-3;
  404.  
  405.     int n = sizeof(ti)/sizeof(ti[0]);
  406.  
  407.     srand(69);
  408.  
  409.     NN nn = NN_create(input, output, numberOfHiddenLayers, widthOfHiddenLayers);
  410.  
  411.     NN g = NN_create(input, output, numberOfHiddenLayers, widthOfHiddenLayers);
  412.  
  413.     NN_randomize(nn);
  414.  
  415. printf("Initial Cost :%f\n", NN_cost(nn));
  416.  
  417.     for(int i=0; i<10000; ++i) {
  418. //printf("Cost %d :%f\n", i, NN_cost(nn));
  419. NN_finite_difference(nn, g, eps);
  420. NN_learn_with_rate(nn, g, rate);
  421.     }
  422.  
  423. printf("Final Cost :%f\n", NN_cost(nn));
  424.  
  425. for(int i=0; i<n; i++) {
  426.   NN_feed(nn, &ti[i], 1);
  427.   printf("ti: %f to: %f NN Output: %f\n", ti[i], to[i], MAT_AT(&nn.matLayerOutputs[nn.numHiddenLayers], 0, 0));
  428. }
  429.  
  430. NN_print(nn);
  431.     // TODO: Free the allocated memory!
  432.     return 0;
  433. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement