Skip to content

Instantly share code, notes, and snippets.

@WKBae
Last active November 9, 2017 13:19
Show Gist options
  • Save WKBae/ced01318202ee860ec52ba5c4ae6dc36 to your computer and use it in GitHub Desktop.
Save WKBae/ced01318202ee860ec52ba5c4ae6dc36 to your computer and use it in GitHub Desktop.
MNIST tester using Flask
<html>
<head><title>MNIST Tester</title></head>
<style>
#number-pad {
padding: 0;
margin: 0;
border-collapse: collapse;
border: 1px solid #000;
}
#number-pad tr {
margin: 0;
padding: 0;
height: 10px;
}
#number-pad tr td {
margin: 0;
padding: 0;
width: 10px;
}
.selected {
background: #000;
}
</style>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script>
var rules = "";
for(var i = 0; i <= 255; i++) {
var color = (255 - i).toString(16)
color = color + color + color
rules += 'td[data-amount="' + i + '"]{background:#' + color + '}'
}
var st = document.createElement('style')
st.innerText = rules
document.head.appendChild(st)
$(function() {
var mousedown = false
var changed = false
var $tds = $("#number-pad td")
var lastRow = -1, lastCol = -1
function applyNear(row, col) {
if(row > 0) {
var $top = $tds.eq((row - 1) * 28 + col)
$top.attr('data-amount', Math.min((($top.attr('data-amount') || 0)|0) + 5, 255))
}
if(col > 0) {
var $left = $tds.eq(row * 28 + (col - 1))
$left.attr('data-amount', Math.min((($left.attr('data-amount') || 0)|0) + 5, 255))
}
if(row < 27) {
var $bottom = $tds.eq((row + 1) * 28 + col)
$bottom.attr('data-amount', Math.min((($bottom.attr('data-amount') || 0)|0) + 5, 255))
}
if(col < 27) {
var $right = $tds.eq(row * 28 + (col + 1))
$right.attr('data-amount', Math.min((($right.attr('data-amount') || 0)|0) + 5, 255))
}
}
darkerTimer = 0
$tds.mouseenter(function() {
if(!mousedown) {
$(this).css('background', '#aaa');
} else {
var idx = $tds.index(this)
var row = idx / 28 |0, col = idx % 28
var $toSet = $(this)
var updated = [[row, col]]
if(lastRow >= 0 && lastCol >= 0) {
var dr = row - lastRow, dc = col - lastCol
var set = {}
for(var r = lastRow; r < row; r++) {
var c = Math.round(lastCol + dc * (r - lastRow) / dr)
set[r * 28 + c] = true
}
for(var c = lastCol; c < col; c++) {
var r = Math.round(lastRow + dr * (c - lastCol) / dc)
set[r * 28 + c] = true
}
for(var key in set) {
if(set.hasOwnProperty(key)) {
$toSet = $toSet.add($tds.eq(key|0))
updated.push([key / 28 | 0, key % 28])
}
}
}
$toSet.attr('data-amount', 255)
console.log(JSON.stringify(updated), $toSet)
applyNear(row, col)
darkerTimer = setInterval(function() {applyNear(row, col)}, 50)
changed = true
lastRow = row
lastCol = col
}
}).mouseleave(function() {
$(this).css('background', '');
clearInterval(darkerTimer)
darkerTimer = 0
})
$tds.add("#number-pad").mousedown(function() {
mousedown = true
lastRow = -1
lastCol = -1
$("#result").text("Result: ...")
$(this).mouseenter()
return false
})
$('body').mouseup(function() {
mousedown = false
})
$("#reset").click(function() {
$("[data-amount]").attr('data-amount', '')
$("#result").text("")
})
setInterval(function() {
if(!mousedown && changed) {
var amounts = Array.prototype.join.call($tds.map(function() {
return (($(this).attr('data-amount') || 0)|0).toString(16)
}), ',')
//console.log(amounts)
$.post('./determine', {'amounts': amounts}, function(data) {
$("#result").text("Result: " + data);
})
changed = false
}
}, 1000)
})
</script>
<body>
<table id="number-pad">
<!-- 28 * 28 matrix, ouch -->
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
</table>
<button id="reset">Reset</button>
<div id="result"></div>
</body>
</html>
from flask import Flask, Response, request
import tensorflow as tf
app = Flask(__name__)
@app.route('/')
def page():
with open('mnist-tester.html', 'r', encoding='utf8') as f:
return f.read()
sess = tf.InteractiveSession()
def setup_nn():
# Same network configuration as the checkpoint to load
x = tf.placeholder(tf.float32, [None, 784])
x_image = tf.reshape(x, [-1, 28, 28, 1])
W_conv1 = tf.get_variable('W_conv1', shape=[5, 5, 1, 32], initializer=tf.truncated_normal_initializer(stddev=0.01))
b_conv1 = tf.get_variable('b_conv1', shape=[32], initializer=tf.zeros_initializer)
h_conv1 = tf.nn.relu(tf.nn.conv2d(x_image, W_conv1, strides=[1, 1, 1, 1], padding='SAME') + b_conv1)
h_pool1 = tf.nn.max_pool(h_conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
W_conv2 = tf.get_variable('W_conv2', shape=[5, 5, 32, 64], initializer=tf.truncated_normal_initializer(stddev=0.01))
b_conv2 = tf.get_variable('b_conv2', shape=[64], initializer=tf.zeros_initializer)
h_conv2 = tf.nn.relu(tf.nn.conv2d(h_pool1, W_conv2, strides=[1, 1, 1, 1], padding='SAME') + b_conv2)
h_pool2 = tf.nn.max_pool(h_conv2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
W_fc1 = tf.get_variable('W_fc1', shape=[7*7*64, 1024], initializer=tf.truncated_normal_initializer(stddev=0.01))
b_fc1 = tf.get_variable('b_fc1', [1024], initializer=tf.zeros_initializer)
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
W_fc2 = tf.get_variable('W_fc2', shape=[1024, 256], initializer=tf.truncated_normal_initializer(stddev=0.01))
b_fc2 = tf.get_variable('b_fc2', [256], initializer=tf.zeros_initializer)
h_fc2 = tf.nn.relu(tf.matmul(h_fc1, W_fc2) + b_fc2)
W_endpoint = tf.get_variable('W_endpoint', shape=[256, 10], initializer=tf.truncated_normal_initializer(stddev=0.01))
b_endpoint = tf.get_variable('b_endpoint', [10], initializer=tf.zeros_initializer)
logits = tf.matmul(h_fc2, W_endpoint) + b_endpoint
result = tf.argmax(logits, 1)
sess.run(tf.global_variables_initializer())
saver = tf.train.Saver([W_conv1, b_conv1, W_conv2, b_conv2, W_fc1, b_fc1, W_fc2, b_fc2, W_endpoint, b_endpoint])
# Load the trained checkpoint weights
saver.restore(sess, './mnist.ckpt-12000')
return x, result
x, result = setup_nn()
@app.route('/determine', methods=['POST'])
def determine():
amts = [int(x, 16) / 255 for x in request.form['amounts'].split(',')]
res = sess.run(result, feed_dict={x: [amts]})
return str(res[0])
if __name__ == '__main__':
app.run(host='0.0.0.0')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment