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