mirror of
https://github.com/wassname/keras-js.git
synced 2026-09-10 12:15:12 +08:00
update demos
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
|
||||
@@ -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: [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user