mirror of
https://github.com/wassname/keras-js.git
synced 2026-09-09 11:25:25 +08:00
working convolutional VAE demo
This commit is contained in:
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user