working convolutional VAE demo

This commit is contained in:
Leon Chen
2016-10-05 21:40:08 -04:00
parent 657cdbdab3
commit d7988600b8
3 changed files with 189 additions and 35 deletions
+1
View File
@@ -1,6 +1,7 @@
$color-1: #1BBC9B;
$color-2: #69707a;
$color-3: #393E46;
$color-4: #E87E04;
$font-1: 'Fira Sans', sans-serif;
$font-2: 'Inconsolata', sans-serif;
+94 -6
View File
@@ -11,8 +11,7 @@
justify-content: flex-end;
.input-container {
display: inline-flex;
flex-direction: column;
text-align: right;
margin: 20px;
position: relative;
@@ -32,20 +31,50 @@
}
.canvas-container {
position: relative;
display: inline-flex;
justify-content: flex-end;
margin: 10px 0;
border: 15px solid rgba(27, 188, 155, 0.3);
transition: border-color 0.2s ease-in;
&:hover {
border-color: rgba(27, 188, 155, 0.6);
}
canvas {
background: white;
border: 15px solid rgba(27, 188, 155, 0.3);
transition: border-color 0.2s ease-in;
&:hover {
border-color: rgba(27, 188, 155, 0.6);
cursor: crosshair;
}
}
.axis {
position: absolute;
cursor: default;
user-select: none;
display: flex;
align-items: center;
justify-content: space-between;
font-family: $font-2;
font-size: 14px;
color: $color-1;
}
.axis.x-axis {
right: 0;
bottom: -45px;
width: 200px;
flex-direction: row;
}
.axis.y-axis {
top: 0;
left: -55px;
height: 200px;
flex-direction: column;
}
}
}
}
@@ -66,6 +95,65 @@
}
.column.output-column {
justify-content: center;
justify-content: flex-start;
.output {
border-radius: 10px;
overflow: hidden;
canvas {
background: white;
}
}
}
.layer-results-container {
position: relative;
.bg-line {
position: absolute;
z-index: 0;
top: 0;
left: 50%;
background: white;
width: 15px;
height: 100%;
}
.layer-result {
position: relative;
z-index: 1;
margin: 30px 20px;
background: white;
border-radius: 10px;
padding: 20px;
overflow-x: auto;
.layer-result-heading {
font-size: 1rem;
color: #999999;
margin-bottom: 10px;
display: flex;
flex-direction: column;
font-size: 12px;
span.layer-class {
color: $color-1;
font-size: 14px;
font-weight: bold;
}
}
.layer-result-canvas-container {
display: inline-flex;
flex-wrap: wrap;
background: white;
canvas {
border: 1px solid lightgray;
margin: 1px;
}
}
}
}
}
+94 -29
View File
@@ -2,7 +2,6 @@
import './mnist-vae.css'
import debounce from 'lodash/debounce'
import * as utils from './utils'
const MODEL_CONFIG = {
@@ -26,13 +25,17 @@ if (process.env.NODE_ENV === 'production') {
const LAYER_DISPLAY_CONFIG = {
'dense_10': {
heading: 'ReLU activation, output dimensions = 128',
heading: 'input dimensions = 2, output dimensions = 128, ReLU activation',
scalingFactor: 2
},
'dense_11': {
heading: 'ReLU activation, output dimensions = 25088 (64 x 14 x 14)',
scalingFactor: 2
},
'reshape_4': {
heading: '',
scalingFactor: 2
},
'deconvolution2d_10': {
heading: '64 3x3 filters, border mode same, 1x1 strides, ReLU activation',
scalingFactor: 2
@@ -66,21 +69,41 @@ export const MnistVae = Vue.extend({
<div class="columns input-output">
<div class="column input-column">
<div class="input-container">
<div class="input-label">Move around the latent space <span class="arrow">⤸</span></div>
<div class="input-label">Click around the latent space <span class="arrow">⤸</span></div>
<div class="canvas-container">
<canvas
id="input-canvas" width="200" height="200"
@mouseenter="activateCrosshairs"
@mouseleave="deactivateCrosshairs"
@mousemove="drawCrosshairs"
@click="selectCoordinate"
@touchend="selectCoordinate"
@mousemove="draw"
@click="selectCoordinates"
@touchend="selectCoordinates"
></canvas>
<div class="axis x-axis">
<span>-1.5</span>
<span>x</span>
<span>1.5</span>
</div>
<div class="axis y-axis">
<span>-1.5</span>
<span>y</span>
<span>1.5</span>
</div>
</div>
</div>
</div>
<div class="column controls-column">
<div class="column is-2 controls-column">
<mdl-switch :checked.sync="useGpu" @click="toggleGpu">Use GPU</mdl-switch>
<div class="coordinates">
<div class="coordinates-x">x: {{ inputCoordinates[0] < 0 ? inputCoordinates[0].toFixed(2) : inputCoordinates[0].toFixed(3) }}</div>
<div class="coordinates-y">y: {{ inputCoordinates[1] < 0 ? inputCoordinates[1].toFixed(2) : inputCoordinates[1].toFixed(3) }}</div>
</div>
</div>
<div class="column output-column">
<div class="output">
<canvas id="output-canvas-scaled" width="150" height="150"></canvas>
<canvas id="output-canvas" width="27" height="27"></canvas>
</div>
</div>
</div>
<div class="layer-results-container">
@@ -115,10 +138,10 @@ export const MnistVae = Vue.extend({
return {
model: new KerasJS.Model(MODEL_CONFIG),
modelLoading: true,
input: new Float32Array(2),
output: new Float32Array(27 * 27),
crosshairsActivated: false,
coordinates: [0, 0],
inputCoordinates: [0, 0],
position: [100, 100],
layerResultImages: [],
layerDisplayConfig: LAYER_DISPLAY_CONFIG,
useGpu: MODEL_CONFIG.gpu
@@ -140,35 +163,56 @@ export const MnistVae = Vue.extend({
})
},
ready: function () {
this.drawPosition()
},
methods: {
toggleGpu: function () {
this.model.gpu = !this.useGpu
},
activateCrosshairs: function (e) {
this.crosshairsActivated = true
},
deactivateCrosshairs: function (e) {
this.crosshairsActivated = false
this.draw(e)
},
drawCrosshairs: function (e) {
if (!this.crosshairsActivated) return
draw: function (e) {
const [x, y] = this.getEventCanvasCoordinates(e)
const ctx = document.getElementById('input-canvas').getContext('2d')
ctx.clearRect(0, 0, 200, 200)
ctx.strokeStyle = '#1BBC9B'
this.drawPosition()
if (this.crosshairsActivated) {
ctx.strokeStyle = '#1BBC9B'
ctx.beginPath()
ctx.moveTo(x, 0)
ctx.lineTo(x, 200)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(0, y)
ctx.lineTo(200, y)
ctx.stroke()
}
},
drawPosition: function () {
const ctx = document.getElementById('input-canvas').getContext('2d')
ctx.clearRect(0, 0, 200, 200)
ctx.fillStyle = '#E87E04'
ctx.beginPath()
ctx.moveTo(x, 0)
ctx.lineTo(x, 200)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(0, y)
ctx.lineTo(200, y)
ctx.stroke()
ctx.arc(...this.position, 5, 0, Math.PI * 2, true)
ctx.closePath()
ctx.fill()
},
getEventCanvasCoordinates: function (e) {
const borderSize = 15
let { clientX, clientY } = e
// for touch event
if (e.touches && e.touches.length) {
@@ -178,20 +222,45 @@ export const MnistVae = Vue.extend({
const canvas = document.getElementById('input-canvas')
const { left, top } = canvas.getBoundingClientRect()
const [x, y] = [clientX - left - borderSize, clientY - top - borderSize]
const [x, y] = [clientX - left, clientY - top]
return [x, y]
},
selectCoordinate: function (e) {
selectCoordinates: function (e) {
const [x, y] = this.getEventCanvasCoordinates(e)
if (!this.model.isRunning) {
this.position = [x, y]
this.inputCoordinates = [x * 3 / 200 - 1.5, y * 3 / 200 - 1.5]
this.draw(e)
const inputData = {
'input_4': new Float32Array(this.inputCoordinates)
}
const outputData = this.model.predict(inputData)
this.output = outputData['convolution2d_8']
this.drawOutput()
this.getIntermediateResults()
}
},
drawOutput: function () {
const ctx = document.getElementById('output-canvas').getContext('2d')
ctx.putImageData(utils.image2Darray(this.output, 27, 27), 0, 0)
// scaled up
// const ctxScaled = document.getElementById('output-canvas-scaled').getContext('2d')
// ctxScaled.save()
// ctxScaled.scale(28 / ctxCenterCrop.canvas.width, 28 / ctxCenterCrop.canvas.height)
// ctxScaled.clearRect(0, 0, ctxCenterCrop.canvas.width, ctxCenterCrop.canvas.height)
// ctxScaled.drawImage(document.getElementById('output-canvas-centercrop'), 0, 0)
// const imageDataScaled = ctxScaled.getImageData(0, 0, ctxScaled.canvas.width, ctxScaled.canvas.height)
// ctxScaled.restore()
},
getIntermediateResults: function () {
let results = []
for (let [name, layer] of this.model.modelLayersMap.entries()) {
if (name === 'input') continue
const layerClass = layer.layerClass || ''
if (layerClass === 'InputLayer') continue
let images = []
if (layer.result && layer.result.tensor.shape.length === 3) {
@@ -240,10 +309,6 @@ export const MnistVae = Vue.extend({
ctxScaled.restore()
})
})
},
toggleGpu: function () {
this.model.gpu = !this.useGpu
}
}
})