зеркало из https://github.com/microsoft/nni.git
Bug fix: after clicking compare button, the dropdown button (for selecting different keys) is not shown (#4990)
This commit is contained in:
Родитель
60e1e01f62
Коммит
33cdb5b60c
|
@ -26,7 +26,7 @@ const OpenRow = (props): any => {
|
|||
const trialId = props.trialId;
|
||||
const trial = TRIALS.getTrial(trialId);
|
||||
const logPathRow = trial.info.logPath || "This trial's log path is not available.";
|
||||
const originParameters = trial.description.parameters;
|
||||
const originParameters = trial.parameter;
|
||||
const hasVisualHyperParams = RETIARIIPARAMETERS in originParameters;
|
||||
|
||||
const hideMessageInfo = (): void => {
|
||||
|
@ -55,7 +55,7 @@ const OpenRow = (props): any => {
|
|||
|
||||
const copyParams = (trial: Trial): void => {
|
||||
// get copy parameters
|
||||
const params = JSON.stringify(reformatRetiariiParameter(trial.description.parameters as any), null, 4);
|
||||
const params = JSON.stringify(reformatRetiariiParameter(trial.parameter as any), null, 4);
|
||||
if (copy.default(params)) {
|
||||
getCopyStatus('Successfully copy parameters to clipboard in form of python dict !', 'success');
|
||||
} else {
|
||||
|
|
|
@ -48,6 +48,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
|
|||
render(): React.ReactNode {
|
||||
const { whichChart } = this.state;
|
||||
const source = TRIALS.toArray();
|
||||
const paraSource = TRIALS.succeededTrials();
|
||||
const succeededTrialIds = TRIALS.succeededTrials().map(trial => trial.id);
|
||||
return (
|
||||
<AppContext.Consumer>
|
||||
|
@ -74,12 +75,12 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
|
|||
{/* <PivotItem tab={this.titleOfhyper} key="2"> */}
|
||||
<PivotItem headerText='Hyper-parameter' itemIcon='Equalizer' key='Hyper-parameter'>
|
||||
<Stack className='graph'>
|
||||
<Para trials={source} searchSpace={EXPERIMENT.searchSpaceNew} />
|
||||
<Para trials={paraSource} searchSpace={EXPERIMENT.searchSpaceNew} />
|
||||
</Stack>
|
||||
</PivotItem>
|
||||
{/* <PivotItem tab={this.titleOfDuration} key="3"> */}
|
||||
<PivotItem headerText='Duration' itemIcon='BarChartHorizontal' key='Duration'>
|
||||
<Duration source={source} />
|
||||
<Duration source={TRIALS.notWaittingTrials()} />
|
||||
</PivotItem>
|
||||
{/* <PivotItem tab={this.titleOfIntermediate} key="4"> */}
|
||||
<PivotItem
|
||||
|
@ -88,7 +89,7 @@ class TrialsDetail extends React.Component<{}, TrialDetailState> {
|
|||
key='Intermediate result'
|
||||
>
|
||||
{/* *why this graph has small footprint? */}
|
||||
<Intermediate source={source} />
|
||||
<Intermediate source={TRIALS.allTrialsIntermediateChart()} />
|
||||
</PivotItem>
|
||||
</Pivot>
|
||||
</div>
|
||||
|
|
|
@ -138,16 +138,17 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState>
|
|||
trial.sequenceId,
|
||||
trial.acc === undefined ? 0 : this.formatAccuracy(trial.acc[userSelectAccuracyNumberKey]),
|
||||
trial.id,
|
||||
trial.description.parameters
|
||||
trial.parameter
|
||||
]);
|
||||
} else {
|
||||
data = trials.map(trial => [
|
||||
trial.sequenceId,
|
||||
this.formatAccuracy(trial.accuracy),
|
||||
trial.id,
|
||||
trial.description.parameters
|
||||
trial.parameter
|
||||
]);
|
||||
}
|
||||
|
||||
return {
|
||||
symbolSize: 6,
|
||||
type: 'scatter',
|
||||
|
@ -163,7 +164,7 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState>
|
|||
best.sequenceId,
|
||||
best.acc === undefined ? 0 : this.formatAccuracy(best.acc[userSelectAccuracyNumberKey]),
|
||||
best.id,
|
||||
best.description.parameters
|
||||
best.parameter
|
||||
]
|
||||
];
|
||||
for (let i = 1; i < trials.length; i++) {
|
||||
|
@ -176,7 +177,7 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState>
|
|||
trial.sequenceId,
|
||||
trial.acc === undefined ? 0 : this.formatAccuracy(trial.acc[userSelectAccuracyNumberKey]),
|
||||
best.id,
|
||||
trial.description.parameters
|
||||
trial.parameter
|
||||
]);
|
||||
best = trial;
|
||||
} else {
|
||||
|
@ -184,7 +185,7 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState>
|
|||
trial.sequenceId,
|
||||
best.acc === undefined ? 0 : this.formatAccuracy(best.acc[userSelectAccuracyNumberKey]),
|
||||
best.id,
|
||||
trial.description.parameters
|
||||
trial.parameter
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
import * as React from 'react';
|
||||
import ReactEcharts from 'echarts-for-react';
|
||||
import { TableObj, EventMap } from '@static/interface';
|
||||
import { filterDuration, convertDuration } from '@static/function';
|
||||
import { EventMap } from '@static/interface';
|
||||
import { Trial } from '@model/trial';
|
||||
import { convertDuration } from '@static/function';
|
||||
import 'echarts/lib/chart/bar';
|
||||
import 'echarts/lib/component/tooltip';
|
||||
import 'echarts/lib/component/title';
|
||||
|
||||
interface Runtrial {
|
||||
trialId: string[];
|
||||
trialId: number[];
|
||||
trialTime: number[];
|
||||
}
|
||||
|
||||
interface DurationProps {
|
||||
source: Array<TableObj>;
|
||||
source: Trial[];
|
||||
}
|
||||
|
||||
interface DurationState {
|
||||
|
@ -31,12 +32,10 @@ class Duration extends React.Component<DurationProps, DurationState> {
|
|||
};
|
||||
}
|
||||
|
||||
initDuration = (source: Array<TableObj>): any => {
|
||||
initDuration = (source: Trial[]): any => {
|
||||
const trialId: number[] = [];
|
||||
const trialTime: number[] = [];
|
||||
const trialJobs = source.filter(filterDuration);
|
||||
|
||||
trialJobs.forEach(item => {
|
||||
source.forEach(item => {
|
||||
trialId.push(item.sequenceId);
|
||||
trialTime.push(item.duration);
|
||||
});
|
||||
|
@ -146,17 +145,16 @@ class Duration extends React.Component<DurationProps, DurationState> {
|
|||
};
|
||||
};
|
||||
|
||||
drawDurationGraph = (source: Array<TableObj>): void => {
|
||||
drawDurationGraph = (source: Trial[]): void => {
|
||||
// why this function run two times when props changed?
|
||||
const trialId: string[] = [];
|
||||
const trialId: number[] = [];
|
||||
const trialTime: number[] = [];
|
||||
const trialRun: Runtrial[] = [];
|
||||
const trialJobs = source.filter(filterDuration);
|
||||
Object.keys(trialJobs).map(item => {
|
||||
const temp = trialJobs[item];
|
||||
trialId.push(temp.sequenceId);
|
||||
trialTime.push(temp.duration);
|
||||
source.forEach(item => {
|
||||
trialId.push(item.sequenceId);
|
||||
trialTime.push(item.duration);
|
||||
});
|
||||
|
||||
trialRun.push({
|
||||
trialId: trialId,
|
||||
trialTime: trialTime
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react';
|
||||
import { Stack, PrimaryButton, Toggle, IStackTokens } from '@fluentui/react';
|
||||
import { TooltipForIntermediate, TableObj, Intermedia, EventMap } from '@static/interface';
|
||||
import { TooltipForIntermediate, EventMap, allTrialsIntermediateChart } from '@static/interface';
|
||||
import { reformatRetiariiParameter } from '@static/function';
|
||||
import ReactEcharts from 'echarts-for-react';
|
||||
import 'echarts/lib/component/tooltip';
|
||||
|
@ -11,20 +11,19 @@ const stackTokens: IStackTokens = {
|
|||
};
|
||||
|
||||
interface IntermediateState {
|
||||
detailSource: Array<TableObj>;
|
||||
detailSource: allTrialsIntermediateChart[];
|
||||
interSource: object;
|
||||
filterSource: Array<TableObj>;
|
||||
filterSource: allTrialsIntermediateChart[];
|
||||
eachIntermediateNum: number; // trial's intermediate number count
|
||||
isLoadconfirmBtn: boolean;
|
||||
isFilter?: boolean | undefined;
|
||||
length: number;
|
||||
clickCounts: number; // user filter intermediate click confirm btn's counts
|
||||
startMediaY: number;
|
||||
endMediaY: number;
|
||||
}
|
||||
|
||||
interface IntermediateProps {
|
||||
source: Array<TableObj>;
|
||||
source: allTrialsIntermediateChart[];
|
||||
}
|
||||
|
||||
class Intermediate extends React.Component<IntermediateProps, IntermediateState> {
|
||||
|
@ -43,43 +42,24 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
|
|||
isLoadconfirmBtn: false,
|
||||
isFilter: false,
|
||||
length: 100000,
|
||||
clickCounts: 0,
|
||||
startMediaY: 0,
|
||||
endMediaY: 100
|
||||
};
|
||||
}
|
||||
|
||||
drawIntermediate = (source: Array<TableObj>): void => {
|
||||
drawIntermediate = (source: allTrialsIntermediateChart[]): void => {
|
||||
if (source.length > 0) {
|
||||
this.setState({
|
||||
length: source.length,
|
||||
detailSource: source
|
||||
});
|
||||
const { startMediaY, endMediaY } = this.state;
|
||||
const trialIntermediate: Array<Intermedia> = [];
|
||||
Object.keys(source).map(item => {
|
||||
const temp = source[item];
|
||||
trialIntermediate.push({
|
||||
name: temp.id,
|
||||
trialNum: temp.sequenceId,
|
||||
data: temp.description.intermediate,
|
||||
type: 'line',
|
||||
hyperPara: temp.description.parameters
|
||||
});
|
||||
});
|
||||
// find max intermediate number
|
||||
trialIntermediate.sort((a, b) => {
|
||||
const xAxis: number[] = [];
|
||||
// find having most intermediate number
|
||||
source.sort((a, b) => {
|
||||
return b.data.length - a.data.length;
|
||||
});
|
||||
const legend: string[] = [];
|
||||
// max length
|
||||
const length = trialIntermediate[0].data.length;
|
||||
const xAxis: number[] = [];
|
||||
Object.keys(trialIntermediate).map(item => {
|
||||
const temp = trialIntermediate[item];
|
||||
legend.push(temp.name);
|
||||
});
|
||||
for (let i = 1; i <= length; i++) {
|
||||
for (let i = 1; i <= source[0].data.length; i++) {
|
||||
xAxis.push(i);
|
||||
}
|
||||
const option = {
|
||||
|
@ -89,20 +69,21 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
|
|||
confine: true,
|
||||
formatter: function (data: TooltipForIntermediate): React.ReactNode {
|
||||
const trialId = data.seriesName;
|
||||
let parameters = {};
|
||||
let trialNum = 0;
|
||||
const temp = trialIntermediate.find(key => key.name === trialId);
|
||||
if (temp !== undefined) {
|
||||
parameters = temp.hyperPara;
|
||||
trialNum = temp.trialNum;
|
||||
// parameter and trialNo need to have the init value otherwise maybe cause page broke down
|
||||
let parameter = {};
|
||||
let trialNo = 0;
|
||||
const renderTrial = source.find(key => key.name === trialId);
|
||||
if (renderTrial !== undefined) {
|
||||
parameter = renderTrial.parameter;
|
||||
trialNo = renderTrial.sequenceId;
|
||||
}
|
||||
return `
|
||||
<div class="tooldetailAccuracy">
|
||||
<div>Trial No.: ${trialNum}</div>
|
||||
<div>Trial No.: ${trialNo}</div>
|
||||
<div>Trial ID: ${trialId}</div>
|
||||
<div>Intermediate: ${data.data}</div>
|
||||
<div>Parameters: <pre>${JSON.stringify(
|
||||
reformatRetiariiParameter(parameters),
|
||||
reformatRetiariiParameter(parameter),
|
||||
null,
|
||||
4
|
||||
)}</pre>
|
||||
|
@ -137,7 +118,7 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
|
|||
end: endMediaY
|
||||
}
|
||||
],
|
||||
series: trialIntermediate
|
||||
series: source
|
||||
};
|
||||
this.setState({
|
||||
interSource: option
|
||||
|
@ -164,7 +145,7 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
|
|||
|
||||
// confirm btn function [filter data]
|
||||
filterLines = (): void => {
|
||||
const filterSource: Array<TableObj> = [];
|
||||
const filterSource: allTrialsIntermediateChart[] = [];
|
||||
this.setState({ isLoadconfirmBtn: true }, () => {
|
||||
const { source } = this.props;
|
||||
// get input value
|
||||
|
@ -175,32 +156,29 @@ class Intermediate extends React.Component<IntermediateProps, IntermediateState>
|
|||
if (pointVal === '' || minVal === '') {
|
||||
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];
|
||||
// user not input max value
|
||||
for (const item of source) {
|
||||
const val = item.data[position - 1];
|
||||
if (val >= min) {
|
||||
filterSource.push(temp);
|
||||
filterSource.push(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const max = JSON.parse(maxVal);
|
||||
Object.keys(source).map(item => {
|
||||
const temp = source[item];
|
||||
const val = temp.description.intermediate[position - 1];
|
||||
for (const item of source) {
|
||||
const val = item.data[position - 1];
|
||||
if (val >= min && val <= max) {
|
||||
filterSource.push(temp);
|
||||
filterSource.push(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
this.setState({ filterSource: filterSource });
|
||||
this.drawIntermediate(filterSource);
|
||||
}
|
||||
const counts = this.state.clickCounts + 1;
|
||||
this.setState({ isLoadconfirmBtn: false, clickCounts: counts });
|
||||
this.setState({ isLoadconfirmBtn: false });
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@ import * as d3 from 'd3';
|
|||
import { Dropdown, IDropdownOption, Stack, DefaultButton } from '@fluentui/react';
|
||||
import ParCoords from 'parcoord-es';
|
||||
import { SearchSpace } from '@model/searchspace';
|
||||
import { filterByStatus } from '@static/function';
|
||||
import { EXPERIMENT, TRIALS } from '@static/datamodel';
|
||||
import { TableObj, SingleAxis, MultipleAxes } from '@static/interface';
|
||||
import { SingleAxis, MultipleAxes } from '@static/interface';
|
||||
import { Trial } from '@model/trial';
|
||||
import ChangeColumnComponent from '../ChangeColumnComponent';
|
||||
import { optimizeModeValue } from './optimizeMode';
|
||||
|
||||
|
@ -25,7 +25,7 @@ interface ParaState {
|
|||
}
|
||||
|
||||
interface ParaProps {
|
||||
trials: Array<TableObj>;
|
||||
trials: Trial[];
|
||||
searchSpace: SearchSpace;
|
||||
}
|
||||
|
||||
|
@ -304,9 +304,8 @@ class Para extends React.Component<ParaProps, ParaState> {
|
|||
|
||||
private getTrialsAsObjectList(inferredSearchSpace: MultipleAxes, inferredMetricSpace: MultipleAxes): {}[] {
|
||||
const { trials } = this.props;
|
||||
const succeededTrials = trials.filter(filterByStatus);
|
||||
|
||||
return succeededTrials.map(s => {
|
||||
return trials.map(s => {
|
||||
const entries = Array.from(s.parameters(inferredSearchSpace).entries());
|
||||
entries.push(...Array.from(s.metrics(inferredMetricSpace).entries()));
|
||||
const ret = {};
|
||||
|
|
|
@ -13,22 +13,15 @@ import {
|
|||
import { Trial } from '@model/trial';
|
||||
import { TOOLTIP_BACKGROUND_COLOR } from '@static/const';
|
||||
import { EXPERIMENT, TRIALS } from '@static/datamodel';
|
||||
import {
|
||||
convertDuration,
|
||||
formatTimestamp,
|
||||
copyAndSort,
|
||||
parametersType,
|
||||
_inferColumnTitle,
|
||||
getIntermediateAllKeys
|
||||
} from '@static/function';
|
||||
import { TableObj, SortInfo, SearchItems } from '@static/interface';
|
||||
import { convertDuration, formatTimestamp, copyAndSort, parametersType, _inferColumnTitle } from '@static/function';
|
||||
import { SortInfo, SearchItems } from '@static/interface';
|
||||
import { blocked, copy, LineChart, tableListIcon } from '@components/fluent/Icon';
|
||||
import Customize from './tableFunction/CustomizedTrial';
|
||||
import TensorboardUI from './tableFunction/tensorboard/TensorboardUI';
|
||||
import Search from './tableFunction/search/Search';
|
||||
import ExpandableDetails from '@components/common/ExpandableDetails/ExpandableIndex';
|
||||
import ChangeColumnComponent from '../ChangeColumnComponent';
|
||||
import Compare from './tableFunction/Compare';
|
||||
import Compare from './tableFunction/CompareIndex';
|
||||
import KillJobIndex from './tableFunction/killJob/KillJobIndex';
|
||||
import { getTrialsBySearchFilters } from './tableFunction/search/searchFunction';
|
||||
import PaginationTable from '@components/common/PaginationTable';
|
||||
|
@ -43,7 +36,7 @@ type SearchOptionType = 'id' | 'trialnum' | 'status' | 'parameters';
|
|||
const defaultDisplayedColumns = ['sequenceId', 'id', 'duration', 'status', 'latestAccuracy'];
|
||||
|
||||
interface TableListProps {
|
||||
tableSource: TableObj[];
|
||||
tableSource: Trial[];
|
||||
}
|
||||
|
||||
interface TableListState {
|
||||
|
@ -55,12 +48,11 @@ interface TableListState {
|
|||
selectedRowIds: string[];
|
||||
customizeColumnsDialogVisible: boolean;
|
||||
compareDialogVisible: boolean;
|
||||
intermediateDialogTrial: TableObj | undefined;
|
||||
intermediateDialogTrial: Trial[] | undefined;
|
||||
copiedTrialId: string | undefined;
|
||||
sortInfo: SortInfo;
|
||||
searchItems: Array<SearchItems>;
|
||||
relation: Map<string, string>;
|
||||
intermediateKeyList: string[];
|
||||
}
|
||||
|
||||
class TableList extends React.Component<TableListProps, TableListState> {
|
||||
|
@ -86,8 +78,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
|
|||
copiedTrialId: undefined,
|
||||
sortInfo: { field: '', isDescend: true },
|
||||
searchItems: [],
|
||||
relation: parametersType(),
|
||||
intermediateKeyList: []
|
||||
relation: parametersType()
|
||||
};
|
||||
|
||||
this._expandedTrialIds = new Set<string>();
|
||||
|
@ -113,8 +104,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
|
|||
selectedRowIds,
|
||||
intermediateDialogTrial,
|
||||
copiedTrialId,
|
||||
searchItems,
|
||||
intermediateKeyList
|
||||
searchItems
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
|
@ -145,10 +135,23 @@ class TableList extends React.Component<TableListProps, TableListState> {
|
|||
text='Compare'
|
||||
className='allList-compare'
|
||||
onClick={(): void => {
|
||||
this.setState({ compareDialogVisible: true });
|
||||
this.setState({
|
||||
compareDialogVisible: true
|
||||
});
|
||||
}}
|
||||
disabled={selectedRowIds.length === 0}
|
||||
/>
|
||||
{/* compare model: trial intermediates graph; table: id,no,status,default dict value */}
|
||||
{compareDialogVisible && (
|
||||
<Compare
|
||||
title='Compare trials'
|
||||
trials={this.props.tableSource.filter(trial => selectedRowIds.includes(trial.id))}
|
||||
onHideDialog={(): void => {
|
||||
this.setState({ compareDialogVisible: false });
|
||||
}}
|
||||
changeSelectTrialIds={this.changeSelectTrialIds}
|
||||
/>
|
||||
)}
|
||||
<TensorboardUI
|
||||
selectedRowIds={selectedRowIds}
|
||||
changeSelectTrialIds={this.changeSelectTrialIds}
|
||||
|
@ -172,23 +175,10 @@ class TableList extends React.Component<TableListProps, TableListState> {
|
|||
}}
|
||||
/>
|
||||
)}
|
||||
{compareDialogVisible && (
|
||||
<Compare
|
||||
title='Compare trials'
|
||||
showDetails={true}
|
||||
trials={this.props.tableSource.filter(trial => selectedRowIds.includes(trial.id))}
|
||||
onHideDialog={(): void => {
|
||||
this.setState({ compareDialogVisible: false });
|
||||
}}
|
||||
changeSelectTrialIds={this.changeSelectTrialIds}
|
||||
/>
|
||||
)}
|
||||
{intermediateDialogTrial !== undefined && (
|
||||
<Compare
|
||||
title='Intermediate results'
|
||||
showDetails={false}
|
||||
trials={[intermediateDialogTrial]}
|
||||
intermediateKeyList={intermediateKeyList}
|
||||
trials={intermediateDialogTrial}
|
||||
onHideDialog={(): void => {
|
||||
this.setState({ intermediateDialogTrial: undefined });
|
||||
}}
|
||||
|
@ -236,32 +226,21 @@ class TableList extends React.Component<TableListProps, TableListState> {
|
|||
);
|
||||
}
|
||||
|
||||
private _trialsToTableItems(trials: TableObj[]): any[] {
|
||||
private _trialsToTableItems(trials: Trial[]): any[] {
|
||||
// TODO: use search space and metrics space from TRIALS will cause update issues.
|
||||
const searchSpace = TRIALS.inferredSearchSpace(EXPERIMENT.searchSpaceNew);
|
||||
const metricSpace = TRIALS.inferredMetricSpace();
|
||||
const { selectedRowIds } = this.state;
|
||||
const items = trials.map(trial => {
|
||||
const ret = {
|
||||
sequenceId: trial.sequenceId,
|
||||
id: trial.id,
|
||||
_checked: selectedRowIds.includes(trial.id) ? true : false,
|
||||
startTime: (trial as Trial).info.startTime, // FIXME: why do we need info here?
|
||||
endTime: (trial as Trial).info.endTime,
|
||||
duration: trial.duration,
|
||||
status: trial.status,
|
||||
message: (trial as Trial).info.message || '--',
|
||||
intermediateCount: trial.intermediates.length,
|
||||
_expandDetails: this._expandedTrialIds.has(trial.id) // hidden field names should start with `_`
|
||||
};
|
||||
const ret = trial.tableRecord;
|
||||
ret['_checked'] = selectedRowIds.includes(trial.id) ? true : false;
|
||||
ret['_expandDetails'] = this._expandedTrialIds.has(trial.id); // hidden field names should start with `_`
|
||||
for (const [k, v] of trial.parameters(searchSpace)) {
|
||||
ret[`space/${k.baseName}`] = v;
|
||||
}
|
||||
for (const [k, v] of trial.metrics(metricSpace)) {
|
||||
ret[`metric/${k.baseName}`] = v;
|
||||
}
|
||||
ret['latestAccuracy'] = (trial as Trial).latestAccuracy;
|
||||
ret['_formattedLatestAccuracy'] = (trial as Trial).formatLatestAccuracy();
|
||||
return ret;
|
||||
});
|
||||
|
||||
|
@ -397,9 +376,6 @@ class TableList extends React.Component<TableListProps, TableListState> {
|
|||
...(k === 'status' && {
|
||||
// color status
|
||||
onRender: (record): React.ReactNode => (
|
||||
// kill 成功之后,重新拉取的数据如果有 endtime 字段,会马上render出user_cancel
|
||||
// 的状态,反之,没有这个字段,table依然是部分刷新,只刷新duration,不会
|
||||
// 刷新 status
|
||||
<span className={`${record.status} commonStyle`}>{record.status}</span>
|
||||
)
|
||||
}),
|
||||
|
@ -461,7 +437,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
|
|||
}
|
||||
}}
|
||||
>
|
||||
<div className='ellipsis'>{record._formattedLatestAccuracy}</div>
|
||||
<div className='ellipsis'>{record.formattedLatestAccuracy}</div>
|
||||
</TooltipHost>
|
||||
)
|
||||
}),
|
||||
|
@ -545,11 +521,9 @@ class TableList extends React.Component<TableListProps, TableListState> {
|
|||
title='Intermediate'
|
||||
onClick={(): void => {
|
||||
const { tableSource } = this.props;
|
||||
const trial = tableSource.find(trial => trial.id === record.id) as TableObj;
|
||||
const intermediateKeyListResult = getIntermediateAllKeys(trial);
|
||||
const trial = tableSource.find(trial => trial.id === record.id) as Trial;
|
||||
this.setState({
|
||||
intermediateDialogTrial: trial,
|
||||
intermediateKeyList: intermediateKeyListResult
|
||||
intermediateDialogTrial: [trial]
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import * as React from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { renderToString } from 'react-dom/server';
|
||||
import { Stack, Modal, IconButton, IDragOptions, ContextualMenu, Dropdown, IDropdownOption } from '@fluentui/react';
|
||||
import ReactEcharts from 'echarts-for-react';
|
||||
import { TooltipForIntermediate, TableObj, SingleAxis } from '@static/interface';
|
||||
import { contentStyles, iconButtonStyles } from '@components/fluent/ModalTheme';
|
||||
import { convertDuration, parseMetrics } from '@static/function';
|
||||
import { EXPERIMENT, TRIALS } from '@static/datamodel';
|
||||
import { Trial } from '@model/trial';
|
||||
import { TooltipForIntermediate, SingleAxis } from '@static/interface';
|
||||
import { contentStyles, iconButtonStyles } from '@components/fluent/ModalTheme';
|
||||
import { convertDuration, parseMetrics, getIntermediateAllKeys } from '@static/function';
|
||||
import '@style/experiment/trialdetail/compare.scss';
|
||||
|
||||
/***
|
||||
|
@ -26,7 +27,7 @@ const dragOptions: IDragOptions = {
|
|||
|
||||
// TODO: this should be refactored to the common modules
|
||||
// copied from trial.ts
|
||||
function _parseIntermediates(trial: TableObj, key: string): number[] {
|
||||
function _parseIntermediates(trial: Trial, key: string): number[] {
|
||||
const intermediates: number[] = [];
|
||||
for (const metric of trial.intermediates) {
|
||||
if (metric === undefined) {
|
||||
|
@ -53,30 +54,46 @@ interface Item {
|
|||
}
|
||||
|
||||
interface CompareProps {
|
||||
trials: TableObj[];
|
||||
trials: Trial[];
|
||||
title: string;
|
||||
showDetails: boolean;
|
||||
intermediateKeyList?: string[];
|
||||
onHideDialog: () => void;
|
||||
changeSelectTrialIds?: () => void;
|
||||
}
|
||||
|
||||
interface CompareState {
|
||||
intermediateKey: string; // default, dict other keys
|
||||
}
|
||||
function CompareIndex(props: CompareProps): any {
|
||||
const { trials, title } = props;
|
||||
const atrial = trials.find(item => item.intermediates.length > 0);
|
||||
const intermediateAllKeysList = getIntermediateAllKeys(atrial === undefined ? trials[0] : atrial);
|
||||
const [intermediateKey, setIntermediateKey] = useState(
|
||||
intermediateAllKeysList.length > 0 ? intermediateAllKeysList[0] : 'default'
|
||||
);
|
||||
const runningTrial = trials.find(item => item.status === 'RUNNING');
|
||||
const runningTrialIntermediateListLength = runningTrial !== undefined ? runningTrial.intermediates.length : -1;
|
||||
|
||||
class Compare extends React.Component<CompareProps, CompareState> {
|
||||
constructor(props: CompareProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
// intermediate result maybe don't have the 'default' key...
|
||||
intermediateKey:
|
||||
this.props.intermediateKeyList !== undefined ? this.props.intermediateKeyList[0] : 'default'
|
||||
function itemsList(): Item[] {
|
||||
const inferredSearchSpace = TRIALS.inferredSearchSpace(EXPERIMENT.searchSpaceNew);
|
||||
const flatten = (m: Map<SingleAxis, any>): Map<string, any> => {
|
||||
return new Map(Array.from(m).map(([key, value]) => [key.baseName, value]));
|
||||
};
|
||||
return trials.map(trial => ({
|
||||
id: trial.id,
|
||||
sequenceId: trial.sequenceId,
|
||||
duration: convertDuration(trial.duration),
|
||||
parameters: flatten(trial.parameters(inferredSearchSpace)),
|
||||
metrics: flatten(trial.metrics(TRIALS.inferredMetricSpace())),
|
||||
intermediates: _parseIntermediates(trial, intermediateKey)
|
||||
}));
|
||||
}
|
||||
|
||||
private _generateTooltipSummary = (row: Item, value: string): string =>
|
||||
const [items, setItems] = useState(itemsList());
|
||||
|
||||
// react componentDidMount & componentDidUpdate
|
||||
useEffect(() => {
|
||||
setItems(itemsList());
|
||||
}, [intermediateKey, runningTrialIntermediateListLength]); // update condition
|
||||
|
||||
// page related function
|
||||
const _generateTooltipSummary = (row: Item, value: string): string =>
|
||||
renderToString(
|
||||
<div className='tooldetailAccuracy'>
|
||||
<div>Trial No.: {row.sequenceId}</div>
|
||||
|
@ -85,7 +102,7 @@ class Compare extends React.Component<CompareProps, CompareState> {
|
|||
</div>
|
||||
);
|
||||
|
||||
private _intermediates(items: Item[]): React.ReactNode {
|
||||
function _intermediates(items: Item[]): React.ReactNode {
|
||||
// Precondition: make sure `items` is not empty
|
||||
const xAxisMax = Math.max(...items.map(item => item.intermediates.length));
|
||||
const xAxis = Array(xAxisMax)
|
||||
|
@ -104,7 +121,7 @@ class Compare extends React.Component<CompareProps, CompareState> {
|
|||
confine: true,
|
||||
formatter: (data: TooltipForIntermediate): string => {
|
||||
const item = items.find(k => k.id === data.seriesName) as Item;
|
||||
return this._generateTooltipSummary(item, data.data);
|
||||
return _generateTooltipSummary(item, data.data);
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
|
@ -141,7 +158,7 @@ class Compare extends React.Component<CompareProps, CompareState> {
|
|||
);
|
||||
}
|
||||
|
||||
private _renderRow(
|
||||
function _renderRow(
|
||||
key: string,
|
||||
rowName: string,
|
||||
className: string,
|
||||
|
@ -160,7 +177,7 @@ class Compare extends React.Component<CompareProps, CompareState> {
|
|||
);
|
||||
}
|
||||
|
||||
private _overlapKeys(s: Map<string, any>[]): string[] {
|
||||
function _overlapKeys(s: Map<string, any>[]): string[] {
|
||||
// Calculate the overlapped keys for multiple
|
||||
const intersection: string[] = [];
|
||||
for (const i of s[0].keys()) {
|
||||
|
@ -179,7 +196,7 @@ class Compare extends React.Component<CompareProps, CompareState> {
|
|||
}
|
||||
|
||||
// render table column ---
|
||||
private _columns(items: Item[]): React.ReactNode {
|
||||
function _columns(items: Item[]): React.ReactNode {
|
||||
// Precondition: make sure `items` is not empty
|
||||
const width = _getWebUIWidth();
|
||||
let scrollClass: string = '';
|
||||
|
@ -190,23 +207,21 @@ class Compare extends React.Component<CompareProps, CompareState> {
|
|||
} else {
|
||||
scrollClass = items.length > 2 ? 'flex' : '';
|
||||
}
|
||||
const parameterKeys = this._overlapKeys(items.map(item => item.parameters));
|
||||
const metricKeys = this._overlapKeys(items.map(item => item.metrics));
|
||||
const parameterKeys = _overlapKeys(items.map(item => item.parameters));
|
||||
const metricKeys = _overlapKeys(items.map(item => item.metrics));
|
||||
|
||||
return (
|
||||
<table className={`compare-modal-table ${scrollClass} fontColor333`}>
|
||||
<tbody>
|
||||
{this._renderRow('id', 'ID', 'value idList', items, item => item.id)}
|
||||
{this._renderRow('trialnum', 'Trial No.', 'value', items, item => item.sequenceId.toString())}
|
||||
{this._renderRow('duration', 'Duration', 'value', items, item => item.duration)}
|
||||
{_renderRow('id', 'ID', 'value idList', items, item => item.id)}
|
||||
{_renderRow('trialnum', 'Trial No.', 'value', items, item => item.sequenceId.toString())}
|
||||
{_renderRow('duration', 'Duration', 'value', items, item => item.duration)}
|
||||
{parameterKeys.map(k =>
|
||||
this._renderRow(`space_${k}`, k, 'value', items, item => item.parameters.get(k))
|
||||
_renderRow(`space_${k}`, k, 'value', items, item => item.parameters.get(k))
|
||||
)}
|
||||
{metricKeys !== undefined
|
||||
? metricKeys.map(k =>
|
||||
this._renderRow(`metrics_${k}`, `Metric: ${k}`, 'value', items, item =>
|
||||
item.metrics.get(k)
|
||||
)
|
||||
_renderRow(`metrics_${k}`, `Metric: ${k}`, 'value', items, item => item.metrics.get(k))
|
||||
)
|
||||
: null}
|
||||
</tbody>
|
||||
|
@ -214,80 +229,62 @@ class Compare extends React.Component<CompareProps, CompareState> {
|
|||
);
|
||||
}
|
||||
|
||||
private closeCompareModal = (): void => {
|
||||
const { showDetails, changeSelectTrialIds, onHideDialog } = this.props;
|
||||
if (showDetails === true) {
|
||||
const closeCompareModal = (): void => {
|
||||
const { title, changeSelectTrialIds, onHideDialog } = props;
|
||||
if (title === 'Compare trials') {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
changeSelectTrialIds!();
|
||||
}
|
||||
onHideDialog();
|
||||
};
|
||||
|
||||
private selectOtherKeys = (_event: React.FormEvent<HTMLDivElement>, item?: IDropdownOption): void => {
|
||||
const selectOtherKeys = (_event: React.FormEvent<HTMLDivElement>, item?: IDropdownOption): void => {
|
||||
if (item !== undefined) {
|
||||
this.setState(() => ({ intermediateKey: item.text }));
|
||||
setIntermediateKey(item.text);
|
||||
}
|
||||
};
|
||||
|
||||
render(): React.ReactNode {
|
||||
const { trials, title, showDetails, intermediateKeyList } = this.props;
|
||||
const { intermediateKey } = this.state;
|
||||
const intermediateAllKeysList: string[] = intermediateKeyList !== undefined ? intermediateKeyList : [];
|
||||
const flatten = (m: Map<SingleAxis, any>): Map<string, any> => {
|
||||
return new Map(Array.from(m).map(([key, value]) => [key.baseName, value]));
|
||||
};
|
||||
const inferredSearchSpace = TRIALS.inferredSearchSpace(EXPERIMENT.searchSpaceNew);
|
||||
const items: Item[] = trials.map(trial => ({
|
||||
id: trial.id,
|
||||
sequenceId: trial.sequenceId,
|
||||
duration: convertDuration(trial.duration),
|
||||
parameters: flatten(trial.parameters(inferredSearchSpace)),
|
||||
metrics: flatten(trial.metrics(TRIALS.inferredMetricSpace())),
|
||||
intermediates: _parseIntermediates(trial, intermediateKey)
|
||||
}));
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={true}
|
||||
containerClassName={contentStyles.container}
|
||||
className='compare-modal'
|
||||
allowTouchBodyScroll={true}
|
||||
dragOptions={dragOptions}
|
||||
onDismiss={this.closeCompareModal}
|
||||
>
|
||||
<div>
|
||||
<div className={contentStyles.header}>
|
||||
<span>{title}</span>
|
||||
<IconButton
|
||||
styles={iconButtonStyles}
|
||||
iconProps={{ iconName: 'Cancel' }}
|
||||
ariaLabel='Close popup modal'
|
||||
onClick={this.closeCompareModal}
|
||||
/>
|
||||
</div>
|
||||
{intermediateAllKeysList.length > 1 ||
|
||||
(intermediateAllKeysList.length === 1 && intermediateAllKeysList !== ['default']) ? (
|
||||
<Stack horizontalAlign='end' className='selectKeys'>
|
||||
<Dropdown
|
||||
className='select'
|
||||
selectedKey={intermediateKey}
|
||||
options={intermediateAllKeysList.map((key, item) => ({
|
||||
key: key,
|
||||
text: intermediateAllKeysList[item]
|
||||
}))}
|
||||
onChange={this.selectOtherKeys}
|
||||
/>
|
||||
</Stack>
|
||||
) : null}
|
||||
<Stack className='compare-modal-intermediate'>
|
||||
{this._intermediates(items)}
|
||||
<Stack className='compare-yAxis fontColor333'># Intermediate result</Stack>
|
||||
</Stack>
|
||||
{showDetails && <Stack>{this._columns(items)}</Stack>}
|
||||
return (
|
||||
<Modal
|
||||
isOpen={true}
|
||||
containerClassName={contentStyles.container}
|
||||
className='compare-modal'
|
||||
allowTouchBodyScroll={true}
|
||||
dragOptions={dragOptions}
|
||||
onDismiss={closeCompareModal}
|
||||
>
|
||||
<div>
|
||||
<div className={contentStyles.header}>
|
||||
<span>{title}</span>
|
||||
<IconButton
|
||||
styles={iconButtonStyles}
|
||||
iconProps={{ iconName: 'Cancel' }}
|
||||
ariaLabel='Close popup modal'
|
||||
onClick={closeCompareModal}
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
{intermediateAllKeysList.length > 1 ||
|
||||
(intermediateAllKeysList.length === 1 && intermediateAllKeysList !== ['default']) ? (
|
||||
<Stack horizontalAlign='end' className='selectKeys'>
|
||||
<Dropdown
|
||||
className='select'
|
||||
selectedKey={intermediateKey}
|
||||
options={intermediateAllKeysList.map((key, item) => ({
|
||||
key: key,
|
||||
text: intermediateAllKeysList[item]
|
||||
}))}
|
||||
onChange={selectOtherKeys}
|
||||
/>
|
||||
</Stack>
|
||||
) : null}
|
||||
<Stack className='compare-modal-intermediate'>
|
||||
{_intermediates(items)}
|
||||
<Stack className='compare-yAxis fontColor333'># Intermediate result</Stack>
|
||||
</Stack>
|
||||
{title === 'Compare trials' && <Stack>{_columns(items)}</Stack>}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default Compare;
|
||||
export default CompareIndex;
|
|
@ -154,7 +154,7 @@ class Customize extends React.Component<CustomizeProps, CustomizeState> {
|
|||
componentDidMount(): void {
|
||||
const { copyTrialId } = this.props;
|
||||
if (copyTrialId !== undefined && TRIALS.getTrial(copyTrialId) !== undefined) {
|
||||
const originCopyTrialPara = TRIALS.getTrial(copyTrialId).description.parameters;
|
||||
const originCopyTrialPara = TRIALS.getTrial(copyTrialId).parameter;
|
||||
this.setState(() => ({ copyTrialParameter: originCopyTrialPara }));
|
||||
}
|
||||
}
|
||||
|
@ -163,7 +163,7 @@ class Customize extends React.Component<CustomizeProps, CustomizeState> {
|
|||
if (this.props.copyTrialId !== prevProps.copyTrialId) {
|
||||
const { copyTrialId } = this.props;
|
||||
if (copyTrialId !== undefined && TRIALS.getTrial(copyTrialId) !== undefined) {
|
||||
const originCopyTrialPara = TRIALS.getTrial(copyTrialId).description.parameters;
|
||||
const originCopyTrialPara = TRIALS.getTrial(copyTrialId).parameter;
|
||||
this.setState(() => ({ copyTrialParameter: originCopyTrialPara }));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ const METRIC_GROUP_UPDATE_SIZE = 20;
|
|||
const prefix = getPrefix();
|
||||
const RESTAPI = '/api/v1/nni';
|
||||
const MANAGER_IP = prefix === undefined ? RESTAPI : `${prefix}${RESTAPI}`;
|
||||
const DOWNLOAD_IP = `${prefix}/logs`;
|
||||
const DOWNLOAD_IP = prefix === undefined ? '/logs' : `${prefix}/logs`;
|
||||
|
||||
const WEBUIDOC = 'https://nni.readthedocs.io/en/latest/experiment/webui.html';
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import axios from 'axios';
|
|||
import { IContextualMenuProps } from '@fluentui/react';
|
||||
import { RETIARIIPARAMETERS } from './const';
|
||||
import { EXPERIMENT } from './datamodel';
|
||||
import { MetricDataRecord, FinalType, TableObj, Tensorboard } from './interface';
|
||||
import { MetricDataRecord, FinalType, Tensorboard } from './interface';
|
||||
|
||||
function getPrefix(): string | undefined {
|
||||
const pathName = window.location.pathname;
|
||||
|
@ -188,15 +188,6 @@ const intermediateGraphOption = (intermediateArr: number[], id: string): any =>
|
|||
};
|
||||
};
|
||||
|
||||
const filterByStatus = (item: TableObj): boolean => {
|
||||
return item.status === 'SUCCEEDED';
|
||||
};
|
||||
|
||||
// a waittiong trial may havn't start time
|
||||
const filterDuration = (item: TableObj): boolean => {
|
||||
return item.status !== 'WAITING';
|
||||
};
|
||||
|
||||
const downFile = (content: string, fileName: string): void => {
|
||||
const aTag = document.createElement('a');
|
||||
const isEdge = navigator.userAgent.indexOf('Edge') !== -1 ? true : false;
|
||||
|
@ -376,8 +367,8 @@ function _inferColumnTitle(columnKey: string): string {
|
|||
|
||||
const getIntermediateAllKeys = (intermediateDialogTrial: any): string[] => {
|
||||
let intermediateAllKeysList: string[] = [];
|
||||
if (intermediateDialogTrial!.intermediateMetrics !== undefined && intermediateDialogTrial!.intermediateMetrics[0]) {
|
||||
const parsedMetric = parseMetrics(intermediateDialogTrial!.intermediateMetrics[0].data);
|
||||
if (intermediateDialogTrial!.intermediates !== undefined && intermediateDialogTrial!.intermediates[0]) {
|
||||
const parsedMetric = parseMetrics(intermediateDialogTrial!.intermediates[0].data);
|
||||
if (parsedMetric !== undefined && typeof parsedMetric === 'object') {
|
||||
const allIntermediateKeys: string[] = [];
|
||||
// just add type=number keys
|
||||
|
@ -407,8 +398,6 @@ export {
|
|||
getFinal,
|
||||
downFile,
|
||||
intermediateGraphOption,
|
||||
filterByStatus,
|
||||
filterDuration,
|
||||
formatAccuracy,
|
||||
formatTimestamp,
|
||||
expformatTimestamp,
|
||||
|
|
|
@ -26,23 +26,6 @@ interface MultipleAxes {
|
|||
axes: Map<string, SingleAxis>;
|
||||
}
|
||||
|
||||
// draw accuracy graph data export interface
|
||||
interface TableObj {
|
||||
key: number;
|
||||
sequenceId: number;
|
||||
id: string;
|
||||
duration: number;
|
||||
status: string;
|
||||
acc?: FinalType; // draw accuracy graph
|
||||
description: Parameters;
|
||||
color?: string;
|
||||
startTime?: number;
|
||||
endTime?: number;
|
||||
intermediates: (MetricDataRecord | undefined)[];
|
||||
parameters(axes: MultipleAxes): Map<SingleAxis, any>;
|
||||
metrics(axes: MultipleAxes): Map<SingleAxis, any>;
|
||||
}
|
||||
|
||||
interface TableRecord {
|
||||
key: string;
|
||||
sequenceId: number;
|
||||
|
@ -53,7 +36,6 @@ interface TableRecord {
|
|||
status: string;
|
||||
message: string;
|
||||
intermediateCount: number;
|
||||
accuracy?: number | any;
|
||||
latestAccuracy: number | undefined;
|
||||
formattedLatestAccuracy: string; // format (LATEST/FINAL),
|
||||
}
|
||||
|
@ -67,17 +49,6 @@ interface FinalType {
|
|||
default: string;
|
||||
}
|
||||
|
||||
interface ErrorParameter {
|
||||
error?: string;
|
||||
}
|
||||
|
||||
interface Parameters {
|
||||
parameters: ErrorParameter;
|
||||
logPath?: string;
|
||||
intermediate: number[];
|
||||
multiProgress?: number;
|
||||
}
|
||||
|
||||
// trial accuracy
|
||||
interface AccurPoint {
|
||||
acc: number;
|
||||
|
@ -120,14 +91,6 @@ interface ParaObj {
|
|||
parallelAxis: Array<Dimobj>;
|
||||
}
|
||||
|
||||
interface Intermedia {
|
||||
name: string; // id
|
||||
type: string;
|
||||
data: Array<number | object>; // intermediate data
|
||||
hyperPara: object; // each trial hyperpara value
|
||||
trialNum: number;
|
||||
}
|
||||
|
||||
interface MetricDataRecord {
|
||||
timestamp: number;
|
||||
trialJobId: string;
|
||||
|
@ -223,20 +186,25 @@ interface SearchItems {
|
|||
isChoice: boolean; // for parameters: type = choice and status also as choice type
|
||||
}
|
||||
|
||||
interface allTrialsIntermediateChart {
|
||||
name: string;
|
||||
// id: string;
|
||||
sequenceId: number;
|
||||
data: number[];
|
||||
parameter: object;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export {
|
||||
TableObj,
|
||||
TableRecord,
|
||||
SearchSpace,
|
||||
FinalType,
|
||||
ErrorParameter,
|
||||
Parameters,
|
||||
AccurPoint,
|
||||
DetailAccurPoint,
|
||||
TooltipForIntermediate,
|
||||
TooltipForAccuracy,
|
||||
Dimobj,
|
||||
ParaObj,
|
||||
Intermedia,
|
||||
MetricDataRecord,
|
||||
TrialJobInfo,
|
||||
ExperimentProfile,
|
||||
|
@ -248,5 +216,6 @@ export {
|
|||
SortInfo,
|
||||
AllExperimentList,
|
||||
Tensorboard,
|
||||
SearchItems
|
||||
SearchItems,
|
||||
allTrialsIntermediateChart
|
||||
};
|
||||
|
|
|
@ -51,7 +51,6 @@ class Experiment {
|
|||
private profileField?: ExperimentProfile;
|
||||
private metadataField?: ExperimentMetadata = undefined;
|
||||
private statusField?: NNIManagerStatus = undefined;
|
||||
private isNestedExperiment: boolean = false;
|
||||
private isexperimentError: boolean = false;
|
||||
private experimentErrorMessage: string = '';
|
||||
private isStatusError: boolean = false;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { SingleAxis, MultipleAxes, TableObj } from '../interface';
|
||||
import { SingleAxis, MultipleAxes } from '../interface';
|
||||
import { Trial } from './trial';
|
||||
import { SUPPORTED_SEARCH_SPACE_TYPE } from '../const';
|
||||
import { formatComplexTypeValue } from '../function';
|
||||
|
||||
|
@ -111,7 +112,7 @@ export class SearchSpace implements MultipleAxes {
|
|||
});
|
||||
}
|
||||
|
||||
static inferFromTrials(searchSpace: SearchSpace, trials: TableObj[]): SearchSpace {
|
||||
static inferFromTrials(searchSpace: SearchSpace, trials: Trial[]): SearchSpace {
|
||||
const newSearchSpace = new SearchSpace(searchSpace.baseName, searchSpace.fullName, undefined);
|
||||
for (const [k, v] of searchSpace.axes) {
|
||||
newSearchSpace.axes.set(k, v);
|
||||
|
@ -153,7 +154,7 @@ export class MetricSpace implements MultipleAxes {
|
|||
baseName = '';
|
||||
fullName = '';
|
||||
|
||||
constructor(trials: TableObj[]) {
|
||||
constructor(trials: Trial[]) {
|
||||
const columns = new Map<string, any[]>();
|
||||
for (const trial of trials) {
|
||||
if (trial.acc === undefined) {
|
||||
|
|
|
@ -1,14 +1,4 @@
|
|||
import * as JSON5 from 'json5';
|
||||
import {
|
||||
MetricDataRecord,
|
||||
TrialJobInfo,
|
||||
TableObj,
|
||||
TableRecord,
|
||||
Parameters,
|
||||
FinalType,
|
||||
MultipleAxes,
|
||||
SingleAxis
|
||||
} from '../interface';
|
||||
import { MetricDataRecord, TrialJobInfo, TableRecord, FinalType, MultipleAxes, SingleAxis } from '../interface';
|
||||
import {
|
||||
getFinal,
|
||||
formatAccuracy,
|
||||
|
@ -59,12 +49,11 @@ function inferTrialParameters(
|
|||
return [parameters, unexpectedEntries];
|
||||
}
|
||||
|
||||
class Trial implements TableObj {
|
||||
class Trial {
|
||||
private metricsInitialized: boolean = false;
|
||||
private infoField: TrialJobInfo | undefined;
|
||||
public accuracy: number | undefined; // trial default metric val: number value or undefined
|
||||
public intermediates: (MetricDataRecord | undefined)[] = [];
|
||||
public final: MetricDataRecord | undefined;
|
||||
private finalAcc: number | undefined;
|
||||
|
||||
constructor(info?: TrialJobInfo, metrics?: MetricDataRecord[]) {
|
||||
this.infoField = info;
|
||||
|
@ -78,7 +67,7 @@ class Trial implements TableObj {
|
|||
return undefined;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return this.finalAcc! - otherTrial.finalAcc!;
|
||||
return this.accuracy! - otherTrial.accuracy!;
|
||||
}
|
||||
|
||||
get info(): TrialJobInfo {
|
||||
|
@ -86,25 +75,58 @@ class Trial implements TableObj {
|
|||
return this.infoField!;
|
||||
}
|
||||
|
||||
get intermediateMetrics(): MetricDataRecord[] {
|
||||
const ret: MetricDataRecord[] = [];
|
||||
for (let i = 0; i < this.intermediates.length; i++) {
|
||||
if (this.intermediates[i]) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
ret.push(this.intermediates[i]!);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
get sequenceId(): number {
|
||||
return this.info.sequenceId;
|
||||
}
|
||||
|
||||
get accuracy(): number | undefined {
|
||||
return this.finalAcc;
|
||||
get id(): string {
|
||||
return this.info.trialJobId;
|
||||
}
|
||||
|
||||
get duration(): number {
|
||||
const endTime = this.info.endTime || new Date().getTime();
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return (endTime - this.info.startTime!) / 1000;
|
||||
}
|
||||
|
||||
get status(): string {
|
||||
return this.info.status;
|
||||
}
|
||||
|
||||
get parameter(): object {
|
||||
return JSON.parse(this.info.hyperParameters![0]).parameters;
|
||||
}
|
||||
|
||||
// return dict final result: {default: xxx...}
|
||||
get acc(): FinalType | undefined {
|
||||
if (this.info === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return getFinal(this.info.finalMetricData);
|
||||
}
|
||||
|
||||
public parameters(axes: MultipleAxes): Map<SingleAxis, any> {
|
||||
const ret = new Map<SingleAxis, any>(Array.from(axes.axes.values()).map(k => [k, null]));
|
||||
if (this.info === undefined || this.info.hyperParameters === undefined) {
|
||||
throw ret;
|
||||
} else {
|
||||
let params = JSON.parse(this.info.hyperParameters[0]).parameters;
|
||||
if (typeof params === 'string') {
|
||||
params = JSON.parse(params);
|
||||
}
|
||||
const [updated, unexpectedEntries] = inferTrialParameters(params, axes);
|
||||
if (unexpectedEntries.size) {
|
||||
throw unexpectedEntries;
|
||||
}
|
||||
for (const [k, v] of updated) {
|
||||
ret.set(k, v);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
get sortable(): boolean {
|
||||
return this.metricsInitialized && this.finalAcc !== undefined && isFinite(this.finalAcc);
|
||||
return this.metricsInitialized && this.accuracy !== undefined && isFinite(this.accuracy);
|
||||
}
|
||||
|
||||
get latestAccuracy(): number | undefined {
|
||||
|
@ -131,66 +153,6 @@ class Trial implements TableObj {
|
|||
return undefined;
|
||||
}
|
||||
}
|
||||
/* table obj start */
|
||||
|
||||
get tableRecord(): TableRecord {
|
||||
const endTime = this.info.endTime || new Date().getTime();
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const duration = (endTime - this.info.startTime!) / 1000;
|
||||
let accuracy;
|
||||
if (this.acc !== undefined && this.acc.default !== undefined) {
|
||||
if (typeof this.acc.default === 'number') {
|
||||
accuracy = JSON5.parse(this.acc.default);
|
||||
} else {
|
||||
accuracy = this.acc.default;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
key: this.info.trialJobId,
|
||||
sequenceId: this.info.sequenceId,
|
||||
id: this.info.trialJobId,
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
startTime: this.info.startTime!,
|
||||
endTime: this.info.endTime,
|
||||
duration,
|
||||
status: this.info.status,
|
||||
message: this.info.message || '--',
|
||||
intermediateCount: this.intermediates.length,
|
||||
accuracy: accuracy,
|
||||
latestAccuracy: this.latestAccuracy,
|
||||
formattedLatestAccuracy: this.formatLatestAccuracy()
|
||||
};
|
||||
}
|
||||
|
||||
get key(): number {
|
||||
return this.info.sequenceId;
|
||||
}
|
||||
|
||||
get sequenceId(): number {
|
||||
return this.info.sequenceId;
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this.info.trialJobId;
|
||||
}
|
||||
|
||||
get duration(): number {
|
||||
const endTime = this.info.endTime || new Date().getTime();
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return (endTime - this.info.startTime!) / 1000;
|
||||
}
|
||||
|
||||
get status(): string {
|
||||
return this.info.status;
|
||||
}
|
||||
|
||||
get acc(): FinalType | undefined {
|
||||
if (this.info === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return getFinal(this.info.finalMetricData);
|
||||
}
|
||||
|
||||
get accuracyNumberTypeDictKeys(): string[] {
|
||||
let accuracyTypeList: string[] = [];
|
||||
|
@ -208,59 +170,27 @@ class Trial implements TableObj {
|
|||
return accuracyTypeList;
|
||||
}
|
||||
|
||||
get description(): Parameters {
|
||||
const ret: Parameters = {
|
||||
parameters: {},
|
||||
intermediate: [],
|
||||
multiProgress: 1
|
||||
/* table obj start */
|
||||
|
||||
get tableRecord(): TableRecord {
|
||||
const endTime = this.info.endTime || new Date().getTime();
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const duration = (endTime - this.info.startTime!) / 1000;
|
||||
|
||||
return {
|
||||
key: this.info.trialJobId,
|
||||
sequenceId: this.info.sequenceId,
|
||||
id: this.info.trialJobId,
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
startTime: this.info.startTime!,
|
||||
endTime: this.info.endTime,
|
||||
duration,
|
||||
status: this.info.status,
|
||||
message: this.info.message ?? '--',
|
||||
intermediateCount: this.intermediates.length,
|
||||
latestAccuracy: this.latestAccuracy,
|
||||
formattedLatestAccuracy: this.formatLatestAccuracy()
|
||||
};
|
||||
const tempHyper = this.info.hyperParameters;
|
||||
if (tempHyper !== undefined) {
|
||||
const getPara = JSON.parse(tempHyper[tempHyper.length - 1]).parameters;
|
||||
ret.multiProgress = tempHyper.length;
|
||||
if (typeof getPara === 'string') {
|
||||
ret.parameters = JSON.parse(getPara);
|
||||
} else {
|
||||
ret.parameters = getPara;
|
||||
}
|
||||
} else {
|
||||
ret.parameters = { error: "This trial's parameters are not available." };
|
||||
}
|
||||
if (this.info.logPath !== undefined) {
|
||||
ret.logPath = this.info.logPath;
|
||||
}
|
||||
|
||||
const mediate: number[] = [];
|
||||
for (const items of this.intermediateMetrics) {
|
||||
if (typeof parseMetrics(items.data) === 'object') {
|
||||
mediate.push(parseMetrics(items.data).default);
|
||||
} else {
|
||||
mediate.push(parseMetrics(items.data));
|
||||
}
|
||||
}
|
||||
ret.intermediate = mediate;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public parameters(axes: MultipleAxes): Map<SingleAxis, any> {
|
||||
const ret = new Map<SingleAxis, any>(Array.from(axes.axes.values()).map(k => [k, null]));
|
||||
if (this.info === undefined || this.info.hyperParameters === undefined) {
|
||||
throw ret;
|
||||
} else {
|
||||
const tempHyper = this.info.hyperParameters;
|
||||
let params = JSON.parse(tempHyper[tempHyper.length - 1]).parameters;
|
||||
if (typeof params === 'string') {
|
||||
params = JSON.parse(params);
|
||||
}
|
||||
const [updated, unexpectedEntries] = inferTrialParameters(params, axes);
|
||||
if (unexpectedEntries.size) {
|
||||
throw unexpectedEntries;
|
||||
}
|
||||
for (const [k, v] of updated) {
|
||||
ret.set(k, v);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public metrics(space: MultipleAxes): Map<SingleAxis, any> {
|
||||
|
@ -270,8 +200,7 @@ class Trial implements TableObj {
|
|||
if (this.acc === undefined) {
|
||||
return ret;
|
||||
}
|
||||
const acc = typeof this.acc === 'number' ? { default: this.acc } : this.acc;
|
||||
Object.entries(acc).forEach(item => {
|
||||
Object.entries(this.acc).forEach(item => {
|
||||
const [k, v] = item;
|
||||
const column = space.axes.get(k);
|
||||
|
||||
|
@ -287,14 +216,6 @@ class Trial implements TableObj {
|
|||
return ret;
|
||||
}
|
||||
|
||||
public finalKeys(): string[] {
|
||||
if (this.acc !== undefined) {
|
||||
return Object.keys(this.acc);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/* table obj end */
|
||||
|
||||
public initialized(): boolean {
|
||||
|
@ -304,7 +225,7 @@ class Trial implements TableObj {
|
|||
public updateMetrics(metrics: MetricDataRecord[]): boolean {
|
||||
// parameter `metrics` must contain all known metrics of this trial
|
||||
this.metricsInitialized = true;
|
||||
const prevMetricCnt = this.intermediates.length + (this.final ? 1 : 0);
|
||||
const prevMetricCnt = this.intermediates.length + (this.accuracy ? 1 : 0);
|
||||
if (metrics.length <= prevMetricCnt) {
|
||||
return false;
|
||||
}
|
||||
|
@ -312,8 +233,7 @@ class Trial implements TableObj {
|
|||
if (metric.type === 'PERIODICAL') {
|
||||
this.intermediates[metric.sequence] = metric;
|
||||
} else {
|
||||
this.final = metric;
|
||||
this.finalAcc = metricAccuracy(metric);
|
||||
this.accuracy = metricAccuracy(metric);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -328,9 +248,8 @@ class Trial implements TableObj {
|
|||
updated = updated || !this.intermediates[metric.sequence];
|
||||
this.intermediates[metric.sequence] = metric;
|
||||
} else {
|
||||
updated = updated || !this.final;
|
||||
this.final = metric;
|
||||
this.finalAcc = metricAccuracy(metric);
|
||||
updated = updated || !this.accuracy;
|
||||
this.accuracy = metricAccuracy(metric);
|
||||
}
|
||||
}
|
||||
return updated;
|
||||
|
@ -340,13 +259,20 @@ class Trial implements TableObj {
|
|||
const same = this.infoField && this.infoField.status === trialJobInfo.status;
|
||||
this.infoField = trialJobInfo;
|
||||
if (trialJobInfo.finalMetricData) {
|
||||
this.final = trialJobInfo.finalMetricData[trialJobInfo.finalMetricData.length - 1];
|
||||
this.finalAcc = metricAccuracy(this.final);
|
||||
this.accuracy = metricAccuracy(trialJobInfo.finalMetricData[0]);
|
||||
}
|
||||
return !same;
|
||||
}
|
||||
|
||||
private renderNumber(val: any): string {
|
||||
/**
|
||||
*
|
||||
* @param val trial latest accuracy
|
||||
* @returns 0.9(FINAL) or 0.9(LATEST)
|
||||
* NaN or Infinity
|
||||
* string object such as: '{tensor: {data}}'
|
||||
*
|
||||
*/
|
||||
private formatLatestAccuracyToString(val: any): string {
|
||||
if (typeof val === 'number') {
|
||||
if (isNaNorInfinity(val)) {
|
||||
return `${val}`; // show 'NaN' or 'Infinity'
|
||||
|
@ -363,19 +289,27 @@ class Trial implements TableObj {
|
|||
}
|
||||
}
|
||||
|
||||
public formatLatestAccuracy(): string {
|
||||
// TODO: this should be private
|
||||
/**
|
||||
*
|
||||
* @param val trial latest accuracy
|
||||
* @returns 0.9(FINAL) or 0.9(LATEST)
|
||||
* NaN or Infinity
|
||||
* string object such as: '{tensor: {data}}'
|
||||
* +1 describe type undefined: --
|
||||
*
|
||||
*/
|
||||
private formatLatestAccuracy(): string {
|
||||
if (this.status === 'SUCCEEDED') {
|
||||
return this.accuracy === undefined ? '--' : this.renderNumber(this.accuracy);
|
||||
return this.accuracy === undefined ? '--' : this.formatLatestAccuracyToString(this.accuracy);
|
||||
} else {
|
||||
if (this.accuracy !== undefined) {
|
||||
return this.renderNumber(this.accuracy);
|
||||
return this.formatLatestAccuracyToString(this.accuracy);
|
||||
} else if (this.intermediates.length === 0) {
|
||||
return '--';
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const latest = this.intermediates[this.intermediates.length - 1]!;
|
||||
return this.renderNumber(metricAccuracy(latest));
|
||||
return this.formatLatestAccuracyToString(metricAccuracy(latest));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,8 @@ import { MANAGER_IP, METRIC_GROUP_UPDATE_THRESHOLD, METRIC_GROUP_UPDATE_SIZE } f
|
|||
import { MetricDataRecord, TableRecord, TrialJobInfo, MultipleAxes } from '../interface';
|
||||
import { Trial } from './trial';
|
||||
import { SearchSpace, MetricSpace } from './searchspace';
|
||||
import { requestAxios } from '../function';
|
||||
import { requestAxios, parseMetrics } from '../function';
|
||||
import { allTrialsIntermediateChart } from '../interface';
|
||||
|
||||
function groupMetricsByTrial(metrics: MetricDataRecord[]): Map<string, MetricDataRecord[]> {
|
||||
const ret = new Map<string, MetricDataRecord[]>();
|
||||
|
@ -86,10 +87,37 @@ class TrialManager {
|
|||
return this.filter(trial => trial.status === 'SUCCEEDED');
|
||||
}
|
||||
|
||||
public notWaittingTrials(): Trial[] {
|
||||
return this.filter(trial => trial.status !== 'WAITING');
|
||||
}
|
||||
|
||||
public allTrialsIntermediateChart(): allTrialsIntermediateChart[] {
|
||||
const ret: allTrialsIntermediateChart[] = [];
|
||||
for (const trial of this.trials.values()) {
|
||||
const mediate: number[] = [];
|
||||
for (const items of trial.intermediates) {
|
||||
if (typeof parseMetrics(items!.data) === 'object') {
|
||||
mediate.push(parseMetrics(items!.data).default);
|
||||
} else {
|
||||
mediate.push(parseMetrics(items!.data));
|
||||
}
|
||||
}
|
||||
ret.push({
|
||||
name: trial.id,
|
||||
sequenceId: trial.sequenceId,
|
||||
data: mediate,
|
||||
parameter: trial.parameter,
|
||||
type: 'line'
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public finalKeys(): string[] {
|
||||
const succeedTrialsList = this.filter(trial => trial.status === 'SUCCEEDED');
|
||||
if (succeedTrialsList !== undefined && succeedTrialsList[0] !== undefined) {
|
||||
return succeedTrialsList[0].finalKeys();
|
||||
return succeedTrialsList[0].acc !== undefined ? Object.keys(succeedTrialsList[0].acc) : [];
|
||||
} else {
|
||||
return ['default'];
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче