Compare commits

...

24 Commits
master ... r0.2

Author SHA1 Message Date
  mindspore-ci-bot b82ac26d4f !760 update securec repository link for r0.2 5 years ago
  liangyongxiong baa15abf65 update securec repository link 5 years ago
  mindspore-ci-bot 656c103349 !99 add readme for parameter distribution visualization 5 years ago
  mindspore-ci-bot 3ecd02cd3c !97 change bugfixes link 5 years ago
  wenkai 7e273658ae add readme for parameter distribution visualization 5 years ago
  kouzhenzhong c9188b5320 Bump the version number to 0.2.0-alpha 5 years ago
  mindspore-ci-bot bd08f8051d !88 Fix: Samples from previous steps are erased due to tags size too large 5 years ago
  李鸿章 fa302959db fix: samples are erased when tag size overflow 5 years ago
  mindspore-ci-bot e21949d7bc !96 rc0.2 release notes 5 years ago
  kouzhenzhong 3989ee3e7a r0.2 release notes 5 years ago
  mindspore-ci-bot 47e362fd1e !94 fix scalar ui bug 5 years ago
  mindspore-ci-bot f346ff8e5e !90 Drop histogram steps if original_buckets_count is too large. [ for r0.2] 5 years ago
  mindspore-ci-bot 9fb427c66d !93 Add mixed type for customized, set operation as 'None' when it is empty or None 5 years ago
  WeiFeng aaa8b84663 fix ui bug 5 years ago
  luopengting c97736438a add mixed type for customized, set operation as None when it is empty or None 5 years ago
  mindspore-ci-bot a7d10ae841 !91 fix lineage issue and scalar issue 5 years ago
  ph 0f7165c5c6 fix issue 5 years ago
  liangyongxiong d0440800b6 Drop histogram steps if original_buckets_count is too large. 5 years ago
  mindspore-ci-bot f78d7f6d13 !89 UI fix histogram and data-map issue 5 years ago
  ph 5b11618180 fix issue 5 years ago
  mindspore-ci-bot 64516265db !87 fix histogram tip issue and support fitting screen for data-map graph 5 years ago
  ph 68fe7b8cbd fix histogram and data-map issue 5 years ago
  mindspore-ci-bot 04782eb940 !80 optimize parsing for out-of-order events 5 years ago
  luopengting 9ae239b5b4 optimize parsing for out-of-order events 5 years ago
28 changed files with 684 additions and 215 deletions
Split View
  1. +1
    -1
      .gitmodules
  2. +5
    -0
      README.md
  3. +23
    -0
      RELEASE.md
  4. +14
    -7
      mindinsight/datavisual/data_transform/events_data.py
  5. +9
    -0
      mindinsight/datavisual/data_transform/histogram_container.py
  6. +21
    -11
      mindinsight/datavisual/data_transform/ms_data_loader.py
  7. +33
    -5
      mindinsight/datavisual/data_transform/reservoir.py
  8. +38
    -15
      mindinsight/lineagemgr/querier/querier.py
  9. +2
    -3
      mindinsight/lineagemgr/summary/_summary_adapter.py
  10. +1
    -1
      mindinsight/ui/public/index.html
  11. BIN
      mindinsight/ui/public/static/img/favicon.ico
  12. BIN
      mindinsight/ui/public/static/img/favicon.png
  13. BIN
      mindinsight/ui/src/assets/images/fit.png
  14. +10
    -0
      mindinsight/ui/src/common/common-property.js
  15. +6
    -0
      mindinsight/ui/src/components/multiselectGroup.vue
  16. +9
    -6
      mindinsight/ui/src/locales/zh-cn.json
  17. +163
    -1
      mindinsight/ui/src/views/train-manage/data-map.vue
  18. +2
    -0
      mindinsight/ui/src/views/train-manage/data-traceback.vue
  19. +4
    -0
      mindinsight/ui/src/views/train-manage/graph.vue
  20. +34
    -10
      mindinsight/ui/src/views/train-manage/histogram.vue
  21. +97
    -17
      mindinsight/ui/src/views/train-manage/model-traceback.vue
  22. +126
    -125
      mindinsight/ui/src/views/train-manage/scalar.vue
  23. +14
    -0
      mindinsight/ui/src/views/train-manage/training-dashboard.vue
  24. +56
    -5
      tests/ut/datavisual/data_transform/test_events_data.py
  25. +4
    -0
      tests/ut/datavisual/data_transform/test_reservoir.py
  26. +3
    -5
      tests/ut/datavisual/processors/test_images_processor.py
  27. +6
    -0
      tests/ut/lineagemgr/querier/event_data.py
  28. +3
    -3
      tests/ut/lineagemgr/querier/test_querier.py

+ 1
- 1
.gitmodules View File

@@ -1,3 +1,3 @@
[submodule "third_party/securec"]
path = third_party/securec
url = https://gitee.com/openeuler/bounds_checking_function.git
url = https://gitee.com/openeuler/libboundscheck.git

+ 5
- 0
README.md View File

@@ -68,6 +68,11 @@ training process, such as loss value and accuracy rate of each iteration.

Two scalar curves can be combined and displayed in one chart.

### Parameter distribution

The GUI of MindInsight displays the distribution change tendency of a tensor such as weight
or gradient during the entire training process.

### Image visualization

The GUI of MindInsight displays both original images and enhanced images during the entire


+ 23
- 0
RELEASE.md View File

@@ -1,5 +1,28 @@
## MindInsight

# Release 0.2.0-alpha

## Major Features and Improvements
* Parameter distribution graph (Histogram).
Now you can use [`HistogramSummary`](https://www.mindspore.cn/api/zh-CN/master/api/python/mindspore/mindspore.ops.operations.html#mindspore.ops.operations.HistogramSummary) and MindInsight to record and visualize distribution info of tensors. See our [tutorial](https://www.mindspore.cn/tutorial/zh-CN/master/advanced_use/visualization_tutorials.html) for details.
* Lineage support Custom information
* GPU support
* Model and dataset tracking linkage support

## Bugfixes
* Reduce cyclomatic complexity of `list_summary_directories` ([!11](https://gitee.com/mindspore/mindinsight/pulls/11)).
* Fix unsafe functions and duplication files and redundant codes ([!14](https://gitee.com/mindspore/mindinsight/pulls/14)).
* Fix sha256 checksum missing bug ([!24](https://gitee.com/mindspore/mindinsight/pulls/24)).
* Fix graph bug when node name is empty ([!34](https://gitee.com/mindspore/mindinsight/pulls/34)).
* Fix start/stop command exit-code incorrect ([!44](https://gitee.com/mindspore/mindinsight/pulls/44)).

## Thanks to our Contributors
Thanks goes to these wonderful people:

Ye Huang, Weifeng Huang, Zhenzhong Kou, Pengting Luo, Hongzhang Li, Yongxiong Liang, Gongchang Ou, Hui Pan, Luyu Qiu, Junyan Qin, Kai Wen, Weining Wang, Yifan Xia, Yunshu Zhang, Ting Zhao

Contributions of any kind are welcome!

# Release 0.1.0-alpha

* Training process observation


+ 14
- 7
mindinsight/datavisual/data_transform/events_data.py View File

@@ -23,13 +23,13 @@ from mindinsight.conf import settings


# Type of the tensor event from external component
_Tensor = collections.namedtuple('_Tensor', ['wall_time', 'step', 'value'])
_Tensor = collections.namedtuple('_Tensor', ['wall_time', 'step', 'value', 'filename'])
TensorEvent = collections.namedtuple(
'TensorEvent', ['wall_time', 'step', 'tag', 'plugin_name', 'value'])
'TensorEvent', ['wall_time', 'step', 'tag', 'plugin_name', 'value', 'filename'])

# config for `EventsData`
_DEFAULT_STEP_SIZES_PER_TAG = settings.DEFAULT_STEP_SIZES_PER_TAG
_MAX_DELETED_TAGS_SIZE = settings.MAX_TAG_SIZE_PER_EVENTS_DATA * 100
CONFIG = {
'max_total_tag_sizes': settings.MAX_TAG_SIZE_PER_EVENTS_DATA,
'max_tag_sizes_per_plugin':
@@ -60,6 +60,7 @@ class EventsData:
self._max_step_sizes_per_tag = self._config['max_step_sizes_per_tag']

self._tags = list()
self._deleted_tags = set()
self._reservoir_by_tag = {}
self._reservoir_mutex_lock = threading.Lock()

@@ -82,6 +83,8 @@ class EventsData:
if tag not in set(self._tags):
deleted_tag = self._check_tag_out_of_spec(plugin_name)
if deleted_tag is not None:
if tag in self._deleted_tags:
return
self.delete_tensor_event(deleted_tag)

self._tags.append(tag)
@@ -99,10 +102,11 @@ class EventsData:

tensor = _Tensor(wall_time=tensor_event.wall_time,
step=tensor_event.step,
value=tensor_event.value)
value=tensor_event.value,
filename=tensor_event.filename)

if self._is_out_of_order_step(tensor_event.step, tensor_event.tag):
self.purge_reservoir_data(tensor_event.step, self._reservoir_by_tag[tag])
self.purge_reservoir_data(tensor_event.filename, tensor_event.step, self._reservoir_by_tag[tag])

self._reservoir_by_tag[tag].add_sample(tensor)

@@ -113,6 +117,8 @@ class EventsData:
Args:
tag (str): The tag name.
"""
if len(self._deleted_tags) < _MAX_DELETED_TAGS_SIZE:
self._deleted_tags.add(tag)
self._tags.remove(tag)
for plugin_name, lock in self._tags_by_plugin_mutex_lock.items():
with lock:
@@ -176,7 +182,7 @@ class EventsData:
return False

@staticmethod
def purge_reservoir_data(start_step, tensor_reservoir):
def purge_reservoir_data(filename, start_step, tensor_reservoir):
"""
Purge all tensor event that are out-of-order step after the given start step.

@@ -188,7 +194,8 @@ class EventsData:
Returns:
int, the number of items removed.
"""
cnt_out_of_order = tensor_reservoir.remove_sample(lambda x: x.step < start_step)
cnt_out_of_order = tensor_reservoir.remove_sample(
lambda x: x.step < start_step or (x.step > start_step and x.filename == filename))

return cnt_out_of_order



+ 9
- 0
mindinsight/datavisual/data_transform/histogram_container.py View File

@@ -72,6 +72,10 @@ class Bucket:


class HistogramContainer:

# Max quantity of original buckets.
MAX_ORIGINAL_BUCKETS_COUNT = 90

"""
Histogram data container.

@@ -114,6 +118,11 @@ class HistogramContainer:
"""Gets original proto message."""
return self._msg

@property
def original_buckets_count(self):
"""Gets original buckets quantity."""
return len(self._original_buckets)

def set_visual_range(self, max_val: float, min_val: float, bins: int) -> None:
"""
Sets visual range for later re-sampling.


+ 21
- 11
mindinsight/datavisual/data_transform/ms_data_loader.py View File

@@ -223,7 +223,8 @@ class MSDataLoader:
step=event.step,
tag=tag,
plugin_name=PluginNameEnum.SCALAR.value,
value=value.scalar_value)
value=value.scalar_value,
filename=self._latest_summary_filename)
self._events_data.add_tensor_event(tensor_event)

if value.HasField('image'):
@@ -232,18 +233,25 @@ class MSDataLoader:
step=event.step,
tag=tag,
plugin_name=PluginNameEnum.IMAGE.value,
value=value.image)
value=value.image,
filename=self._latest_summary_filename)
self._events_data.add_tensor_event(tensor_event)

if value.HasField('histogram'):
histogram_msg = HistogramContainer(value.histogram)
tag = '{}/{}'.format(value.tag, PluginNameEnum.HISTOGRAM.value)
tensor_event = TensorEvent(wall_time=event.wall_time,
step=event.step,
tag=tag,
plugin_name=PluginNameEnum.HISTOGRAM.value,
value=histogram_msg)
self._events_data.add_tensor_event(tensor_event)
# Drop steps if original_buckets_count exceeds HistogramContainer.MAX_ORIGINAL_BUCKETS_COUNT
# to avoid time-consuming re-sample process.
if histogram_msg.original_buckets_count > HistogramContainer.MAX_ORIGINAL_BUCKETS_COUNT:
logger.warning('original_buckets_count exceeds HistogramContainer.MAX_ORIGINAL_BUCKETS_COUNT')
else:
tag = '{}/{}'.format(value.tag, PluginNameEnum.HISTOGRAM.value)
tensor_event = TensorEvent(wall_time=event.wall_time,
step=event.step,
tag=tag,
plugin_name=PluginNameEnum.HISTOGRAM.value,
value=histogram_msg,
filename=self._latest_summary_filename)
self._events_data.add_tensor_event(tensor_event)

if event.HasField('graph_def'):
graph_proto = event.graph_def
@@ -253,7 +261,8 @@ class MSDataLoader:
step=event.step,
tag=self._latest_summary_filename,
plugin_name=PluginNameEnum.GRAPH.value,
value=graph)
value=graph,
filename=self._latest_summary_filename)

try:
graph_tags = self._events_data.list_tags_by_plugin(PluginNameEnum.GRAPH.value)
@@ -436,7 +445,8 @@ class _PbParser:
step=0,
tag=filename,
plugin_name=PluginNameEnum.GRAPH.value,
value=graph)
value=graph,
filename=filename)

logger.info("Build graph success, file path: %s.", file_path)
return tensor_event

+ 33
- 5
mindinsight/datavisual/data_transform/reservoir.py View File

@@ -23,6 +23,24 @@ from mindinsight.utils.exceptions import ParamValueError
from mindinsight.datavisual.utils.utils import calc_histogram_bins


def binary_search(samples, target):
"""Binary search target in samples."""
left = 0
right = len(samples) - 1
while left <= right:
mid = (left + right) // 2
if target < samples[mid].step:
right = mid - 1
elif target > samples[mid].step:
left = mid + 1
else:
return mid

# if right is -1, it is less than the first one.
# if list is [1, 2, 4], target is 3, right will be 1, so wo will insert by 2.
return right + 1


class Reservoir:
"""
A container based on Reservoir Sampling algorithm.
@@ -68,18 +86,28 @@ class Reservoir:
"""
with self._mutex:
if len(self._samples) < self._samples_max_size or self._samples_max_size == 0:
self._samples.append(sample)
self._add_sample(sample)
else:
# Use the Reservoir Sampling algorithm to replace the old sample.
rand_int = self._sample_selector.randint(
0, self._sample_counter)
rand_int = self._sample_selector.randint(0, self._sample_counter)
if rand_int < self._samples_max_size:
self._samples.pop(rand_int)
self._samples.append(sample)
else:
self._samples[-1] = sample
self._samples = self._samples[:-1]
self._add_sample(sample)
self._sample_counter += 1

def _add_sample(self, sample):
"""Search the index and add sample."""
if not self._samples or sample.step > self._samples[-1].step:
self._samples.append(sample)
return
index = binary_search(self._samples, sample.step)
if index == len(self._samples):
self._samples.append(sample)
else:
self._samples.insert(index, sample)

def remove_sample(self, filter_fun):
"""
Remove the samples from Reservoir that do not meet the filter criteria.


+ 38
- 15
mindinsight/lineagemgr/querier/querier.py View File

@@ -288,8 +288,8 @@ class Querier:
try:
cmp_result = (value1 > value2) - (value1 < value2)
except TypeError:
type1 = str(type(value1))
type2 = str(type(value2))
type1 = type(value1).__name__
type2 = type(value2).__name__
cmp_result = (type1 > type2) - (type1 < type2)
return cmp_result

@@ -314,19 +314,7 @@ class Querier:

offset_results = self._handle_limit_and_offset(condition, results)

customized = dict()
for offset_result in offset_results:
for obj_name in ["metric", "user_defined"]:
obj = getattr(offset_result, obj_name)
require = bool(obj_name == "metric")
if obj and isinstance(obj, dict):
for key, value in obj.items():
label = f'{obj_name}/{key}'
customized[label] = dict()
customized[label]["label"] = label
# user defined info is not displayed by default
customized[label]["required"] = require
customized[label]["type"] = type(value).__name__
customized = self._organize_customized(offset_results)

lineage_types = condition.get(ConditionParam.LINEAGE_TYPE.value)
lineage_types = self._get_lineage_types(lineage_types)
@@ -348,6 +336,41 @@ class Querier:

return lineage_info

def _organize_customized(self, offset_results):
"""Organize customized."""
customized = dict()
for offset_result in offset_results:
for obj_name in ["metric", "user_defined"]:
self._organize_customized_item(customized, offset_result, obj_name)

# If types contain numbers and string, it will be "mixed".
# If types contain "int" and "float", it will be "float".
for key, value in customized.items():
types = value["type"]
if len(types) == 1:
customized[key]["type"] = list(types)[0]
elif types.issubset(["int", "float"]):
customized[key]["type"] = "float"
else:
customized[key]["type"] = "mixed"
return customized

def _organize_customized_item(self, customized, offset_result, obj_name):
"""Organize customized item."""
obj = getattr(offset_result, obj_name)
require = bool(obj_name == "metric")
if obj and isinstance(obj, dict):
for key, value in obj.items():
label = f'{obj_name}/{key}'
current_type = type(value).__name__
if customized.get(label) is None:
customized[label] = dict()
customized[label]["label"] = label
# user defined info is not displayed by default
customized[label]["required"] = require
customized[label]["type"] = set()
customized[label]["type"].add(current_type)

def _get_lineage_types(self, lineage_type_param):
"""
Get lineage types.


+ 2
- 3
mindinsight/lineagemgr/summary/_summary_adapter.py View File

@@ -70,13 +70,13 @@ def _package_current_dataset(operation, message):
message (Operation): Operation proto message.
"""
for key, value in operation.items():
if key == "operations":
if value and key == "operations":
for operator in value:
_package_enhancement_operation(
operator,
message.operations.add()
)
elif key == "sampler":
elif value and key == "sampler":
_package_enhancement_operation(
value,
message.sampler
@@ -93,7 +93,6 @@ def _package_enhancement_operation(operation, message):
operation (dict): Enhancement operation.
message (Operation): Enhancement operation proto message.
"""

for key, value in operation.items():
if isinstance(value, list):
if all(isinstance(ele, int) for ele in value):


+ 1
- 1
mindinsight/ui/public/index.html View File

@@ -21,7 +21,7 @@ limitations under the License.
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" href="<%= BASE_URL %>/static/img/favicon.ico" />
<link rel="icon" href="<%= BASE_URL %>/static/img/favicon.png" />
<title>MindInsight</title>
<style>
.errorInfo {


BIN
mindinsight/ui/public/static/img/favicon.ico View File


BIN
mindinsight/ui/public/static/img/favicon.png View File

Before After
Width: 128  |  Height: 128  |  Size: 6.9 kB Width: 128  |  Height: 128  |  Size: 6.9 kB

BIN
mindinsight/ui/src/assets/images/fit.png View File

Before After
Width: 16  |  Height: 14  |  Size: 403 B Width: 16  |  Height: 14  |  Size: 403 B

+ 10
- 0
mindinsight/ui/src/common/common-property.js View File

@@ -96,4 +96,14 @@ export default {
'.edge.highlighted path {stroke: red;}.edge.highlighted polygon {' +
'stroke: red;fill: red;}' +
'.edge.highlighted marker path {fill: red;}</style>',
dataMapDownloadStyle: '<style> #graph0 > polygon { fill: transparent; }' +
'.node, .cluster { cursor: pointer; }' +
'.selected { polygon, ellipse { stroke: red !important; stroke-width: 2px; } }' +
'.CreatDataset > polygon, .Operator > ellipse { stroke: #58a4e0; fill: #d1ebff; }' +
'.cluster > polygon { fill: #c1f5d5; stroke: #56b077; }' +
'.RepeatDataset > polygon { stroke: #fdca5a; fill: #fff2d4; }' +
'.ShuffleDataset > polygon { stroke: #f79666; fill: #fed78e; }' +
'.BatchDataset > polygon { stroke: #fa8e5a; fill: #ffcfb8; }' +
'.edge { path { stroke: rgb(167, 167, 167); }' +
'polygon { fill: rgb(167, 167, 167); stroke: rgb(167, 167, 167); } }</style>',
};

+ 6
- 0
mindinsight/ui/src/components/multiselectGroup.vue View File

@@ -99,6 +99,7 @@ export default {
multiSelectedItemNames: {}, // Dictionary for storing the name of the selected tags.
operateSelectAll: true, // Indicates whether to select all tags.
perSelectItemMarginBottom: 1, // Outer margin of the bottom of each selection box.
searching: false,
};
},
computed: {},
@@ -156,11 +157,13 @@ export default {
* Tag Filter
*/
listFilter() {
this.searching = true;
if (this.searchInputTimer) {
clearTimeout(this.searchInputTimer);
this.searchInputTimer = null;
}
this.searchInputTimer = setTimeout(() => {
this.searching = false;
let reg;
try {
reg = new RegExp(this.searchInput);
@@ -234,6 +237,9 @@ export default {
* @return {Object} Dictionary containing selected tags
*/
updateSelectedDic() {
if (this.searching) {
return this.multiSelectedItemNames;
}
let reg;
try {
reg = new RegExp(this.searchInput);


+ 9
- 6
mindinsight/ui/src/locales/zh-cn.json View File

@@ -45,19 +45,20 @@
"learningRate": "学习率",
"modelSize": "模型大小",
"dataProcess": "数据处理",
"noDataFound":"暂无满足筛选条件的数据",
"noDataTips":"请点击“显示全量数据”按钮查看全量数据",
"noDataFound": "暂无满足筛选条件的数据",
"noDataTips": "请点击“显示全量数据”按钮查看全量数据",
"userDefined": "自定义数据",
"metric": "度量指标",
"deviceNum": "device数目"
"deviceNum": "device数目",
"mixedItemMessage": "该参数含有多种类型数据,无法筛选展示"
},
"dataTraceback": {
"details": "详情",
"key": "KEY",
"value": "VALUE",
"dataTraceTips": "该数据涉及合并操作",
"noDataFound":"暂无满足筛选条件的数据",
"noDataTips":"请点击“显示全量数据”按钮查看全量数据"
"noDataFound": "暂无满足筛选条件的数据",
"noDataTips": "请点击“显示全量数据”按钮查看全量数据"
},
"trainingDashboard": {
"trainingDashboardTitle": "训练看板",
@@ -66,7 +67,8 @@
"trainingScalar": "训练标量信息",
"samplingData": "数据抽样",
"imagesampleSwitch": "切换标签",
"invalidId": "无效的训练作业"
"invalidId": "无效的训练作业",
"summaryDirPath": "summary路径:"
},
"scalar": {
"titleText": "标量",
@@ -120,6 +122,7 @@
"graph": {
"titleText": "计算图",
"downloadPic": "下载",
"fitScreen": "适应屏幕",
"nodeInfo": "节点信息",
"legend": "图例",
"nameSpace": "命名空间",


+ 163
- 1
mindinsight/ui/src/views/train-manage/data-map.vue View File

@@ -46,6 +46,12 @@ limitations under the License.
<div :title="$t('graph.fullScreen')"
class="full-screen-button"
@click="fullScreen = !fullScreen"></div>
<div :title="$t('graph.fitScreen')"
class="fit-screen"
@click="fit()"></div>
<div :title="$t('graph.downloadPic')"
class="download-button"
@click="downLoadSVG"></div>
</div>
<!-- Right column -->
<div id="sidebar"
@@ -138,6 +144,7 @@ limitations under the License.

<script>
import RequestService from '../../services/request-service';
import CommonProperty from '@/common/common-property.js';
import {select, selectAll, zoom} from 'd3';
import 'd3-graphviz';
const d3 = {select, selectAll, zoom};
@@ -340,6 +347,7 @@ export default {
* Initialization method executed after the graph rendering is complete
*/
startApp() {
this.initZooming();
const nodes = d3.selectAll('g.node, g.cluster');
nodes.on('click', (target, index, nodesList) => {
this.selectNodeInfo(target);
@@ -347,9 +355,109 @@ export default {
nodes.classed('selected', false);
d3.select(`g[id="${selectedNode.id}"]`).classed('selected', true);
});
d3.select('svg').on('dblclick.zoom', null);
},

/**
* Initializing the Zoom Function of a Graph
*/
initZooming() {
const graphDom = document.querySelector('#graph0');
const graphBox = graphDom.getBBox();
const padding = 4;
const pointer = {
start: {
x: 0,
y: 0,
},
end: {
x: 0,
y: 0,
},
};
const zoom = d3
.zoom()
.on('start', (target) => {
pointer.start.x = event.x;
pointer.start.y = event.y;
})
.on('zoom', (target) => {
const transformData = this.getTransformData(graphDom);
let tempStr = '';
let change = {};
let scale = transformData.scale[0];
const graphRect = graphDom.getBoundingClientRect();
const mapping = {
width: graphBox.width / graphRect.width,
height: graphBox.height / graphRect.height,
};
if (event.type === 'mousemove') {
pointer.end.x = event.x;
pointer.end.y = event.y;
change = {
x: (pointer.end.x - pointer.start.x) * mapping.width * scale,
y: (pointer.end.y - pointer.start.y) * mapping.height * scale,
};
pointer.start.x = pointer.end.x;
pointer.start.y = pointer.end.y;
} else if (event.type === 'wheel') {
const wheelDelta = event.wheelDelta;
const rate = Math.abs(wheelDelta / 100);
scale =
wheelDelta > 0
? transformData.scale[0] * rate
: transformData.scale[0] / rate;
scale = Math.max(this.scaleRange[0], scale);
scale = Math.min(this.scaleRange[1], scale);
change = {
x:
(graphRect.x + padding / mapping.width - event.x) *
mapping.width *
(scale - transformData.scale[0]),
y:
(graphRect.bottom - padding / mapping.height - event.y) *
mapping.height *
(scale - transformData.scale[0]),
};
}
tempStr = `translate(${transformData.translate[0] +
change.x},${transformData.translate[1] +
change.y}) scale(${scale})`;
graphDom.setAttribute('transform', tempStr);
});

const svg = d3.select('svg');
svg.on('.zoom', null);
svg.call(zoom);
svg.on('dblclick.zoom', null);
},
/**
* Obtains the transform data of a node.
* @param {Object} node Node dom data
* @return {Object} transform data of a node
*/
getTransformData(node) {
if (!node) {
return [];
}
const transformData = node.getAttribute('transform');
const attrObj = {};
if (transformData) {
const lists = transformData.trim().split(' ');
lists.forEach((item) => {
const index1 = item.indexOf('(');
const index2 = item.indexOf(')');
const name = item.substring(0, index1);
const params = item
.substring(index1 + 1, index2)
.split(',')
.map((i) => {
return parseFloat(i) || 0;
});
attrObj[name] = params;
});
}
return attrObj;
},
/**
* Process the selected node information.
* @param {Object} target Selected Object
@@ -397,6 +505,36 @@ export default {
});
}
},
/**
* Adapt to the screen
*/
fit() {
const graphDom = document.getElementById('graph0');
const box = graphDom.getBBox();
const str = `translate(${-box.x},${-box.y}) scale(1)`;
graphDom.setAttribute('transform', str);
},
/**
* Download svg
*/
downLoadSVG() {
const svgXml = document.querySelector('#graph #graph0').innerHTML;
const bbox = document.getElementById('graph0').getBBox();
const viewBoxSize = `${bbox.x} ${bbox.y} ${bbox.width} ${bbox.height}`;
const encodeStr =
`<svg xmlns="http://www.w3.org/2000/svg" ` +
`xmlns:xlink="http://www.w3.org/1999/xlink" ` +
`width="${bbox.width}" height="${bbox.height}" ` +
`viewBox="${viewBoxSize}">${CommonProperty.dataMapDownloadStyle}<g>${svgXml}</g></svg>`;

// Write the svg stream encoded by base64 to the image object.
const src = `data:image/svg+xml;base64,
${window.btoa(unescape(encodeURIComponent(encodeStr)))}`;
const a = document.createElement('a');
a.href = src; // Export the information in the canvas as image data.
a.download = 'dataMap'; // Set the download name.
a.click(); // Click to trigger download.
},
/**
* Collapse on the right
*/
@@ -515,6 +653,30 @@ export default {
display: inline-block;
background-image: url('../../assets/images/full-screen.png');
}
.fit-screen {
position: absolute;
width: 16px;
height: 14px;
right: 32px;
top: 10px;
z-index: 999;
cursor: pointer;
display: inline-block;
background-image: url('../../assets/images/fit.png');
}
.download-button {
position: absolute;
width: 16px;
height: 14px;
right: 54px;
top: 10px;
z-index: 999;
cursor: pointer;
display: inline-block;
background-image: url('../../assets/images/download.png');
background-size: 14px 14px;
background-repeat: no-repeat;
}
}
}
.cl-data-map.full-screen {


+ 2
- 0
mindinsight/ui/src/views/train-manage/data-traceback.vue View File

@@ -509,6 +509,7 @@ export default {
});
// The summaryList value could not be saved in the destroy state.
this.dataCheckedSummary = [];
this.tableFilter.summary_dir = {in: summaryList};
this.$store.commit('setSummaryDirList', summaryList);
if (!tempList.length) {
this.summaryDirList = [];
@@ -967,6 +968,7 @@ export default {
sorted_name: data.prop,
sorted_type: data.order,
};
this.pagination.currentPage = 1;
params.body = Object.assign({}, tempParam, this.tableFilter);
this.queryLineagesData(params);
},


+ 4
- 0
mindinsight/ui/src/views/train-manage/graph.vue View File

@@ -1010,6 +1010,10 @@ export default {
source = node.name;
for (let i = 0; i < Math.min(5, keys.length); i++) {
target = keys[i];
isConst = !!(
this.allGraphData[keys[i]] &&
this.allGraphData[keys[i]].type === 'Const'
);
const nodeStr = isConst
? `shape="circle";width="0.14";height="0.14";fixedsize=true;` +
`label="${target.split('/').pop()}\n\n\n";`


+ 34
- 10
mindinsight/ui/src/views/train-manage/histogram.vue View File

@@ -138,6 +138,8 @@ export default {
zrDrawElement: {hoverDots: []},
chartTipFlag: false,
charResizeTimer: null,
changeAxisTimer: null,
changeViewTimer: null,
};
},
computed: {
@@ -220,6 +222,14 @@ export default {
this.$store.commit('setIsReload', false);
this.isReloading = false;
}
if (this.changeAxisTimer) {
clearTimeout(this.changeAxisTimer);
this.changeAxisTimer = null;
}
if (this.changeViewTimer) {
clearTimeout(this.changeViewTimer);
this.changeViewTimer = null;
}
},
mounted() {
this.init();
@@ -394,19 +404,31 @@ export default {
* @param {Number} val Current mode
*/
timeTypeChange(val) {
this.curPageArr.forEach((item) => {
this.updateSampleData(item);
});
if (this.changeAxisTimer) {
clearTimeout(this.changeAxisTimer);
this.changeAxisTimer = null;
}
this.changeAxisTimer = setTimeout(() => {
this.curPageArr.forEach((item) => {
this.updateSampleData(item);
});
}, 500);
},
/**
* The view display type is changed
* @param {Number} val Current mode
*/
viewTypeChange(val) {
this.curPageArr.forEach((item) => {
this.formatDataToChar(item);
this.updateSampleData(item);
});
if (this.changeViewTimer) {
clearTimeout(this.changeViewTimer);
this.changeViewTimer = null;
}
this.changeViewTimer = setTimeout(() => {
this.curPageArr.forEach((item) => {
this.formatDataToChar(item);
this.updateSampleData(item);
});
}, 200);
},
/**
* Update the data list based on the filtered tags
@@ -743,7 +765,7 @@ export default {
}</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).toString(),
new Date(hoveredItem.wall_time * 1000).toString(),
)}</td>`;
const dom = document.querySelector('#tipTr');
dom.innerHTML = htmlStr;
@@ -996,7 +1018,9 @@ export default {
const z = this.getValue(rawData, dataIndex, i++);
const pt = getCoord([x, y], sampleObject);
// linear map in z axis
pt[1] -= ((z - minZ) / (maxZ - minZ)) * yValueMapHeight;
if (maxZ !== minZ) {
pt[1] -= ((z - minZ) / (maxZ - minZ)) * yValueMapHeight;
}
points.push(pt);
}
return points;
@@ -1216,7 +1240,7 @@ export default {
if (filter.length) {
if (this.curAxisName === 2) {
data = sampleObject.fullScreen
? this.dealrelativeTime(new Date(filter[0].wall_time).toString())
? this.dealrelativeTime(new Date(filter[0].wall_time * 1000).toString())
: [];
} else if (this.curAxisName === 1) {
data = `${(filter[0].relative_time / 3600).toFixed(3)}h`;


+ 97
- 17
mindinsight/ui/src/views/train-manage/model-traceback.vue View File

@@ -151,6 +151,7 @@ export default {
'batch_size',
'device_num',
], // All keys whose values are int
keysOfMixed: [],
echart: {
chart: null,
allData: [],
@@ -166,10 +167,7 @@ export default {
},
chartFilter: {}, // chart filter condition
tableFilter: {lineage_type: {in: ['model']}}, // table filter condition
sortInfo: {
sorted_name: 'summary_dir',
sorted_type: null,
},
sortInfo: {},
showTable: false,
noData: false,
haveCustomizedParams: false,
@@ -281,6 +279,7 @@ export default {
selectAll: false, // Whether to select all columns
indeterminate: false,
};
this.keysOfMixed = [];
this.queryLineagesData(true);
},
/**
@@ -324,10 +323,15 @@ export default {

tempParam.limit = this.pagination.pageSize;
tempParam.offset = this.pagination.currentPage - 1;
params.body = Object.assign(params.body, this.chartFilter);
params.body = Object.assign(
params.body,
this.chartFilter,
tempParam,
this.tableFilter,
);
} else {
params.body = Object.assign(params.body, this.tableFilter);
}
params.body = Object.assign(params.body, tempParam, this.tableFilter);

RequestService.queryLineagesData(params)
.then(
(res) => {
@@ -344,6 +348,10 @@ export default {
this.keysOfIntValue.push(i);
} else if (customized[i].type === 'str') {
this.keysOfStringValue.push(i);
} else if (customized[i].type === 'mixed') {
// list of type mixed
this.keysOfMixed.push(i);
this.keysOfStringValue.push(i);
}
if (i.startsWith(this.replaceStr.userDefined)) {
customized[i].label = customized[i].label.replace(
@@ -568,7 +576,16 @@ export default {
};

chartAxis.forEach((key) => {
item.value.push(i[key]);
if (
(i[key] || i[key] == 0) &&
this.keysOfMixed &&
this.keysOfMixed.length &&
this.keysOfMixed.includes(key)
) {
item.value.push(i[key].toString());
} else {
item.value.push(i[key]);
}
});
data.push(item);
});
@@ -581,7 +598,7 @@ export default {
const values = {};
this.echart.showData.forEach((i) => {
if (i[key] || i[key] === 0) {
values[i[key]] = i[key];
values[i[key].toString()] = i[key].toString();
}
});
obj.type = 'category';
@@ -693,6 +710,15 @@ export default {
// select use api
this.echart.chart.on('axisareaselected', (params) => {
const key = params.parallelAxisId;
if (
this.keysOfMixed &&
this.keysOfMixed.length &&
this.keysOfMixed.includes(key)
) {
this.$message.error(this.$t('modelTraceback.mixedItemMessage'));
this.initChart();
return;
}
const list = this.$store.state.selectedBarList || [];
const selectedAxisId = params.parallelAxisId;
if (list.length) {
@@ -741,6 +767,12 @@ export default {
{},
this.chartFilter,
this.tableFilter,
);
const tableParams = {};
tableParams.body = Object.assign(
{},
this.chartFilter,
this.tableFilter,
this.sortInfo,
);
RequestService.queryLineagesData(filterParams)
@@ -752,20 +784,47 @@ export default {
res.data.object &&
res.data.object.length
) {
let customized = {};
customized = JSON.parse(JSON.stringify(res.data.customized));
const customizedKeys = Object.keys(customized);
if (customizedKeys.length) {
this.keysOfStringValue = [
'summary_dir',
'network',
'optimizer',
'loss_function',
'train_dataset_path',
'test_dataset_path',
'dataset_mark',
];
this.keysOfIntValue = [
'train_dataset_count',
'test_dataset_count',
'epoch',
'batch_size',
'device_num',
];
this.keysOfMixed = [];
customizedKeys.forEach((i) => {
if (customized[i].type === 'int') {
this.keysOfIntValue.push(i);
} else if (customized[i].type === 'str') {
this.keysOfStringValue.push(i);
} else if (customized[i].type === 'mixed') {
// list of type mixed
this.keysOfMixed.push(i);
this.keysOfStringValue.push(i);
}
});
}

const list = this.setDataOfModel(res.data.object);
const summaryDirList = list.map((i) => i.summary_dir);
this.$store.commit('setSummaryDirList', summaryDirList);

this.echart.showData = this.echart.brushData = list;
this.initChart();

this.table.data = this.echart.brushData.slice(
0,
this.pagination.pageSize,
);
this.pagination.currentPage = 1;
this.pagination.total = this.echart.brushData.length;
this.$refs.table.clearSelection();
this.getTableList(tableParams);
} else {
this.summaryDirList = [];
this.$store.commit('setSummaryDirList', []);
@@ -779,6 +838,26 @@ export default {
}
});
},
/**
* Get table data
* @param {Object} tableParams
*/
getTableList(tableParams) {
RequestService.queryLineagesData(tableParams)
.then(
(res) => {
if (res && res.data && res.data.object && res.data.object.length) {
const list = this.setDataOfModel(res.data.object);
this.table.data = list.slice(0, this.pagination.pageSize);
this.pagination.currentPage = 1;
this.pagination.total = this.echart.brushData.length;
this.$refs.table.clearSelection();
}
},
(error) => {},
)
.catch(() => {});
},
/**
* Resetting the Eechart
*/
@@ -790,6 +869,7 @@ export default {
this.showTable = false;
this.chartFilter = {};
this.tableFilter.summary_dir = undefined;
this.sortInfo = {};
this.pagination.currentPage = 1;
this.echart.allData = [];
if (this.echart.chart) {


+ 126
- 125
mindinsight/ui/src/views/train-manage/scalar.vue View File

@@ -34,10 +34,9 @@ limitations under the License.
<!--operation area -->
<div class="cl-eval-operate-content"
v-show="!compare">
<multiselectGroupComponents ref="multiselectGroupComponents"
:checkListArr="tagOperateList"
@selectedChange="tagSelectedChanged"
></multiselectGroupComponents>
<multiselectGroupComponents ref="multiselectGroupComponents"
:checkListArr="tagOperateList"
@selectedChange="tagSelectedChanged"></multiselectGroupComponents>
</div>
<!-- Slider -->
<div class="cl-eval-slider-operate-content"
@@ -56,14 +55,12 @@ limitations under the License.
<el-slider v-model="smoothValue"
:step="0.01"
:max="0.99"
@input="updataInputValue"
></el-slider>

<el-input v-model="smoothValueNumber"
class="w60"
@input="smoothValueChange"
@blur="smoothValueBlur"
></el-input>
@input="updataInputValue"></el-slider>

<el-input v-model="smoothValueNumber"
class="w60"
@input="smoothValueChange"
@blur="smoothValueBlur"></el-input>
</div>
<!-- Content display -->
<div class="cl-eval-show-data-content"
@@ -247,6 +244,10 @@ export default {
clearInterval(this.autoUpdateTimer);
this.autoUpdateTimer = null;
}
if (this.axisBenchChangeTimer) {
clearTimeout(this.axisBenchChangeTimer);
this.axisBenchChangeTimer = null;
}
},
mounted() {
// Adding a Listener
@@ -290,7 +291,7 @@ export default {
const runNmeColor = CommonProperty.commonColorArr[0];
data.tags.forEach((tagObj) => {
if (!this.oriDataDictionaries[tagObj]) {
this.oriDataDictionaries[tagObj]=true;
this.oriDataDictionaries[tagObj] = true;
// Add the tag list
tempTagList.push({
label: tagObj,
@@ -337,7 +338,7 @@ export default {
this.initOver = true;

this.$nextTick(() => {
this.multiSelectedTagNames=this.$refs.multiselectGroupComponents.updateSelectedDic();
this.multiSelectedTagNames = this.$refs.multiselectGroupComponents.updateSelectedDic();

// Obtains data on the current page
this.updateTagInPage();
@@ -365,10 +366,10 @@ export default {
const curPageArr = [];

for (let i = startIndex; i < endIndex; i++) {
const sampleItem=this.curFilterSamples[i];
const sampleItem = this.curFilterSamples[i];
if (sampleItem) {
sampleItem.updateFlag=true;
sampleItem.show=true;
sampleItem.updateFlag = true;
sampleItem.show = true;
curPageArr.push(sampleItem);
}
}
@@ -381,110 +382,110 @@ export default {
* Load the data on the current page
*/
updateCurPageSamples() {
this.curPageArr.forEach((sampleObject)=>{
const sampleIndex=sampleObject.sampleIndex;
this.curPageArr.forEach((sampleObject) => {
const sampleIndex = sampleObject.sampleIndex;
if (!sampleObject) {
return;
}
sampleObject.updateFlag = true;


const params = {
train_id: this.trainingJobId,
tag: sampleObject.tagName,
};

RequestService.getScalarsSample(params).then((res)=>{
// error
if (!res || !res.data || !res.data.metadatas) {
if (sampleObject.charObj) {
sampleObject.charObj.clear();
}
return;
}
let hasInvalidData = false;

if (sampleObject.charObj) {
sampleObject.charObj.showLoading();
}
RequestService.getScalarsSample(params)
.then((res) => {
// error
if (!res || !res.data || !res.data.metadatas) {
if (sampleObject.charObj) {
sampleObject.charObj.clear();
}
return;
}
let hasInvalidData = false;

const resData = res.data;
if (sampleObject.charObj) {
sampleObject.charObj.showLoading();
}

const tempObject = {
valueData: {
stepData: [],
absData: [],
relativeData: [],
},
logData: {
stepData: [],
absData: [],
relativeData: [],
},
};
let relativeTimeBench = 0;
if (resData.metadatas.length) {
relativeTimeBench = resData.metadatas[0].wall_time;
}
const resData = res.data;

const tempObject = {
valueData: {
stepData: [],
absData: [],
relativeData: [],
},
logData: {
stepData: [],
absData: [],
relativeData: [],
},
};
let relativeTimeBench = 0;
if (resData.metadatas.length) {
relativeTimeBench = resData.metadatas[0].wall_time;
}

// Initializing Chart Data
resData.metadatas.forEach((metaData) => {
if (metaData.value === null && !hasInvalidData) {
hasInvalidData = true;
}
tempObject.valueData.stepData.push([
metaData.step,
metaData.value,
]);
tempObject.valueData.absData.push([
metaData.wall_time,
metaData.value,
]);
tempObject.valueData.relativeData.push([
metaData.wall_time - relativeTimeBench,
metaData.value,
]);
const logValue = metaData.value >= 0 ? metaData.value : '';
tempObject.logData.stepData.push([metaData.step, logValue]);
tempObject.logData.absData.push([metaData.wall_time, logValue]);
tempObject.logData.relativeData.push([
metaData.wall_time - relativeTimeBench,
logValue,
]);
});
// Initializing Chart Data
resData.metadatas.forEach((metaData) => {
if (metaData.value === null && !hasInvalidData) {
hasInvalidData = true;
}
tempObject.valueData.stepData.push([
metaData.step,
metaData.value,
]);
tempObject.valueData.absData.push([
metaData.wall_time,
metaData.value,
]);
tempObject.valueData.relativeData.push([
metaData.wall_time - relativeTimeBench,
metaData.value,
]);
const logValue = metaData.value >= 0 ? metaData.value : '';
tempObject.logData.stepData.push([metaData.step, logValue]);
tempObject.logData.absData.push([metaData.wall_time, logValue]);
tempObject.logData.relativeData.push([
metaData.wall_time - relativeTimeBench,
logValue,
]);
});

sampleObject.charData.oriData[0] = tempObject;
sampleObject.charData.oriData[0] = tempObject;

if (hasInvalidData) {
this.$set(sampleObject, 'invalidData', true);
}
sampleObject.charData.charOption = this.formateCharOption(
sampleIndex,
);
if (hasInvalidData) {
this.$set(sampleObject, 'invalidData', true);
}
sampleObject.charData.charOption = this.formateCharOption(
sampleIndex,
);

this.$forceUpdate();
this.$forceUpdate();

this.$nextTick(() => {
if (sampleObject.charObj) {
sampleObject.charObj.hideLoading();
}
this.$nextTick(() => {
if (sampleObject.charObj) {
sampleObject.charObj.hideLoading();
}

// Draw chart
if (!this.compare) {
this.updateOrCreateChar(sampleIndex);
} else {
this.abort = true;
}
});
}).catch((e)=>{
if (sampleObject.charObj) {
sampleObject.charObj.clear();
}
});
// Draw chart
if (!this.compare) {
this.updateOrCreateChar(sampleIndex);
} else {
this.abort = true;
}
});
})
.catch((e) => {
if (sampleObject.charObj) {
sampleObject.charObj.clear();
}
});
});
},


/**
* Formatting Chart Data
* @param {Number} sampleIndex Chart subscript
@@ -499,7 +500,7 @@ export default {
let returnFlag = false;
const seriesData = [];
const oriData = sampleObject.charData.oriData;
const runName=sampleObject.runNames;
const runName = sampleObject.runNames;
const curBackName = runName + this.backendString;
const dataObj = {
name: runName,
@@ -645,7 +646,7 @@ export default {
},
grid: {
left: 80,
right: 10,
right: sampleObject.fullScreen ? 80 : 10,
},
animation: true,
dataZoom: [
@@ -886,9 +887,11 @@ export default {
if (sampleObject.fullScreen) {
sampleObject.charData.charOption.toolbox.feature.myToolFullScreen.iconStyle.borderColor =
'#3E98C5';
sampleObject.charData.charOption.grid.right = 80;
} else {
sampleObject.charData.charOption.toolbox.feature.myToolFullScreen.iconStyle.borderColor =
'#6D7278';
sampleObject.charData.charOption.grid.right = 10;
}
sampleObject.updateFlag = true;

@@ -901,14 +904,13 @@ export default {
}, 0);
},


/**
* Update Chart by tag
* @param {Boolean} noPageDataNumChange No new data is added or deleted
*/

updateTagInPage(noPageDataNumChange) {
const curFilterSamples=[];
const curFilterSamples = [];
// Obtains the chart subscript
this.originDataArr.forEach((sampleItem) => {
if (this.multiSelectedTagNames[sampleItem.tagName]) {
@@ -1012,13 +1014,12 @@ export default {
if (!selectedItemDict) {
return;
}
this.multiSelectedTagNames=selectedItemDict;
this.multiSelectedTagNames = selectedItemDict;
// Reset to the first page
this.pageIndex=0;
this.pageIndex = 0;
this.updateTagInPage();
},


/**
*window resize
*/
@@ -1050,7 +1051,7 @@ export default {
this.tagOperateList = [];
this.pageIndex = 0;
this.originDataArr = [];
this.oriDataDictionaries={};
this.oriDataDictionaries = {};
this.curPageArr = [];
this.tagPropsList = [];
this.propsList = [];
@@ -1090,10 +1091,10 @@ export default {
if (!oriData) {
return false;
}
const newTagDictionaries={}; // Index of the tag in the new data
const newTagDictionaries = {}; // Index of the tag in the new data
let dataRemoveFlag = false;
oriData.tags.forEach((tagName) => {
newTagDictionaries[tagName]=true;
newTagDictionaries[tagName] = true;
});
// Delete the tag that does not exist
const oldTagListLength = this.tagOperateList.length;
@@ -1132,7 +1133,7 @@ export default {
const runColor = CommonProperty.commonColorArr[0];
oriData.tags.forEach((tagObj) => {
if (!this.oriDataDictionaries[tagObj]) {
this.oriDataDictionaries[tagObj]=true;
this.oriDataDictionaries[tagObj] = true;
this.tagOperateList.push({
label: tagObj,
checked: true,
@@ -1198,7 +1199,7 @@ export default {
const tagAddFlag = this.checkNewDataAndComplete(data);

this.$nextTick(() => {
this.multiSelectedTagNames=this.$refs.multiselectGroupComponents.updateSelectedDic();
this.multiSelectedTagNames = this.$refs.multiselectGroupComponents.updateSelectedDic();
this.updateTagInPage(!tagRemoveFlag && !tagAddFlag);

this.resizeCallback();
@@ -1209,7 +1210,7 @@ export default {

// Initial chart data
data.tags.forEach((tagObj) => {
// Check whether the tag with the same name exists
// Check whether the tag with the same name exists
tempTagList.push({
label: tagObj,
checked: true,
@@ -1266,7 +1267,7 @@ export default {
if (this.firstNum === 0) {
return;
}
this.smoothValueNumber=Number(val);
this.smoothValueNumber = Number(val);
if (this.smoothSliderValueTimer) {
clearTimeout(this.smoothSliderValueTimer);
this.smoothSliderValueTimer = null;
@@ -1279,26 +1280,26 @@ export default {

smoothValueChange(val) {
if (!isNaN(val)) {
if (Number(val)===0) {
this.smoothValue=0;
if (Number(val) === 0) {
this.smoothValue = 0;
}
if (Number(val)<0) {
this.smoothValue=0;
this.smoothValueNumber=0;
if (Number(val) < 0) {
this.smoothValue = 0;
this.smoothValueNumber = 0;
}
if (Number(val)>0) {
if (Number(val)>0.99) {
this.smoothValue=0.99;
this.smoothValueNumber=0.99;
if (Number(val) > 0) {
if (Number(val) > 0.99) {
this.smoothValue = 0.99;
this.smoothValueNumber = 0.99;
} else {
this.smoothValue=Number(val);
this.smoothValue = Number(val);
}
}
}
},

smoothValueBlur() {
this.smoothValueNumber=this.smoothValue;
this.smoothValueNumber = this.smoothValue;
},

/**


+ 14
- 0
mindinsight/ui/src/views/train-manage/training-dashboard.vue View File

@@ -21,6 +21,12 @@ limitations under the License.
<div class="cl-dashboard-top-title">
{{$t('trainingDashboard.trainingDashboardTitle')}}
</div>
<div class="path-message">
<span>{{$t('symbols.leftbracket')}}</span>
<span>{{$t('trainingDashboard.summaryDirPath')}}</span>
<span>{{summaryPath}}</span>
<span>{{$t('symbols.rightbracket')}}</span>
</div>
</div>
<div class="cl-dashboard-center">
<div class="cl-dashboard-con-up"
@@ -166,6 +172,7 @@ export default {
return {
// training job id
trainingJobId: '',
summaryPath: '',
defColorCount: CommonProperty.commonColorArr.length, // default color
colorNum: 0,
charObj: null,
@@ -284,6 +291,7 @@ export default {
init() {
if (this.$route.query && this.$route.query.id) {
this.trainingJobId = this.$route.query.id;
this.summaryPath = decodeURIComponent(this.trainingJobId);
} else {
this.trainingJobId = '';
this.$message.error(this.$t('trainingDashboard.invalidId'));
@@ -1720,6 +1728,12 @@ export default {
height: 56px;
vertical-align: middle;
background: #ffffff;
.path-message {
display: inline-block;
line-height: 20px;
padding: 18px 16px;
font-weight: bold;
}
.cl-dashboard-top-title {
float: left;
color: #000000;


+ 56
- 5
tests/ut/datavisual/data_transform/test_events_data.py View File

@@ -36,9 +36,9 @@ class MockReservoir:
def __init__(self, size):
self.size = size
self._samples = [
_Tensor('wall_time1', 1, 'value1'),
_Tensor('wall_time2', 2, 'value2'),
_Tensor('wall_time3', 3, 'value3')
_Tensor('wall_time1', 1, 'value1', 'filename1'),
_Tensor('wall_time2', 2, 'value2', 'filename2'),
_Tensor('wall_time3', 3, 'value3', 'filename3')
]

def samples(self):
@@ -107,7 +107,8 @@ class TestEventsData:
"""Test add_tensor_event success."""

ev_data = self.get_ev_data()
t_event = TensorEvent(wall_time=1, step=4, tag='new_tag', plugin_name='plugin_name1', value='value1')
t_event = TensorEvent(wall_time=1, step=4, tag='new_tag',
plugin_name='plugin_name1', value='value1', filename='filename')

ev_data.add_tensor_event(t_event)
assert 'tag0' not in ev_data._tags
@@ -116,4 +117,54 @@ class TestEventsData:
assert 'tag0' not in ev_data._reservoir_by_tag
assert 'new_tag' in ev_data._tags_by_plugin['plugin_name1']
assert ev_data._reservoir_by_tag['new_tag'].samples()[-1] == _Tensor(t_event.wall_time, t_event.step,
t_event.value)
t_event.value, 'filename')

def test_add_tensor_event_out_of_order(self):
"""Test add_tensor_event success for out_of_order summaries."""
wall_time = 1
value = '1'
tag = 'tag'
plugin_name = 'scalar'
file1 = 'file1'
ev_data = EventsData()
steps = [i for i in range(2, 10)]
for step in steps:
t_event = TensorEvent(wall_time=1, step=step, tag=tag,
plugin_name=plugin_name, value=value, filename=file1)
ev_data.add_tensor_event(t_event)

t_event = TensorEvent(wall_time=1, step=1, tag=tag,
plugin_name=plugin_name, value=value, filename=file1)
ev_data.add_tensor_event(t_event)

# Current steps should be: [1, 2, 3, 4, 5, 6, 7, 8, 9]
assert len(ev_data._reservoir_by_tag[tag].samples()) == len(steps) + 1

file2 = 'file2'
new_steps_1 = [5, 10]
for step in new_steps_1:
t_event = TensorEvent(wall_time=1, step=step, tag=tag,
plugin_name=plugin_name, value=value, filename=file2)
ev_data.add_tensor_event(t_event)
assert ev_data._reservoir_by_tag[tag].samples()[-1] == _Tensor(wall_time, step, value, file2)

# Current steps should be: [1, 2, 3, 4, 5, 10]
steps = [1, 2, 3, 4, 5, 10]
samples = ev_data._reservoir_by_tag[tag].samples()
for step, sample in zip(steps, samples):
filename = file1 if sample.step < 5 else file2
assert sample == _Tensor(wall_time, step, value, filename)

new_steps_2 = [7, 11, 3]
for step in new_steps_2:
t_event = TensorEvent(wall_time=1, step=step, tag=tag,
plugin_name=plugin_name, value=value, filename=file2)
ev_data.add_tensor_event(t_event)

# Current steps should be: [1, 2, 3, 5, 7, 10, 11], file2: [3, 5, 7, 10, 11]
steps = [1, 2, 3, 5, 7, 10, 11]
new_steps_2.extend(new_steps_1)
samples = ev_data._reservoir_by_tag[tag].samples()
for step, sample in zip(steps, samples):
filename = file2 if sample.step in new_steps_2 else file1
assert sample == _Tensor(wall_time, step, value, filename)

+ 4
- 0
tests/ut/datavisual/data_transform/test_reservoir.py View File

@@ -27,10 +27,14 @@ class TestHistogramReservoir:
sample1.value.count = 1
sample1.value.max = 102
sample1.value.min = 101
sample1.step = 2
sample1.filename = 'filename'
sample2 = mock.MagicMock()
sample2.value.count = 2
sample2.value.max = 102
sample2.value.min = 101
sample2.step = 1
sample2.filename = 'filename'
my_reservoir.add_sample(sample1)
my_reservoir.add_sample(sample2)
samples = my_reservoir.samples()


+ 3
- 5
tests/ut/datavisual/processors/test_images_processor.py View File

@@ -216,9 +216,8 @@ class TestImagesProcessor:
"""
Test removing sample in reservoir.

If step list is [1, 3, 5, 7, 9, 2, 3, 4, 15],
and then [3, 5, 7, 9] will be deleted.
Results will be [1, 2, 3, 4, 15].
If step list is [1, 3, 5, 7, 9, 2, 3, 4, 15] in one summary,
Results will be [1, 2, 3, 4, 5, 7, 9, 15].
"""
test_tag_name = self._complete_tag_name

@@ -237,5 +236,4 @@ class TestImagesProcessor:
except ImageNotExistError:
not_found_step_list.append(test_step)

assert current_step_list == [1, 2, 3, 4, 15]
assert not_found_step_list == [5, 7, 9]
assert current_step_list == [1, 2, 3, 4, 5, 7, 9, 15]

+ 6
- 0
tests/ut/lineagemgr/querier/event_data.py View File

@@ -204,6 +204,12 @@ CUSTOMIZED_1 = {
'metric/mse': {'label': 'metric/mse', 'required': True, 'type': 'float'}
}

CUSTOMIZED_2 = {
'metric/accuracy': {'label': 'metric/accuracy', 'required': True, 'type': 'mixed'},
'metric/mae': {'label': 'metric/mae', 'required': True, 'type': 'float'},
'metric/mse': {'label': 'metric/mse', 'required': True, 'type': 'float'}
}

METRIC_1 = {
'accuracy': 1.0000002,
'mae': 2.00000002,


+ 3
- 3
tests/ut/lineagemgr/querier/test_querier.py View File

@@ -463,7 +463,7 @@ class TestQuerier(TestCase):
def test_filter_summary_lineage_success_4(self):
"""Test the success of filter_summary_lineage."""
expected_result = {
'customized': event_data.CUSTOMIZED_0,
'customized': event_data.CUSTOMIZED_2,
'object': [
LINEAGE_FILTRATION_0,
LINEAGE_FILTRATION_1,
@@ -500,7 +500,7 @@ class TestQuerier(TestCase):
'sorted_type': 'ascending'
}
expected_result = {
'customized': event_data.CUSTOMIZED_0,
'customized': event_data.CUSTOMIZED_2,
'object': [
LINEAGE_FILTRATION_0,
LINEAGE_FILTRATION_5,
@@ -522,7 +522,7 @@ class TestQuerier(TestCase):
'sorted_type': 'descending'
}
expected_result = {
'customized': event_data.CUSTOMIZED_1,
'customized': event_data.CUSTOMIZED_2,
'object': [
LINEAGE_FILTRATION_6,
LINEAGE_FILTRATION_4,


Loading…
Cancel
Save