TL;DR You can try it on this page. http://handwritten-classifier.app.ginrou.com/ The source code is here. https://github.com/ginrou/handwritten_classifier
It is like this.
It is an application that applies the input image to a neural network and outputs which number was input. The source code is based on this. handwritten_classifier
It may be helpful for those who want to see the source code of neural networks and want to use it quickly from a web application.
It is a general 3-layer perceptron consisting of an input layer-hidden layer-output layer. We do not handle deep learning. The sigmoid function is used as the activation function.
The following will be helpful for details of the algorithm.
The outline is as follows.
The image from (0,0) to (W, H) is made into a one-dimensional vector, and a constant term is added to make the input signal $ {\ bf x} $.
Multiply $ {\ bf x} $ by the weight $ w ^ 1_ {i, j} $ to get the input $ z_ {in, i} $ to the hidden layer. The output of the hidden layer is obtained by multiplying this by the sigmoid function.
z_{in,i} = \sum_{j=0}^{N} w^1_{i,j} x_j \\
z_{out,i} = sigmoid(z_{in,i})
Furthermore, the output $ z_ {out, i} $ of the hidden layer multiplied by the weight $ w ^ 2_ {i, j} $ becomes the input $ y_ {in, i} $ to the output layer, and the sigmoid function is applied. What is the final output $ y_ {out, i} $.
y_{in,i} = \sum_{j=0}^{N} w^2_{i,j} z_{out,j} \\
y_{out,i} = sigmoid(y_{in,i})
The output {\ bf y} is a 10-dimensional vector. The one with the largest output value is the estimation result.
Neural network variables have weights $ {\ bf w ^ 1}, {\ bf w ^ 2} $. Learn its value. It is summarized as follows. Writing / reading of learned parameters is also added.
https://github.com/ginrou/handwritten_classifier/blob/master/NeuralNetwork.py
NeuralNetwork.py
#!/usr/bin/env python
from math import exp
import numpy
def sigmoid(x):
return 1.0 / (1.0 + exp(-x))
def sigmoid_a(array):
return numpy.vectorize(sigmoid)(array)
class NeuralNetwork:
def __init__(self, in_size, hidden_size, out_size):
self.hidden_weight = 0.1 * (numpy.random.random_sample((hidden_size, in_size+1)) - 0.5)
self.output_weight = 0.1 * (numpy.random.random_sample((out_size, hidden_size+1)) - 0.5)
def fit(self, x, t, update_ratio = 0.1):
z, y = self.fire(x)
dy = ( y - t ) *y * ( 1 - y )
dz = (self.output_weight.T.dot(dy))[1:] * z * ( 1- z )
output_input = numpy.r_[ numpy.array([1]), z ]
self.output_weight -= update_ratio * dy.reshape(-1,1) * output_input
hidden_input = numpy.r_[ numpy.array([1]), x ]
self.hidden_weight -= update_ratio * dz.reshape(-1,1) * hidden_input
def fire(self, x):
z = sigmoid_a(self.hidden_weight.dot(numpy.r_[ numpy.array([1]), x ]))
y = sigmoid_a(self.output_weight.dot(numpy.r_[ numpy.array([1]), z ]))
return (z, y)
def predicate(self, x):
z, y = self.fire(x)
return numpy.array(y).argmax()
def save(self, filepath):
numpy.savez(filepath, hidden = self.hidden_weight, output = self.output_weight)
def load(self, filepath):
npzfiles = numpy.load(filepath)
self.hidden_weight = npzfiles['hidden']
self.output_weight = npzfiles['output']
We will train this neural network.
For the data set, I used MNIST data set. Some of the http://deeplearning.net/tutorial/gettingstarted.html is in Pickle format because parsing the raw dataset is a hassle. So I used this.
Since the size of the input image of this dataset is 28x28, the size of the input layer is 784 dimensions, the output layer recognizes numbers from 0 to 9, so it is 10 dimensions, and the intermediate layer is appropriately set to 300 dimensions.
Run on handwritten_classifier.py included in Repository I can do it.
It took about 2-3 minutes to train 50,000 points of the MNIST data set, and the accuracy was 92.52%.
Use the number recognition system using this neural network from the browser. The idea is
It is a mechanism. handwritten_classifier uses Flask to create a web front end.
It is like this to get the brightness value of the image from canvas with JS and send it.
var estimate = function(context) {
var img_buf = getImageBuffer(context, 28, 28);
$.ajax({
type:"post",
url:"/estimate",
data: JSON.stringify({"input": img_buf}),
contentType: 'application/json',
success: function(result) {
$("#estimated").text("Estimated = " + result.estimated);
}
});
};
var getImageBuffer = function(context, width, height) {
var tmpCanvas = $('<canvas>').get(0);
tmpCanvas.width = width;
tmpCanvas.height = height;
var tmpContext = tmpCanvas.getContext('2d');
tmpContext.drawImage(context.canvas, 0, 0, width, height);
var image = tmpContext.getImageData(0,0,width,height);
var buffer = []
for( var i = 0; i < image.data.length; i += 4 ) {
var sum = image.data[i+0] + image.data[i+1] + image.data[i+2] + image.data[i+3];
buffer.push(Math.min(sum,255));
}
return buffer;
};
If the size of the canvas is 28x28, it is too small, so draw on a large canvas on the browser and Then shrink.
The json sent is
{"input":[0,0,255,255,,,,255]}
Like this.
This is what you get at Flask
@app.route("/estimate", methods = ["POST"])
def estimate():
try:
x = numpy.array(request.json["input"]) / 255.0
y = int(nn.predicate(x))
return jsonify({"estimated":y})
except Exception as e:
print(e)
return jsonify({"error":e})
In the previous repository
$ python app.py
You can try it at http: // localhost: 5000
.
Docker Because it supports Docker with glue https://registry.hub.docker.com/u/ginrou/handwritten-classifier/ Can be used with. If you want to try it quickly, you may want to deploy it on heroku.
Writing in Python makes it so short! Web application, unexpectedly poor recognition accuracy
Recommended Posts