diff --git a/README.md b/README.md index 721aa7ee114223bdf7f4b91e65aeb10bbfc19069..37cdaed11c432976fe8065ecaa29e157376e3511 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ cp -r frontend/dist server/visualdl/frontend/dist #### Step 3: build and install Python package ```shell cd server/ -python setup.py bdist_wheel +sh build.sh cd dist sudo pip install --upgrade visualdl-0.0.1-py2-none-any.whl ``` diff --git a/frontend/mock/data/plugin/images/images.js b/frontend/mock/data/plugin/images/images.js new file mode 100644 index 0000000000000000000000000000000000000000..98ee5bc9531160294bf73ffc368b66dd2b571dce --- /dev/null +++ b/frontend/mock/data/plugin/images/images.js @@ -0,0 +1,42 @@ +/** + * get mock data + * + * @param {string} path request path + * @param {Object} queryParam query params + * @param {Object} postParam post params + * @return {Object} + */ +module.exports = function (path, queryParam, postParam) { + return { + // moock delay + _timeout: 0, + // mock http status + _status: 200, + // mock response data + _data: { + status: 0, + msg: 'SUCCESS', + data: [ + { + "wall_time": 1512549785.061623, + "step": 60, + "query": + "sample=0&index=0&tag=input_reshape%2Finput%2Fimage%2F0&run=test", + "width": 28, + "height": 28 + }, + {"wall_time": 1512886109.672786, "step": 60, "query": "sample=0&index=1&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28}, + {"wall_time": 1512886124.266915, "step": 210, "query": "sample=0&index=2&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28}, + {"wall_time": 1512886138.898628, "step": 330, "query": "sample=0&index=3&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28}, + {"wall_time": 1512886139.883663, "step": 340, "query": "sample=0&index=4&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28}, + {"wall_time": 1512886147.195567, "step": 410, "query": "sample=0&index=5&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28}, + {"wall_time": 1512886156.47856, "step": 500, "query": "sample=0&index=6&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28}, + {"wall_time": 1512886187.82793, "step": 810, "query": "sample=0&index=7&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28}, + {"wall_time": 1512886200.386198, "step": 950, "query": "sample=0&index=8&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28}, + {"wall_time": 1512886204.224405, "step": 990, "query": "sample=0&index=9&tag=input_reshape%2Finput%2Fimage%2F0&run=test", "width": 28, "height": 28} + ] + } + }; +}; + + diff --git a/frontend/mock/data/plugin/images/individualImage.js b/frontend/mock/data/plugin/images/individualImage.js new file mode 100644 index 0000000000000000000000000000000000000000..c284c9ddfd2baad62afed489dfc5e09b23945328 --- /dev/null +++ b/frontend/mock/data/plugin/images/individualImage.js @@ -0,0 +1,22 @@ +/** + * get mock data + * + * @param {string} path request path + * @param {Object} queryParam query params + * @param {Object} postParam post params + * @return {Object} + */ +module.exports = function (path, queryParam, postParam) { + return { + // moock delay + _timeout: 0, + // mock http status + _status: 200, + // mock response data + _data: { + status: 0, + msg: 'SUCCESS', + data: '' + } + }; +}; diff --git a/frontend/mock/data/plugin/images/tags.js b/frontend/mock/data/plugin/images/tags.js new file mode 100644 index 0000000000000000000000000000000000000000..bf0b3acf59b82d9bf9a6f020c30755bfc6b7f74a --- /dev/null +++ b/frontend/mock/data/plugin/images/tags.js @@ -0,0 +1,25 @@ +/** + * get mock data + * + * @param {string} path request path + * @param {Object} queryParam query params + * @param {Object} postParam post params + * @return {Object} + */ + module.exports = function (path, queryParam, postParam) { + return { + // moock delay + _timeout: 0, + // mock http status + _status: 200, + // mock response data + _data: { + status: 0, + msg: 'SUCCESS', + data: { + "test": { + "input_reshape/input/image/6": { + "displayName": "input_reshape/input/image/6", "description": "", "samples": 1}, "input_reshape/input/image/7": {"displayName": "input_reshape/input/image/7", "description": "", "samples": 1}, "input_reshape/input/image/4": {"displayName": "input_reshape/input/image/4", "description": "", "samples": 1}, "input_reshape/input/image/5": {"displayName": "input_reshape/input/image/5", "description": "", "samples": 1}, "input_reshape/input/image/2": {"displayName": "input_reshape/input/image/2", "description": "", "samples": 1}, "input_reshape/input/image/3": {"displayName": "input_reshape/input/image/3", "description": "", "samples": 1}, "input_reshape/input/image/0": {"displayName": "input_reshape/input/image/0", "description": "", "samples": 1}, "input_reshape/input/image/1": {"displayName": "input_reshape/input/image/1", "description": "", "samples": 1}, "input_reshape/input/image/8": {"displayName": "input_reshape/input/image/8", "description": "", "samples": 1}, "input_reshape/input/image/9": {"displayName": "input_reshape/input/image/9", "description": "", "samples": 1}}, "train": {"input_reshape/input/image/6": {"displayName": "input_reshape/input/image/6", "description": "", "samples": 1}, "input_reshape/input/image/7": {"displayName": "input_reshape/input/image/7", "description": "", "samples": 1}, "input_reshape/input/image/4": {"displayName": "input_reshape/input/image/4", "description": "", "samples": 1}, "input_reshape/input/image/5": {"displayName": "input_reshape/input/image/5", "description": "", "samples": 1}, "input_reshape/input/image/2": {"displayName": "input_reshape/input/image/2", "description": "", "samples": 1}, "input_reshape/input/image/3": {"displayName": "input_reshape/input/image/3", "description": "", "samples": 1}, "input_reshape/input/image/0": {"displayName": "input_reshape/input/image/0", "description": "", "samples": 1}, "input_reshape/input/image/1": {"displayName": "input_reshape/input/image/1", "description": "", "samples": 1}, "input_reshape/input/image/8": {"displayName": "input_reshape/input/image/8", "description": "", "samples": 1}, "input_reshape/input/image/9": {"displayName": "input_reshape/input/image/9", "description": "", "samples": 1}}} + } + }; +}; diff --git a/frontend/mock/data/runs.js b/frontend/mock/data/runs.js index 4e9854de65a0a6026d55fdd2c3b8234dacb3354c..1a7897bc5266a1d5fb1936c1bf32b6e1753f2a13 100644 --- a/frontend/mock/data/runs.js +++ b/frontend/mock/data/runs.js @@ -16,7 +16,7 @@ module.exports = function (path, queryParam, postParam) { _data: { status: 0, msg: 'SUCCESS', - data: ["train", "test", "model"] + data: ["train", "test"] } }; }; diff --git a/frontend/package.json b/frontend/package.json index 7297c4fccc410b815795f4f6767fb40588adc6b6..c131147fce20094c2ccee76968e1b27506ef3b9b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,9 +6,7 @@ "release": "cross-env NODE_ENV=production node ./tool/build.js", "build": "cross-env NODE_ENV=dev node ./tool/build.js", "dev": "cross-env NODE_ENV=dev node tool/dev-server.js", - "lint": "./node_modules/fecs/bin/fecs --rule", - "precommit": "npm run lint", - "prepush": "npm run lint" + "lint": "./node_modules/fecs/bin/fecs --rule" }, "engines": { "node": ">= 6.4.0" @@ -52,7 +50,6 @@ "html-loader": "^0.4.4", "html-webpack-plugin": "^2.28.0", "http-proxy-middleware": "^0.17.4", - "husky": "^0.14.3", "json-loader": "^0.5.4", "opn": "^5.1.0", "optimize-css-assets-webpack-plugin": "^1.3.2", diff --git a/frontend/src/common/component/AppMenu.san b/frontend/src/common/component/AppMenu.san index df93520329b4d0610bb639288cb9fa06d6cabfc9..a89279c97d018ccea216d710ee154197114dadd2 100644 --- a/frontend/src/common/component/AppMenu.san +++ b/frontend/src/common/component/AppMenu.san @@ -32,7 +32,7 @@ export default { }, { value: '2', - url: '/image', + url: '/images', title: 'IMAGES' } ] diff --git a/frontend/src/common/component/Charts/chart.san b/frontend/src/common/component/Charts/chart.san index 9aad89535359f8ea4165d968a29ff0e00e78aaa9..98f4eb0b0803a9b69377e258526dbd4ec5eb4a5a 100644 --- a/frontend/src/common/component/Charts/chart.san +++ b/frontend/src/common/component/Charts/chart.san @@ -3,26 +3,52 @@
+ + settings_overscan + - - file_download + + file_download
diff --git a/frontend/src/common/component/Charts/image.san b/frontend/src/common/component/Charts/image.san new file mode 100644 index 0000000000000000000000000000000000000000..03288b0c85daee659e9c7330fc44a7a40276c8c0 --- /dev/null +++ b/frontend/src/common/component/Charts/image.san @@ -0,0 +1,101 @@ + + + + diff --git a/frontend/src/common/component/ExpandPanel.san b/frontend/src/common/component/ExpandPanel.san index 243e43296db59c6814af72791f4e051c88397475..0ddf3f648545119dc3fa962ad07c84a2872f2c80 100644 --- a/frontend/src/common/component/ExpandPanel.san +++ b/frontend/src/common/component/ExpandPanel.san @@ -5,9 +5,13 @@ on-click="handleHeadClick()" > {{title}} + + {{iconName}} + ({{info}}) +
@@ -15,11 +19,15 @@
@@ -53,7 +64,7 @@ export default { margin-right 20px margin-top 4px float left - width 100px + width 160px .sm-slider-thumb transform translate(0, -50%) .sm-inputNumber diff --git a/frontend/src/common/component/ui-common.styl b/frontend/src/common/component/ui-common.styl index 9384719dd62a022d7df263c09f8f11ea205cd171..9266923a6c32687d93792e99190d0dc9468ff692 100644 --- a/frontend/src/common/component/ui-common.styl +++ b/frontend/src/common/component/ui-common.styl @@ -1,3 +1,7 @@ +html { + font-family: "RobotoDraft", "Roboto", sans-serif; +} + // initual style body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, form, fieldset, legend, button, input, textarea, th, td { margin:0; padding:0; } body, button, input, select, textarea { font:12px/1.5tahoma, arial, \5b8b\4f53; } h1, h2, h3, h4, h5, h6{ font-size:100%; } address, cite, dfn, em, var { font-style:normal; } code, kbd, pre, samp { font-family:couriernew, courier, monospace; } small{ font-size:12px; } ul, ol { list-style:none; } a { text-decoration:none; } a:hover { text-decoration:underline; } sup { vertical-align:text-top; } sub{ vertical-align:text-bottom; } legend { color:#000; } fieldset, img { border:0; } button, input, select, textarea { font-size:100%; } table { border-collapse:collapse; border-spacing:0; } @@ -5,8 +9,8 @@ body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, fo // modify some styles of san-mui .sm-icon - width 100% - height 100% + width 20px + height 20px overflow hidden .sm-radio, @@ -52,7 +56,22 @@ body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, fo color #58666e .sm-dropdown-menu + .sm-text-field + margin-bottom 0px + min-height 30px + border-bottom solid 1px #e4e4e4 + + .sm-text-field-content + padding 0 + padding-left 4px + + input + cursor pointer + .sm-dropdown-menu-icon + top 6px + right 0 + bottom 0 .sm-icon color #58666e @@ -64,12 +83,14 @@ body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, fo .sm-menu-item.state-selected color #ff4081 - +.sm-text-field + width 100% .sm-text-field-input color #58666e .sm-form-item margin-top 10px + overflow hidden .label color rgba(0,0,0,0.54) diff --git a/frontend/src/common/util/http.js b/frontend/src/common/util/http.js index 9f77fdedd1ca18b6009301488ef695d424729c8f..3d62938635a8d57bf7f06b619025aed426192e4e 100644 --- a/frontend/src/common/util/http.js +++ b/frontend/src/common/util/http.js @@ -1,6 +1,6 @@ import axios from 'axios'; import qs from 'qs'; -import Notification from '../ui/Notification'; +import Notification from '../component/Notification'; const STATUS = 'status'; const STATUSINFO = 'msg'; diff --git a/frontend/src/common/util/index.js b/frontend/src/common/util/index.js new file mode 100644 index 0000000000000000000000000000000000000000..4f0efd2c205b4f6dd5bd7c2335d6af6a7c638a3f --- /dev/null +++ b/frontend/src/common/util/index.js @@ -0,0 +1,5 @@ +import quantile from './quantile'; + +export { + quantile +}; diff --git a/frontend/src/common/util/number.js b/frontend/src/common/util/number.js new file mode 100644 index 0000000000000000000000000000000000000000..0adc69d8b0c90e3385945393acae53b69f1fb1bc --- /dev/null +++ b/frontend/src/common/util/number.js @@ -0,0 +1,3 @@ +export default function (x) { + return x === null ? NaN : +x; +} diff --git a/frontend/src/common/util/quantile.js b/frontend/src/common/util/quantile.js new file mode 100644 index 0000000000000000000000000000000000000000..11db42b7d591eccdb02df04bbada8d08fa6d7b55 --- /dev/null +++ b/frontend/src/common/util/quantile.js @@ -0,0 +1,21 @@ +import number from './number'; +export default function (values, p, valueof) { + if (valueof == null) { + return valueof = number; + } + let n = values.length; + if (!(n)) { + return; + } + if ((p = +p) <= 0 || n < 2) { + return +valueof(values[0], 0, values); + } + if (p >= 1) { + return +valueof(values[n - 1], n - 1, values); + } + let i = (n - 1) * p; + let i0 = Math.floor(i); + let value0 = +valueof(values[i0], i0, values); + let value1 = +valueof(values[i0 + 1], i0 + 1, values); + return value0 + (value1 - value0) * (i - i0); +} diff --git a/frontend/src/images/Images.san b/frontend/src/images/Images.san new file mode 100644 index 0000000000000000000000000000000000000000..fab509cf14ebc2701be636dba099b3f632301286 --- /dev/null +++ b/frontend/src/images/Images.san @@ -0,0 +1,155 @@ + + + + + diff --git a/frontend/src/images/index.js b/frontend/src/images/index.js new file mode 100644 index 0000000000000000000000000000000000000000..01d1fd72d5686124edd7042ee4d52c50c4a5f0a1 --- /dev/null +++ b/frontend/src/images/index.js @@ -0,0 +1,9 @@ +import {router} from 'san-router'; + +import Images from './Images'; + +router.add({ + target: '#content', + rule: '/images', + Component: Images +}); diff --git a/frontend/src/images/ui/chartPage.san b/frontend/src/images/ui/chartPage.san new file mode 100644 index 0000000000000000000000000000000000000000..19ddfb157925f1f9dd00396303ff74da74293e6e --- /dev/null +++ b/frontend/src/images/ui/chartPage.san @@ -0,0 +1,71 @@ + + + + + diff --git a/frontend/src/images/ui/config.san b/frontend/src/images/ui/config.san new file mode 100644 index 0000000000000000000000000000000000000000..0cfdb4cb53332f066b97802d953ecaa7b8335fa8 --- /dev/null +++ b/frontend/src/images/ui/config.san @@ -0,0 +1,55 @@ + + + diff --git a/frontend/src/index.js b/frontend/src/index.js index 4c2921d0a70d2bfeb3b31ddcff191a4d0d13a969..99656cf8bb6b292748cc6c89ced167eb406f6846 100644 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -4,6 +4,7 @@ import './common/component/ui-common.styl'; import './home/index'; import './scalars/index'; +import './images/index'; import App from './App'; new App({ diff --git a/frontend/src/scalars/Scalars.san b/frontend/src/scalars/Scalars.san index 7c907ce9b4b7b548a950847f54b8571b1388dad7..bdee9d625fc164ca1233e97f6b36c2d367528dba 100644 --- a/frontend/src/scalars/Scalars.san +++ b/frontend/src/scalars/Scalars.san @@ -10,14 +10,14 @@
{ + let val = config[key]; + if (tansformArr.indexOf(key) > -1) { + filteredConfig[key] = isArray(val) && val[0] === 'yes'; + } + else { + filteredConfig[key] = val; + } + }); + return filteredConfig; } }, initData() { @@ -99,11 +114,13 @@ export default { tags: [], config: { groupNameReg: '.*', - smoothing: '0.5', - horizontal: '1', - sortingMethod: '2', - link: [], - chart: [] + smoothing: 0.6, + horizontal: 'step', + sortingMethod: 'default', + downloadLink: [], + outlier: [], + runs: [], + running: true } }; }, @@ -117,6 +134,7 @@ export default { }); getRuns().then(({errno, data}) => { this.data.set('runsArray', data); + this.data.set('config.runs', data); }); // need debounce, can't use computed diff --git a/frontend/src/scalars/ui/chartPage.san b/frontend/src/scalars/ui/chartPage.san index 3272ea0c081a5bd909ad3bc0cc82ccec32bdb418..96ca0a5c97b9bb6ead6cd352cf86d4e9c3bea338 100644 --- a/frontend/src/scalars/ui/chartPage.san +++ b/frontend/src/scalars/ui/chartPage.san @@ -1,11 +1,18 @@ diff --git a/frontend/src/service.js b/frontend/src/service.js index e30dfbf3355167d1fae3a48359b1c13e176a096c..d759f32b4e5c9bfc395013b473c7431b0218a816 100644 --- a/frontend/src/service.js +++ b/frontend/src/service.js @@ -5,3 +5,7 @@ export const getPluginScalarsTags = makeService('/data/plugin/scalars/tags'); export const getRuns = makeService('/data/runs'); export const getPluginScalarsScalars = makeService('/data/plugin/scalars/scalars'); + +export const getPluginImagesTags = makeService('/data/plugin/images/tags'); + +export const getPluginImagesImages = makeService('/data/plugin/images/images'); diff --git a/frontend/tool/HtmlReplacePlugin.js b/frontend/tool/HtmlReplacePlugin.js index ce67a520d36f6c7d0fa0fe61e9cee42241cec5f6..31acce6c1e436f51c390bc486b5bedef2a7d6492 100644 --- a/frontend/tool/HtmlReplacePlugin.js +++ b/frontend/tool/HtmlReplacePlugin.js @@ -1,3 +1,4 @@ +'use strict'; function noopReplace (val) { return val; } function HtmlReplacePlugin(options) { diff --git a/frontend/tool/build.js b/frontend/tool/build.js index d8a67b1f106f82df771200a8c6bb096c441d77cc..3cfaf7cc1b5a81a1cafc8b3c828540eafe3942a9 100644 --- a/frontend/tool/build.js +++ b/frontend/tool/build.js @@ -1,3 +1,4 @@ +'use strict'; const webpack = require('webpack'); const rm = require('rimraf'); const ora = require('ora'); diff --git a/frontend/tool/dev-client.js b/frontend/tool/dev-client.js index d882b7bf83be35b3fca65f4d790c7451120e35c9..61627601e3a46c71554ba1c2f94dbea798d0a9cd 100644 --- a/frontend/tool/dev-client.js +++ b/frontend/tool/dev-client.js @@ -1,3 +1,4 @@ +'use strict'; var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true') hotClient.subscribe(function (event) { diff --git a/frontend/tool/dev-server.js b/frontend/tool/dev-server.js index 98299e0c2632d7b0633a5685525e63fe424d9ea1..feed66fb507a1860b349d36d6c5615d044b2b67a 100644 --- a/frontend/tool/dev-server.js +++ b/frontend/tool/dev-server.js @@ -1,3 +1,4 @@ +'use strict'; process.env.NODE_ENV = 'dev'; let devPort = 8999; let opn = require('opn'); diff --git a/frontend/tool/entry.js b/frontend/tool/entry.js index 78754c6a5dc394ade713c1522657690d9be3b3fc..f19b4630a06662757d1eb34e930048ace2623cf6 100644 --- a/frontend/tool/entry.js +++ b/frontend/tool/entry.js @@ -1,3 +1,4 @@ +'use strict'; const path = require('path'); const projectPath = path.resolve(__dirname, '..'); const HtmlWebpackPlugin = require('html-webpack-plugin'); diff --git a/frontend/tool/webpack.config.js b/frontend/tool/webpack.config.js index 24764208c456bb784655b5934e7c272782abf101..ae2862a0a93abee232ea94946053c174d2ee9b59 100644 --- a/frontend/tool/webpack.config.js +++ b/frontend/tool/webpack.config.js @@ -1,3 +1,4 @@ +'use strict'; const webpack = require('webpack'); const path = require('path'); const projectPath = path.resolve(__dirname, '..'); diff --git a/frontend/tool/webpack.dev.config.js b/frontend/tool/webpack.dev.config.js index 5b16958fafa422373ac0ba00075de5616d4d97ca..334304e4e06c4d112f8214e9b0b9d4222b86edf2 100644 --- a/frontend/tool/webpack.dev.config.js +++ b/frontend/tool/webpack.dev.config.js @@ -1,3 +1,4 @@ +'use strict'; const webpack = require('webpack'); const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin'); let merge = require('webpack-merge'); diff --git a/frontend/tool/webpack.prod.config.js b/frontend/tool/webpack.prod.config.js index fa73c7837ef2a7ea12803ba5d6885f01520bbc8b..7c76cff713e00e38222f4b9764729c054b334b60 100644 --- a/frontend/tool/webpack.prod.config.js +++ b/frontend/tool/webpack.prod.config.js @@ -1,3 +1,4 @@ +'use strict'; const webpack = require('webpack'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); const path = require('path'); diff --git a/server/build.sh b/server/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..c9c490768e44e3faa525f9f58ff68e1ff9d4f9a3 --- /dev/null +++ b/server/build.sh @@ -0,0 +1,7 @@ +script=$(readlink -f "$0") +script_path=$(dirname "$script") + +pushd $script_path + protoc visualdl/onnx/onnx.proto --python_out . + python setup.py bdist_wheel +popd diff --git a/server/setup.py b/server/setup.py index 6b8c71ed23929662725d683af708a20e10b5eb10..b20a62b86891819e50e76bcb561d6ac4f4f0e829 100644 --- a/server/setup.py +++ b/server/setup.py @@ -1,6 +1,9 @@ from setuptools import setup -packages = ['visualdl', 'visualdl.frontend.dist', 'visualdl.mock'] +packages = ['visualdl', + 'visualdl.onnx', + 'visualdl.mock', + 'visualdl.frontend.dist'] setup( name="visualdl", diff --git a/server/visualdl/graph.py b/server/visualdl/graph.py new file mode 100644 index 0000000000000000000000000000000000000000..e9a996bc8edcb06699440fba1f543629265d3428 --- /dev/null +++ b/server/visualdl/graph.py @@ -0,0 +1,49 @@ +import onnx +import json +from google.protobuf.json_format import MessageToJson + + +def reorganize_inout(json_obj, key): + """ + :param json_obj: the model's json obj + :param key: "input or output" + :return: + """ + for index in range(len(json_obj[key])): + var = json_obj[key][index] + var_new = dict() + + # set name + var_new['name'] = var['name'] + + tensor_type = var['type']['tensorType'] + + # set data_type + var_new['data_type'] = tensor_type['elemType'] + + # set shape + shape = [dim['dimValue'] for dim in tensor_type['shape']['dim']] + var_new['shape'] = shape + + json_obj[key][index] = var_new + + +def load_model(model_pb_path): + model = onnx.load(model_pb_path) + graph = model.graph + del graph.initializer[:] + + # to json string + json_str = MessageToJson(model.graph) + json_obj = json.loads(json_str) + reorganize_inout(json_obj, 'input') + reorganize_inout(json_obj, 'output') + return json.dumps(json_obj, sort_keys=True, indent=4, separators=(',', ': ')) + + +if __name__ == '__main__': + import os + import sys + current_path = os.path.abspath(os.path.dirname(sys.argv[0])) + json_str = load_model(current_path + "/mock/inception_v1.pb") + print(json_str) diff --git a/server/visualdl/onnx/READEME.md b/server/visualdl/onnx/READEME.md new file mode 100644 index 0000000000000000000000000000000000000000..22e92699745dba7db39332261032a80ebf95727d --- /dev/null +++ b/server/visualdl/onnx/READEME.md @@ -0,0 +1,2 @@ +## Note +the onnx.proto is port from onnx commit c3137751923f576d14b4212ea444d1562b127a66 diff --git a/server/visualdl/onnx/__init__.py b/server/visualdl/onnx/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0c77f8f6a5dbcc5981d15c06de215b7dffb8216b --- /dev/null +++ b/server/visualdl/onnx/__init__.py @@ -0,0 +1,43 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import google.protobuf.message + +from .onnx_pb2 import ModelProto + + +def load(obj): + ''' + Loads a binary protobuf that stores onnx model + + @params + Takes a file-like object (has "read" function) + or a string containing a file name + @return ONNX ModelProto object + ''' + if hasattr(obj, 'read') and callable(obj.read): + s = obj.read() + else: + with open(obj, 'rb') as f: + s = f.read() + return load_from_string(s) + + +def load_from_string(s): + ''' + Loads a binary string that stores onnx model + + @params + Takes a string object containing protobuf + @return ONNX ModelProto object + ''' + model = ModelProto() + decoded = model.ParseFromString(s) + # in python implementation ParseFromString returns None + if decoded is not None and decoded != len(s): + raise google.protobuf.message.DecodeError( + "Protobuf decoding consumed too few bytes: {} out of {}".format( + decoded, len(s))) + return model diff --git a/server/visualdl/onnx/onnx.proto b/server/visualdl/onnx/onnx.proto new file mode 100644 index 0000000000000000000000000000000000000000..3a8bb9b66604c45d016fd05f0419a08c40d2d04c --- /dev/null +++ b/server/visualdl/onnx/onnx.proto @@ -0,0 +1,420 @@ +// +// WARNING: This file is automatically generated! Please edit onnx.in.proto. +// + + +// Copyright (c) Facebook Inc. and Microsoft Corporation. +// Licensed under the MIT license. + +syntax = "proto2"; + +package onnx; + +// Note [Release] +// We are still in the very early stage of defining ONNX. The current +// version of ONNX is a starting point. While we are actively working +// towards a complete spec, we would like to get the community involved +// by sharing our working version of ONNX. + +// Note [Protobuf compatibility] +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Based on experience working with downstream vendors, we generally can't +// assume recent versions of protobufs. This means that we do not use any +// protobuf features that are only available in proto3. +// +// Here are the most notable contortions we have to carry out to work around +// these limitations: +// +// - No 'map' (added protobuf 3.0). We instead represent mappings as lists +// of key-value pairs, where order does not matter and duplicates +// are not allowed. + +// Note [Namespaces] +// ~~~~~~~~~~~~~~~~~ +// ONNX gives explicit names to graphs, intermediate values and +// serialized tensors. To make it easier to generate names, we organize +// these into separate namespaces (so, e.g., a graph can have the same +// name as a serialized tensor.) The namespaces are as follows: +// +// - Node: These names identify specific nodes in the graph (but not, necessarily +// any particular input or output of the node. +// - Graph: These names identify graphs in the protobuf. +// - Attribute: These names identify attribute names for extra attributes that +// are passed to operators. +// - Operator: These names identify particular operators. +// - Value: These names identify intermediate values (typically tensors) flowing through +// the computation of a graph. +// - Shape: These names represent parameters for unknown shape dimensions. +// +// We specify the namespace of a name in ONNX as comments in the form +// of "namespace {Node,Graph,Operator,Attribute,Value,Shape}". Framework is responsible +// for supporting the namespaces. +// +// Naming things is hard. Every element with a name has an optional doc_string associated +// with it, providing a human-readable description in text markdown. + +// To be compatible with both proto2 and proto3, we will use a version number +// that is not defined by the default value but an explicit enum number. +enum Version { + // proto3 requires the first enum value to be zero. + // We add this just to appease the compiler. + _START_VERSION = 0; + // The version field is always serialized and we will use it to store the + // version that the graph is generated from. This helps us set up version + // control. We should use version as + // xx(major) - xx(minor) - xxxx(bugfix) + // and we are starting with 0x00000001 (0.0.1), which was the + // version we published on Oct 10, 2017. + IR_VERSION_2017_10_10 = 0x00000001; + + // IR_VERSION 0.0.2 published on Oct 30, 2017 + // - Added type discriminator to AttributeProto to support proto3 users + IR_VERSION_2017_10_30 = 0x00000002; + + // IR VERSION 0.0.3 published on Nov 3, 2017 + // - For operator versioning: + // - Added new message OperatorSetIdProto + // - Added opset_import in ModelProto + // - For vendor extensions, added domain in NodeProto + IR_VERSION = 0x00000003; +} + +// A named attribute containing either singular float, integer, string +// and tensor values, or repeated float, integer, string and tensor values. +// An AttributeProto MUST contain the name field, and *only one* of the +// following content fields, effectively enforcing a C/C++ union equivalent. +message AttributeProto { + + // Note: this enum is structurally identical to the OpSchema::AttrType + // enum defined in schema.h. If you rev one, you likely need to rev the other. + enum AttributeType { + UNDEFINED = 0; + FLOAT = 1; + INT = 2; + STRING = 3; + TENSOR = 4; + GRAPH = 5; + + FLOATS = 6; + INTS = 7; + STRINGS = 8; + TENSORS = 9; + GRAPHS = 10; + } + + // The name field MUST be present for this version of the IR. + optional string name = 1; // namespace Attribute + + // A human-readable documentation for this attribute. Markdown is allowed. + optional string doc_string = 13; + + // The type field MUST be present for this version of the IR. + // For 0.0.1 versions of the IR, this field was not defined, and + // implementations needed to use has_field hueristics to determine + // which value field was in use. For IR_VERSION 0.0.2 or later, this + // field MUST be set and match the f|i|s|t|... field in use. This + // change was made to accomodate proto3 implementations. + optional AttributeType type = 20; // discriminator that indicates which field below is in use + + // Exactly ONE of the following fields must be present for this version of the IR + optional float f = 2; // float + optional int64 i = 3; // int + optional bytes s = 4; // UTF-8 string + optional TensorProto t = 5; // tensor value + optional GraphProto g = 6; // graph + // Do not use field below, it's deprecated. + // optional ValueProto v = 12; // value - subsumes everything but graph + + repeated float floats = 7; // list of floats + repeated int64 ints = 8; // list of ints + repeated bytes strings = 9; // list of UTF-8 strings + repeated TensorProto tensors = 10; // list of tensors + repeated GraphProto graphs = 11; // list of graph +} + +// Defines information on value, including the name, the type, and +// the shape of the value. +message ValueInfoProto { + // This field MUST be present in this version of the IR. + optional string name = 1; // namespace Value + // This field MUST be present in this version of the IR. + optional TypeProto type = 2; + // A human-readable documentation for this value. Markdown is allowed. + optional string doc_string = 3; +} + +// NodeProto stores a node that is similar to the notion of "layer" +// or "operator" in many deep learning frameworks. For example, it can be a +// node of type "Conv" that takes in an image, a filter tensor and a bias +// tensor, and produces the convolved output. +message NodeProto { + repeated string input = 1; // namespace Value + repeated string output = 2; // namespace Value + + // An optional identifier for this node in a graph. + // This field MAY be absent in ths version of the IR. + optional string name = 3; // namespace Node + + // The symbolic identifier of the Operator to execute. + optional string op_type = 4; // namespace Operator + // The domain of the OperatorSet that specifies the operator named by op_type. + optional string domain = 7; // namespace Domain + + // Additional named attributes. + // NOTE: Simply using ValueProto.NameValuePairProto is the most general + // solution. I kept AttributeProto to minimize churn on CI results. + repeated AttributeProto attribute = 5; + + // A human-readable documentation for this node. Markdown is allowed. + optional string doc_string = 6; +} + +// ModelProto is a top-level file/container format for bundling a ML model. +// The semantics of the model are described by the GraphProto that represents +// a parameterized computation graph against a set of named operators that are +// defined independently from the graph. +message ModelProto { + // The version of the IR this model targets. See Version enum above. + // This field MUST be present. + optional int64 ir_version = 1; + + // The OperatorSets this model relies on. + // All ModelProtos MUST have at least one entry that + // specifies which version of the ONNX OperatorSet is + // being imported. + // + // All nodes in the ModelProto's graph will bind against the operator + // with the same-domain/same-op_type operator with the HIGHEST version + // in the referenced operator sets. + repeated OperatorSetIdProto opset_import = 8; + + // The name of the framework or tool used to generate this model. + // This field SHOULD be present to indicate which implementation/tool/framework + // emitted the model. + optional string producer_name = 2; + + // The version of the framework or tool used to generate this model. + // This field SHOULD be present to indicate which implementation/tool/framework + // emitted the model. + optional string producer_version = 3; + + // Domain name of the model. + // We use reverse domain names as name space indicators. For example: + // `com.facebook.fair` or `com.microsoft.cognitiveservices` + // + // Together with `model_version` and GraphProto.name, this forms the unique identity of + // the graph. + optional string domain = 4; + + // The version of the graph encoded. See Version enum below. + optional int64 model_version = 5; + + // A human-readable documentation for this model. Markdown is allowed. + optional string doc_string = 6; + + // The parameterized graph that is evaluated to execute the model. + optional GraphProto graph = 7; + + // Named metadata values; keys should be distinct. + repeated StringStringEntryProto metadata_props = 14; +}; + +// StringStringEntryProto follows the pattern for cross-proto-version maps. +// See https://developers.google.com/protocol-buffers/docs/proto3#maps +message StringStringEntryProto { + optional string key = 1; + optional string value= 2; +}; + +// GraphProto defines a parameterized series of nodes to form a directed acyclic graph. +// This is the equivalent of the "network" and "graph" in many deep learning +// frameworks. +message GraphProto { + // The nodes in the graph. + repeated NodeProto node = 1; + + // The name of the graph. + optional string name = 2; // namespace Graph + + // A list of named tensor values (constants), used to specify default + // values for some of the inputs of the graph. + // Each TensorProto entry must have a distinct name (within the list) that + // also appears in the input list. + // In an evaluation, the default value specified here is used if and only if + // user specifies no value for the corresponding input parameter. + // May be used to pass serialized parameters for networks. + repeated TensorProto initializer = 5; + + // A human-readable documentation for this graph. Markdown is allowed. + optional string doc_string = 10; + + // The inputs and outputs of the graph. + repeated ValueInfoProto input = 11; + repeated ValueInfoProto output = 12; + + // Information for the values in the graph. The ValueInfoProto.name's + // must be distinct. It is optional for a value to appear in value_info list. + repeated ValueInfoProto value_info = 13; + + // DO NOT USE the following fields, they were deprecated before + // repeated string input = 3; + // repeated string output = 4; + // optional int64 ir_version = 6; + // optional int64 producer_version = 7; + // optional string producer_tag = 8; + // optional string domain = 9; +} + +// A message defined to store a tensor in its serialized format. +message TensorProto { + enum DataType { + UNDEFINED = 0; + // Basic types. + FLOAT = 1; // float + UINT8 = 2; // uint8_t + INT8 = 3; // int8_t + UINT16 = 4; // uint16_t + INT16 = 5; // int16_t + INT32 = 6; // int32_t + INT64 = 7; // int64_t + STRING = 8; // string + BOOL = 9; // bool + + // Advanced types + FLOAT16 = 10; + DOUBLE = 11; + UINT32 = 12; + UINT64 = 13; + COMPLEX64 = 14; // complex with float32 real and imaginary components + COMPLEX128 = 15; // complex with float64 real and imaginary components + // Future extensions go here. + } + + // The shape of the tensor. + repeated int64 dims = 1; + + // The data type of the tensor. + optional DataType data_type = 2; + + // For very large tensors, we may want to store them in chunks, in which + // case the following fields will specify the segment that is stored in + // the current TensorProto. + message Segment { + optional int64 begin = 1; + optional int64 end = 2; + } + optional Segment segment = 3; + + // Tensor content must be in the row major order. + // + // Depending on the data_type field, exactly one of the fields below with + // name ending in _data is used to store the elements of the tensor. + + // For float and complex64 values + // Complex64 tensors are encoded as a single array of floats, + // with the real components appearing in odd numbered positions, + // and the corresponding imaginary component apparing in the + // subsequent even numbered position. (e.g., [1.0 + 2.0i, 3.0 + 4.0i] + // is encoded as [1.0, 2.0 ,3.0 ,4.0] + // When this field is present, the data_type field MUST be FLOAT or COMPLEX64. + repeated float float_data = 4 [packed = true]; + + // For int32, uint8, int8, uint16, int16, bool, and float16 values + // float16 values must be bit-wise converted to an uint16_t prior + // to writing to the buffer. + // When this field is present, the data_type field MUST be + // INT32, INT16, INT8, UINT16, INT8, BOOL, or FLOAT32 + repeated int32 int32_data = 5 [packed = true]; + + // For strings. + // Each element of string_data is a UTF-8 encoded Unicode + // string. No trailing null, no leading BOM. The protobuf "string" + // scalar type is not used to match ML community conventions. + // When this field is present, the data_type field MUST be STRING + repeated bytes string_data = 6; + + // For int64. + // When this field is present, the data_type field MUST be INT64 + repeated int64 int64_data = 7 [packed = true]; + + // Optionally, a name for the tensor. + optional string name = 8; // namespace Value + + // A human-readable documentation for this tensor. Markdown is allowed. + optional string doc_string = 12; + + // Serializations can either use one of the fields above, or use this + // raw bytes field. The only exception is the string case, where one is + // required to store the content in the repeated bytes string_data field. + // + // When this raw_data field is used to store tensor value, elements MUST + // be stored in as fixed-width, little-endian order. + // Floating-point data types MUST be stored in IEEE 754 format. + // Complex64 elements must be written as two consecutive FLOAT values, real component first. + // Complex128 elements must be written as two consecutive DOUBLE values, real component first. + // Boolean type MUST be written one byte per tensor element (00000001 for true, 00000000 for false). + // + // Note: the advantage of specific field rather than the raw_data field is + // that in some cases (e.g. int data), protobuf does a better packing via + // variable length storage, and may lead to smaller binary footprint. + // When this field is present, the data_type field MUST NOT be STRING or UNDEFINED + optional bytes raw_data = 9; + + // For double + // Complex64 tensors are encoded as a single array of doubles, + // with the real components appearing in odd numbered positions, + // and the corresponding imaginary component apparing in the + // subsequent even numbered position. (e.g., [1.0 + 2.0i, 3.0 + 4.0i] + // is encoded as [1.0, 2.0 ,3.0 ,4.0] + // When this field is present, the data_type field MUST be DOUBLE or COMPLEX128 + repeated double double_data = 10 [packed = true]; + + // For uint64 and uint32 values + // When this field is present, the data_type field MUST be + // UINT32 or UINT64 + repeated uint64 uint64_data = 11 [packed = true]; +} + +// Defines a tensor shape. A dimension can be either an integer value +// or a symbolic variable. A symbolic variable represents an unknown +// dimension. +message TensorShapeProto { + message Dimension { + oneof value { + int64 dim_value = 1; + string dim_param = 2; // namespace Shape + }; + }; + repeated Dimension dim = 1; +} + +// Define the types. +message TypeProto { + + message Tensor { + // This field MUST NOT have the value of UNDEFINED + // This field MUST be present for this version of the IR. + optional TensorProto.DataType elem_type = 1; + optional TensorShapeProto shape = 2; + } + + + oneof value { + // The type of a tensor. + Tensor tensor_type = 1; + + } +} + +// OperatorSets are uniquely identified by a (domain, opset_version) pair. +message OperatorSetIdProto { + // The domain of the operator set being identified. + // The empty string ("") or absence of this field implies the operator + // set that is defined as part of the ONNX specification. + // This field MUST be present in this version of the IR when referring to any other operator set. + optional string domain = 1; + + // The version of the operator set being identified. + // This field MUST be present in this version of the IR. + optional int64 version = 2; +} \ No newline at end of file diff --git a/server/visualdl/visual_dl.py b/server/visualdl/visual_dl.py index 6620bef8222aa208113289be80bb5929f6c86886..fd1446464cab39b433f175c557d22d84ee2e9b4e 100644 --- a/server/visualdl/visual_dl.py +++ b/server/visualdl/visual_dl.py @@ -12,6 +12,8 @@ import storage import visualdl.mock.data as mock_data import visualdl.mock.tags as mock_tags from visualdl.log import logger +import storage +import graph app = Flask(__name__, static_url_path="") @@ -149,6 +151,11 @@ def individual_image(): imagefile, as_attachment=True, attachment_filename='img.png') return response +@app.route('/data/plugin/graphs/graph') +def graph(): + model_json = graph.load_model("") + return Response(model_json, mimetype='application/json') + if __name__ == '__main__': logger.info(" port=" + str(options.port))