From b2bf9f8424e0abc8e9c4ddd63dfeacd5b7b386bf Mon Sep 17 00:00:00 2001 From: Unai Sarasola Date: Wed, 6 Apr 2016 10:15:45 +0200 Subject: [PATCH] Finished but I need to put regularization into the gradient --- src/main/resources/recog/test.png | Bin 0 -> 177 bytes src/main/scala/recog/ImageNumberReader.scala | 8 +++- src/main/scala/recog/Main.scala | 20 +++++++-- src/main/scala/recog/NeuralNetwork.scala | 6 +-- src/main/scala/recog/Neuron.scala | 2 +- src/main/scala/recog/Trainer.scala | 45 +++++++++++-------- 6 files changed, 53 insertions(+), 28 deletions(-) create mode 100644 src/main/resources/recog/test.png diff --git a/src/main/resources/recog/test.png b/src/main/resources/recog/test.png new file mode 100644 index 0000000000000000000000000000000000000000..21f63f3c9b87813f8f600ec8abed2ef05892be72 GIT binary patch literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sDEfH31!Z9ZwBpqr4V#mGLBhvfAB7;)^pUXO@geCyI;WAJF literal 0 HcmV?d00001 diff --git a/src/main/scala/recog/ImageNumberReader.scala b/src/main/scala/recog/ImageNumberReader.scala index 8a872bb..2611980 100644 --- a/src/main/scala/recog/ImageNumberReader.scala +++ b/src/main/scala/recog/ImageNumberReader.scala @@ -6,7 +6,7 @@ import java.io.File /** * Reader object for patterns - * @author Pianista + * @author Unai Sarasola */ object ImageNumberReader { @@ -44,5 +44,11 @@ object ImageNumberReader { (0 to 9).toList map {x => (x,loadPattern(x))} } + def loadImage(name: String): Image = { + val img = ImageIO.read(getClass.getResourceAsStream(name)) + val pixels = img.getRGB(0, 0, img.getWidth, img.getHeight, null, 0, img.getWidth).toList + pixels map {x => if(new Color(x).getRed() == 255) 0.0 else 1.0} //White 0 Black 1 + } + } \ No newline at end of file diff --git a/src/main/scala/recog/Main.scala b/src/main/scala/recog/Main.scala index 8014b74..a9063a3 100644 --- a/src/main/scala/recog/Main.scala +++ b/src/main/scala/recog/Main.scala @@ -1,5 +1,15 @@ package recog - +/** + * @author Unai Sarasola + * + * In this program we are going to train a Neural Network in order to identify the numbers in a image PNG of 8x8 pixels + * After that we will try out trained Network with a test.png image to see how the network is able to identify the number on it + * + * You can play with the lambda, step, and number of iterations changing Trainer() with Trainer(iter, lambda, step) to obtain different networks + * + * If you use that an approach like that into a production environment, you should change my fmincg function, by a better solution from a Common library + * + */ object Main extends App { @@ -32,7 +42,7 @@ object Main extends App { (img, empty updated(number, 1.0)) } } - val trainedNetwork = trainer.train(network, trainingExamplesFormmated, 1) + val trainedNetwork = trainer.train(network, trainingExamplesFormmated) //Once we have the trainedNetwork, test how can fit the trainingSet val examplesCorrect = trainingExamplesFormmated map { @@ -45,6 +55,10 @@ object Main extends App { val percentage = (examplesCorrect.sum/examplesCorrect.length)*100 println("% correct: "+percentage) - println("Theta1 finish: " + trainedNetwork.hiddenLayer.map {x => x.theta}) + + //Try to identify the target image + val test = ImageNumberReader.loadImage("test.png") + val result = trainedNetwork.apply(test) + println("Test image identified as number: "+ result.indexOf(result.max)) } \ No newline at end of file diff --git a/src/main/scala/recog/NeuralNetwork.scala b/src/main/scala/recog/NeuralNetwork.scala index d6a381b..a463c3d 100644 --- a/src/main/scala/recog/NeuralNetwork.scala +++ b/src/main/scala/recog/NeuralNetwork.scala @@ -16,7 +16,7 @@ package recog * * (But you can use more layers/change the layer sizes if the problem is not well fit) * - * @author Pianista + * @author Unai Sarasola */ case class NeuralNetwork( inputLayer: Int, //Number of inputs of the neural Network (also called input neurons /input units) @@ -85,10 +85,6 @@ case class NeuralNetwork( val new_theta1 = (theta1 zip grad_theta1) map { case(x,y) => x-alpha*y} val newHiddenLayer = new_theta1.grouped(inputSize) map {x => Neuron(x)} toList - /*println("Theta1:"+theta1) - println("////") - println("New theta1:"+new_theta1)*/ - val grad_theta2 = grad drop inputSize*hiddenLayerSize val new_theta2 = (theta2 zip grad_theta2) map { case(x,y) => x-alpha*y} val newOutputLayer = new_theta2.grouped(hiddenLayerSize+1) map {x => Neuron(x)} toList diff --git a/src/main/scala/recog/Neuron.scala b/src/main/scala/recog/Neuron.scala index 9793275..698d67d 100644 --- a/src/main/scala/recog/Neuron.scala +++ b/src/main/scala/recog/Neuron.scala @@ -2,7 +2,7 @@ package recog /** * Define a Neuron of a Neural network - * @author Pianista + * @author Unai Sarasola */ case class Neuron(theta: List[Double]) { diff --git a/src/main/scala/recog/Trainer.scala b/src/main/scala/recog/Trainer.scala index da82832..3231350 100644 --- a/src/main/scala/recog/Trainer.scala +++ b/src/main/scala/recog/Trainer.scala @@ -2,11 +2,23 @@ package recog import scala.util.Random +/** + * Overload object compation for constructor + */ + +object Trainer { + def apply() = new Trainer() +} + /** * In charge of train the Neural Network selecting initial values for theta - * @author Pianista + * @author Unai Sarasola */ -case class Trainer() { +case class Trainer(maxIter: Int, lambda: Double, step: Double) { + + def this() = { + this(400, 1.0, 0.15) //Default values + } /** * Calculate the cost of the current neural network for a training set given with the expected results @@ -60,15 +72,12 @@ case class Trainer() { /** * Return the trained neural network */ - def train(network:NeuralNetwork, trainingSet: TrainingSet, lambda: Double): NeuralNetwork = { + def train(network:NeuralNetwork, trainingSet: TrainingSet): NeuralNetwork = { //Start with random thetas val hiddenLayer = (1 to network.hiddenLayer.length) map { x=> Neuron(initThetaForHidden(network)) } toList val outputLayer = (1 to network.outputLayer.length) map { x=> Neuron(initThetaForOutput(network)) } toList val initialNetwork = NeuralNetwork(network.inputLayer, hiddenLayer, outputLayer) - val maxIter = 400 - val lambda = 1.0 - customFmin(initialNetwork, trainingSet, maxIter) @@ -78,30 +87,30 @@ case class Trainer() { /** * Custom Fmin for get the best theta values (Don't use in your projects. Just for educational purpose) */ - def customFmin(network: NeuralNetwork, training: TrainingSet, maxIter: Int) : NeuralNetwork = { + def customFmin(network: NeuralNetwork, training: TrainingSet, iteration: Int) : NeuralNetwork = { - def newNetworkFromGradient(step:Double): NeuralNetwork = { - val currCost = costFunction(network, training, 1.0) - println("Current cost: "+currCost) //TODO: Lambda + def newNetworkFromGradient(stepUsed:Double): NeuralNetwork = { + + val currCost = costFunction(network, training, lambda) + println("Current cost: "+currCost) val grad = gradient(network, training) //Modify theta val newNetwork = network.updateThetasWithGradient(grad, step) - val newCost = costFunction(newNetwork, training, 1.0) + val newCost = costFunction(newNetwork, training, lambda) - if(newCost > currCost) newNetworkFromGradient(step/2) //step too big + if(newCost > currCost) newNetworkFromGradient(stepUsed/2) //step too big else { - println("New cost: "+newCost) //TODO: Lambda + println("New cost: "+newCost) newNetwork } } - //TODO: More iterations - println("Iter:"+maxIter) - if(maxIter==0)network - else customFmin(newNetworkFromGradient(0.15), training, maxIter-1) + println("Iter:"+iteration) + if(iteration==0)network + else customFmin(newNetworkFromGradient(step), training, iteration-1) } @@ -189,7 +198,7 @@ case class Trainer() { val grad = finalTheta1_grad:::finalTheta2_grad //TODO: Regularization assert(grad.length == (network.inputLayer+1)*network.hiddenLayer.length + (network.hiddenLayer.length+1)*network.outputLayer.length) - grad //TODO: Repasar el gradiente esta mal, devuelve 0 para todas las derivadas de la primera capa + grad } } \ No newline at end of file