Browse Source

add tensor feature

tags/v0.6.0-beta
xiayifan 5 years ago
parent
commit
bfd1c841ca
12 changed files with 2755 additions and 27 deletions
  1. +2
    -0
      mindinsight/ui/package.json
  2. +486
    -0
      mindinsight/ui/src/components/gridTableSimple.vue
  3. +1
    -0
      mindinsight/ui/src/components/header.vue
  4. +838
    -0
      mindinsight/ui/src/components/histogramUnit.vue
  5. +16
    -2
      mindinsight/ui/src/locales/zh-cn.json
  6. +2
    -0
      mindinsight/ui/src/main.js
  7. +4
    -0
      mindinsight/ui/src/router.js
  8. +12
    -0
      mindinsight/ui/src/services/request-service.js
  9. +4
    -0
      mindinsight/ui/src/store.js
  10. +5
    -3
      mindinsight/ui/src/views/train-manage/histogram.vue
  11. +1222
    -0
      mindinsight/ui/src/views/train-manage/tensor.vue
  12. +163
    -22
      mindinsight/ui/src/views/train-manage/training-dashboard.vue

+ 2
- 0
mindinsight/ui/package.json View File

@@ -14,6 +14,8 @@
"d3": "5.9.7",
"d3-graphviz": "3.0.4",
"element-ui": "2.11.1",
"jquery": "3.5.0",
"slickgrid": "2.4.22",
"vue": "2.6.11",
"vue-i18n": "8.15.0",
"vue-router": "3.1.3",


+ 486
- 0
mindinsight/ui/src/components/gridTableSimple.vue View File

@@ -0,0 +1,486 @@
<!--
Copyright 2020 Huawei Technologies Co., Ltd.All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<template>
<div class="cl-slickgrid-container">
<div class="data-show-container">
<div v-show="incorrectData"
class="error-msg-container">
{{$t('components.gridIncorrectDataError')}}
</div>
<div v-show="!fullData.length && updated && !incorrectData"
class="error-msg-container">
{{$t('components.gridTableNoData')}}
</div>
<div :id="itemId"
v-show="!!fullData.length && !incorrectData"
class="grid-item"></div>
</div>
<div class="operate-container"
v-if="showOperate && fullData.length">
<div class="filter-container"
@keyup.enter="filterChange">
<div v-for="(item, itemIndex) in filterArr"
:key="itemIndex">
<el-input class="filter-input"
:class="item.showError ? 'error-border' : ''"
v-model="item.model"></el-input>
<span class="input-behind"
v-if="itemIndex === filterArr.length - 1">{{$t('symbols.slashes')}}</span>
<span class="input-behind"
v-else>{{$t('symbols.point')}}</span>
</div>
<el-button class="filter-check"
size="mini"
v-if="!!filterArr.length"
@click="filterChange">
<i class="el-icon-check"></i></el-button>
<span class="filter-incorrect-text"
v-if="!filterCorrect">{{$t('components.inCorrectInput')}}</span>
</div>
<div class="accuracy-container">
{{$t('components.gridAccuracy')}}<el-select v-model="accuracy"
class="select-item"
@change="accuracyChange">
<el-option v-for="item in accuracyArr"
:key="item.label"
:label="item.label"
:value="item.value"></el-option>
</el-select>
<div class="full-screen-icon"
:title="$t('scalar.fullScreen')"
@click="toggleFullScreen"
:class="fullScreen ? 'active-color' : ''">
<span><i class="el-icon-full-screen"></i></span>
</div>
</div>
</div>
</div>
</template>

<script>
import 'slickgrid/css/smoothness/jquery-ui-1.11.3.custom.css';
import 'slickgrid/slick.grid.css';
import 'slickgrid/lib/jquery-3.1.0';
import 'slickgrid/lib/jquery-ui-1.9.2';
import 'slickgrid/lib/jquery.event.drag-2.3.0.js';
import 'slickgrid/slick.core.js';
import 'slickgrid/slick.dataview.js';
import 'slickgrid/slick.grid.js';
export default {
props: {
// Table data
fullData: {
type: Array,
default() {
return [];
},
},
// Display operation Bar
showOperate: {
type: Boolean,
default: true,
},
// Display full screen
fullScreen: {
type: Boolean,
default: false,
},
},
data() {
return {
itemId: '', // Dom id
gridObj: null, // slickgrid object
columnsData: [], // Column information
filterArr: [], // Dimension selection array
formateData: [], // formatted data
formateArr: [], // formatted Array
statistics: {}, // Object contain maximun and minimun
accuracy: 5, // accuracy value
incorrectData: false, // Wheather the dimension is correctly selected
updated: false, // Updated
scrollTop: false, // Wheather scroll to the top
filterCorrect: true, // Wheather the dimension input is correct
// Accuray options
accuracyArr: [
{label: 0, value: 0},
{label: 1, value: 1},
{label: 2, value: 2},
{label: 3, value: 3},
{label: 4, value: 4},
{label: 5, value: 5},
],
// Table configuration items
optionObj: {
enableCellNavigation: true,
frozenColumn: 0,
frozenRow: 0,
},
};
},
computed: {},
watch: {},
mounted() {
this.init();
},
methods: {
/**
* Initialize
*/
init() {
this.itemId =
`${new Date().getTime()}` + `${this.$store.state.componentsCount}`;
this.$store.commit('componentsNum');
},
/**
* Initialize dimension selection
* @param {Array} dimension Dimension array
* @param {String} filterStr Dimension String
*/
initializeFilterArr(dimension, filterStr) {
if (!filterStr) {
this.filterArr = [];
return;
}
const tempFilterArr = filterStr.slice(1, filterStr.length - 1).split(',');
const tempArr = [];
for (let i = 0; i < tempFilterArr.length; i++) {
tempArr.push({
model: tempFilterArr[i],
max: dimension[i] - 1,
showError: false,
});
}
this.filterArr = tempArr;
},
/**
* Initialize column information
*/
formateColumnsData() {
this.columnsData = [
{
id: -1,
name: ' ',
field: -1,
width: 100,
headerCssClass: 'headerStyle',
},
];
const columnSample = this.formateData[0];
if (columnSample) {
columnSample.forEach((num, numIndex) => {
const order = numIndex;
this.columnsData.push({
id: order,
name: order,
field: order,
width: 100,
headerCssClass: 'headerStyle',
formatter: this.formateValueColor,
});
});
} else {
this.columnsData = [];
}
},
/**
* Setting the Background color of data
* @param {Number} row
* @param {Number} cell
* @param {String} value,
* @param {Object} columnDef
* @param {Object} dataContext
* @return {String}
*/
formateValueColor(row, cell, value, columnDef, dataContext) {
if (
!cell ||
!value ||
isNaN(value) ||
value === Infinity ||
value === -Infinity
) {
return value;
} else if (value < 0) {
return `<span class="table-item-span" style="background:rgba(94, 124, 224, ${value /
this.statistics.min})">${value}</span>`;
} else {
return `<span class="table-item-span" style="background:rgba(246, 111, 106, ${value /
this.statistics.max})">${value}</span>`;
}
},
/**
* Convetring raw data into table data
*/
formateGridArray() {
if (this.fullData instanceof Array) {
if (this.fullData.length) {
if (this.fullData[0] instanceof Array) {
this.formateData = this.fullData;
} else {
this.formateData = [this.fullData];
}
} else {
this.formateData = [[]];
this.columnsData = [];
}
} else {
this.formateData = [[this.fullData]];
}
const tempArr = [];
this.formateData.forEach((outerData, outerIndex) => {
const tempData = {
'-1': outerIndex,
};
outerData.forEach((innerData, innerIndex) => {
const innerOrder = innerIndex;
if (isNaN(innerData)) {
tempData[innerOrder] = innerData;
} else {
tempData[innerOrder] = innerData.toFixed(this.accuracy);
}
});
tempArr.push(tempData);
});
this.formateArr = tempArr;
},
/**
* Update the table
*/
updateGrid() {
this.$nextTick(() => {
if (!this.gridObj) {
this.gridObj = new Slick.Grid(
`#${this.itemId}`,
this.formateArr,
this.columnsData,
this.optionObj,
);
}
this.gridObj.setData(this.formateArr, this.scrollTop);
this.scrollTop = false;
this.gridObj.setColumns(this.columnsData);
this.gridObj.render();
});
},
/**
* accuracy changed
* @param {Number} value The value after changed
*/
accuracyChange(value) {
this.formateGridArray();
this.updateGrid();
},
/**
* Dimension selection changed
*/
filterChange() {
// 校验检索条件
let filterCorrect = true;
let incorrectData = false;
let limitCount = 2;
const tempArr = [];
this.filterArr.forEach((filter) => {
const value = filter.model.trim();
tempArr.push(value);
if (!isNaN(value)) {
if (value < -(filter.max + 1) || value > filter.max || value === '') {
filter.showError = true;
filterCorrect = false;
} else {
filter.showError = false;
}
} else if (value === ':') {
filter.showError = false;
if (!limitCount) {
incorrectData = true;
} else {
limitCount--;
}
} else {
filter.showError = true;
filterCorrect = false;
}
});
this.filterCorrect = filterCorrect;
if (incorrectData && filterCorrect) {
this.incorrectData = true;
return;
} else {
this.incorrectData = false;
}
if (filterCorrect) {
this.$emit('martixFilterChange', tempArr);
}
},
/**
* Updating Table Data
* @param {Boolean} newDataFlag Wheather data is updated
* @param {Array} dimension Array of dimension
* @param {Object} statistics Object contains maximun and minimun
* @param {String} filterStr String of dimension selection
*/
updateGridData(newDataFlag, dimension, statistics, filterStr) {
this.updated = true;
this.$nextTick(() => {
if (!this.fullData || !this.fullData.length) {
return;
}
if (newDataFlag) {
this.initializeFilterArr(dimension, filterStr);
this.scrollTop = true;
}
if (newDataFlag || this.statistics.max === undefined) {
this.statistics = statistics;
}
this.formateGridArray();
this.formateColumnsData();
this.updateGrid();
});
},
/**
* Update the view Size
*/
resizeView() {
if (this.gridObj) {
this.$nextTick(() => {
this.gridObj.resizeCanvas();
this.gridObj.render();
});
}
},
/**
* Expand/Collapse in full screen
*/
toggleFullScreen() {
this.$emit('toggleFullScreen');
},
},
destroyed() {},
};
</script>
<style lang="scss">
.cl-slickgrid-container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
.data-show-container {
width: 100%;
flex: 1;
.grid-item {
width: 100%;
height: 100%;
}
.error-msg-container {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
}
.info-show-container {
width: 100%;
}
.operate-container {
width: 100%;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
z-index: 99;
flex-wrap: wrap;
.full-screen-icon {
float: right;
margin-left: 15px;
height: 100%;
line-height: 34px;
cursor: pointer;
:hover {
color: #3e98c5;
}
}
.active-color {
color: #3e98c5;
}
.filter-container {
float: left;
flex-wrap: wrap;
display: flex;
.error-border {
input {
border-color: red;
}
}
.filter-input {
width: 50px;
text-align: center;
}
.input-behind {
padding: 0 5px;
}
.filter-incorrect-text {
margin-left: 10px;
line-height: 32px;
color: red;
}
}
.accuracy-container {
float: right;
.select-item {
width: 60px;
}
}
}
}
.slick-cell,
.slick-headerrow-column,
.slick-footerrow-column {
padding: 0;
border-top: none;
border-left: none;
text-align: center;
}
.grid-canvas-left .slick-cell {
height: 54px;
}
.slick-viewport-left {
overflow: hidden !important;
}
.slick-viewport-left .slick-row {
background-color: white !important;
::-webkit-scrollbar {
width: 0px;
}
}
.ui-widget-content {
background: none;
}
.headerStyle {
vertical-align: middle;
text-align: center;
}
.filter-check {
font-size: 18px;
color: #00a5a7;
cursor: pointer;
}
.table-item-span {
display: block;
width: 100%;
height: 100%;
text-align: center;
}
</style>

+ 1
- 0
mindinsight/ui/src/components/header.vue View File

@@ -40,6 +40,7 @@ limitations under the License.
v-if="this.$route.path.indexOf('/scalar') > 0
|| this.$route.path.indexOf('/image') > 0
|| this.$route.path.indexOf('/histogram') > 0
|| this.$route.path.indexOf('/tensor') > 0
|| this.$route.path.indexOf('/training-dashboard') > 0
|| !this.$route.path.indexOf('/compare-plate')">
<!-- automatic refresh switch -->


+ 838
- 0
mindinsight/ui/src/components/histogramUnit.vue View File

@@ -0,0 +1,838 @@
<!--
Copyright 2020 Huawei Technologies Co., Ltd.All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<template>
<div class="cl-histogram-container">
<div class="data-show-container">
<div :id="itemId"
v-show="!!fullData.length"
class="data-item"></div>
</div>
</div>
</template>

<script>
import echarts from 'echarts';
import CommonProperty from '../common/common-property';
import {format, precisionRound} from 'd3';
const d3 = {format, precisionRound};
export default {
props: {
// histogram data
fullData: {
type: Array,
default() {
return [];
},
},
// view name
viewName: {
type: Number,
default: 1,
},
// axis name
axisName: {
type: Number,
default: 0,
},
// Display full screen
fullScreen: {
type: Boolean,
default: false,
},
},
data() {
return {
itemId: '', // Dom id
oriData: {}, // Original data
charOption: {}, // Chart configuration
charObj: null, // chart Object
updated: false, // Updated
zrDrawElement: {hoverDots: []},
zr: null,
chartTipFlag: false, // Wheather to display tips of the histogram
};
},
computed: {},
watch: {},
mounted() {
this.init();
},
methods: {
/**
* Initialize
*/
init() {
this.itemId =
`${new Date().getTime()}` + `${this.$store.state.componentsCount}`;
this.$store.commit('componentsNum');
},
/**
* Update the view Size
*/
resizeView() {
if (this.charObj) {
this.charObj.resize();
}
},
/**
* Convert to chart data
*/
formatDataToChar() {
const chartData = this.fullData;
const seriesData = [];
let maxX = -Infinity;
let minX = Infinity;
let maxZ = -Infinity;
let minZ = Infinity;
const gridData = [];
if (chartData && chartData.length) {
chartData.forEach((histogram) => {
const seriesItem = [];
gridData.push(histogram.step);
histogram.items.forEach((bucket) => {
if (this.viewName === 0) {
seriesItem.push([bucket[2], bucket[3]]);
} else if (this.viewName === 1) {
seriesItem.push(bucket[2], histogram.step, bucket[3]);
}
maxX = Math.max(maxX, bucket[2]);
minX = Math.min(minX, bucket[2]);
minZ = Math.min(minZ, bucket[3]);
maxZ = Math.max(maxZ, bucket[3]);
});
seriesData.push(seriesItem);
});
}
this.oriData = {
seriesData,
maxX,
minX,
maxZ,
minZ,
gridData,
};
},
/**
* update sample data
*/
updateSampleData() {
this.charOption = this.formatCharOption();
if (!this.charObj) {
const chartItem = document.getElementById(this.itemId);
if (!chartItem) {
return;
}
this.charObj = echarts.init(chartItem, null);
}
this.removeTooltip();
this.charObj.setOption(this.charOption, true);
},
/**
* Binding interaction event
*/
sampleEventBind() {
if (!this.zr) {
this.zr = this.charObj.getZr();
this.zr.off('mouseout', 'mousemove');
this.zr.on('mouseout', (e) => {
this.removeTooltip();
this.chartTipFlag = false;
this.$emit('chartTipFlagChange', this.chartTipFlag);
});
this.zr.on('mousemove', (e) => {
this.removeTooltip();
this.mousemoveEvent(e);
});
}
},
/**
* Formate chart option
* @return {Object} chatr option
*/
formatCharOption() {
const colorMin = '#346E69';
const colorMax = '#EBFFFD';
const oriData = this.oriData;
const colorArr = this.getGrientColor(
colorMin,
colorMax,
oriData.seriesData.length,
);
const fullScreenFun = this.toggleFullScreen;
const axisName = this.axisName;
const option = {
grid: {
left: 24,
top: 60,
right: 50,
bottom: 60,
},
xAxis: {
max: oriData.maxX,
min: oriData.minX,
axisLine: {onZero: false},
axisLabel: {
fontSize: '11',
formatter: function(value) {
if (value.toString().length >= 6) {
return value.toExponential(3);
} else {
return Math.round(value * 1000) / 1000;
}
},
},
splitLine: {show: false},
},
yAxis: {
position: 'right',
axisLine: {onZero: false, show: false},
splitLine: {show: true},
axisTick: {show: false},
boundaryGap: false,
axisLabel: {
fontSize: '11',
},
},
toolbox: {
top: 20,
right: 20,
emphasis: {
iconStyle: {
textPosition: 'top',
},
},
// toolbox
feature: {
// fullScreen
myToolFullScreen: {
show: true,
title: this.$t('histogram.fullScreen'),
iconStyle: {
borderColor: this.fullScreen ? '#3E98C5' : '#6D7278',
},
icon: CommonProperty.fullScreenIcon,
onclick() {
fullScreenFun();
},
},
},
},
};
if (this.viewName === 1) {
const seriesData = [];
oriData.seriesData.forEach((item, dataIndex) => {
const dataItem = {
name: item[1],
value: item,
itemStyle: {
color: colorArr[dataIndex],
},
};
seriesData.push(dataItem);
});
option.series = [
{
type: 'custom',
dimensions: ['x', 'y'],
renderItem: (params, api) => {
const points = this.makePolyPoints(
params.dataIndex,
api.coord,
params.coordSys.y - 10,
);

return {
type: 'polyline',
silent: true,
shape: {
points,
},
style: api.style({
stroke: '#bbb',
lineWidth: 1,
}),
};
},
data: seriesData,
},
];
option.yAxis.data = oriData.gridData;
option.yAxis.type = 'category';
option.grid.top = 126;
if (axisName === 2 && this.fullScreen) {
option.grid.right = 140;
}
option.yAxis.inverse = true;
const that = this;
option.yAxis.axisLabel.formatter = function(value) {
return that.yAxisFormatter(value);
};
} else if (this.viewName === 0) {
option.color = colorArr;
option.series = [];
oriData.seriesData.forEach((k) => {
option.series.push({
type: 'line',
symbol: 'none',
lineStyle: {
width: 1,
},
data: k.slice(1, -1),
});
});
}
return option;
},
/**
* Expand/Collapse in full screen
*/
toggleFullScreen() {
this.removeTooltip();
if (!this.fullScreen) {
if (this.axisName === 2) {
// this.charObj.setOption({grid: {right: 140}});
this.charOption.grid.right = 140;
}
this.charOption.toolbox.feature.myToolFullScreen.iconStyle.borderColor =
'#3E98C5';
} else {
// this.charObj.setOption({grid: {right: 40}});
this.charOption.grid.right = 40;
this.charOption.toolbox.feature.myToolFullScreen.iconStyle.borderColor =
'#6D7278';
}
this.charObj.setOption(this.charOption);
this.$emit('toggleFullScreen');
},
/**
* remove tooltip
*/
removeTooltip() {
if (this.zr) {
if (this.zrDrawElement.hoverDots) {
this.zrDrawElement.hoverDots.forEach((dot) => this.zr.remove(dot));
}
if (this.zrDrawElement.hoverLine) {
this.zr.remove(this.zrDrawElement.hoverLine);
}
if (this.zrDrawElement.tooltip) {
this.zr.remove(this.zrDrawElement.tooltip);
}
if (this.zrDrawElement.tooltipY) {
this.zr.remove(this.zrDrawElement.tooltipY);
}
if (this.zrDrawElement.tooltipX) {
this.zr.remove(this.zrDrawElement.tooltipX);
}
}
},
/**
* Calculate polygon points
* @param {Number} dataIndex
* @param {Object} getCoord
* @param {Number} yValueMapHeight
* @return {Array} Array of ploygon points
*/
makePolyPoints(dataIndex, getCoord, yValueMapHeight) {
const points = [];
const rawData = this.oriData.seriesData;
const maxZ = this.oriData.maxZ;
const minZ = this.oriData.minZ;
for (let i = 0; i < rawData[dataIndex].length; ) {
const x = this.getValue(rawData, dataIndex, i++);
const y = this.getValue(rawData, dataIndex, i++);
const z = this.getValue(rawData, dataIndex, i++);
const pt = getCoord([x, y]);
// linear map in z axis
if (maxZ !== minZ) {
pt[1] -= ((z - minZ) / (maxZ - minZ)) * yValueMapHeight;
}
points.push(pt);
}
return points;
},
/**
* get convert point
* @param {Array} pt value
* @return {Array}
*/
getCoord(pt) {
return this.charObj.convertToPixel('grid', pt);
},
/**
* Formate Y coordinate display
* @param {Number} value
* @return {Object}
*/
yAxisFormatter(value) {
let data = '';
const filter = this.fullData.filter((k) => k.step === value);
if (filter.length) {
if (this.axisName === 2) {
data = this.fullScreen
? this.dealrelativeTime(
new Date(filter[0].wall_time * 1000).toString(),
)
: [];
} else if (this.axisName === 1) {
data = `${(filter[0].relative_time / 3600).toFixed(3)}h`;
} else {
data = filter[0].step;
}
}
return data;
},
/**
* Formate time display
* @param {Onject} time
* @return {String} Formatted time
*/
dealrelativeTime(time) {
const arr = time.split(' ');
const str = arr[0] + ' ' + arr[1] + ' ' + arr[2] + ',' + ' ' + arr[4];
return str;
},
/**
* Mouse move event
* @param {Object} e Original event
*/
mousemoveEvent(e) {
const unit = 's';
const nearestIndex = this.findNearestValue([e.offsetX, e.offsetY]);
if (
nearestIndex &&
nearestIndex.yIndex !== null &&
nearestIndex.binIndex !== null
) {
const {binIndex, yIndex} = nearestIndex;
const chartData = this.fullData;
const hoveredItem = chartData[yIndex];
const p = Math.max(0, d3.precisionRound(0.01, 1.01) - 1);
const yValueFormat = d3.format(`.${p}e`);
const gridRect = this.charObj
.getModel()
.getComponent('grid', 0)
.coordinateSystem.getRect();
const gridRectY = gridRect.y - 10;
let linePoints = [];
if (!hoveredItem || !hoveredItem.items[binIndex]) {
return;
}
if (!this.chartTipFlag) {
this.chartTipFlag = true;
this.$emit('chartTipFlagChange', this.chartTipFlag);
}
if (this.viewName === 1 && yIndex !== null) {
linePoints = this.makePolyPoints(yIndex, this.getCoord, gridRectY);
} else if (this.viewName === 0 && hoveredItem.items) {
hoveredItem.items.forEach((item) => {
linePoints.push(this.getCoord([item[2], item[3]]));
});
}

this.zrDrawElement.hoverLine = new echarts.graphic.Polyline({
silent: true,
shape: {
points: linePoints.slice(1, -1),
},
z: 999,
});
this.zr.add(this.zrDrawElement.hoverLine);

this.zrDrawElement.tooltip = new echarts.graphic.Text({});
let itemX;
const x = hoveredItem.items[binIndex][2];
let z = 0;
chartData.forEach((dataItem, index) => {
const y = dataItem.step;
const pt = this.getCoord([x, y]);
if (index === yIndex) {
z = hoveredItem.items[binIndex][3];
} else {
const items = dataItem.items;
for (let k = 1; k < items.length - 1; k++) {
const nextX = items[k + 1][2];
const nextZ = items[k + 1][3];
if (items[k][2] === x) {
z = items[k][3];
break;
} else if (items[k][2] < x && nextX > x) {
const proportionX = (x - items[k][2]) / (nextX - items[k][2]);
z = (nextZ - items[k][3]) * proportionX + items[k][3];
break;
}
}
}
itemX = pt[0];
const circleOption = {
z: 1000,
};
if (this.viewName === 1) {
pt[1] -=
((z - this.oriData.minZ) /
(this.oriData.maxZ - this.oriData.minZ)) *
gridRectY;
circleOption.shape = {
cx: itemX,
cy: pt[1],
r: 1.5,
};
} else {
circleOption.shape = {
cx: 0,
cy: 0,
r: 1.5,
};
circleOption.position = this.charObj.convertToPixel('grid', [x, z]);
}
const dot = new echarts.graphic.Circle(circleOption);
this.zr.add(dot);
this.zrDrawElement.hoverDots.push(dot);
});
this.zrDrawElement.tooltip = new echarts.graphic.Text({});

let htmlStr = '';
const hoveredAxis = hoveredItem.items[binIndex][3];
htmlStr = `<td>${
hoveredAxis.toString().length >= 6
? yValueFormat(hoveredAxis)
: hoveredAxis
}</td><td style="text-align:center;">${hoveredItem.step}</td><td>${(
hoveredItem.relative_time / 1000
).toFixed(3)}${unit}</td><td>${this.dealrelativeTime(
new Date(hoveredItem.wall_time * 1000).toString(),
)}</td>`;
const dom = document.querySelector('#tipTr');
dom.innerHTML = htmlStr;
const chartElement = document.getElementById(this.itemId);
if (chartElement) {
if (!this.fullScreen) {
const chartWidth =
chartElement.parentNode.parentNode.parentNode.parentNode
.clientWidth;
const chartHeight =
chartElement.parentNode.parentNode.parentNode.parentNode
.clientHeight;
const left =
chartElement.parentNode.parentNode.parentNode.parentNode
.offsetLeft;
const top =
chartElement.parentNode.parentNode.parentNode.parentNode
.offsetTop;
const echartTip = document.querySelector('#echartTip');
echartTip.style.top = `${top + chartHeight - 60}px`;
if (left > echartTip.clientWidth) {
echartTip.style.left = `${left - echartTip.clientWidth}px`;
} else {
echartTip.style.left = `${left + chartWidth}px`;
}
} else {
const width = document.querySelector('#echartTip').clientWidth;
const height = document.querySelector('#echartTip').clientHeight;
const screenWidth = document.body.scrollWidth;
const screenHeight = document.body.scrollHeight;
const scrollTop = document.querySelector('.cl-show-data-content')
.scrollTop;
const offsetTop = document.querySelector('.cl-show-data-content')
.offsetTop;
if (
height + e.event.y + 20 > screenHeight &&
screenHeight > height
) {
document.querySelector('#echartTip').style.top = `${e.event.y +
scrollTop -
height -
20 -
offsetTop}px`;
} else {
document.querySelector('#echartTip').style.top = `${e.event.y +
scrollTop +
20 -
offsetTop}px`;
}
if (width + e.event.x + 50 > screenWidth && screenWidth > width) {
document.querySelector('#echartTip').style.left = `${e.event.x -
width -
20}px`;
} else {
document.querySelector('#echartTip').style.left = `${e.event.x +
20}px`;
}
}
}

this.zrDrawElement.tooltipX = new echarts.graphic.Text({
position: [itemX, gridRect.y + gridRect.height],
style: {
text:
x.toString().length >= 6
? x.toExponential(3)
: Math.round(x * 1000) / 1000,
textFill: '#fff',
textAlign: 'center',
fontSize: 12,
textBackgroundColor: '#333',
textBorderWidth: 2,
textPadding: [5, 7],
rich: {},
},
z: 2000,
});
this.zr.add(this.zrDrawElement.tooltipX);
if (this.viewName === 1 && linePoints && linePoints.length) {
let text = '';
if (yIndex !== null) {
text = this.yAxisFormatter(hoveredItem.step);
}
this.zrDrawElement.tooltipY = new echarts.graphic.Text({
position: [
gridRect.x + gridRect.width,
linePoints[linePoints.length - 1][1],
],
style: {
text: text,
textFill: '#fff',
textVerticalAlign: 'middle',
fontSize: 12,
textBackgroundColor: '#333',
textBorderWidth: 2,
textPadding: [5, 7],
rich: {},
},
z: 2000,
});
this.zr.add(this.zrDrawElement.tooltipY);
}
}
},
/**
* find nearest value
* @param {Array} eventPoint value
* @return {Object}
*/
findNearestValue(eventPoint) {
if (!eventPoint || !eventPoint.length || !this.charObj || !this.oriData) {
return;
}
const value = this.charObj.convertFromPixel('grid', eventPoint);
if (!value || !value.length) {
return;
}
let binIndex = null;
let yIndex = null;
let nearestX = Infinity;
let nearestY = -Infinity;
let nearestYData = Infinity;
const gridRect = this.charObj
.getModel()
.getComponent('grid', 0)
.coordinateSystem.getRect();
const gridRectY = gridRect.y - 10;
const x = value[0];
this.fullData.forEach((dataItem, i) => {
let distY;
let yAxis;
for (let k = 0; k < dataItem.items.length - 1; k++) {
const item = dataItem.items[k];
const itemNext = dataItem.items[k + 1];
const nextX = itemNext[2];
const nextZ = itemNext[3];
if (item.length >= 4) {
if (item[2] < x && nextX >= x) {
const proportionX = (x - item[2]) / (nextX - item[2]);
yAxis = (nextZ - item[3]) * proportionX + item[3];
distY = Math.abs(value[1] - yAxis);
break;
}
}
}
if (this.viewName === 0 && distY < nearestYData) {
nearestYData = distY;
yIndex = i;
} else if (this.viewName === 1) {
const pt = this.getCoord([x, dataItem.step]);
const ptStep = pt[1];
pt[1] -=
((yAxis - this.oriData.minZ) /
(this.oriData.maxZ - this.oriData.minZ)) *
gridRectY;
if (
eventPoint[1] > pt[1] &&
eventPoint[1] < ptStep &&
ptStep > nearestY
) {
nearestY = ptStep;
yIndex = i;
}
}
});
if (yIndex === null && this.viewName === 1) {
this.fullData.forEach((item, index) => {
if (index > value[1]) {
yIndex = yIndex === null ? index : Math.min(yIndex, index);
}
});
}
if (yIndex !== null) {
const yData = this.fullData[yIndex].items;
yData.forEach((ele, index) => {
const distX = Math.abs(ele[2] - value[0]);
if (distX < nearestX) {
nearestX = distX;
binIndex = index;
}
});
binIndex =
binIndex === 0
? 1
: binIndex === yData.length - 1
? yData.length - 2
: binIndex;
}
return {
binIndex,
yIndex,
};
},
/**
* Calculate gradient color
* @param {String} startColor
* @param {String} endColor
* @param {Number} step
* @return {Array} Array of gradient color
*/
getGrientColor(startColor, endColor, step) {
const startRgb = this.formatColor(startColor);
const endRgb = this.formatColor(endColor);
const gapRgbR = (endRgb[0] - startRgb[0]) / step;
const gapRgbG = (endRgb[1] - startRgb[1]) / step;
const gapRgbB = (endRgb[2] - startRgb[2]) / step;
const colorResult = [];
for (let i = 0; i < step; i++) {
const sR = parseInt(gapRgbR * i + startRgb[0]);
const sG = parseInt(gapRgbG * i + startRgb[1]);
const sB = parseInt(gapRgbB * i + startRgb[2]);
const hex = this.formatColorToHex(`rgb(${sR},${sG},${sB})`);
colorResult.push(hex);
}
return colorResult;
},
/**
* Converts a color string to recognizable format
* @param {String} str Color string
* @return {String}
*/
formatColor(str) {
if (!str) {
return;
}
const colorReg = /^([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
let colorStr = str.toLowerCase().slice(1);
if (colorReg.test(colorStr)) {
let colorStrNew = '';
if (colorStr.length === 3) {
for (let i = 0; i < 3; i++) {
colorStrNew += colorStrNew
.slice(i, i + 1)
.concat(colorStrNew.slice(i, i + 1));
}
colorStr = colorStrNew;
}
const colorFormat = [];
for (let i = 0; i < 6; i += 2) {
colorFormat.push(parseInt(`0x${colorStr.slice(i, i + 2)}`));
}
return colorFormat;
} else {
return colorStr;
}
},
/**
* Converts rgb color string to hex
* @param {String} rgb Rgb color
* @return {String} Hex color
*/
formatColorToHex(rgb) {
const regRgb = /^(rgb|RGB)/g;
if (regRgb.test(rgb)) {
const colorSplit = rgb.replace(/(?:(|)|rgb|RGB)*/g, '').split(',');
let hexStr = '';
for (let i = 0; i < colorSplit.length; i++) {
let hexItem = Number(colorSplit[i]).toString(16);
hexItem = hexItem < 10 ? `0${hexItem}` : hexItem;
if (hexItem === '0') {
hexItem += hexItem;
}
hexStr += hexItem;
}
if (hexStr.length !== 6) {
hexStr = rgb;
}
return hexStr;
}
},
/**
* Get value
* @param {Object} seriesData
* @param {Number} dataIndex
* @param {Number} i
* @return {Number}
*/
getValue(seriesData, dataIndex, i) {
return seriesData[dataIndex][i];
},
/**
* Unbing event
*/
clearZrData() {
if (this.zr) {
this.removeTooltip();
this.zr.off('mouseout', 'mousemove');
this.zr = null;
}
},
/**
* Update histogram data
*/
updateHistogramData() {
this.$nextTick(() => {
this.formatDataToChar();
this.updateSampleData();
this.sampleEventBind();
});
},
},
destroyed() {
this.clearZrData();
},
};
</script>
<style lang="scss">
.cl-histogram-container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
.data-show-container {
width: 100%;
flex: 1;
.data-item {
width: 100%;
height: 100%;
}
}
}
</style>

+ 16
- 2
mindinsight/ui/src/locales/zh-cn.json View File

@@ -21,7 +21,9 @@
},
"symbols": {
"leftbracket": "(",
"rightbracket": ")"
"rightbracket": ")",
"point": "·",
"slashes": "/"
},
"header": {
"refreshData": "刷新数据",
@@ -166,6 +168,14 @@
"dataMap": {
"titleText": "数据图"
},
"tensors": {
"titleText": "张量",
"dimension": "形状:",
"tensorType": "数据类型:",
"viewTypeTitle": "视图",
"chartViewType": "表格",
"histogramViewType": "直方图"
},
"graph": {
"titleText": "计算图",
"downloadPic": "下载",
@@ -381,7 +391,11 @@
"selectAll": "全选",
"tagFilterPlaceHolder": "请输入需要的标签(支持正则表达式)",
"open": "展开",
"close": "折叠"
"close": "折叠",
"gridIncorrectDataError": "当前只支持最多二维数组的展示",
"gridAccuracy": "保留小数位",
"inCorrectInput": "无效输入",
"gridTableNoData": "表格无数据"
},
"error": {
"50540000": "系统错误",


+ 2
- 0
mindinsight/ui/src/main.js View File

@@ -22,7 +22,9 @@ import ElementUI from 'element-ui';
import './assets/css/element.css';
import './assets/css/reset.scss';
import i18n from './i18n';
import $ from 'jquery';

window.$ = window.jQuery = $;
Vue.use(ElementUI);

Vue.prototype.$bus = new Vue();


+ 4
- 0
mindinsight/ui/src/router.js View File

@@ -54,6 +54,10 @@ export default new Router({
path: '/train-manage/histogram',
component: () => import('./views/train-manage/histogram.vue'),
},
{
path: '/train-manage/tensor',
component: () => import('./views/train-manage/tensor.vue'),
},
{
path: '/train-manage/graph',
component: () => import('./views/train-manage/graph.vue'),


+ 12
- 0
mindinsight/ui/src/services/request-service.js View File

@@ -66,6 +66,18 @@ export default {
});
},

// query tensors sample
getTensorsSample(params) {
return axios({
method: 'get',
url: 'v1/mindinsight/datavisual/tensors',
params: params,
headers: {
ignoreError: true,
},
});
},

// query graph data
queryGraphData(params) {
return axios({


+ 4
- 0
mindinsight/ui/src/store.js View File

@@ -33,6 +33,7 @@ export default new Vuex.Store({
// multiSelevtGroup component count
multiSelectedGroupCount: 0,
tableId: 0,
componentsCount: 0,
},
mutations: {
// set cancelTokenArr
@@ -76,6 +77,9 @@ export default new Vuex.Store({
increaseTableId(state) {
state.tableId++;
},
componentsNum(state) {
state.componentsCount++;
},
},
actions: {},
});

+ 5
- 3
mindinsight/ui/src/views/train-manage/histogram.vue View File

@@ -90,7 +90,8 @@ limitations under the License.
<div class="chars-container">
<div class="char-item-content"
:id="sampleItem.domId"></div>
<div class="tag-title">{{sampleItem.tagName}}</div>
<div class="tag-title"
:title="sampleItem.tagName">{{sampleItem.tagName}}</div>
</div>
</div>
</div>
@@ -1325,16 +1326,17 @@ export default {
sampleObject.fullScreen = !sampleObject.fullScreen;
if (sampleObject.fullScreen) {
if (this.curAxisName === 2) {
sampleObject.charObj.setOption({grid: {right: 140}});
sampleObject.charOption.grid.right = 140;
}
sampleObject.charOption.toolbox.feature.myToolFullScreen.iconStyle.borderColor =
'#3E98C5';
} else {
sampleObject.charObj.setOption({grid: {right: 40}});
sampleObject.charOption.grid.right = 40;
sampleObject.charOption.toolbox.feature.myToolFullScreen.iconStyle.borderColor =
'#6D7278';
}
setTimeout(() => {
sampleObject.charObj.setOption(sampleObject.charOption);
sampleObject.charObj.resize();
document.getElementById(sampleObject.domId).scrollIntoView();
}, 0);


+ 1222
- 0
mindinsight/ui/src/views/train-manage/tensor.vue
File diff suppressed because it is too large
View File


+ 163
- 22
mindinsight/ui/src/views/train-manage/training-dashboard.vue View File

@@ -146,12 +146,28 @@ limitations under the License.
</div>
</div>
</div>
<div class="cl-dashboard-con-up no-data-hover">
<div class="coming-soon-content">
<div class="coming-soon-container">
<img :src="require('@/assets/images/coming-soon.png')" />
<p class='coming-soon-text'>
{{$t("public.stayTuned")}}
<div class="cl-dashboard-con-up"
:class="!!tensorTag && !wrongPlugin ? '' : 'no-data-hover'"
@click="viewMoreTensors">
<div class="cl-dashboard-title">{{$t("tensors.titleText")}}</div>
<div class="cl-module">
<div class="tensor-char-container"
v-show="!!tensorTag && !wrongPlugin">
<div id="tensor-chart-container">
<gridTableComponents ref="tensorChart"
:showOperate="false"
:fullData="tensorData"></gridTableComponents>
</div>
<div class="tag-text"
:title="tensorTag">{{tensorTag}}</div>
</div>
<div class="no-data-img"
key="no-chart-data"
v-show="!tensorTag || wrongPlugin">
<img :src="require('@/assets/images/nodata.png')"
alt="" />
<p class='no-data-text'>
{{$t("public.noData")}}
</p>
</div>
</div>
@@ -168,6 +184,7 @@ import {select, selectAll, format, precisionRound} from 'd3';
import 'd3-graphviz';
const d3 = {select, selectAll, format, precisionRound};
import echarts from 'echarts';
import gridTableComponents from '../../components/gridTableSimple';
export default {
data() {
return {
@@ -201,6 +218,8 @@ export default {
reloadStopTime: 1000,
wrongPlugin: false,
fileTag: '',
tensorData: [],
tensorTag: '',
};
},
computed: {
@@ -218,6 +237,9 @@ export default {
return this.$store.state.timeReloadValue;
},
},
components: {
gridTableComponents,
},
watch: {
isReload(newVal) {
if (newVal) {
@@ -282,6 +304,10 @@ export default {
if (this.histogramObj) {
this.histogramObj.resize();
}
const elementItem = this.$refs.tensorChart;
if (elementItem) {
elementItem.resizeView();
}
}, 500);
},

@@ -330,6 +356,7 @@ export default {
const imageTags = data.image || [];
const scalarTags = data.scalar || [];
const graphIds = data.graph || [];
const tensorTags = data.tensor || [];
if (graphIds.length) {
this.fileTag = graphIds[0];
}
@@ -337,6 +364,7 @@ export default {
this.getHistogramTag(histogramTags);
this.dealImageData(imageTags);
this.getScalarList(scalarTags);
this.dealTensorData(tensorTags);
if (!this.firstFloorNodes.length && graphIds.length) {
this.queryGraphData();
}
@@ -383,6 +411,20 @@ export default {
},
});
},
/**
* Viewing more tensors information
*/
viewMoreTensors() {
if (!this.tensorTag) {
return;
}
this.$router.push({
path: '/train-manage/tensor',
query: {
train_id: this.trainingJobId,
},
});
},
/**
* Go to data.
*/
@@ -785,6 +827,117 @@ export default {
this.originImageDataArr = dataList;
this.getSampleRandomly();
},
dealTensorData(tags) {
if (tags.length) {
this.tensorTag = tags[0];
} else {
this.tensorTag = '';
}
if (this.tensorTag) {
this.getTensorGridData();
}
},
getTensorGridData() {
const params = {
train_id: this.trainingJobId,
tag: this.tensorTag,
detail: 'stats',
};
RequestService.getTensorsSample(params).then(
(res) => {
if (!res || !res.data) {
return;
}
if (!res.data.tensors.length) {
return;
}
const resData = JSON.parse(JSON.stringify(res.data.tensors[0]));
if (!resData.values.length) {
this.tensorData = [];
this.$nextTick(() => {
const elementItem = this.$refs.tensorChart;
if (elementItem) {
elementItem.updateGridData();
}
});
return;
}
const data = resData.values[resData.values.length - 1];
const filterStr = this.initFilterStr(data.value.dims);
this.freshtMartixData(data.step, filterStr);
},
() => {
this.tensorData = [];
this.$nextTick(() => {
const elementItem = this.$refs.tensorChart;
if (elementItem) {
elementItem.updateGridData();
}
});
},
);
},
initFilterStr(array) {
if (!array) {
return [];
}
const countLinit = array.length - 2;
const tempArr = [];
for (let i = 0; i < array.length; i++) {
tempArr.push(i >= countLinit ? ':' : '0');
}
return `[${tempArr.toString()}]`;
},
freshtMartixData(step, filterStr) {
const params = {
train_id: this.trainingJobId,
tag: this.tensorTag,
detail: 'data',
step: step,
dims: filterStr,
};
RequestService.getTensorsSample(params).then(
(res) => {
if (!res || !res.data) {
return;
}
if (!res.data.tensors.length) {
return;
}
const resData = res.data.tensors[0];
const curStepData = resData.values[0];
let statistics = {};
if (curStepData) {
this.tensorData =
curStepData.value.data instanceof Array
? curStepData.value.data
: [curStepData.value.data];
statistics = curStepData.value.statistics;
} else {
this.tensorData = [[]];
}
this.$nextTick(() => {
const elementItem = this.$refs.tensorChart;
if (elementItem) {
elementItem.updateGridData(
true,
curStepData.value.dims,
statistics,
);
}
});
},
() => {
this.tensorData = [];
this.$nextTick(() => {
const elementItem = this.$refs.tensorChart;
if (elementItem) {
elementItem.updateGridData();
}
});
},
);
},
getHistogramTag(tagList) {
if (!tagList) {
return;
@@ -1889,20 +2042,6 @@ export default {
}
}

.coming-soon-content {
width: 100%;
height: 100%;
text-align: center;
.coming-soon-container {
position: relative;
top: calc(50% - 88px);
.coming-soon-text {
color: #000000;
font-size: 16px;
}
}
}

.no-data-hover {
cursor: not-allowed;
}
@@ -1928,13 +2067,15 @@ export default {
cursor: pointer;
}
}
#distribution-chart {
#distribution-chart,
#tensor-chart-container {
height: calc(100% - 19px);
canvas {
cursor: pointer;
}
}
.histogram-char-container {
.histogram-char-container,
.tensor-char-container {
height: 100%;
width: 100%;
cursor: pointer;


Loading…
Cancel
Save