Advertisement
NokitaKaze

RProp neural network

Aug 2nd, 2016
182
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 27.49 KB | None | 0 0
  1. using System;
  2.  
  3. // See "A Direct Adaptive Method for Faster Backpropagation Learning: The RPROP Algorithm",
  4. // M. Riedmiller and H. Braun,
  5. // Proceedings of the 1993 IEEE International Conference on Neural Networks,
  6. // pp. 586-591
  7. // This is the orginal version of the algorithm. There are many later variations.
  8. // (c) Dr. James McCaffrey
  9. //
  10.  
  11. namespace ResilientBackProp
  12. {
  13.     class RpropProgram
  14.     {
  15.         static void Main(string[] args)
  16.         {
  17.             Console.WriteLine("\nBegin neural network with Resilient Back-Propagation (RPROP) training demo");
  18.  
  19.             int numInput = 4; // number features
  20.             int numHidden = 5;
  21.             int numOutput = 3; // number of classes for Y
  22.             int numRows = 10000;
  23.  
  24.             Console.WriteLine("\nGenerating " + numRows +
  25.               " artificial data items with " + numInput + " features");
  26.             double[][] allData = MakeAllData(numInput, numHidden, numOutput, numRows);
  27.             Console.WriteLine("Done");
  28.  
  29.             Console.WriteLine("\nCreating train (80%) and test (20%) matrices");
  30.             double[][] trainData;
  31.             double[][] testData;
  32.             MakeTrainTest(allData, 0.80, out trainData, out testData);
  33.             Console.WriteLine("Done");
  34.  
  35.             Console.WriteLine("\nTraining data: \n");
  36.             ShowData(trainData, 4, 2, true);
  37.  
  38.             Console.WriteLine("Test data: \n");
  39.             ShowData(testData, 3, 2, true);
  40.  
  41.             Console.WriteLine("Creating a 4-5-3 neural network");
  42.             NeuralNetwork nn = new NeuralNetwork(numInput, numHidden, numOutput);
  43.  
  44.             int maxEpochs = 1000;
  45.             Console.WriteLine("\nSetting maxEpochs = " + maxEpochs);
  46.  
  47.             Console.WriteLine("\nStarting RPROP training");
  48.             double[] weights = nn.TrainRPROP(trainData, maxEpochs); // RPROP
  49.             Console.WriteLine("Done");
  50.             Console.WriteLine("\nFinal neural network model weights:\n");
  51.             ShowVector(weights, 4, 10, true);
  52.  
  53.             double trainAcc = nn.Accuracy(trainData, weights);
  54.             Console.WriteLine("\nAccuracy on training data = " + trainAcc.ToString("F4"));
  55.  
  56.             double testAcc = nn.Accuracy(testData, weights);
  57.             Console.WriteLine("\nAccuracy on test data = " + testAcc.ToString("F4"));
  58.  
  59.             Console.WriteLine("\nEnd neural network with Resilient Propagation demo\n");
  60.             Console.ReadLine();
  61.         } // Main
  62.  
  63.         static double[][] MakeAllData(int numInput, int numHidden, int numOutput, int numRows)
  64.         {
  65.             Random rnd = new Random();
  66.             int numWeights = (numInput * numHidden) + numHidden +
  67.               (numHidden * numOutput) + numOutput;
  68.             double[] weights = new double[numWeights]; // actually weights & biases
  69.             for (int i = 0; i < numWeights; ++i)
  70.                 weights[i] = 20.0 * rnd.NextDouble() - 10.0; // [-10.0 to -10.0]
  71.  
  72.             Console.WriteLine("Generating weights:");
  73.             ShowVector(weights, 4, 10, true);
  74.  
  75.             double[][] result = new double[numRows][]; // allocate return-result matrix
  76.             for (int i = 0; i < numRows; ++i)
  77.                 result[i] = new double[numInput + numOutput]; // 1-of-N Y in last column
  78.  
  79.             NeuralNetwork gnn =
  80.               new NeuralNetwork(numInput, numHidden, numOutput); // generating NN
  81.             gnn.SetWeights(weights);
  82.  
  83.             for (int r = 0; r < numRows; ++r) // for each row
  84.             {
  85.                 // generate random inputs
  86.                 double[] inputs = new double[numInput];
  87.                 for (int i = 0; i < numInput; ++i)
  88.                     inputs[i] = 20.0 * rnd.NextDouble() - 10.0; // [-10.0 to -10.0]
  89.  
  90.                 // compute outputs
  91.                 double[] outputs = gnn.ComputeOutputs(inputs);
  92.                 // translate outputs to 1-of-N
  93.                 double[] oneOfN = new double[numOutput]; // all 0.0
  94.                 int maxIndex = 0;
  95.                 double maxValue = outputs[0];
  96.                 for (int i = 0; i < numOutput; ++i)
  97.                 {
  98.                     if (outputs[i] > maxValue)
  99.                     {
  100.                         maxIndex = i;
  101.                         maxValue = outputs[i];
  102.                     }
  103.                 }
  104.                 oneOfN[maxIndex] = 1.0;
  105.  
  106.                 // place inputs and 1-of-N output values into curr row
  107.                 int c = 0; // column into result[][]
  108.                 for (int i = 0; i < numInput; ++i) // inputs
  109.                     result[r][c++] = inputs[i];
  110.                 for (int i = 0; i < numOutput; ++i) // outputs
  111.                     result[r][c++] = oneOfN[i];
  112.             } // each row
  113.             return result;
  114.         } // MakeAllData
  115.  
  116.         static void MakeTrainTest(double[][] allData, double trainPct, out double[][] trainData, out double[][] testData)
  117.         {
  118.             Random rnd = new Random();
  119.             int totRows = allData.Length;
  120.             int numTrainRows = (int)(totRows * trainPct); // usually 0.80
  121.             int numTestRows = totRows - numTrainRows;
  122.             trainData = new double[numTrainRows][];
  123.             testData = new double[numTestRows][];
  124.  
  125.             double[][] copy = new double[allData.Length][]; // ref copy of all data
  126.             for (int i = 0; i < copy.Length; ++i)
  127.                 copy[i] = allData[i];
  128.  
  129.             for (int i = 0; i < copy.Length; ++i) // scramble order
  130.             {
  131.                 int r = rnd.Next(i, copy.Length); // use Fisher-Yates
  132.                 double[] tmp = copy[r];
  133.                 copy[r] = copy[i];
  134.                 copy[i] = tmp;
  135.             }
  136.             for (int i = 0; i < numTrainRows; ++i)
  137.                 trainData[i] = copy[i];
  138.  
  139.             for (int i = 0; i < numTestRows; ++i)
  140.                 testData[i] = copy[i + numTrainRows];
  141.         } // MakeTrainTest
  142.  
  143.         public static void ShowData(double[][] data, int numRows,
  144.           int decimals, bool indices)
  145.         {
  146.             int len = data.Length.ToString().Length;
  147.             for (int i = 0; i < numRows; ++i)
  148.             {
  149.                 if (indices == true)
  150.                     Console.Write("[" + i.ToString().PadLeft(len) + "]  ");
  151.                 for (int j = 0; j < data[i].Length; ++j)
  152.                 {
  153.                     double v = data[i][j];
  154.                     if (v >= 0.0)
  155.                         Console.Write(" "); // '+'
  156.                     Console.Write(v.ToString("F" + decimals) + "    ");
  157.                 }
  158.                 Console.WriteLine("");
  159.             }
  160.             Console.WriteLine(". . .");
  161.             int lastRow = data.Length - 1;
  162.             if (indices == true)
  163.                 Console.Write("[" + lastRow.ToString().PadLeft(len) + "]  ");
  164.             for (int j = 0; j < data[lastRow].Length; ++j)
  165.             {
  166.                 double v = data[lastRow][j];
  167.                 if (v >= 0.0)
  168.                     Console.Write(" "); // '+'
  169.                 Console.Write(v.ToString("F" + decimals) + "    ");
  170.             }
  171.             Console.WriteLine("\n");
  172.         }
  173.  
  174.         public static void ShowVector(double[] vector, int decimals,
  175.           int lineLen, bool newLine)
  176.         {
  177.             for (int i = 0; i < vector.Length; ++i)
  178.             {
  179.                 if (i > 0 && i % lineLen == 0) Console.WriteLine("");
  180.                 if (vector[i] >= 0) Console.Write(" ");
  181.                 Console.Write(vector[i].ToString("F" + decimals) + " ");
  182.             }
  183.             if (newLine == true)
  184.                 Console.WriteLine("");
  185.         }
  186.  
  187.     } // Program
  188.  
  189.     public struct WeightComposite
  190.     {
  191.         public double[][] weights;
  192.         public double[] biases;
  193.     }
  194.  
  195.     public class NeuralNetwork
  196.     {
  197.         const double etaPlus = 1.2; // values are from the paper
  198.         const double etaMinus = 0.5;
  199.         const double deltaMax = 50.0;
  200.         const double deltaMin = 1.0E-6;
  201.  
  202.         private double[][] values;
  203.         private double[][] biases;
  204.         private double[][][] weights;
  205.  
  206.         private Random rnd;
  207.         const int layer_count = 3;
  208.         int[] sizes;
  209.  
  210.         public NeuralNetwork(int numInput, int numHidden, int numOutput)
  211.         {
  212.             this.sizes = new int[NeuralNetwork.layer_count];
  213.             this.sizes[0] = numInput;
  214.             this.sizes[1] = numHidden;
  215.             this.sizes[2] = numOutput;
  216.  
  217.             this.values = new double[NeuralNetwork.layer_count][];
  218.             this.biases = new double[NeuralNetwork.layer_count][];
  219.             this.weights = new double[NeuralNetwork.layer_count][][];
  220.             for (int layer = 0; layer < NeuralNetwork.layer_count; layer++)
  221.             {
  222.                 this.values[layer] = new double[this.sizes[layer]];
  223.             }
  224.             for (int layer = 1; layer < NeuralNetwork.layer_count; layer++)
  225.             {
  226.                 this.biases[layer] = new double[this.sizes[layer]];
  227.                 this.weights[layer] = MakeMatrix(this.sizes[layer - 1], this.sizes[layer], 0.0);
  228.             }
  229.  
  230.             this.rnd = new Random();
  231.             this.InitializeWeights(); // all weights and biases
  232.         } // ctor
  233.  
  234.         private static double[][] MakeMatrix(int rows, int cols, double v) // helper for ctor, Train
  235.         {
  236.             double[][] result = new double[rows][];
  237.             for (int r = 0; r < result.Length; ++r)
  238.                 result[r] = new double[cols];
  239.             for (int i = 0; i < rows; ++i)
  240.                 for (int j = 0; j < cols; ++j)
  241.                     result[i][j] = v;
  242.             return result;
  243.         }
  244.  
  245.         private static double[] MakeVector(int len, double v) // helper for Train
  246.         {
  247.             double[] result = new double[len];
  248.             for (int i = 0; i < len; ++i)
  249.                 result[i] = v;
  250.             return result;
  251.         }
  252.  
  253.         private void InitializeWeights() // helper for ctor
  254.         {
  255.             // initialize weights and biases to random values between 0.0001 and 0.001
  256.             int numWeights = (this.sizes[0] * this.sizes[1]) + (this.sizes[1] * this.sizes[NeuralNetwork.layer_count - 1]) + this.sizes[1] + this.sizes[NeuralNetwork.layer_count - 1];
  257.             double[] initialWeights = new double[numWeights];
  258.             for (int i = 0; i < initialWeights.Length; ++i)
  259.                 initialWeights[i] = (0.001 - 0.0001) * rnd.NextDouble() + 0.0001;
  260.             this.SetWeights(initialWeights);
  261.         }
  262.  
  263.         public double[] TrainRPROP(double[][] trainData, int maxEpochs) // using RPROP
  264.         {
  265.             double[][] allGradTerms = new double[layer_count][];
  266.             WeightComposite[] allGradsAcc = new WeightComposite[layer_count];
  267.             WeightComposite[] allPrevGradsAcc = new WeightComposite[layer_count];
  268.             WeightComposite[] allPrevDeltas = new WeightComposite[layer_count];
  269.  
  270.             for (int layer = 1; layer < layer_count; layer++)
  271.             {
  272.                 int size = sizes[layer];
  273.                 int prev_size = sizes[layer - 1];
  274.                 allGradTerms[layer] = new double[size];
  275.  
  276.                 allGradsAcc[layer].weights = MakeMatrix(prev_size, size, 0.0);
  277.                 allGradsAcc[layer].biases = new double[size];
  278.  
  279.                 allPrevGradsAcc[layer].weights = MakeMatrix(prev_size, size, 0.0);
  280.                 allPrevGradsAcc[layer].biases = new double[size];
  281.  
  282.                 allPrevDeltas[layer].weights = MakeMatrix(prev_size, size, 0.01);
  283.                 allPrevDeltas[layer].biases = MakeVector(size, 0.01);
  284.             }
  285.  
  286.             int epoch = 0;
  287.             while (epoch < maxEpochs)
  288.             {
  289.                 ++epoch;
  290.  
  291.                 if (epoch % 100 == 0 && epoch != maxEpochs)
  292.                 {
  293.                     double[] currWts = this.GetWeights();
  294.                     double err = MeanSquaredError(trainData, currWts);
  295.                     Console.WriteLine("epoch = " + epoch + " err = " + err.ToString("F4"));
  296.                 }
  297.  
  298.                 // 1. compute and accumulate all gradients
  299.                 for (int layer = 1; layer < layer_count; layer++)
  300.                 {
  301.                     ZeroOut(allGradsAcc[layer].weights);// zero-out values from prev iteration
  302.                     ZeroOut(allGradsAcc[layer].biases);
  303.                 }
  304.  
  305.                 for (int row = 0; row < trainData.Length; ++row)  // walk thru all training data
  306.                 {
  307.                     double[] xValues = new double[this.sizes[0]]; // inputs
  308.                     double[] tValues = new double[this.sizes[2]]; // target values
  309.                     // no need to visit in random order because all rows processed before any updates ('batch')
  310.                     Array.Copy(trainData[row], xValues, this.sizes[0]); // get the inputs
  311.                     Array.Copy(trainData[row], this.sizes[0], tValues, 0, this.sizes[2]); // get the target values
  312.                     ComputeOutputs(xValues); // copy xValues in, compute outputs using curr weights (and store outputs internally)
  313.  
  314.                     // compute the h-o gradient term/component as in regular back-prop
  315.                     // this term usually is lower case Greek delta but there are too many other deltas below
  316.                     for (int i = 0; i < this.sizes[NeuralNetwork.layer_count - 1]; ++i)
  317.                     {
  318.                         double value = this.values[NeuralNetwork.layer_count - 1][i];
  319.                         double derivative = (1 - value) * value; // derivative of softmax = (1 - y) * y (same as log-sigmoid)
  320.                         allGradTerms[NeuralNetwork.layer_count - 1][i] = derivative * (value - tValues[i]); // careful with O-T vs. T-O, O-T is the most usual
  321.                     }
  322.  
  323.                     // compute the i-h gradient term/component as in regular back-prop
  324.                     for (int layer = layer_count - 2; layer >= 1; layer--)
  325.                     {
  326.                         for (int i = 0; i < this.sizes[layer]; ++i)
  327.                         {
  328.                             double derivative = (1 - this.values[layer][i]) * (1 + this.values[layer][i]); // derivative of tanh = (1 - y) * (1 + y)
  329.                             double sum = 0.0;
  330.                             for (int j = 0; j < this.sizes[layer + 1]; ++j) // each hidden delta is the sum of sizes[2] terms
  331.                             {
  332.                                 double x = allGradTerms[layer + 1][j] * this.weights[layer + 1][i][j];
  333.                                 sum += x;
  334.                             }
  335.                             allGradTerms[layer][i] = derivative * sum;
  336.                         }
  337.                     }
  338.  
  339.                     for (int layer = layer_count - 1; layer >= 1; layer--)
  340.                     {
  341.                         for (int j = 0; j < this.sizes[layer]; ++j)
  342.                         {
  343.                             // the (hidden-to-) output bias gradients
  344.                             allGradsAcc[layer].biases[j] += allGradTerms[layer][j];
  345.                             // add input to h-o component to make h-o weight gradients, and accumulate
  346.                             for (int i = 0; i < this.sizes[layer - 1]; ++i)
  347.                             {
  348.                                 double grad = allGradTerms[layer][j] * this.values[layer - 1][i];
  349.                                 allGradsAcc[layer].weights[i][j] += grad;
  350.                             }
  351.                         }
  352.                     }
  353.                 } // for (int row = 0; row < trainData.Length; ++row
  354.  
  355.                 ///////////////////////////////////////////////////////////////////////////////////////////////
  356.                 // end compute all gradients
  357.                 // update all weights and biases (in any order)
  358.                 for (int layer = 1; layer < layer_count; layer++)
  359.                 {
  360.                     int size = sizes[layer];
  361.                     int previous_size = sizes[layer - 1];
  362.  
  363.                     // update input-hidden weights
  364.                     for (int j = 0; j < size; ++j)
  365.                     {
  366.                         double delta, t;
  367.                         // Weights
  368.                         for (int i = 0; i < previous_size; ++i)
  369.                         {
  370.                             delta = allPrevDeltas[layer].weights[i][j];
  371.                             t = allPrevGradsAcc[layer].weights[i][j] * allGradsAcc[layer].weights[i][j];
  372.                             if (t > 0) // no sign change, increase delta
  373.                             {
  374.                                 delta *= NeuralNetwork.etaPlus; // compute delta
  375.                                 if (delta > deltaMax) delta = deltaMax; // keep it in range
  376.                                 double tmp = -Math.Sign(allGradsAcc[layer].weights[i][j]) * delta; // determine direction and magnitude
  377.                                 this.weights[layer][i][j] += tmp; // update weights
  378.                             }
  379.                             else if (t < 0) // grad changed sign, decrease delta
  380.                             {
  381.                                 delta *= NeuralNetwork.etaMinus; // the delta (not used, but saved for later)
  382.                                 if (delta < deltaMin) delta = deltaMin; // keep it in range
  383.                                 this.weights[layer][i][j] -= allPrevDeltas[layer].weights[i][j]; // revert to previous weight
  384.                                 allGradsAcc[layer].weights[i][j] = 0; // forces next if-then branch, next iteration
  385.                             }
  386.                             else // this happens next iteration after 2nd branch above (just had a change in gradient)
  387.                             {
  388.                                 double tmp = -Math.Sign(allGradsAcc[layer].weights[i][j]) * delta; // determine direction
  389.                                 this.weights[layer][i][j] += tmp; // update
  390.                             }
  391.  
  392.                             allPrevDeltas[layer].weights[i][j] = delta; // save delta
  393.                             allPrevGradsAcc[layer].weights[i][j] = allGradsAcc[layer].weights[i][j]; // save the (accumulated) gradient
  394.                         } // j
  395.  
  396.                         // Biases
  397.                         delta = allPrevDeltas[layer].biases[j];
  398.                         t = allPrevGradsAcc[layer].biases[j] * allGradsAcc[layer].biases[j];
  399.                         if (t > 0) // no sign change, increase delta
  400.                         {
  401.                             delta *=  NeuralNetwork.etaPlus; // compute delta
  402.                             if (delta > NeuralNetwork.deltaMax) delta = NeuralNetwork.deltaMax;
  403.                             double tmp = -Math.Sign(allGradsAcc[layer].biases[j]) * delta; // determine direction
  404.                             this.biases[layer][j] += tmp; // update
  405.                         }
  406.                         else if (t < 0) // grad changed sign, decrease delta
  407.                         {
  408.                             delta *= NeuralNetwork.etaMinus; // the delta (not used, but saved later)
  409.                             if (delta < NeuralNetwork.deltaMin) delta = NeuralNetwork.deltaMin;
  410.                             this.biases[layer][j] -= allPrevDeltas[layer].biases[j]; // revert to previous weight
  411.                             allGradsAcc[layer].biases[j] = 0; // forces next branch, next iteration
  412.                         }
  413.                         else // this happens next iteration after 2nd branch above (just had a change in gradient)
  414.                         {
  415.                             if (delta > deltaMax) delta = deltaMax;
  416.                             else if (delta < NeuralNetwork.deltaMin) delta = NeuralNetwork.deltaMin;
  417.                             // no way should delta be 0 . . .
  418.                             double tmp = -Math.Sign(allGradsAcc[layer].biases[j]) * delta; // determine direction
  419.                             this.biases[layer][j] += tmp; // update
  420.                         }
  421.                         allPrevDeltas[layer].biases[j] = delta;
  422.                         allPrevGradsAcc[layer].biases[j] = allGradsAcc[layer].biases[j];
  423.                     } // i
  424.                 }// for (int layer = 1; layer < layer_count; layer++)
  425.             } // while
  426.  
  427.             double[] wts = this.GetWeights();
  428.             return wts;
  429.         } // Train
  430.  
  431.         private static void ZeroOut(double[][] matrix)
  432.         {
  433.             for (int i = 0; i < matrix.Length; ++i)
  434.                 for (int j = 0; j < matrix[i].Length; ++j)
  435.                     matrix[i][j] = 0.0;
  436.         }
  437.  
  438.         private static void ZeroOut(double[] array) // helper for Train
  439.         {
  440.             for (int i = 0; i < array.Length; ++i)
  441.                 array[i] = 0.0;
  442.         }
  443.  
  444.         public void SetWeights(double[] weights)
  445.         {
  446.             // copy weights and biases in weights[] array to i-h weights, i-h biases, h-o weights, h-o biases
  447.             int numWeights = (this.sizes[0] * this.sizes[1]) +
  448.                 (this.sizes[1] * this.sizes[NeuralNetwork.layer_count - 1]) + this.sizes[1] +
  449.                 this.sizes[NeuralNetwork.layer_count - 1];
  450.             if (weights.Length != numWeights)
  451.                 throw new Exception("Bad weights array in SetWeights");
  452.  
  453.             int k = 0; // points into weights param
  454.  
  455.             for (int layer = 1; layer < NeuralNetwork.layer_count; layer++)
  456.             {
  457.                 for (int i = 0; i < this.sizes[layer - 1]; ++i)
  458.                     for (int j = 0; j < this.sizes[layer]; ++j)
  459.                         this.weights[layer][i][j] = weights[k++];
  460.                 for (int i = 0; i < this.sizes[layer]; ++i)
  461.                     this.biases[layer][i] = weights[k++];
  462.             }
  463.         }
  464.  
  465.         public double[] GetWeights()
  466.         {
  467.             int numWeights = (this.sizes[0] * this.sizes[1]) +
  468.                 (this.sizes[1] * this.sizes[NeuralNetwork.layer_count - 1]) + this.sizes[1] +
  469.                 this.sizes[NeuralNetwork.layer_count - 1];
  470.             double[] result = new double[numWeights];
  471.             int k = 0;
  472.             for (int layer = 1; layer < NeuralNetwork.layer_count; layer++)
  473.             {
  474.                 for (int i = 0; i < this.weights[layer].Length; ++i)
  475.                     for (int j = 0; j < this.weights[layer][0].Length; ++j)
  476.                         result[k++] = this.weights[layer][i][j];
  477.                 for (int i = 0; i < this.biases[layer].Length; ++i)
  478.                     result[k++] = this.biases[layer][i];
  479.             }
  480.             return result;
  481.         }
  482.  
  483.         public double[] ComputeOutputs(double[] xValues)
  484.         {
  485.             Array.Copy(xValues, this.values[0], this.sizes[0]);
  486.             for (int layer = 1; layer < NeuralNetwork.layer_count; layer++)
  487.             {
  488.                 double[] sums = new double[this.sizes[layer]]; // hidden nodes sums scratch array
  489.                 Array.Copy(this.biases[layer], sums, this.sizes[layer]);
  490.                 for (int j = 0; j < this.sizes[layer]; ++j)  // compute i-h sum of weights * inputs
  491.                     for (int i = 0; i < this.sizes[layer - 1]; ++i)
  492.                         sums[j] += this.values[layer - 1][i] * this.weights[layer][i][j]; // note +=
  493.                 if (layer < NeuralNetwork.layer_count - 1)
  494.                 {
  495.                     for (int i = 0; i < this.sizes[layer]; ++i)   // apply activation
  496.                         this.values[layer][i] = HyperTan(sums[i]); // hard-coded
  497.                 }
  498.                 else
  499.                 {
  500.                     this.values[NeuralNetwork.layer_count - 1] = Softmax(sums);
  501.                 }
  502.             }
  503.  
  504.             double[] retResult = new double[this.sizes[NeuralNetwork.layer_count - 1]]; // could define a GetOutputs method instead
  505.             Array.Copy(this.values[NeuralNetwork.layer_count - 1], retResult, retResult.Length);
  506.             return retResult;
  507.         }
  508.  
  509.         private static double HyperTan(double x)
  510.         {
  511.             if (x < -20.0) return -1.0; // approximation is correct to 30 decimals
  512.             else if (x > 20.0) return 1.0;
  513.             else return Math.Tanh(x);
  514.         }
  515.  
  516.         private static double[] Softmax(double[] oSums)
  517.         {
  518.             // does all output nodes at once so scale doesn't have to be re-computed each time
  519.             // determine max output-sum
  520.             double max = oSums[0];
  521.             for (int i = 0; i < oSums.Length; ++i)
  522.                 if (oSums[i] > max) max = oSums[i];
  523.  
  524.             // determine scaling factor -- sum of exp(each val - max)
  525.             double scale = 0.0;
  526.             for (int i = 0; i < oSums.Length; ++i)
  527.                 scale += Math.Exp(oSums[i] - max);
  528.  
  529.             double[] result = new double[oSums.Length];
  530.             for (int i = 0; i < oSums.Length; ++i)
  531.                 result[i] = Math.Exp(oSums[i] - max) / scale;
  532.  
  533.             return result; // now scaled so that xi sum to 1.0
  534.         }
  535.  
  536.         public double Accuracy(double[][] testData, double[] weights)
  537.         {
  538.             this.SetWeights(weights);
  539.             // percentage correct using winner-takes all
  540.             int numCorrect = 0;
  541.             int numWrong = 0;
  542.             double[] xValues = new double[this.sizes[0]]; // inputs
  543.             double[] tValues = new double[this.sizes[NeuralNetwork.layer_count - 1]]; // targets
  544.             double[] yValues; // computed Y
  545.  
  546.             for (int i = 0; i < testData.Length; ++i)
  547.             {
  548.                 Array.Copy(testData[i], xValues, this.sizes[0]); // parse data into x-values and t-values
  549.                 Array.Copy(testData[i], this.sizes[0], tValues, 0, this.sizes[NeuralNetwork.layer_count - 1]);
  550.                 yValues = this.ComputeOutputs(xValues);
  551.                 int maxIndex = MaxIndex(yValues); // which cell in yValues has largest value?
  552.  
  553.                 if (tValues[maxIndex] == 1.0) // ugly. consider AreEqual(double x, double y, double epsilon)
  554.                     ++numCorrect;
  555.                 else
  556.                     ++numWrong;
  557.             }
  558.             return (numCorrect * 1.0) / (numCorrect + numWrong); // ugly 2 - check for divide by zero
  559.         }
  560.  
  561.         public double MeanSquaredError(double[][] trainData, double[] weights)
  562.         {
  563.             this.SetWeights(weights); // copy the weights to evaluate in
  564.  
  565.             double[] xValues = new double[this.sizes[0]]; // this.values[0]
  566.             double[] tValues = new double[this.sizes[NeuralNetwork.layer_count - 1]]; // targets
  567.             double sumSquaredError = 0.0;
  568.             for (int i = 0; i < trainData.Length; ++i) // walk through each training data item
  569.             {
  570.                 // following assumes data has all x-values first, followed by y-values!
  571.                 Array.Copy(trainData[i], xValues, this.sizes[0]); // extract inputs
  572.                 Array.Copy(trainData[i], this.sizes[0], tValues, 0, this.sizes[NeuralNetwork.layer_count - 1]); // extract targets
  573.                 double[] yValues = this.ComputeOutputs(xValues);
  574.                 for (int j = 0; j < yValues.Length; ++j)
  575.                     sumSquaredError += ((yValues[j] - tValues[j]) * (yValues[j] - tValues[j]));
  576.             }
  577.             return sumSquaredError / trainData.Length;
  578.         }
  579.  
  580.         private static int MaxIndex(double[] vector) // helper for Accuracy()
  581.         {
  582.             // index of largest value
  583.             int bigIndex = 0;
  584.             double biggestVal = vector[0];
  585.             for (int i = 0; i < vector.Length; ++i)
  586.             {
  587.                 if (vector[i] > biggestVal)
  588.                 {
  589.                     biggestVal = vector[i];
  590.                     bigIndex = i;
  591.                 }
  592.             }
  593.             return bigIndex;
  594.         }
  595.  
  596.     } // NeuralNetwork
  597. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement