Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Drawing;
- using System.Drawing.Drawing2D;
- using System.Drawing.Imaging;
- using System.Runtime.InteropServices;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using Image = System.Drawing.Image;
- using Rectangle = System.Drawing.Rectangle;
- namespace CodingBackProp
- {
- class BackPropProgram
- {
- public static Bitmap ResizeImage(Image image, int width, int height)
- {
- var destRect = new Rectangle(0, 0, width, height);
- var destImage = new Bitmap(width, height);
- destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
- using (var graphics = Graphics.FromImage(destImage))
- {
- graphics.CompositingMode = CompositingMode.SourceCopy;
- graphics.CompositingQuality = CompositingQuality.HighQuality;
- graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
- graphics.SmoothingMode = SmoothingMode.HighQuality;
- graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
- using (var wrapMode = new ImageAttributes())
- {
- wrapMode.SetWrapMode(WrapMode.TileFlipXY);
- graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
- }
- }
- return destImage;
- }
- public static byte[] BitmapToByteArray(Bitmap bitmap)
- {
- BitmapData bmpdata = null;
- try
- {
- bmpdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly,
- bitmap.PixelFormat);
- int numbytes = bmpdata.Stride * bitmap.Height;
- byte[] bytedata = new byte[numbytes];
- IntPtr ptr = bmpdata.Scan0;
- Marshal.Copy(ptr, bytedata, 0, numbytes);
- return bytedata;
- }
- finally
- {
- if (bmpdata != null)
- bitmap.UnlockBits(bmpdata);
- }
- }
- private static double[] PrepareBitmapArray(Bitmap bmp)
- {
- double[] pix = new double[bmp.Width * bmp.Height];
- int x, y, rgb;
- double val;
- for (y = 0; y < bmp.Height; y++)
- {
- for (x = 0; x < bmp.Width; x++)
- {
- System.Drawing.Color c = bmp.GetPixel(x, y);
- //rgb = c.ToKnownColor());
- if (c.R == 0 && c.B == 0 && c.G == 0) // if black
- {
- val = 1;
- }
- else if (c.R < 64 && c.B < 64 && c.G < 64)
- {
- val = 1;
- }
- else val = 0;
- Console.Write(val);
- pix[y * bmp.Width + x] = val;
- }
- Console.WriteLine("");
- }
- return pix;
- }
- static void Main(string[] args)
- {
- int MAX_LITER = 37;
- int MAX_OBRAZKI = 50;
- int numInput = 625; // number features
- int numHidden = 300;
- int numOutput = 37; // number of classes for Y
- bool TrainedYes = true;
- if (!TrainedYes)
- {
- Console.WriteLine("Reading training data...");
- var readData = new List<double[]>();
- for (int literka = 1; literka <= MAX_LITER; literka++)
- {
- var numberLiterka = "0";
- if (literka <= 9)
- {
- numberLiterka += "0" + literka;
- }
- else
- {
- numberLiterka += literka;
- }
- for (int id = 1; id <= MAX_OBRAZKI; id++)
- {
- string number = "0";
- if (id <= 9)
- {
- number += "0" + id;
- }
- else
- {
- number += id;
- }
- Bitmap bitmap2 =
- ResizeImage(
- Image.FromFile(
- @"C:\Users\usr\Downloads\EnglishHnd (1)\EnglishHnd (1)\English\Hnd\Img\Sample" +
- numberLiterka + @"\img" + numberLiterka + "-" +
- number + ".png"), 25, 25);
- double[] preparedBitmap2 = PrepareBitmapArray(bitmap2);
- bitmap2.Dispose();
- var row = new double[preparedBitmap2.Length + numOutput];
- for (var idx = 0; idx < preparedBitmap2.Length; ++idx)
- {
- row[idx] = preparedBitmap2[idx];
- }
- for (var idx = 0; idx < numOutput; ++idx)
- {
- row[numOutput] = 0;
- }
- row[preparedBitmap2.Length + literka - 1] = 1;
- readData.Add(row);
- }
- }
- var readyData = readData.ToArray();
- Console.WriteLine("Done!");
- Console.WriteLine("Creating a " + numInput + "-" + numHidden + "-" + numOutput + " neural network");
- NeuralNetwork nn = new NeuralNetwork(numInput, numHidden, numOutput);
- nn.LoadData();
- int maxEpochs = 1000;
- double learnRate = 0.01;
- double momentum = 0.01;
- Console.WriteLine("\nSetting maxEpochs = " + maxEpochs);
- Console.WriteLine("Setting learnRate = " + learnRate.ToString("F2"));
- Console.WriteLine("Setting momentum = " + momentum.ToString("F2"));
- Console.WriteLine("\nStarting training...");
- double[] weights = nn.Train(readyData, maxEpochs, learnRate, momentum);
- double trainAcc = nn.Accuracy(readyData);
- Console.WriteLine("\nFinal accuracy on training data = " + trainAcc.ToString("F4"));
- double testAcc = nn.Accuracy(readyData);
- Console.WriteLine("Final accuracy on test data = " + testAcc.ToString("F4"));
- }
- if (TrainedYes)
- {
- NeuralNetwork nn = new NeuralNetwork(numInput, numHidden, numOutput);
- int maxEpochs = 1000;
- double learnRate = 0.05;
- double momentum = 0.01;
- Console.WriteLine("Warming up...");
- Console.WriteLine("Loading neural network ...");
- nn.LoadData();
- Console.WriteLine("Loaded!");
- while (Console.ReadKey().Key != ConsoleKey.Q)
- {
- Console.WriteLine("Please enter path for image file to be recognized");
- var file = Console.ReadLine();
- Console.WriteLine("\nStarting...");
- //double[] weights = nn.Train(readyData, maxEpochs, learnRate, momentum);
- Bitmap bitmap2x =
- ResizeImage(
- Image.FromFile(
- file),
- 25, 25);
- Console.WriteLine("Prepared image: \n");
- double[] preparedBitmap2x = PrepareBitmapArray(bitmap2x);
- bitmap2x.Dispose();
- var rowx = new double[preparedBitmap2x.Length];
- for (var idx = 0; idx < preparedBitmap2x.Length; ++idx)
- {
- rowx[idx] = preparedBitmap2x[idx];
- }
- double[] weights = nn.ComputeOutputs(rowx);
- string recognized = "ERROR";
- string[] values = new[]
- {
- "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C",
- "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P",
- "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "0",
- };
- for (int i = 0; i < weights.Length; i++)
- {
- if (weights[i] == weights.Max())
- {
- recognized = values[i];
- }
- // Console.WriteLine(i + " : " + weights[i]);
- }
- Console.WriteLine("Done\n");
- Console.WriteLine("Recognized: " + recognized);
- }
- }
- Console.WriteLine("Done");
- //Console.WriteLine("\nFinal neural network model weights and biases:\n");
- //ShowVector(weights, 2, 10, true);
- Console.ReadLine();
- }
- public static void ShowMatrix(double[][] matrix, int numRows,
- int decimals, bool indices)
- {
- int len = matrix.Length.ToString().Length;
- for (int i = 0; i < numRows; ++i)
- {
- if (indices == true)
- Console.Write("[" + i.ToString().PadLeft(len) + "] ");
- for (int j = 0; j < matrix[i].Length; ++j)
- {
- double v = matrix[i][j];
- if (v >= 0.0)
- Console.Write(" "); // '+'
- Console.Write(v.ToString("F" + decimals) + " ");
- }
- Console.WriteLine("");
- }
- if (numRows < matrix.Length)
- {
- Console.WriteLine(". . .");
- int lastRow = matrix.Length - 1;
- if (indices == true)
- Console.Write("[" + lastRow.ToString().PadLeft(len) + "] ");
- for (int j = 0; j < matrix[lastRow].Length; ++j)
- {
- double v = matrix[lastRow][j];
- if (v >= 0.0)
- Console.Write(" "); // '+'
- Console.Write(v.ToString("F" + decimals) + " ");
- }
- }
- Console.WriteLine("\n");
- }
- public static void ShowVector(double[] vector, int decimals,
- int lineLen, bool newLine)
- {
- for (int i = 0; i < vector.Length; ++i)
- {
- if (i > 0 && i % lineLen == 0) Console.WriteLine("");
- if (vector[i] >= 0) Console.Write(" ");
- Console.Write(vector[i].ToString("F" + decimals) + " ");
- }
- if (newLine == true)
- Console.WriteLine("");
- }
- }
- public class NeuralNetwork
- {
- private int numInput; // number input nodes
- private int numHidden;
- private int numOutput;
- private double[] inputs;
- private double[][] ihWeights; // input-hidden
- private double[] hBiases;
- private double[] hOutputs;
- private double[][] hoWeights; // hidden-output
- private double[] oBiases;
- private double[] outputs;
- private Random rnd;
- public void SaveData()
- {
- string saveData = "";
- saveData += numInput + "\t" + numHidden + "\t" + numOutput + "\t";
- for (int i = 0; i < ihWeights.Length; i++)
- {
- saveData += String.Join("\t", ihWeights[i].Select(p => p.ToString()).ToArray()) + "\t";
- }
- for (int j = 0; j < hBiases.Length; j++)
- {
- saveData += hBiases[j] + "\t";
- }
- for (int j = 0; j < hOutputs.Length; j++)
- {
- saveData += hOutputs[j] + "\t";
- }
- for (int i = 0; i < hoWeights.Length; i++)
- {
- saveData += String.Join("\t", hoWeights[i].Select(p => p.ToString()).ToArray()) + "\t";
- }
- for (int j = 0; j < oBiases.Length; j++)
- {
- saveData += oBiases[j] + "\t";
- }
- File.WriteAllText("network.net", saveData);
- }
- public void LoadData()
- {
- string data = File.ReadAllText("network - 37 - error 00506.net");
- var data0 = data.Split('\t');
- this.numInput = Convert.ToInt32(data.Split('\t')[0]);
- this.numHidden = Convert.ToInt32(data.Split('\t')[1]);
- this.numOutput = Convert.ToInt32(data.Split('\t')[2]);
- int counter = 3;
- for (int i = 0; i < this.numInput; i++)
- {
- for (int j = 0; j < this.numHidden; j++)
- {
- this.ihWeights[i][j] = Convert.ToDouble(data0[counter++]);
- }
- }
- for (int j = 0; j < this.numHidden; j++)
- {
- hBiases[j] = Convert.ToDouble(data0[counter++]);
- }
- for (int j = 0; j < this.numHidden; j++)
- {
- hOutputs[j] = Convert.ToDouble(data0[counter++]);
- }
- for (int i = 0; i < this.numHidden; i++)
- {
- for (int j = 0; j < this.numOutput; j++)
- {
- this.hoWeights[i][j] = Convert.ToDouble(data0[counter++]);
- }
- }
- for (int j = 0; j < this.numOutput; j++)
- {
- oBiases[j] = Convert.ToDouble(data0[counter++]);
- }
- Console.WriteLine("Load OK!");
- }
- public NeuralNetwork(int numInput, int numHidden, int numOutput)
- {
- this.numInput = numInput;
- this.numHidden = numHidden;
- this.numOutput = numOutput;
- this.inputs = new double[numInput];
- this.ihWeights = MakeMatrix(numInput, numHidden, 0.0);
- this.hBiases = new double[numHidden];
- this.hOutputs = new double[numHidden];
- this.hoWeights = MakeMatrix(numHidden, numOutput, 0.0);
- this.oBiases = new double[numOutput];
- this.outputs = new double[numOutput];
- this.rnd = new Random(0);
- this.InitializeWeights(); // all weights and biases
- } // ctor
- private static double[][] MakeMatrix(int rows, int cols, double v) // helper for ctor, Train
- {
- double[][] result = new double[rows][];
- for (int r = 0; r < result.Length; ++r)
- result[r] = new double[cols];
- for (int i = 0; i < rows; ++i)
- for (int j = 0; j < cols; ++j)
- result[i][j] = v;
- return result;
- }
- private void InitializeWeights() // helper for ctor
- {
- // initialize weights and biases to small random values
- int numWeights = (numInput * numHidden) +
- (numHidden * numOutput) + numHidden + numOutput;
- double[] initialWeights = new double[numWeights];
- for (int i = 0; i < initialWeights.Length; ++i)
- initialWeights[i] = (0.001 - 0.0001) * rnd.NextDouble() + 0.0001;
- this.SetWeights(initialWeights);
- }
- public void SetWeights(double[] weights)
- {
- // copy serialized weights and biases in weights[] array
- // to i-h weights, i-h biases, h-o weights, h-o biases
- int numWeights = (numInput * numHidden) +
- (numHidden * numOutput) + numHidden + numOutput;
- if (weights.Length != numWeights)
- throw new Exception("Bad weights array in SetWeights");
- int k = 0; // points into weights param
- for (int i = 0; i < numInput; ++i)
- for (int j = 0; j < numHidden; ++j)
- ihWeights[i][j] = weights[k++];
- for (int i = 0; i < numHidden; ++i)
- hBiases[i] = weights[k++];
- for (int i = 0; i < numHidden; ++i)
- for (int j = 0; j < numOutput; ++j)
- hoWeights[i][j] = weights[k++];
- for (int i = 0; i < numOutput; ++i)
- oBiases[i] = weights[k++];
- }
- public double[] GetWeights()
- {
- int numWeights = (numInput * numHidden) +
- (numHidden * numOutput) + numHidden + numOutput;
- double[] result = new double[numWeights];
- int k = 0;
- for (int i = 0; i < ihWeights.Length; ++i)
- for (int j = 0; j < ihWeights[0].Length; ++j)
- result[k++] = ihWeights[i][j];
- for (int i = 0; i < hBiases.Length; ++i)
- result[k++] = hBiases[i];
- for (int i = 0; i < hoWeights.Length; ++i)
- for (int j = 0; j < hoWeights[0].Length; ++j)
- result[k++] = hoWeights[i][j];
- for (int i = 0; i < oBiases.Length; ++i)
- result[k++] = oBiases[i];
- return result;
- }
- public double[] ComputeOutputs(double[] xValues)
- {
- double[] hSums = new double[numHidden]; // hidden nodes sums scratch array
- double[] oSums = new double[numOutput]; // output nodes sums
- for (int i = 0; i < xValues.Length; ++i) // copy x-values to inputs
- this.inputs[i] = xValues[i];
- // note: no need to copy x-values unless you implement a ToString.
- // more efficient is to simply use the xValues[] directly.
- for (int j = 0; j < numHidden; ++j) // compute i-h sum of weights * inputs
- for (int i = 0; i < numInput; ++i)
- hSums[j] += this.inputs[i] * this.ihWeights[i][j]; // note +=
- for (int i = 0; i < numHidden; ++i) // add biases to hidden sums
- hSums[i] += this.hBiases[i];
- for (int i = 0; i < numHidden; ++i) // apply activation
- this.hOutputs[i] = HyperTan(hSums[i]); // hard-coded
- for (int j = 0; j < numOutput; ++j) // compute h-o sum of weights * hOutputs
- for (int i = 0; i < numHidden; ++i)
- oSums[j] += hOutputs[i] * hoWeights[i][j];
- for (int i = 0; i < numOutput; ++i) // add biases to output sums
- oSums[i] += oBiases[i];
- double[] softOut = Softmax(oSums); // all outputs at once for efficiency
- Array.Copy(softOut, outputs, softOut.Length);
- double[] retResult = new double[numOutput]; // could define a GetOutputs
- Array.Copy(this.outputs, retResult, retResult.Length);
- return retResult;
- }
- private static double HyperTan(double x)
- {
- if (x < -20.0) return -1.0; // approximation is correct to 30 decimals
- else if (x > 20.0) return 1.0;
- else return Math.Tanh(x);
- }
- private static double[] Softmax(double[] oSums)
- {
- // does all output nodes at once so scale
- // doesn't have to be re-computed each time
- double sum = 0.0;
- for (int i = 0; i < oSums.Length; ++i)
- sum += Math.Exp(oSums[i]);
- double[] result = new double[oSums.Length];
- for (int i = 0; i < oSums.Length; ++i)
- result[i] = Math.Exp(oSums[i]) / sum;
- return result; // now scaled so that xi sum to 1.0
- }
- public double[] Train(double[][] trainData, int maxEpochs, double learnRate, double momentum)
- {
- // train using back-prop
- // back-prop specific arrays
- double[][] hoGrads = MakeMatrix(numHidden, numOutput, 0.0); // hidden-to-output weight gradients
- double[] obGrads = new double[numOutput]; // output bias gradients
- double[][] ihGrads = MakeMatrix(numInput, numHidden, 0.0); // input-to-hidden weight gradients
- double[] hbGrads = new double[numHidden]; // hidden bias gradients
- double[] oSignals = new double[numOutput];
- // local gradient output signals - gradients w/o associated input terms
- double[] hSignals = new double[numHidden]; // local gradient hidden node signals
- // back-prop momentum specific arrays
- double[][] ihPrevWeightsDelta = MakeMatrix(numInput, numHidden, 0.0);
- double[] hPrevBiasesDelta = new double[numHidden];
- double[][] hoPrevWeightsDelta = MakeMatrix(numHidden, numOutput, 0.0);
- double[] oPrevBiasesDelta = new double[numOutput];
- int epoch = 0;
- double[] xValues = new double[numInput]; // inputs
- double[] tValues = new double[numOutput]; // target values
- double derivative = 0.0;
- double errorSignal = 0.0;
- int[] sequence = new int[trainData.Length];
- for (int i = 0; i < sequence.Length; ++i)
- sequence[i] = i;
- while (epoch < maxEpochs)
- {
- ++epoch;
- double trainErr = Error(trainData);
- Console.WriteLine("epoch = " + epoch + " error = " + trainErr.ToString("F4"));
- if (trainErr < 5e-3)
- break;
- Shuffle(sequence); // visit each training data in random order
- for (int ii = 0; ii < trainData.Length; ++ii)
- {
- int idx = sequence[ii];
- Array.Copy(trainData[idx], xValues, numInput);
- Array.Copy(trainData[idx], numInput, tValues, 0, numOutput);
- ComputeOutputs(xValues); // copy xValues in, compute outputs
- // indices: i = inputs, j = hiddens, k = outputs
- // 1. compute output node signals (assumes softmax)
- for (int k = 0; k < numOutput; ++k)
- {
- errorSignal = tValues[k] - outputs[k]; // Wikipedia uses (o-t)
- derivative = (1 - outputs[k]) * outputs[k]; // for softmax
- oSignals[k] = errorSignal * derivative;
- }
- // 2. compute hidden-to-output weight gradients using output signals
- for (int j = 0; j < numHidden; ++j)
- for (int k = 0; k < numOutput; ++k)
- hoGrads[j][k] = oSignals[k] * hOutputs[j];
- // 2b. compute output bias gradients using output signals
- for (int k = 0; k < numOutput; ++k)
- obGrads[k] = oSignals[k] * 1.0; // dummy assoc. input value
- // 3. compute hidden node signals
- for (int j = 0; j < numHidden; ++j)
- {
- derivative = (1 + hOutputs[j]) * (1 - hOutputs[j]); // for tanh
- double sum = 0.0; // need sums of output signals times hidden-to-output weights
- for (int k = 0; k < numOutput; ++k)
- {
- sum += oSignals[k] * hoWeights[j][k]; // represents error signal
- }
- hSignals[j] = derivative * sum;
- }
- // 4. compute input-hidden weight gradients
- for (int i = 0; i < numInput; ++i)
- for (int j = 0; j < numHidden; ++j)
- ihGrads[i][j] = hSignals[j] * inputs[i];
- // 4b. compute hidden node bias gradients
- for (int j = 0; j < numHidden; ++j)
- hbGrads[j] = hSignals[j] * 1.0; // dummy 1.0 input
- // == update weights and biases
- // update input-to-hidden weights
- for (int i = 0; i < numInput; ++i)
- {
- for (int j = 0; j < numHidden; ++j)
- {
- double delta = ihGrads[i][j] * learnRate;
- ihWeights[i][j] += delta; // would be -= if (o-t)
- ihWeights[i][j] += ihPrevWeightsDelta[i][j] * momentum;
- ihPrevWeightsDelta[i][j] = delta; // save for next time
- }
- }
- // update hidden biases
- for (int j = 0; j < numHidden; ++j)
- {
- double delta = hbGrads[j] * learnRate;
- hBiases[j] += delta;
- hBiases[j] += hPrevBiasesDelta[j] * momentum;
- hPrevBiasesDelta[j] = delta;
- }
- // update hidden-to-output weights
- for (int j = 0; j < numHidden; ++j)
- {
- for (int k = 0; k < numOutput; ++k)
- {
- double delta = hoGrads[j][k] * learnRate;
- hoWeights[j][k] += delta;
- hoWeights[j][k] += hoPrevWeightsDelta[j][k] * momentum;
- hoPrevWeightsDelta[j][k] = delta;
- }
- }
- // update output node biases
- for (int k = 0; k < numOutput; ++k)
- {
- double delta = obGrads[k] * learnRate;
- oBiases[k] += delta;
- oBiases[k] += oPrevBiasesDelta[k] * momentum;
- oPrevBiasesDelta[k] = delta;
- }
- } // each training item
- if (epoch%10 == 0)
- {
- Console.WriteLine("Saving ...");
- SaveData();
- Console.WriteLine("Saved!");
- }
- } // while
- double[] bestWts = GetWeights();
- return bestWts;
- } // Train
- private void Shuffle(int[] sequence) // instance method
- {
- for (int i = 0; i < sequence.Length; ++i)
- {
- int r = this.rnd.Next(i, sequence.Length);
- int tmp = sequence[r];
- sequence[r] = sequence[i];
- sequence[i] = tmp;
- }
- } // Shuffle
- private double Error(double[][] trainData)
- {
- // average squared error per training item
- double sumSquaredError = 0.0;
- double[] xValues = new double[numInput]; // first numInput values in trainData
- double[] tValues = new double[numOutput]; // last numOutput values
- // walk thru each training case. looks like (6.9 3.2 5.7 2.3) (0 0 1)
- for (int i = 0; i < trainData.Length; ++i)
- {
- Array.Copy(trainData[i], xValues, numInput);
- Array.Copy(trainData[i], numInput, tValues, 0, numOutput); // get target values
- double[] yValues = this.ComputeOutputs(xValues); // outputs using current weights
- for (int j = 0; j < numOutput; ++j)
- {
- double err = tValues[j] - yValues[j];
- sumSquaredError += err * err;
- }
- }
- return sumSquaredError / trainData.Length;
- } // MeanSquaredError
- public double Accuracy(double[][] testData)
- {
- // percentage correct using winner-takes all
- int numCorrect = 0;
- int numWrong = 0;
- double[] xValues = new double[numInput]; // inputs
- double[] tValues = new double[numOutput]; // targets
- double[] yValues; // computed Y
- for (int i = 0; i < testData.Length; ++i)
- {
- Array.Copy(testData[i], xValues, numInput); // get x-values
- Array.Copy(testData[i], numInput, tValues, 0, numOutput); // get t-values
- yValues = this.ComputeOutputs(xValues);
- int maxIndex = MaxIndex(yValues); // which cell in yValues has largest value?
- int tMaxIndex = MaxIndex(tValues);
- if (maxIndex == tMaxIndex)
- ++numCorrect;
- else
- ++numWrong;
- }
- return (numCorrect * 1.0) / (numCorrect + numWrong);
- }
- private static int MaxIndex(double[] vector) // helper for Accuracy()
- {
- // index of largest value
- int bigIndex = 0;
- double biggestVal = vector[0];
- for (int i = 0; i < vector.Length; ++i)
- {
- if (vector[i] > biggestVal)
- {
- biggestVal = vector[i];
- bigIndex = i;
- }
- }
- return bigIndex;
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement