Unverified Commit 3e62e60b authored by liuzhe-lz's avatar liuzhe-lz Committed by GitHub
Browse files

Refactor web UI to support incremental metric loading (#1557)

* Refactor web UI to support incremental metric loading

* refactor

* Remove host job

* Move sequence ID to NNI manager

* implement incremental loading
parent 99f7d79c
...@@ -22,8 +22,6 @@ interface DurationState { ...@@ -22,8 +22,6 @@ interface DurationState {
class Duration extends React.Component<DurationProps, DurationState> { class Duration extends React.Component<DurationProps, DurationState> {
public _isMounted = false;
constructor(props: DurationProps) { constructor(props: DurationProps) {
super(props); super(props);
...@@ -142,15 +140,12 @@ class Duration extends React.Component<DurationProps, DurationState> { ...@@ -142,15 +140,12 @@ class Duration extends React.Component<DurationProps, DurationState> {
trialId: trialId, trialId: trialId,
trialTime: trialTime trialTime: trialTime
}); });
if (this._isMounted) { this.setState({
this.setState({ durationSource: this.getOption(trialRun[0])
durationSource: this.getOption(trialRun[0]) });
});
}
} }
componentDidMount() { componentDidMount() {
this._isMounted = true;
const { source } = this.props; const { source } = this.props;
this.drawDurationGraph(source); this.drawDurationGraph(source);
} }
...@@ -187,10 +182,6 @@ class Duration extends React.Component<DurationProps, DurationState> { ...@@ -187,10 +182,6 @@ class Duration extends React.Component<DurationProps, DurationState> {
return false; return false;
} }
componentWillUnmount() {
this._isMounted = false;
}
render() { render() {
const { durationSource } = this.state; const { durationSource } = this.state;
return ( return (
...@@ -206,4 +197,4 @@ class Duration extends React.Component<DurationProps, DurationState> { ...@@ -206,4 +197,4 @@ class Duration extends React.Component<DurationProps, DurationState> {
} }
} }
export default Duration; export default Duration;
\ No newline at end of file
...@@ -24,7 +24,6 @@ interface IntermediateProps { ...@@ -24,7 +24,6 @@ interface IntermediateProps {
class Intermediate extends React.Component<IntermediateProps, IntermediateState> { class Intermediate extends React.Component<IntermediateProps, IntermediateState> {
static intervalMediate = 1; static intervalMediate = 1;
public _isMounted = false;
public pointInput: HTMLInputElement | null; public pointInput: HTMLInputElement | null;
public minValInput: HTMLInputElement | null; public minValInput: HTMLInputElement | null;
public maxValInput: HTMLInputElement | null; public maxValInput: HTMLInputElement | null;
...@@ -45,12 +44,10 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState> ...@@ -45,12 +44,10 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
drawIntermediate = (source: Array<TableObj>) => { drawIntermediate = (source: Array<TableObj>) => {
if (source.length > 0) { if (source.length > 0) {
if (this._isMounted) { this.setState({
this.setState(() => ({ length: source.length,
length: source.length, detailSource: source
detailSource: source });
}));
}
const trialIntermediate: Array<Intermedia> = []; const trialIntermediate: Array<Intermedia> = [];
Object.keys(source).map(item => { Object.keys(source).map(item => {
const temp = source[item]; const temp = source[item];
...@@ -118,11 +115,9 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState> ...@@ -118,11 +115,9 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
}, },
series: trialIntermediate series: trialIntermediate
}; };
if (this._isMounted) { this.setState({
this.setState(() => ({ interSource: option
interSource: option });
}));
}
} else { } else {
const nullData = { const nullData = {
grid: { grid: {
...@@ -139,71 +134,60 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState> ...@@ -139,71 +134,60 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
name: 'Metric' name: 'Metric'
} }
}; };
if (this._isMounted) { this.setState({ interSource: nullData });
this.setState(() => ({ interSource: nullData }));
}
} }
} }
// confirm btn function [filter data] // confirm btn function [filter data]
filterLines = () => { filterLines = () => {
if (this._isMounted) { const filterSource: Array<TableObj> = [];
const filterSource: Array<TableObj> = []; this.setState({ isLoadconfirmBtn: true }, () => {
this.setState({ isLoadconfirmBtn: true }, () => { const { source } = this.props;
const { source } = this.props; // get input value
// get input value const pointVal = this.pointInput !== null ? this.pointInput.value : '';
const pointVal = this.pointInput !== null ? this.pointInput.value : ''; const minVal = this.minValInput !== null ? this.minValInput.value : '';
const minVal = this.minValInput !== null ? this.minValInput.value : ''; const maxVal = this.maxValInput !== null ? this.maxValInput.value : '';
const maxVal = this.maxValInput !== null ? this.maxValInput.value : ''; // user not input message
// user not input message if (pointVal === '' || minVal === '') {
if (pointVal === '' || minVal === '') { alert('Please input filter message');
alert('Please input filter message'); } else {
// user not input max value
const position = JSON.parse(pointVal);
const min = JSON.parse(minVal);
if (maxVal === '') {
Object.keys(source).map(item => {
const temp = source[item];
const val = temp.description.intermediate[position - 1];
if (val >= min) {
filterSource.push(temp);
}
});
} else { } else {
// user not input max value const max = JSON.parse(maxVal);
const position = JSON.parse(pointVal); Object.keys(source).map(item => {
const min = JSON.parse(minVal); const temp = source[item];
if (maxVal === '') { const val = temp.description.intermediate[position - 1];
Object.keys(source).map(item => { if (val >= min && val <= max) {
const temp = source[item]; filterSource.push(temp);
const val = temp.description.intermediate[position - 1]; }
if (val >= min) { });
filterSource.push(temp);
}
});
} else {
const max = JSON.parse(maxVal);
Object.keys(source).map(item => {
const temp = source[item];
const val = temp.description.intermediate[position - 1];
if (val >= min && val <= max) {
filterSource.push(temp);
}
});
}
if (this._isMounted) {
this.setState({ filterSource: filterSource });
}
this.drawIntermediate(filterSource);
}
const counts = this.state.clickCounts + 1;
if (this._isMounted) {
this.setState({ isLoadconfirmBtn: false, clickCounts: counts });
} }
}); this.setState({ filterSource: filterSource });
} this.drawIntermediate(filterSource);
}
const counts = this.state.clickCounts + 1;
this.setState({ isLoadconfirmBtn: false, clickCounts: counts });
});
} }
switchTurn = (checked: boolean) => { switchTurn = (checked: boolean) => {
if (this._isMounted) { this.setState({ isFilter: checked });
this.setState({ isFilter: checked });
}
if (checked === false) { if (checked === false) {
this.drawIntermediate(this.props.source); this.drawIntermediate(this.props.source);
} }
} }
componentDidMount() { componentDidMount() {
this._isMounted = true;
const { source } = this.props; const { source } = this.props;
this.drawIntermediate(source); this.drawIntermediate(source);
} }
...@@ -272,10 +256,6 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState> ...@@ -272,10 +256,6 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
return false; return false;
} }
componentWillUnmount() {
this._isMounted = false;
}
render() { render() {
const { interSource, isLoadconfirmBtn, isFilter } = this.state; const { interSource, isLoadconfirmBtn, isFilter } = this.state;
return ( return (
......
...@@ -40,8 +40,6 @@ message.config({ ...@@ -40,8 +40,6 @@ message.config({
class Para extends React.Component<ParaProps, ParaState> { class Para extends React.Component<ParaProps, ParaState> {
public _isMounted = false;
private chartMulineStyle = { private chartMulineStyle = {
width: '100%', width: '100%',
height: 392, height: 392,
...@@ -121,15 +119,12 @@ class Para extends React.Component<ParaProps, ParaState> { ...@@ -121,15 +119,12 @@ class Para extends React.Component<ParaProps, ParaState> {
this.swapGraph(paraData, swapAxisArr); this.swapGraph(paraData, swapAxisArr);
} }
this.getOption(paraData, lengthofTrials); this.getOption(paraData, lengthofTrials);
if (this._isMounted === true) { this.setState({ paraBack: paraData });
this.setState(() => ({ paraBack: paraData }));
}
} }
hyperParaPic = (source: Array<TableObj>, searchSpace: string) => { hyperParaPic = (source: Array<TableObj>, searchSpace: string) => {
// filter succeed trials [{}, {}, {}] // filter succeed trials [{}, {}, {}]
const origin = source.filter(filterByStatus); const dataSource = source.filter(filterByStatus);
const dataSource: Array<TableObj> = JSON.parse(JSON.stringify(origin));
const lenOfDataSource: number = dataSource.length; const lenOfDataSource: number = dataSource.length;
const accPara: Array<number> = []; const accPara: Array<number> = [];
// specific value array // specific value array
...@@ -139,15 +134,13 @@ class Para extends React.Component<ParaProps, ParaState> { ...@@ -139,15 +134,13 @@ class Para extends React.Component<ParaProps, ParaState> {
// nest search space // nest search space
let isNested: boolean = false; let isNested: boolean = false;
Object.keys(searchRange).map(item => { Object.keys(searchRange).map(item => {
if (typeof searchRange[item]._value[0] === 'object') { if (searchRange[item]._value && typeof searchRange[item]._value[0] === 'object') {
isNested = true; isNested = true;
return; return;
} }
}); });
const dimName = Object.keys(searchRange); const dimName = Object.keys(searchRange);
if (this._isMounted === true) { this.setState({ dimName: dimName });
this.setState(() => ({ dimName: dimName }));
}
const parallelAxis: Array<Dimobj> = []; const parallelAxis: Array<Dimobj> = [];
// search space range and specific value [only number] // search space range and specific value [only number]
...@@ -324,23 +317,21 @@ class Para extends React.Component<ParaProps, ParaState> { ...@@ -324,23 +317,21 @@ class Para extends React.Component<ParaProps, ParaState> {
color: ['#CA0000', '#FFC400', '#90EE90'] color: ['#CA0000', '#FFC400', '#90EE90']
} }
}; };
if (this._isMounted === true) { this.setState({
this.setState({ paraNodata: 'No data',
paraNodata: 'No data', option: optionOfNull,
option: optionOfNull, sutrialCount: 0,
sutrialCount: 0, succeedRenderCount: 0
succeedRenderCount: 0 });
});
}
} else { } else {
Object.keys(dataSource).map(item => { Object.keys(dataSource).map(item => {
const temp = dataSource[item]; const trial = dataSource[item];
eachTrialParams.push(temp.description.parameters); eachTrialParams.push(trial.description.parameters.error || '');
// may be a succeed trial hasn't final result // may be a succeed trial hasn't final result
// all detail page may be break down if havn't if // all detail page may be break down if havn't if
if (temp.acc !== undefined) { if (trial.acc !== undefined) {
if (temp.acc.default !== undefined) { if (trial.acc.default !== undefined) {
accPara.push(temp.acc.default); accPara.push(JSON.parse(trial.acc.default));
} }
} }
}); });
...@@ -361,14 +352,12 @@ class Para extends React.Component<ParaProps, ParaState> { ...@@ -361,14 +352,12 @@ class Para extends React.Component<ParaProps, ParaState> {
}); });
}); });
} }
if (this._isMounted) { // if not return final result
// if not return final result const maxVal = accPara.length === 0 ? 1 : Math.max(...accPara);
const maxVal = accPara.length === 0 ? 1 : Math.max(...accPara); const minVal = accPara.length === 0 ? 1 : Math.min(...accPara);
const minVal = accPara.length === 0 ? 1 : Math.min(...accPara); this.setState({ max: maxVal, min: minVal }, () => {
this.setState({ max: maxVal, min: minVal }, () => { this.getParallelAxis(dimName, parallelAxis, accPara, eachTrialParams, lenOfDataSource);
this.getParallelAxis(dimName, parallelAxis, accPara, eachTrialParams, lenOfDataSource); });
});
}
} }
} }
...@@ -376,11 +365,9 @@ class Para extends React.Component<ParaProps, ParaState> { ...@@ -376,11 +365,9 @@ class Para extends React.Component<ParaProps, ParaState> {
percentNum = (value: string) => { percentNum = (value: string) => {
let vals = parseFloat(value); let vals = parseFloat(value);
if (this._isMounted) { this.setState({ percent: vals }, () => {
this.setState({ percent: vals }, () => { this.reInit();
this.reInit(); });
});
}
} }
// deal with response data into pic data // deal with response data into pic data
...@@ -445,22 +432,17 @@ class Para extends React.Component<ParaProps, ParaState> { ...@@ -445,22 +432,17 @@ class Para extends React.Component<ParaProps, ParaState> {
} }
}; };
// please wait the data // please wait the data
if (this._isMounted) { this.setState({
this.setState(() => ({ option: optionown,
option: optionown, paraNodata: '',
paraNodata: '', succeedRenderCount: lengthofTrials,
succeedRenderCount: lengthofTrials, sutrialCount: paralleData.length
sutrialCount: paralleData.length });
}));
}
} }
// get swap parallel axis // get swap parallel axis
getSwapArr = (value: Array<string>) => { getSwapArr = (value: Array<string>) => {
this.setState({ swapAxisArr: value });
if (this._isMounted) {
this.setState(() => ({ swapAxisArr: value }));
}
} }
reInit = () => { reInit = () => {
...@@ -471,9 +453,7 @@ class Para extends React.Component<ParaProps, ParaState> { ...@@ -471,9 +453,7 @@ class Para extends React.Component<ParaProps, ParaState> {
swapReInit = () => { swapReInit = () => {
const { clickCounts, succeedRenderCount } = this.state; const { clickCounts, succeedRenderCount } = this.state;
const val = clickCounts + 1; const val = clickCounts + 1;
if (this._isMounted) { this.setState({ isLoadConfirm: true, clickCounts: val, });
this.setState({ isLoadConfirm: true, clickCounts: val, });
}
const { paraBack, swapAxisArr } = this.state; const { paraBack, swapAxisArr } = this.state;
const paralDim = paraBack.parallelAxis; const paralDim = paraBack.parallelAxis;
const paraData = paraBack.data; const paraData = paraBack.data;
...@@ -523,11 +503,9 @@ class Para extends React.Component<ParaProps, ParaState> { ...@@ -523,11 +503,9 @@ class Para extends React.Component<ParaProps, ParaState> {
}); });
this.getOption(paraBack, succeedRenderCount); this.getOption(paraBack, succeedRenderCount);
// please wait the data // please wait the data
if (this._isMounted) { this.setState({
this.setState(() => ({ isLoadConfirm: false
isLoadConfirm: false });
}));
}
} }
sortDimY = (a: Dimobj, b: Dimobj) => { sortDimY = (a: Dimobj, b: Dimobj) => {
...@@ -585,7 +563,6 @@ class Para extends React.Component<ParaProps, ParaState> { ...@@ -585,7 +563,6 @@ class Para extends React.Component<ParaProps, ParaState> {
} }
componentDidMount() { componentDidMount() {
this._isMounted = true;
this.reInit(); this.reInit();
} }
...@@ -623,10 +600,6 @@ class Para extends React.Component<ParaProps, ParaState> { ...@@ -623,10 +600,6 @@ class Para extends React.Component<ParaProps, ParaState> {
return false; return false;
} }
componentWillUnmount() {
this._isMounted = false;
}
render() { render() {
const { option, paraNodata, dimName, isLoadConfirm } = this.state; const { option, paraNodata, dimName, isLoadConfirm } = this.state;
return ( return (
...@@ -687,4 +660,4 @@ class Para extends React.Component<ParaProps, ParaState> { ...@@ -687,4 +660,4 @@ class Para extends React.Component<ParaProps, ParaState> {
} }
} }
export default Para; export default Para;
\ No newline at end of file
// when there are more trials than this threshold, metrics will be updated in group of this size to avoid freezing
const METRIC_GROUP_UPDATE_THRESHOLD = 100;
const METRIC_GROUP_UPDATE_SIZE = 20;
const MANAGER_IP = `/api/v1/nni`; const MANAGER_IP = `/api/v1/nni`;
const DOWNLOAD_IP = `/logs`; const DOWNLOAD_IP = `/logs`;
const trialJobStatus = [ const trialJobStatus = [
...@@ -65,9 +69,10 @@ const COLUMN_INDEX = [ ...@@ -65,9 +69,10 @@ const COLUMN_INDEX = [
// defatult selected column // defatult selected column
const COLUMN = ['Trial No.', 'ID', 'Duration', 'Status', 'Default', 'Operation']; const COLUMN = ['Trial No.', 'ID', 'Duration', 'Status', 'Default', 'Operation'];
// all choice column !dictory final // all choice column !dictory final
const COLUMNPro = ['Trial No.', 'ID', 'StartTime', 'EndTime', 'Duration', 'Status', const COLUMNPro = ['Trial No.', 'ID', 'Start Time', 'End Time', 'Duration', 'Status',
'Intermediate count', 'Default', 'Operation']; 'Intermediate count', 'Default', 'Operation'];
export { export {
MANAGER_IP, DOWNLOAD_IP, trialJobStatus, COLUMNPro, MANAGER_IP, DOWNLOAD_IP, trialJobStatus, COLUMNPro,
CONTROLTYPE, MONACO, COLUMN, COLUMN_INDEX, DRAWEROPTION CONTROLTYPE, MONACO, COLUMN, COLUMN_INDEX, DRAWEROPTION,
METRIC_GROUP_UPDATE_THRESHOLD, METRIC_GROUP_UPDATE_SIZE,
}; };
import { Experiment } from './model/experiment';
import { TrialManager } from './model/trialmanager';
const EXPERIMENT = new Experiment();
const TRIALS = new TrialManager();
export { EXPERIMENT, TRIALS };
import axios from 'axios'; import axios from 'axios';
import { message } from 'antd'; import { message } from 'antd';
import { MANAGER_IP } from './const'; import { MANAGER_IP } from './const';
import { FinalResult, FinalType, TableObj } from './interface'; import { MetricDataRecord, FinalType, TableObj } from './interface';
const convertTime = (num: number) => { const convertTime = (num: number) => {
if (num <= 0) {
return '0';
}
if (num % 3600 === 0) { if (num % 3600 === 0) {
return num / 3600 + 'h'; return num / 3600 + 'h';
} else { } else {
...@@ -15,24 +18,28 @@ const convertTime = (num: number) => { ...@@ -15,24 +18,28 @@ const convertTime = (num: number) => {
// trial's duration, accurate to seconds for example 10min 30s // trial's duration, accurate to seconds for example 10min 30s
const convertDuration = (num: number) => { const convertDuration = (num: number) => {
if (num < 1) {
return '0s';
}
const hour = Math.floor(num / 3600); const hour = Math.floor(num / 3600);
const min = Math.floor(num / 60 % 60); const minute = Math.floor(num / 60 % 60);
const second = Math.floor(num % 60); const second = Math.floor(num % 60);
const result = hour > 0 ? `${hour} h ${min} min ${second}s` : `${min} min ${second}s`; let result = [ ];
if (hour <= 0 && min === 0 && second !== 0) { if (hour > 0) {
return `${second}s`; result.push(`${hour}h`);
} else if (hour === 0 && min !== 0 && second === 0) { }
return `${min}min`; if (minute > 0) {
} else if (hour === 0 && min !== 0 && second !== 0) { result.push(`${minute}min`);
return `${min}min ${second}s`; }
} else { if (second > 0) {
return result; result.push(`${second}s`);
} }
return result.join(' ');
}; };
// get final result value // get final result value
// draw Accuracy point graph // draw Accuracy point graph
const getFinalResult = (final: Array<FinalResult>) => { const getFinalResult = (final?: MetricDataRecord[]) => {
let acc; let acc;
let showDefault = 0; let showDefault = 0;
if (final) { if (final) {
...@@ -51,7 +58,7 @@ const getFinalResult = (final: Array<FinalResult>) => { ...@@ -51,7 +58,7 @@ const getFinalResult = (final: Array<FinalResult>) => {
}; };
// get final result value // acc obj // get final result value // acc obj
const getFinal = (final: Array<FinalResult>) => { const getFinal = (final?: MetricDataRecord[]) => {
let showDefault: FinalType; let showDefault: FinalType;
if (final) { if (final) {
showDefault = JSON.parse(final[final.length - 1].data); showDefault = JSON.parse(final[final.length - 1].data);
...@@ -101,7 +108,7 @@ const intermediateGraphOption = (intermediateArr: number[], id: string) => { ...@@ -101,7 +108,7 @@ const intermediateGraphOption = (intermediateArr: number[], id: string) => {
}; };
// kill job // kill job
const killJob = (key: number, id: string, status: string, updateList: Function) => { const killJob = (key: number, id: string, status: string, updateList?: Function) => {
axios(`${MANAGER_IP}/trial-jobs/${id}`, { axios(`${MANAGER_IP}/trial-jobs/${id}`, {
method: 'DELETE', method: 'DELETE',
headers: { headers: {
...@@ -113,7 +120,9 @@ const killJob = (key: number, id: string, status: string, updateList: Function) ...@@ -113,7 +120,9 @@ const killJob = (key: number, id: string, status: string, updateList: Function)
message.destroy(); message.destroy();
message.success('Cancel the job successfully'); message.success('Cancel the job successfully');
// render the table // render the table
updateList(); if (updateList) {
updateList(); // FIXME
}
} else { } else {
message.error('fail to cancel the job'); message.error('fail to cancel the job');
} }
...@@ -160,7 +169,22 @@ const downFile = (content: string, fileName: string) => { ...@@ -160,7 +169,22 @@ const downFile = (content: string, fileName: string) => {
} }
}; };
function formatTimestamp(timestamp?: number, placeholder?: string = 'N/A'): string {
return timestamp ? new Date(timestamp).toLocaleString('en-US') : placeholder;
}
function metricAccuracy(metric: MetricDataRecord): number {
const data = JSON.parse(metric.data);
return typeof data === 'number' ? data : NaN;
}
function formatAccuracy(accuracy: number): string {
// TODO: how to format NaN?
return accuracy.toFixed(6).replace(/0+$/, '').replace(/\.$/, '');
}
export { export {
convertTime, convertDuration, getFinalResult, getFinal, downFile, convertTime, convertDuration, getFinalResult, getFinal, downFile,
intermediateGraphOption, killJob, filterByStatus, filterDuration intermediateGraphOption, killJob, filterByStatus, filterDuration,
formatAccuracy, formatTimestamp, metricAccuracy,
}; };
// tslint:disable:no-any
// draw accuracy graph data interface // draw accuracy graph data interface
interface TableObj { interface TableObj {
key: number; key: number;
...@@ -12,6 +14,19 @@ interface TableObj { ...@@ -12,6 +14,19 @@ interface TableObj {
endTime?: number; endTime?: number;
} }
interface TableRecord {
key: string;
sequenceId: number;
startTime: number;
endTime?: number;
id: string;
duration: number;
status: string;
intermediateCount: number;
accuracy?: number;
latestAccuracy: string; // formatted string
}
interface SearchSpace { interface SearchSpace {
_value: Array<number | string>; _value: Array<number | string>;
_type: string; _type: string;
...@@ -32,26 +47,6 @@ interface Parameters { ...@@ -32,26 +47,6 @@ interface Parameters {
multiProgress?: number; multiProgress?: number;
} }
interface Experiment {
id: string;
author: string;
revision?: number;
experName: string;
logDir?: string;
runConcurren: number;
maxDuration: number;
execDuration: number;
MaxTrialNum: number;
startTime: number;
endTime?: number;
trainingServicePlatform: string;
tuner: object;
assessor?: object;
advisor?: object;
clusterMetaData?: object;
logCollection?: string;
}
// trial accuracy // trial accuracy
interface AccurPoint { interface AccurPoint {
acc: number; acc: number;
...@@ -74,21 +69,6 @@ interface TooltipForAccuracy { ...@@ -74,21 +69,6 @@ interface TooltipForAccuracy {
data: Array<number | object>; data: Array<number | object>;
} }
interface TrialNumber {
succTrial: number;
failTrial: number;
stopTrial: number;
waitTrial: number;
runTrial: number;
unknowTrial: number;
totalCurrentTrial: number;
}
interface TrialJob {
text: string;
value: string;
}
interface Dimobj { interface Dimobj {
dim: number; dim: number;
name: string; name: string;
...@@ -108,10 +88,6 @@ interface ParaObj { ...@@ -108,10 +88,6 @@ interface ParaObj {
parallelAxis: Array<Dimobj>; parallelAxis: Array<Dimobj>;
} }
interface FinalResult {
data: string;
}
interface Intermedia { interface Intermedia {
name: string; // id name: string; // id
type: string; type: string;
...@@ -119,13 +95,93 @@ interface Intermedia { ...@@ -119,13 +95,93 @@ interface Intermedia {
hyperPara: object; // each trial hyperpara value hyperPara: object; // each trial hyperpara value
} }
interface ExperimentInfo { interface MetricDataRecord {
platform: string; timestamp: number;
optimizeMode: string; trialJobId: string;
parameterId: string;
type: string;
sequence: number;
data: string;
}
interface TrialJobInfo {
id: string;
sequenceId: number;
status: string;
startTime?: number;
endTime?: number;
hyperParameters?: string[];
logPath?: string;
finalMetricData?: MetricDataRecord[];
stderrPath?: string;
}
interface ExperimentParams {
authorName: string;
experimentName: string;
description?: string;
trialConcurrency: number;
maxExecDuration: number; // seconds
maxTrialNum: number;
searchSpace: string;
trainingServicePlatform: string;
multiPhase?: boolean;
multiThread?: boolean;
versionCheck?: boolean;
logCollection?: string;
tuner?: {
className: string;
builtinTunerName?: string;
codeDir?: string;
classArgs?: any;
classFileName?: string;
checkpointDir: string;
gpuNum?: number;
includeIntermediateResults?: boolean;
};
assessor?: {
className: string;
builtinAssessorName?: string;
codeDir?: string;
classArgs?: any;
classFileName?: string;
checkpointDir: string;
gpuNum?: number;
};
advisor?: {
className: string;
builtinAdvisorName?: string;
codeDir?: string;
classArgs?: any;
classFileName?: string;
checkpointDir: string;
gpuNum?: number;
};
clusterMetaData?: {
key: string;
value: string;
}[];
}
interface ExperimentProfile {
params: ExperimentParams;
id: string;
execDuration: number;
logDir?: string;
startTime?: number;
endTime?: number;
maxSequenceId: number;
revision: number;
}
interface NNIManagerStatus {
status: string;
errors: string[];
} }
export { export {
TableObj, Parameters, Experiment, AccurPoint, TrialNumber, TrialJob, TableObj, TableRecord, Parameters, ExperimentProfile, AccurPoint,
DetailAccurPoint, TooltipForAccuracy, ParaObj, Dimobj, FinalResult, FinalType, DetailAccurPoint, TooltipForAccuracy, ParaObj, Dimobj, FinalType,
TooltipForIntermediate, SearchSpace, Intermedia, ExperimentInfo TooltipForIntermediate, SearchSpace, Intermedia, MetricDataRecord, TrialJobInfo,
NNIManagerStatus,
}; };
import axios from 'axios';
import { MANAGER_IP } from '../const';
import { ExperimentProfile, NNIManagerStatus } from '../interface';
function compareProfiles(profile1?: ExperimentProfile, profile2?: ExperimentProfile): boolean {
if (!profile1 || !profile2) {
return false;
}
const copy1 = Object.assign({}, profile1, { execDuration: undefined });
const copy2 = Object.assign({}, profile2, { execDuration: undefined });
return JSON.stringify(copy1) === JSON.stringify(copy2);
}
class Experiment {
private profileField?: ExperimentProfile = undefined;
private statusField?: NNIManagerStatus = undefined;
public async init(): Promise<void> {
while (!this.profileField || !this.statusField) {
await this.update();
}
}
public async update(): Promise<boolean> {
const profilePromise = axios.get(`${MANAGER_IP}/experiment`);
const statusPromise = axios.get(`${MANAGER_IP}/check-status`);
const [ profileResponse, statusResponse ] = await Promise.all([ profilePromise, statusPromise ]);
let updated = false;
if (statusResponse.status === 200) {
updated = JSON.stringify(this.statusField) === JSON.stringify(statusResponse.data);
this.statusField = statusResponse.data;
}
if (profileResponse.status === 200) {
updated = updated || compareProfiles(this.profileField, profileResponse.data);
this.profileField = profileResponse.data;
}
return updated;
}
get profile(): ExperimentProfile {
if (!this.profileField) {
throw Error('Experiment profile not initialized');
}
return this.profileField!;
}
get trialConcurrency(): number {
return this.profile.params.trialConcurrency;
}
get optimizeMode(): string {
const tuner = this.profile.params.tuner;
return (tuner && tuner.classArgs && tuner.classArgs.optimize_mode) ? tuner.classArgs.optimize_mode : 'unknown';
}
get trainingServicePlatform(): string {
return this.profile.params.trainingServicePlatform;
}
get searchSpace(): object {
return JSON.parse(this.profile.params.searchSpace);
}
get logCollectionEnabled(): boolean {
return !!(this.profile.params.logCollection && this.profile.params.logCollection !== 'none');
}
get multiPhase(): boolean {
return !!(this.profile.params.multiPhase);
}
get status(): string {
if (!this.statusField) {
throw Error('Experiment status not initialized');
}
return this.statusField!.status;
}
get error(): string {
if (!this.statusField) {
throw Error('Experiment status not initialized');
}
return this.statusField!.errors[0] || '';
}
}
export { Experiment };
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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