Commit f92b3ed7 authored by Lijiao's avatar Lijiao Committed by liuzhe-lz
Browse files

[Fixed issue#1218 V2] Show the best metric curve during search progress in webui (#1395)

[Fixed issue#1218 V2] Show the best metric curve during search progress in webui
parent 140dd83d
......@@ -3,7 +3,7 @@ import axios from 'axios';
import { MANAGER_IP } from '../static/const';
import { Row, Col, Tabs, Select, Button, Icon } from 'antd';
const Option = Select.Option;
import { TableObj, Parameters } from '../static/interface';
import { TableObj, Parameters, ExperimentInfo } from '../static/interface';
import { getFinal } from '../static/function';
import DefaultPoint from './trial-detail/DefaultMetricPoint';
import Duration from './trial-detail/Duration';
......@@ -21,8 +21,6 @@ interface TrialDetailState {
tableListSource: Array<TableObj>;
searchResultSource: Array<TableObj>;
isHasSearch: boolean;
experimentStatus: string;
experimentPlatform: string;
experimentLogCollection: boolean;
entriesTable: number; // table components val
entriesInSelect: string;
......@@ -32,6 +30,7 @@ interface TrialDetailState {
hyperCounts: number; // user click the hyper-parameter counts
durationCounts: number;
intermediateCounts: number;
experimentInfo: ExperimentInfo;
searchFilter: string;
searchPlaceHolder: string;
}
......@@ -78,8 +77,6 @@ class TrialsDetail extends React.Component<TrialsDetailProps, TrialDetailState>
accNodata: '',
tableListSource: [],
searchResultSource: [],
experimentStatus: '',
experimentPlatform: '',
experimentLogCollection: false,
entriesTable: 20,
entriesInSelect: '20',
......@@ -90,6 +87,10 @@ class TrialsDetail extends React.Component<TrialsDetailProps, TrialDetailState>
hyperCounts: 0,
durationCounts: 0,
intermediateCounts: 0,
experimentInfo: {
platform: '',
optimizeMode: 'maximize'
},
searchFilter: 'id',
searchPlaceHolder: 'Search by id'
};
......@@ -326,7 +327,7 @@ class TrialsDetail extends React.Component<TrialsDetailProps, TrialDetailState>
})
.then(res => {
if (res.status === 200) {
const trainingPlatform = res.data.params.trainingServicePlatform !== undefined
const trainingPlatform: string = res.data.params.trainingServicePlatform !== undefined
?
res.data.params.trainingServicePlatform
:
......@@ -336,12 +337,24 @@ class TrialsDetail extends React.Component<TrialsDetailProps, TrialDetailState>
let expLogCollection: boolean = false;
const isMultiy: boolean = res.data.params.multiPhase !== undefined
? res.data.params.multiPhase : false;
const tuner = res.data.params.tuner;
// I'll set optimize is maximize if user not set optimize
let optimize: string = 'maximize';
if (tuner !== undefined) {
if (tuner.classArgs !== undefined) {
if (tuner.classArgs.optimize_mode !== undefined) {
if (tuner.classArgs.optimize_mode === 'minimize') {
optimize = 'minimize';
}
}
}
}
if (logCollection !== undefined && logCollection !== 'none') {
expLogCollection = true;
}
if (this._isMounted) {
this.setState({
experimentPlatform: trainingPlatform,
experimentInfo: { platform: trainingPlatform, optimizeMode: optimize },
searchSpace: res.data.params.searchSpace,
experimentLogCollection: expLogCollection,
isMultiPhase: isMultiy
......@@ -380,7 +393,7 @@ class TrialsDetail extends React.Component<TrialsDetailProps, TrialDetailState>
const {
tableListSource, searchResultSource, isHasSearch, isMultiPhase,
entriesTable, experimentPlatform, searchSpace, experimentLogCollection,
entriesTable, experimentInfo, searchSpace, experimentLogCollection,
whichGraph, searchPlaceHolder
} = this.state;
const source = isHasSearch ? searchResultSource : tableListSource;
......@@ -391,9 +404,10 @@ class TrialsDetail extends React.Component<TrialsDetailProps, TrialDetailState>
<TabPane tab={this.titleOfacc} key="1">
<Row className="graph">
<DefaultPoint
height={432}
height={402}
showSource={source}
whichGraph={whichGraph}
optimize={experimentInfo.optimizeMode}
/>
</Row>
</TabPane>
......@@ -465,7 +479,7 @@ class TrialsDetail extends React.Component<TrialsDetailProps, TrialDetailState>
entries={entriesTable}
tableSource={source}
isMultiPhase={isMultiPhase}
platform={experimentPlatform}
platform={experimentInfo.platform}
updateList={this.getDetailSource}
logCollection={experimentLogCollection}
ref={(tabList) => this.tableList = tabList}
......
import * as React from 'react';
import { Switch } from 'antd';
import ReactEcharts from 'echarts-for-react';
import { filterByStatus } from '../../static/function';
import { TableObj, DetailAccurPoint, TooltipForAccuracy } from '../../static/interface';
......@@ -10,32 +11,36 @@ interface DefaultPointProps {
showSource: Array<TableObj>;
height: number;
whichGraph: string;
optimize: string;
}
interface DefaultPointState {
defaultSource: object;
accNodata: string;
succeedTrials: number;
isViewBestCurve: boolean;
}
class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState> {
public _isMounted = false;
public _isDefaultMounted = false;
constructor(props: DefaultPointProps) {
super(props);
this.state = {
defaultSource: {},
accNodata: '',
succeedTrials: 10000000
succeedTrials: 10000000,
isViewBestCurve: false
};
}
defaultMetric = (succeedSource: Array<TableObj>) => {
defaultMetric = (succeedSource: Array<TableObj>, isCurve: boolean) => {
const { optimize } = this.props;
const accSource: Array<DetailAccurPoint> = [];
const showSource: Array<TableObj> = succeedSource.filter(filterByStatus);
const lengthOfSource = showSource.length;
const tooltipDefault = lengthOfSource === 0 ? 'No data' : '';
if (this._isMounted === true) {
if (this._isDefaultMounted === true) {
this.setState(() => ({
succeedTrials: lengthOfSource,
accNodata: tooltipDefault
......@@ -55,95 +60,195 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState>
type: 'value',
}
};
if (this._isMounted === true) {
if (this._isDefaultMounted === true) {
this.setState(() => ({
defaultSource: nullGraph
}));
}
} else {
const resultList: Array<number | string>[] = [];
const resultList: Array<number | object>[] = [];
const lineListDefault: Array<number> = [];
Object.keys(showSource).map(item => {
const temp = showSource[item];
if (temp.acc !== undefined) {
if (temp.acc.default !== undefined) {
const searchSpace = temp.description.parameters;
lineListDefault.push(temp.acc.default);
accSource.push({
acc: temp.acc.default,
index: temp.sequenceId,
searchSpace: JSON.stringify(searchSpace)
searchSpace: searchSpace
});
}
}
});
// deal with best metric line
const bestCurve: Array<number | object>[] = []; // best curve data source
bestCurve.push([0, lineListDefault[0], accSource[0].searchSpace]); // push the first value
if (optimize === 'maximize') {
for (let i = 1; i < lineListDefault.length; i++) {
const val = lineListDefault[i];
const latest = bestCurve[bestCurve.length - 1][1];
if (val >= latest) {
bestCurve.push([i, val, accSource[i].searchSpace]);
} else {
bestCurve.push([i, latest, accSource[i].searchSpace]);
}
}
} else {
for (let i = 1; i < lineListDefault.length; i++) {
const val = lineListDefault[i];
const latest = bestCurve[bestCurve.length - 1][1];
if (val <= latest) {
bestCurve.push([i, val, accSource[i].searchSpace]);
} else {
bestCurve.push([i, latest, accSource[i].searchSpace]);
}
}
}
Object.keys(accSource).map(item => {
const items = accSource[item];
let temp: Array<number | string>;
temp = [items.index, items.acc, JSON.parse(items.searchSpace)];
let temp: Array<number | object>;
temp = [items.index, items.acc, items.searchSpace];
resultList.push(temp);
});
// isViewBestCurve: false show default metric graph
// isViewBestCurve: true show best curve
if (isCurve === true) {
if (this._isDefaultMounted === true) {
this.setState(() => ({
defaultSource: this.drawBestcurve(bestCurve, resultList)
}));
}
} else {
if (this._isDefaultMounted === true) {
this.setState(() => ({
defaultSource: this.drawDefaultMetric(resultList)
}));
}
}
}
}
const allAcuracy = {
grid: {
left: '8%'
},
tooltip: {
trigger: 'item',
enterable: true,
position: function (point: Array<number>, data: TooltipForAccuracy) {
if (data.data[0] < resultList.length / 2) {
return [point[0], 80];
} else {
return [point[0] - 300, 80];
}
},
formatter: function (data: TooltipForAccuracy) {
const result = '<div class="tooldetailAccuracy">' +
'<div>Trial No.: ' + data.data[0] + '</div>' +
'<div>Default metric: ' + data.data[1] + '</div>' +
'<div>Parameters: ' +
'<pre>' + JSON.stringify(data.data[2], null, 4) + '</pre>' +
'</div>' +
'</div>';
return result;
drawBestcurve = (realDefault: Array<number | object>[], resultList: Array<number | object>[]) => {
return {
grid: {
left: '8%'
},
tooltip: {
trigger: 'item',
enterable: true,
position: function (point: Array<number>, data: TooltipForAccuracy) {
if (data.data[0] < realDefault.length / 2) {
return [point[0], 80];
} else {
return [point[0] - 300, 80];
}
},
xAxis: {
name: 'Trial',
type: 'category',
},
yAxis: {
name: 'Default metric',
type: 'value',
scale: true
formatter: function (data: TooltipForAccuracy) {
const result = '<div class="tooldetailAccuracy">' +
'<div>Trial No.: ' + data.data[0] + '</div>' +
'<div>Optimization curve: ' + data.data[1] + '</div>' +
'<div>Parameters: ' +
'<pre>' + JSON.stringify(data.data[2], null, 4) + '</pre>' +
'</div>' +
'</div>';
return result;
}
},
xAxis: {
name: 'Trial',
type: 'category',
},
yAxis: {
name: 'Default metric',
type: 'value',
scale: true
},
series: [{
symbolSize: 6,
type: 'scatter',
data: resultList
}, {
type: 'line',
lineStyle: { color: '#FF6600' },
data: realDefault
}]
};
}
drawDefaultMetric = (resultList: Array<number | object>[]) => {
return {
grid: {
left: '8%'
},
tooltip: {
trigger: 'item',
enterable: true,
position: function (point: Array<number>, data: TooltipForAccuracy) {
if (data.data[0] < resultList.length / 2) {
return [point[0], 80];
} else {
return [point[0] - 300, 80];
}
},
series: [{
symbolSize: 6,
type: 'scatter',
data: resultList
}]
};
if (this._isMounted === true) {
this.setState(() => ({
defaultSource: allAcuracy
}));
}
formatter: function (data: TooltipForAccuracy) {
const result = '<div class="tooldetailAccuracy">' +
'<div>Trial No.: ' + data.data[0] + '</div>' +
'<div>Default metric: ' + data.data[1] + '</div>' +
'<div>Parameters: ' +
'<pre>' + JSON.stringify(data.data[2], null, 4) + '</pre>' +
'</div>' +
'</div>';
return result;
}
},
xAxis: {
name: 'Trial',
type: 'category',
},
yAxis: {
name: 'Default metric',
type: 'value',
scale: true
},
series: [{
symbolSize: 6,
type: 'scatter',
data: resultList
}]
};
}
loadDefault = (checked: boolean) => {
// checked: true show best metric curve
const { showSource } = this.props;
if (this._isDefaultMounted === true) {
this.defaultMetric(showSource, checked);
// ** deal with data and then update view layer
this.setState(() => ({ isViewBestCurve: checked }));
}
}
// update parent component state
componentWillReceiveProps(nextProps: DefaultPointProps) {
const { whichGraph, showSource } = nextProps;
const { isViewBestCurve } = this.state;
if (whichGraph === '1') {
this.defaultMetric(showSource);
this.defaultMetric(showSource, isViewBestCurve);
}
}
shouldComponentUpdate(nextProps: DefaultPointProps, nextState: DefaultPointState) {
const { whichGraph } = nextProps;
const succTrial = this.state.succeedTrials;
const { succeedTrials } = nextState;
if (whichGraph === '1') {
const { succeedTrials, isViewBestCurve } = nextState;
const succTrial = this.state.succeedTrials;
const isViewBestCurveBefore = this.state.isViewBestCurve;
if (isViewBestCurveBefore !== isViewBestCurve) {
return true;
}
if (succeedTrials !== succTrial) {
return true;
}
......@@ -153,11 +258,11 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState>
}
componentDidMount() {
this._isMounted = true;
this._isDefaultMounted = true;
}
componentWillUnmount() {
this._isMounted = false;
this._isDefaultMounted = false;
}
render() {
......@@ -165,6 +270,12 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState>
const { defaultSource, accNodata } = this.state;
return (
<div>
<div className="default-metric">
<div className="position">
<span className="bold">optimization curve</span>
<Switch defaultChecked={false} onChange={this.loadDefault} />
</div>
</div>
<ReactEcharts
option={defaultSource}
style={{
......@@ -174,7 +285,6 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState>
}}
theme="my_theme"
notMerge={true} // update now
// lazyUpdate={true}
/>
<div className="showMess">{accNodata}</div>
</div>
......
......@@ -114,7 +114,7 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
},
yAxis: {
type: 'value',
name: 'metric'
name: 'Metric'
},
series: trialIntermediate
};
......@@ -136,7 +136,7 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
},
yAxis: {
type: 'value',
name: 'metric'
name: 'Metric'
}
};
if (this._isMounted) {
......@@ -283,9 +283,9 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
{/* style in para.scss */}
<Row className="meline intermediate">
<Col span={8} />
<Col span={3} style={{ height: 34 }}>
<Col span={3} className="inter-filter-btn">
{/* filter message */}
<span>filter</span>
<span>Filter</span>
<Switch
defaultChecked={false}
onChange={this.switchTurn}
......
......@@ -59,7 +59,7 @@ interface AccurPoint {
interface DetailAccurPoint {
acc: number;
index: number;
searchSpace: string;
searchSpace: object;
}
interface TooltipForIntermediate {
......@@ -117,8 +117,13 @@ interface Intermedia {
hyperPara: object; // each trial hyperpara value
}
interface ExperimentInfo {
platform: string;
optimizeMode: string;
}
export {
TableObj, Parameters, Experiment, AccurPoint, TrialNumber, TrialJob,
DetailAccurPoint, TooltipForAccuracy, ParaObj, Dimobj, FinalResult, FinalType,
TooltipForIntermediate, SearchSpace, Intermedia
TooltipForIntermediate, SearchSpace, Intermedia, ExperimentInfo
};
......@@ -36,6 +36,10 @@
.strange{
margin-top: 2px;
}
.inter-filter-btn{
height: 34px;
line-height: 34px;
}
.range{
.heng{
margin-left: 6px;
......
......@@ -31,14 +31,12 @@
text-align: center;
color:#212121;
font-size: 14px;
/* background-color: #f2f2f2; */
}
th{
padding: 2px;
background-color:white !important;
font-size: 14px;
color: #808080;
border-bottom: 1px solid #d0d0d0;
text-align: center;
}
......@@ -105,3 +103,9 @@
.ant-table-selection{
display: none;
}
/* fix the border-bottom bug in firefox and edge */
.ant-table-thead > tr > th .ant-table-column-sorters::before{
padding-bottom: 25px;
border-bottom: 1px solid #e8e8e8;
}
\ No newline at end of file
......@@ -70,3 +70,16 @@
.allList{
margin-top: 15px;
}
.default-metric{
width: 90%;
text-align: right;
margin-top: 15px;
.position{
color: #333;
.bold{
font-weight: 600;
margin-right: 10px;
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment