update demos

This commit is contained in:
Leon Chen
2016-10-07 01:58:45 -04:00
parent d5063b4062
commit fc46b17ec1
8 changed files with 125 additions and 141 deletions
+1 -1
View File
@@ -93,7 +93,7 @@
border-bottom: 2px solid $color-1-lighter;
.output-label {
font-family: 'Inconsolata', sans-serif;
font-family: $font-2;
font-size: 1.5rem;
color: $color-2;
}
-2
View File
@@ -110,8 +110,6 @@ export const MnistCnn = Vue.extend({
},
ready: function () {
// initialize KerasJS model
this.model.initialize()
this.model.ready().then(() => {
this.modelLoading = false
this.$nextTick(function () {
-2
View File
@@ -81,8 +81,6 @@ export const MnistVae = Vue.extend({
},
ready: function () {
// initialize KerasJS model
this.model.initialize()
this.model.ready().then(() => {
this.modelLoading = false
this.$nextTick(function () {
+66 -33
View File
@@ -73,50 +73,83 @@
}
.column.output-column {
justify-content: center;
justify-content: flex-start;
.output {
height: 160px;
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: center;
user-select: none;
cursor: default;
}
}
.layer-results-container {
position: relative;
.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;
.output-class {
display: flex;
flex-direction: column;
font-size: 12px;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 6px 0;
span.layer-class {
color: $color-1;
font-size: 14px;
font-weight: bold;
.output-label {
font-family: $font-2;
font-size: 20px;
color: $color-2;
padding: 0 6px;
border-right: 2px solid $color-1-lighter;
}
.output-bar {
height: 8px;
transition: height 0.2s ease-out;
}
.output-bar-rest {
height: 8px;
background: none;
}
.output-value {
width: 50px;
font-family: $font-2;
font-size: 16px;
color: $color-2;
}
}
.layer-result-canvas-container {
display: inline-flex;
flex-wrap: wrap;
background: white;
canvas {
border: 1px solid lightgray;
margin: 1px;
.output-class.predicted {
.output-label {
color: $color-1;
border-right-color: $color-1;
}
.output-value {
color: $color-1;
}
}
}
}
.architecture-container {
position: relative;
.layer {
display: inline-block;
position: relative;
background: white;
border-radius: 5px;
padding: 10px;
.layer-class-name {
color: $color-1;
font-size: 14px;
font-weight: bold;
}
.layer-details {
color: #999999;
font-size: 12px;
font-weight: bold;
}
}
}
+22 -69
View File
@@ -23,11 +23,18 @@ const MODEL_CONFIG = {
const IMAGE_URL_LIST = [
{ name: 'cat', value: 'http://i.imgur.com/CzXTtJV.jpg' },
{ name: 'dog', value: 'URL2' }
{ name: 'dog', value: 'http://i.imgur.com/OB0y6MR.jpg' },
{ name: 'bridge', value: 'http://i.imgur.com/Bvke53p.jpg' }
]
const LAYER_DISPLAY_CONFIG = {
}
const ARCHITECTURE_DIAGRAM = [
{
name: 'zeropadding2d_1',
className: 'ZeroPadding2D',
details: '3x3 padding',
layout: 'full'
},
]
/**
*
@@ -43,14 +50,14 @@ export const ResNet50 = Vue.extend({
return {
model: new KerasJS.Model(Object.assign({ gpu: this.hasWebgl }, MODEL_CONFIG)),
modelLoading: true,
modelRunning: false,
imageURLInput: null,
imageURLSelect: null,
imageURLSelectList: IMAGE_URL_LIST,
imageLoading: false,
imageLoadingError: false,
output: new Float32Array(1000),
layerResultImages: [],
layerDisplayConfig: LAYER_DISPLAY_CONFIG,
output: null,
architectureDiagram: ARCHITECTURE_DIAGRAM,
useGpu: this.hasWebgl
}
},
@@ -58,17 +65,16 @@ export const ResNet50 = Vue.extend({
computed: {
loadingProgress: function () {
return this.model.getLoadingProgress()
},
outputClasses: function () {
if (!this.output) return []
return utils.imagenetClassesTopK(this.output, 5)
}
},
ready: function () {
// initialize KerasJS model
this.model.initialize()
this.model.ready().then(() => {
this.modelLoading = false
this.$nextTick(function () {
//this.getIntermediateResults()
})
})
},
@@ -102,9 +108,12 @@ export const ResNet50 = Vue.extend({
ctx.drawImage(img, 0, 0)
this.imageLoadingError = false
this.imageLoading = false
this.modelRunning = true
// model predict
this.$nextTick(function () {
this.runModel()
setTimeout(() => {
this.runModel()
}, 200)
})
}
},
@@ -140,63 +149,7 @@ export const ResNet50 = Vue.extend({
}
const outputData = this.model.predict(inputData)
this.output = outputData['fc1000']
console.log(JSON.stringify(utils.imagenetClassesTopK(this.output, 5)))
//this.getIntermediateResults()
},
getIntermediateResults: function () {
let results = []
for (let [name, layer] of this.model.modelLayersMap.entries()) {
const layerClass = layer.layerClass || ''
if (layerClass === 'InputLayer') continue
let images = []
if (layer.result && layer.result.tensor.shape.length === 3) {
images = utils.unroll3Dtensor(layer.result.tensor)
} else if (layer.result && layer.result.tensor.shape.length === 2) {
images = [utils.image2Dtensor(layer.result.tensor)]
} else if (layer.result && layer.result.tensor.shape.length === 1) {
images = [utils.image1Dtensor(layer.result.tensor)]
}
results.push({
name,
layerClass,
images
})
}
this.layerResultImages = results
setTimeout(() => {
this.showIntermediateResults()
}, 0)
},
showIntermediateResults: function () {
this.layerResultImages.forEach((result, layerNum) => {
const scalingFactor = this.layerDisplayConfig[result.name].scalingFactor
result.images.forEach((image, imageNum) => {
const ctx = document.getElementById(`intermediate-result-${layerNum}-${imageNum}`).getContext('2d')
ctx.putImageData(image, 0, 0)
const ctxScaled = document.getElementById(`intermediate-result-${layerNum}-${imageNum}-scaled`).getContext('2d')
ctxScaled.save()
ctxScaled.scale(scalingFactor, scalingFactor)
ctxScaled.clearRect(0, 0, ctxScaled.canvas.width, ctxScaled.canvas.height)
ctxScaled.drawImage(document.getElementById(`intermediate-result-${layerNum}-${imageNum}`), 0, 0)
ctxScaled.restore()
})
})
},
clearIntermediateResults: function () {
this.layerResultImages.forEach((result, layerNum) => {
const scalingFactor = this.layerDisplayConfig[result.name].scalingFactor
result.images.forEach((image, imageNum) => {
const ctxScaled = document.getElementById(`intermediate-result-${layerNum}-${imageNum}-scaled`).getContext('2d')
ctxScaled.save()
ctxScaled.scale(scalingFactor, scalingFactor)
ctxScaled.clearRect(0, 0, ctxScaled.canvas.width, ctxScaled.canvas.height)
ctxScaled.restore()
})
})
this.modelRunning = false
}
}
})
+29 -31
View File
@@ -7,8 +7,15 @@
Loading...{{ loadingProgress }}%
</div>
<div class="input-container" v-if="!modelLoading">
<div class="input-label">Select an image from dropdown or enter a valid image URL</div>
<div class="input-label">Enter a valid image URL or select an image from the dropdown:</div>
<div class="image-url">
<mdl-textfield
floating-label="enter image url"
:value.sync="imageURLInput"
spellcheck="false"
@change="imageURLInputChanged"
></mdl-textfield>
<span>or</span>
<mdl-select
label="select image"
id="image-url-select"
@@ -17,19 +24,12 @@
style="width:200px;"
@change="imageURLSelectChanged"
></mdl-select>
<span>or</span>
<mdl-textfield
floating-label="enter image url"
:value.sync="imageURLInput"
spellcheck="false"
@change="imageURLInputChanged"
></mdl-textfield>
</div>
</div>
<div class="columns input-output" v-if="!modelLoading">
<div class="column input-column">
<div class="loading-indicator">
<mdl-spinner v-if="imageLoading"></mdl-spinner>
<mdl-spinner v-if="imageLoading || modelRunning"></mdl-spinner>
<div class="error" v-if="imageLoadingError">Error loading URL</div>
</div>
<div class="canvas-container">
@@ -39,37 +39,35 @@
<div class="column is-2 controls-column">
<mdl-switch
:checked.sync="useGpu"
:disabled="modelLoading || !hasWebgl"
:disabled="modelLoading || modelRunning || !hasWebgl"
@click="toggleGpu"
>Use GPU</mdl-switch>
</div>
<div class="column output-column">
<div class="output">
<div class="output-class"
v-bind:class="{ 'predicted': $index === 0 }"
v-for="entry in outputClasses"
>
<div class="output-label">{{ entry.name }}</div>
<div class="output-bar"
style="width: {{ Math.round(100 * entry.probability) }}px; background: rgba(27, 188, 155, {{ entry.probability.toFixed(2) }});"
></div>
<div class="output-bar-rest"
style="width: {{ 100 - Math.round(100 * entry.probability) }}px;"
></div>
<div class="output-value">{{ Math.round(100 * entry.probability) }}%</div>
</div>
</div>
</div>
</div>
<!--<div class="layer-results-container" v-if="!modelLoading">
<div class="architecture-container" v-if="!modelLoading">
<div
v-for="layerResult in layerResultImages"
class="layer-result"
v-for="layer in architectureDiagram"
class="layer"
>
<div class="layer-result-heading">
<span class="layer-class">{{ layerResult.layerClass }}</span>
<span> {{ layerDisplayConfig[layerResult.name].heading }}</span>
</div>
<div class="layer-result-canvas-container">
<canvas v-for="image in layerResult.images"
id="intermediate-result-{{ $parent.$index }}-{{ $index }}"
width="{{ image.width }}"
height="{{ image.height }}"
style="display:none;"
></canvas>
<canvas v-for="image in layerResult.images"
id="intermediate-result-{{ $parent.$index }}-{{ $index }}-scaled"
width="{{ layerDisplayConfig[layerResult.name].scalingFactor * image.width }}"
height="{{ layerDisplayConfig[layerResult.name].scalingFactor * image.height }}"
></canvas>
</div>
<div class="layer-class-name">{{ layer.className }}</div>
<div class="layer-details"> {{ layer.details }}</div>
</div>
</div>-->
</div>
</div>
+6 -2
View File
@@ -108,8 +108,12 @@ export function tensorStats (tensor) {
* calculates min and max for a ndarray tensor
*/
export function tensorMinMax (tensor) {
const min = Math.min(...tensor.data)
const max = Math.max(...tensor.data)
let min = Infinity
let max = -Infinity
for (let i = 0, len = tensor.data.length; i < len; i++) {
if (tensor.data[i] < min) min = tensor.data[i]
if (tensor.data[i] > max) max = tensor.data[i]
}
return { min, max }
}
+1 -1
View File
@@ -11,7 +11,7 @@ module.exports = {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js'
},
devtool: 'cheap-module-eval-source-map',
devtool: 'eval-source-map',
module: {
loaders: [
{