提交 aeed834f 编写于 作者: M mindspore-ci-bot 提交者: Gitee

!412 UI add tensors feature

Merge pull request !412 from 夏易凡/0708master
......@@ -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",
......
<!--
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>
......@@ -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 -->
......
此差异已折叠。
......@@ -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": "系统错误",
......
......@@ -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();
......
......@@ -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'),
......
......@@ -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({
......
......@@ -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: {},
});
......@@ -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);
......
此差异已折叠。
......@@ -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;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册