Unverified Commit ef176d29 authored by SparkSnail's avatar SparkSnail Committed by GitHub
Browse files

Merge pull request #116 from Microsoft/master

merge master
parents 97866505 4553de75
import * as React from 'react'; import * as React from 'react';
import axios from 'axios'; import axios from 'axios';
import JSONTree from 'react-json-tree'; import { Modal, Input, Table } from 'antd';
import { Row, Modal, Input, Table, Tabs } from 'antd';
const TabPane = Tabs.TabPane;
const { TextArea } = Input; const { TextArea } = Input;
import OpenRow from '../public-child/OpenRow';
import DefaultMetric from '../public-child/DefaultMetrc';
import { DOWNLOAD_IP } from '../../static/const'; import { DOWNLOAD_IP } from '../../static/const';
import { TableObj } from '../../static/interface'; import { TableObj } from '../../static/interface';
import { convertDuration } from '../../static/function'; import { convertDuration } from '../../static/function';
import PaiTrialLog from '../logPath/PaiTrialLog';
import TrialLog from '../logPath/TrialLog';
import '../../static/style/tableStatus.css'; import '../../static/style/tableStatus.css';
import '../../static/style/tableList.scss'; import '../../static/style/tableList.scss';
...@@ -74,6 +72,17 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState> ...@@ -74,6 +72,17 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
} }
} }
openRow = (record: TableObj) => {
const { trainingPlatform } = this.props;
return (
<OpenRow
showLogModalOverview={this.showLogModalOverview}
trainingPlatform={trainingPlatform}
record={record}
/>
);
}
componentDidMount() { componentDidMount() {
this._isMounted = true; this._isMounted = true;
} }
...@@ -83,7 +92,7 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState> ...@@ -83,7 +92,7 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
} }
render() { render() {
const { tableSource, trainingPlatform } = this.props; const { tableSource } = this.props;
const { isShowLogModal, logContent } = this.state; const { isShowLogModal, logContent } = this.state;
let bgColor = ''; let bgColor = '';
...@@ -137,92 +146,24 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState> ...@@ -137,92 +146,24 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
title: 'Default Metric', title: 'Default Metric',
dataIndex: 'acc', dataIndex: 'acc',
key: 'acc', key: 'acc',
sorter: (a: TableObj, b: TableObj) => (a.acc as number) - (b.acc as number), sorter: (a: TableObj, b: TableObj) => {
render: (text: string, record: TableObj) => { if (a.acc !== undefined && b.acc !== undefined) {
const accuracy = record.acc; return JSON.parse(a.acc.default) - JSON.parse(b.acc.default);
let wei = 0; } else {
if (accuracy) { return NaN;
if (accuracy.toString().indexOf('.') !== -1) {
wei = accuracy.toString().length - accuracy.toString().indexOf('.') - 1;
}
} }
},
render: (text: string, record: TableObj) => {
return ( return (
<div> <DefaultMetric record={record} />
{
record.acc
?
wei > 6
?
record.acc.toFixed(6)
:
record.acc
:
'--'
}
</div>
); );
} }
// width: 150
}]; }];
const openRow = (record: TableObj) => {
let isHasParameters = true;
if (record.description.parameters.error) {
isHasParameters = false;
}
const openRowDataSource = {
parameters: record.description.parameters
};
const logPathRow = record.description.logPath !== undefined
?
record.description.logPath
:
'This trial\'s logPath are not available.';
return (
<pre id="description" className="hyperpar">
<Row className="openRowContent">
<Tabs tabPosition="left" className="card">
<TabPane tab="Parameters" key="1">
{
isHasParameters
?
<JSONTree
hideRoot={true}
shouldExpandNode={() => true} // default expandNode
getItemString={() => (<span />)} // remove the {} items
data={openRowDataSource}
/>
:
<div className="logpath">
<span className="logName">Error: </span>
<span className="error">'This trial's parameters are not available.'</span>
</div>
}
</TabPane>
<TabPane tab="Log" key="2">
{
trainingPlatform === 'frameworkcontroller' || trainingPlatform === 'kubeflow' ||
trainingPlatform === 'pai'
?
<PaiTrialLog
logStr={logPathRow}
id={record.id}
showLogModal={this.showLogModalOverview}
/>
:
<TrialLog logStr={logPathRow} id={record.id} />
}
</TabPane>
</Tabs>
</Row>
</pre>
);
};
return ( return (
<div className="tabScroll"> <div className="tabScroll" >
<Table <Table
columns={columns} columns={columns}
expandedRowRender={openRow} expandedRowRender={this.openRow}
dataSource={tableSource} dataSource={tableSource}
className="commonTableStyle" className="commonTableStyle"
pagination={false} pagination={false}
...@@ -244,9 +185,8 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState> ...@@ -244,9 +185,8 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
/> />
</div> </div>
</Modal> </Modal>
</div> </div >
); );
} }
} }
export default SuccessTable; export default SuccessTable;
\ No newline at end of file
import * as React from 'react';
import { TableObj } from '../../static/interface';
interface DefaultMetricProps {
record: TableObj;
}
class DefaultMetric extends React.Component<DefaultMetricProps, {}> {
constructor(props: DefaultMetricProps) {
super(props);
}
render() {
const { record } = this.props;
let accuracy;
if (record.acc !== undefined) {
accuracy = record.acc.default;
}
let wei = 0;
if (accuracy) {
if (accuracy.toString().indexOf('.') !== -1) {
wei = accuracy.toString().length - accuracy.toString().indexOf('.') - 1;
}
}
return (
<div>
{
record.acc && record.acc.default
?
wei > 6
?
JSON.parse(record.acc.default).toFixed(6)
:
record.acc.default
:
'--'
}
</div>
);
}
}
export default DefaultMetric;
\ No newline at end of file
import * as React from 'react';
import PaiTrialLog from '../public-child/PaiTrialLog';
import TrialLog from '../public-child/TrialLog';
import JSONTree from 'react-json-tree';
import { TableObj } from '../../static/interface';
import { Row, Tabs } from 'antd';
const TabPane = Tabs.TabPane;
interface OpenRowProps {
trainingPlatform: string;
showLogModalOverview: Function;
record: TableObj;
}
class OpenRow extends React.Component<OpenRowProps, {}> {
constructor(props: OpenRowProps) {
super(props);
}
render() {
const { trainingPlatform, record, showLogModalOverview } = this.props;
let isHasParameters = true;
if (record.description.parameters.error) {
isHasParameters = false;
}
const openRowDataSource = {
parameters: record.description.parameters
};
const logPathRow = record.description.logPath !== undefined
?
record.description.logPath
:
'This trial\'s logPath are not available.';
return (
<pre id="description" className="hyperpar">
<Row className="openRowContent">
<Tabs tabPosition="left" className="card">
<TabPane tab="Parameters" key="1">
{
isHasParameters
?
<JSONTree
hideRoot={true}
shouldExpandNode={() => true} // default expandNode
getItemString={() => (<span />)} // remove the {} items
data={openRowDataSource}
/>
:
<div className="logpath">
<span className="logName">Error: </span>
<span className="error">'This trial's parameters are not available.'</span>
</div>
}
</TabPane>
<TabPane tab="Log" key="2">
{
trainingPlatform === 'pai' || trainingPlatform === 'kubeflow' ||
trainingPlatform === 'frameworkcontroller'
?
<PaiTrialLog
logStr={logPathRow}
id={record.id}
showLogModal={showLogModalOverview}
/>
:
<TrialLog logStr={logPathRow} id={record.id} />
}
</TabPane>
</Tabs>
</Row>
</pre>
);
}
}
export default OpenRow;
\ No newline at end of file
...@@ -60,81 +60,90 @@ class Para extends React.Component<{}, ParaState> { ...@@ -60,81 +60,90 @@ class Para extends React.Component<{}, ParaState> {
}; };
} }
getParallelAxis = getParallelAxis =
( dimName: Array<string>, searchRange: SearchSpace, (
parallelAxis: Array<Dimobj>, accPara: Array<number>, dimName: Array<string>, searchRange: SearchSpace,
eachTrialParams: Array<string>, paraYdata: number[][] parallelAxis: Array<Dimobj>, accPara: Array<number>,
) => { eachTrialParams: Array<string>, paraYdata: number[][]
if (this._isMounted) { ) => {
this.setState(() => ({ if (this._isMounted) {
dimName: dimName, this.setState(() => ({
visualValue: { dimName: dimName,
minAccuracy: accPara.length !== 0 ? Math.min(...accPara) : 0, visualValue: {
maxAccuracy: accPara.length !== 0 ? Math.max(...accPara) : 1 minAccuracy: accPara.length !== 0 ? Math.min(...accPara) : 0,
} maxAccuracy: accPara.length !== 0 ? Math.max(...accPara) : 1
}));
}
// search space range and specific value [only number]
for (let i = 0; i < dimName.length; i++) {
const searchKey = searchRange[dimName[i]];
switch (searchKey._type) {
case 'uniform':
case 'quniform':
parallelAxis.push({
dim: i,
name: dimName[i],
max: searchKey._value[1],
min: searchKey._value[0]
});
break;
case 'randint':
parallelAxis.push({
dim: i,
name: dimName[i],
max: searchKey._value[0],
min: 0
});
break;
case 'choice':
const data: Array<string> = [];
for (let j = 0; j < searchKey._value.length; j++) {
data.push(searchKey._value[j].toString());
} }
parallelAxis.push({ }));
dim: i,
name: dimName[i],
type: 'category',
data: data
});
break;
default:
parallelAxis.push({
dim: i,
name: dimName[i]
});
} }
} // search space range and specific value [only number]
// get data for every lines. if dim is choice type, number -> toString()
Object.keys(eachTrialParams).map(item => {
let temp: Array<number> = [];
for (let i = 0; i < dimName.length; i++) { for (let i = 0; i < dimName.length; i++) {
if ('type' in parallelAxis[i]) { const searchKey = searchRange[dimName[i]];
temp.push( switch (searchKey._type) {
eachTrialParams[item][dimName[i]].toString() case 'uniform':
); case 'quniform':
} else { parallelAxis.push({
temp.push( dim: i,
eachTrialParams[item][dimName[i]] name: dimName[i],
); max: searchKey._value[1],
min: searchKey._value[0]
});
break;
case 'randint':
parallelAxis.push({
dim: i,
name: dimName[i],
max: searchKey._value[0],
min: 0
});
break;
case 'choice':
const data: Array<string> = [];
for (let j = 0; j < searchKey._value.length; j++) {
data.push(searchKey._value[j].toString());
}
parallelAxis.push({
dim: i,
name: dimName[i],
type: 'category',
data: data
});
break;
// support log distribute
case 'loguniform':
parallelAxis.push({
dim: i,
name: dimName[i],
type: 'log',
});
break;
default:
parallelAxis.push({
dim: i,
name: dimName[i]
});
} }
} }
paraYdata.push(temp); // get data for every lines. if dim is choice type, number -> toString()
}); Object.keys(eachTrialParams).map(item => {
} let temp: Array<number> = [];
for (let i = 0; i < dimName.length; i++) {
if ('type' in parallelAxis[i]) {
temp.push(
eachTrialParams[item][dimName[i]].toString()
);
} else {
temp.push(
eachTrialParams[item][dimName[i]]
);
}
}
paraYdata.push(temp);
});
}
hyperParaPic = () => { hyperParaPic = () => {
axios axios
...@@ -253,7 +262,21 @@ class Para extends React.Component<{}, ParaState> { ...@@ -253,7 +262,21 @@ class Para extends React.Component<{}, ParaState> {
parallelAxisDefault: { parallelAxisDefault: {
tooltip: { tooltip: {
show: true show: true
} },
axisLabel: {
formatter: function (value: string) {
const length = value.length;
if (length > 16) {
const temp = value.split('');
for (let i = 16; i < temp.length; i += 17) {
temp[i] += '\n';
}
return temp.join('');
} else {
return value;
}
}
},
} }
}, },
visualMap: visualMapObj, visualMap: visualMapObj,
......
import * as React from 'react'; import * as React from 'react';
import axios from 'axios'; import axios from 'axios';
import JSONTree from 'react-json-tree';
import ReactEcharts from 'echarts-for-react'; import ReactEcharts from 'echarts-for-react';
import { import {
Row, Input, Table, Tabs, Button, Popconfirm, Modal, message, Checkbox Row, Input, Table, Button, Popconfirm, Modal, Checkbox
} from 'antd'; } from 'antd';
const { TextArea } = Input; const { TextArea } = Input;
const TabPane = Tabs.TabPane;
const CheckboxGroup = Checkbox.Group; const CheckboxGroup = Checkbox.Group;
import { MANAGER_IP, DOWNLOAD_IP, trialJobStatus, COLUMN, COLUMN_INDEX } from '../../static/const'; import { MANAGER_IP, DOWNLOAD_IP, trialJobStatus, COLUMN, COLUMN_INDEX } from '../../static/const';
import { convertDuration } from '../../static/function'; import { convertDuration, intermediateGraphOption, killJob } from '../../static/function';
import { TableObjFianl, TrialJob } from '../../static/interface'; import { TableObj, TrialJob } from '../../static/interface';
import PaiTrialLog from '../logPath/PaiTrialLog'; import OpenRow from '../public-child/OpenRow';
import TrialLog from '../logPath/TrialLog'; import DefaultMetric from '../public-child/DefaultMetrc';
import '../../static/style/search.scss'; import '../../static/style/search.scss';
require('../../static/style/tableStatus.css'); require('../../static/style/tableStatus.css');
require('../../static/style/logPath.scss'); require('../../static/style/logPath.scss');
...@@ -30,8 +28,8 @@ echarts.registerTheme('my_theme', { ...@@ -30,8 +28,8 @@ echarts.registerTheme('my_theme', {
interface TableListProps { interface TableListProps {
entries: number; entries: number;
tableSource: Array<TableObjFianl>; tableSource: Array<TableObj>;
searchResult: Array<TableObjFianl>; searchResult: Array<TableObj>;
updateList: Function; updateList: Function;
isHasSearch: boolean; isHasSearch: boolean;
platform: string; platform: string;
...@@ -83,7 +81,7 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -83,7 +81,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
Object.keys(res.data).map(item => { Object.keys(res.data).map(item => {
intermediateArr.push(parseFloat(res.data[item].data)); intermediateArr.push(parseFloat(res.data[item].data));
}); });
const intermediate = this.intermediateGraphOption(intermediateArr, id); const intermediate = intermediateGraphOption(intermediateArr, id);
if (this._isMounted) { if (this._isMounted) {
this.setState(() => ({ this.setState(() => ({
intermediateOption: intermediate intermediateOption: intermediate
...@@ -159,70 +157,6 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -159,70 +157,6 @@ class TableList extends React.Component<TableListProps, TableListState> {
} }
} }
intermediateGraphOption = (intermediateArr: number[], id: string) => {
const sequence: number[] = [];
const lengthInter = intermediateArr.length;
for (let i = 1; i <= lengthInter; i++) {
sequence.push(i);
}
return {
title: {
text: id,
left: 'center',
textStyle: {
fontSize: 16,
color: '#333',
}
},
tooltip: {
trigger: 'item'
},
xAxis: {
name: 'Trial',
data: sequence
},
yAxis: {
name: 'Default Metric',
type: 'value',
data: intermediateArr
},
series: [{
symbolSize: 6,
type: 'scatter',
data: intermediateArr
}]
};
}
// kill job
killJob = (key: number, id: string, status: string) => {
const { updateList } = this.props;
axios(`${MANAGER_IP}/trial-jobs/${id}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json;charset=utf-8'
}
})
.then(res => {
if (res.status === 200) {
message.success('Cancel the job successfully');
// render the table
updateList();
} else {
message.error('fail to cancel the job');
}
})
.catch(error => {
if (error.response.status === 500) {
if (error.response.data.error) {
message.error(error.response.data.error);
} else {
message.error('500 error, fail to cancel the job');
}
}
});
}
// click add column btn, just show the modal of addcolumn // click add column btn, just show the modal of addcolumn
addColumn = () => { addColumn = () => {
// show user select check button // show user select check button
...@@ -284,6 +218,17 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -284,6 +218,17 @@ class TableList extends React.Component<TableListProps, TableListState> {
} }
} }
openRow = (record: TableObj) => {
const { platform } = this.props;
return (
<OpenRow
showLogModalOverview={this.showLogModal}
trainingPlatform={platform}
record={record}
/>
);
}
componentDidMount() { componentDidMount() {
this._isMounted = true; this._isMounted = true;
} }
...@@ -294,7 +239,7 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -294,7 +239,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
render() { render() {
const { entries, tableSource, searchResult, isHasSearch, platform } = this.props; const { entries, tableSource, searchResult, isHasSearch, updateList } = this.props;
const { intermediateOption, modalVisible, isShowColumn, columnSelected, const { intermediateOption, modalVisible, isShowColumn, columnSelected,
logMessage, logModal logMessage, logModal
} = this.state; } = this.state;
...@@ -335,7 +280,7 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -335,7 +280,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
width: 120, width: 120,
className: 'tableHead', className: 'tableHead',
sorter: sorter:
(a: TableObjFianl, b: TableObjFianl) => (a: TableObj, b: TableObj) =>
(a.sequenceId as number) - (b.sequenceId as number) (a.sequenceId as number) - (b.sequenceId as number)
}); });
break; break;
...@@ -347,8 +292,8 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -347,8 +292,8 @@ class TableList extends React.Component<TableListProps, TableListState> {
width: 60, width: 60,
className: 'tableHead idtitle', className: 'tableHead idtitle',
// the sort of string // the sort of string
sorter: (a: TableObjFianl, b: TableObjFianl): number => a.id.localeCompare(b.id), sorter: (a: TableObj, b: TableObj): number => a.id.localeCompare(b.id),
render: (text: string, record: TableObjFianl) => { render: (text: string, record: TableObj) => {
return ( return (
<div>{record.id}</div> <div>{record.id}</div>
); );
...@@ -362,8 +307,8 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -362,8 +307,8 @@ class TableList extends React.Component<TableListProps, TableListState> {
key: 'duration', key: 'duration',
width: 140, width: 140,
// the sort of number // the sort of number
sorter: (a: TableObjFianl, b: TableObjFianl) => (a.duration as number) - (b.duration as number), sorter: (a: TableObj, b: TableObj) => (a.duration as number) - (b.duration as number),
render: (text: string, record: TableObjFianl) => { render: (text: string, record: TableObj) => {
let duration; let duration;
if (record.duration !== undefined && record.duration > 0) { if (record.duration !== undefined && record.duration > 0) {
duration = convertDuration(record.duration); duration = convertDuration(record.duration);
...@@ -383,15 +328,15 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -383,15 +328,15 @@ class TableList extends React.Component<TableListProps, TableListState> {
key: 'status', key: 'status',
width: 150, width: 150,
className: 'tableStatus', className: 'tableStatus',
render: (text: string, record: TableObjFianl) => { render: (text: string, record: TableObj) => {
bgColor = record.status; bgColor = record.status;
return ( return (
<span className={`${bgColor} commonStyle`}>{record.status}</span> <span className={`${bgColor} commonStyle`}>{record.status}</span>
); );
}, },
filters: trialJob, filters: trialJob,
onFilter: (value: string, record: TableObjFianl) => record.status.indexOf(value) === 0, onFilter: (value: string, record: TableObj) => record.status.indexOf(value) === 0,
sorter: (a: TableObjFianl, b: TableObjFianl): number => a.status.localeCompare(b.status) sorter: (a: TableObj, b: TableObj): number => a.status.localeCompare(b.status)
}); });
break; break;
case 'Default': case 'Default':
...@@ -400,38 +345,16 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -400,38 +345,16 @@ class TableList extends React.Component<TableListProps, TableListState> {
dataIndex: 'acc', dataIndex: 'acc',
key: 'acc', key: 'acc',
width: 200, width: 200,
sorter: (a: TableObjFianl, b: TableObjFianl) => { sorter: (a: TableObj, b: TableObj) => {
if (a.acc !== undefined && b.acc !== undefined) { if (a.acc !== undefined && b.acc !== undefined) {
return JSON.parse(a.acc.default) - JSON.parse(b.acc.default); return JSON.parse(a.acc.default) - JSON.parse(b.acc.default);
} else { } else {
return NaN; return NaN;
} }
}, },
render: (text: string, record: TableObjFianl) => { render: (text: string, record: TableObj) => {
let accuracy;
if (record.acc !== undefined) {
accuracy = record.acc.default;
}
let wei = 0;
if (accuracy) {
if (accuracy.toString().indexOf('.') !== -1) {
wei = accuracy.toString().length - accuracy.toString().indexOf('.') - 1;
}
}
return ( return (
<div> <DefaultMetric record={record}/>
{
record.acc && record.acc.default
?
wei > 6
?
JSON.parse(record.acc.default).toFixed(6)
:
record.acc.default
:
'--'
}
</div>
); );
} }
}); });
...@@ -442,7 +365,7 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -442,7 +365,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
dataIndex: 'operation', dataIndex: 'operation',
key: 'operation', key: 'operation',
width: 90, width: 90,
render: (text: string, record: TableObjFianl) => { render: (text: string, record: TableObj) => {
let trialStatus = record.status; let trialStatus = record.status;
let flagKill = false; let flagKill = false;
if (trialStatus === 'RUNNING') { if (trialStatus === 'RUNNING') {
...@@ -456,7 +379,8 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -456,7 +379,8 @@ class TableList extends React.Component<TableListProps, TableListState> {
( (
<Popconfirm <Popconfirm
title="Are you sure to cancel this trial?" title="Are you sure to cancel this trial?"
onConfirm={this.killJob.bind(this, record.key, record.id, record.status)} onConfirm={killJob.
bind(this, record.key, record.id, record.status, updateList)}
> >
<Button type="primary" className="tableButton">Kill</Button> <Button type="primary" className="tableButton">Kill</Button>
</Popconfirm> </Popconfirm>
...@@ -482,7 +406,7 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -482,7 +406,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
dataIndex: 'intermediate', dataIndex: 'intermediate',
key: 'intermediate', key: 'intermediate',
width: '16%', width: '16%',
render: (text: string, record: TableObjFianl) => { render: (text: string, record: TableObj) => {
return ( return (
<Button <Button
type="primary" type="primary"
...@@ -501,7 +425,7 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -501,7 +425,7 @@ class TableList extends React.Component<TableListProps, TableListState> {
dataIndex: item, dataIndex: item,
key: item, key: item,
width: 150, width: 150,
render: (text: string, record: TableObjFianl) => { render: (text: string, record: TableObj) => {
return ( return (
<div> <div>
{ {
...@@ -518,72 +442,12 @@ class TableList extends React.Component<TableListProps, TableListState> { ...@@ -518,72 +442,12 @@ class TableList extends React.Component<TableListProps, TableListState> {
} }
}); });
const openRow = (record: TableObjFianl) => {
let isHasParameters = true;
if (record.description.parameters.error) {
isHasParameters = false;
}
const parametersRow = {
parameters: record.description.parameters
};
const logPathRow = record.description.logPath !== undefined
?
record.description.logPath
:
'This trial\'s logPath are not available.';
const isdisLogbutton = record.status === 'WAITING'
?
true
:
false;
return (
<pre id="allList" className="hyperpar">
<Row className="openRowContent">
<Tabs tabPosition="left" className="card">
<TabPane tab="Parameters" key="1">
{
isHasParameters
?
<JSONTree
hideRoot={true}
shouldExpandNode={() => true} // default expandNode
getItemString={() => (<span />)} // remove the {} items
data={parametersRow}
/>
:
<div className="logpath">
<span className="logName">Error: </span>
<span className="error">'This trial's parameters are not available.'</span>
</div>
}
</TabPane>
<TabPane tab="Log" key="2">
{
platform === 'pai' || platform === 'kubeflow' || platform === 'frameworkcontroller'
?
<PaiTrialLog
logStr={logPathRow}
id={record.id}
showLogModal={this.showLogModal}
trialStatus={record.status}
isdisLogbutton={isdisLogbutton}
/>
:
<TrialLog logStr={logPathRow} id={record.id} />
}
</TabPane>
</Tabs>
</Row>
</pre>
);
};
return ( return (
<Row className="tableList"> <Row className="tableList">
<div id="tableList"> <div id="tableList">
<Table <Table
columns={showColumn} columns={showColumn}
expandedRowRender={openRow} expandedRowRender={this.openRow}
dataSource={isHasSearch ? searchResult : tableSource} dataSource={isHasSearch ? searchResult : tableSource}
className="commonTableStyle" className="commonTableStyle"
pagination={{ pageSize: entries }} pagination={{ pageSize: entries }}
......
import axios from 'axios';
import {
message
} from 'antd';
import { MANAGER_IP } from './const';
import { FinalResult, FinalType } from './interface'; import { FinalResult, FinalType } from './interface';
const convertTime = (num: number) => { const convertTime = (num: number) => {
...@@ -61,7 +66,71 @@ const getFinal = (final: FinalResult) => { ...@@ -61,7 +66,71 @@ const getFinal = (final: FinalResult) => {
} }
}; };
// detail page table intermediate button
const intermediateGraphOption = (intermediateArr: number[], id: string) => {
const sequence: number[] = [];
const lengthInter = intermediateArr.length;
for (let i = 1; i <= lengthInter; i++) {
sequence.push(i);
}
return {
title: {
text: id,
left: 'center',
textStyle: {
fontSize: 16,
color: '#333',
}
},
tooltip: {
trigger: 'item'
},
xAxis: {
name: 'Trial',
data: sequence
},
yAxis: {
name: 'Default Metric',
type: 'value',
data: intermediateArr
},
series: [{
symbolSize: 6,
type: 'scatter',
data: intermediateArr
}]
};
};
// kill job
const killJob = (key: number, id: string, status: string, updateList: Function) => {
axios(`${MANAGER_IP}/trial-jobs/${id}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json;charset=utf-8'
}
})
.then(res => {
if (res.status === 200) {
message.success('Cancel the job successfully');
// render the table
updateList();
} else {
message.error('fail to cancel the job');
}
})
.catch(error => {
if (error.response.status === 500) {
if (error.response.data.error) {
message.error(error.response.data.error);
} else {
message.error('500 error, fail to cancel the job');
}
}
});
};
export { export {
convertTime, convertDuration, getFinalResult, convertTime, convertDuration, getFinalResult,
getFinal getFinal, intermediateGraphOption, killJob
}; };
...@@ -5,18 +5,7 @@ interface TableObj { ...@@ -5,18 +5,7 @@ interface TableObj {
id: string; id: string;
duration: number; duration: number;
status: string; status: string;
acc?: number; // draw accuracy graph acc?: FinalType; // draw accuracy graph
description: Parameters;
color?: string;
}
interface TableObjFianl {
key: number;
sequenceId: number;
id: string;
duration: number;
status: string;
acc?: FinalType;
description: Parameters; description: Parameters;
color?: string; color?: string;
} }
...@@ -111,6 +100,5 @@ export { ...@@ -111,6 +100,5 @@ export {
TableObj, Parameters, Experiment, TableObj, Parameters, Experiment,
AccurPoint, TrialNumber, TrialJob, AccurPoint, TrialNumber, TrialJob,
DetailAccurPoint, TooltipForAccuracy, DetailAccurPoint, TooltipForAccuracy,
ParaObj, VisualMapValue, Dimobj, FinalResult, ParaObj, VisualMapValue, Dimobj, FinalResult, FinalType
TableObjFianl, FinalType
}; };
# Copyright (c) Microsoft Corporation
# All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge,
# to any person obtaining a copy of this software and associated
# documentation files (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and
# to permit persons to whom the Software is furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
# BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import os
import argparse
import glob
import subprocess
import time
import traceback
from utils import setup_experiment, get_experiment_status, get_yml_content, dump_yml_content, \
parse_max_duration_time, get_succeeded_trial_num, print_stderr
from utils import GREEN, RED, CLEAR, STATUS_URL, TRIAL_JOBS_URL
def gen_new_config(config_file, training_service='local'):
'''
Generates temporary config file for integration test, the file
should be deleted after testing.
'''
config = get_yml_content(config_file)
new_config_file = config_file + '.tmp'
ts = get_yml_content('training_service.yml')[training_service]
print(config)
print(ts)
config.update(ts)
print(config)
dump_yml_content(new_config_file, config)
return new_config_file, config
def run_test(config_file, training_service, local_gpu=False):
'''run test per configuration file'''
new_config_file, config = gen_new_config(config_file, training_service)
if training_service == 'local' and not local_gpu and config['trial']['gpuNum'] > 0:
print('no gpu, skiping: ', config_file)
return
try:
print('Testing %s...' % config_file)
proc = subprocess.run(['nnictl', 'create', '--config', new_config_file])
assert proc.returncode == 0, '`nnictl create` failed with code %d' % proc.returncode
max_duration, max_trial_num = get_max_values(config_file)
sleep_interval = 3
for _ in range(0, max_duration+30, sleep_interval):
time.sleep(sleep_interval)
status = get_experiment_status(STATUS_URL)
if status == 'DONE':
num_succeeded = get_succeeded_trial_num(TRIAL_JOBS_URL)
if training_service == 'local':
print_stderr(TRIAL_JOBS_URL)
assert num_succeeded == max_trial_num, 'only %d succeeded trial jobs, there should be %d' % (num_succeeded, max_trial_num)
break
assert status == 'DONE', 'Failed to finish in maxExecDuration'
finally:
if os.path.exists(new_config_file):
os.remove(new_config_file)
def get_max_values(config_file):
'''Get maxExecDuration and maxTrialNum of experiment'''
experiment_config = get_yml_content(config_file)
return parse_max_duration_time(experiment_config['maxExecDuration']), experiment_config['maxTrialNum']
def run(args):
'''test all configuration files'''
if args.config is None:
config_files = glob.glob('./config_test/**/*.test.yml')
else:
config_files = args.config.split(',')
print(config_files)
for config_file in config_files:
try:
# sleep 5 seconds here, to make sure previous stopped exp has enough time to exit to avoid port conflict
time.sleep(5)
run_test(config_file, args.ts, args.local_gpu)
print(GREEN + 'Test %s: TEST PASS' % (config_file) + CLEAR)
except Exception as error:
print(RED + 'Test %s: TEST FAIL' % (config_file) + CLEAR)
print('%r' % error)
traceback.print_exc()
raise error
finally:
subprocess.run(['nnictl', 'stop'])
if __name__ == '__main__':
import tensorflow as tf
print('TF VERSION:', tf.__version__)
parser = argparse.ArgumentParser()
parser.add_argument("--config", type=str, default=None)
parser.add_argument("--ts", type=str, choices=['local', 'remote', 'pai'], default='local')
parser.add_argument("--local_gpu", action='store_true')
parser.add_argument("--preinstall", action='store_true')
args = parser.parse_args()
setup_experiment(args.preinstall)
run(args)
authorName: nni
experimentName: default_test
maxExecDuration: 15m
maxTrialNum: 4
trialConcurrency: 2
searchSpacePath: ./cifar10_search_space.json
tuner:
builtinTunerName: Random
classArgs:
optimize_mode: maximize
assessor:
builtinAssessorName: Medianstop
classArgs:
optimize_mode: maximize
trial:
codeDir: ../../../examples/trials/cifar10_pytorch
command: python3 main.py --epochs 2
gpuNum: 1
useAnnotation: false
multiPhase: false
multiThread: false
trainingServicePlatform: local
{
"lr":{"_type":"choice", "_value":[0.1, 0.01, 0.001, 0.0001]},
"optimizer":{"_type":"choice", "_value":["SGD", "Adadelta", "Adagrad", "Adam", "Adamax"]},
"model":{"_type":"choice", "_value":["vgg", "resnet18"]}
}
authorName: nni
experimentName: default_test
maxExecDuration: 5m
maxTrialNum: 2
trialConcurrency: 1
tuner:
builtinTunerName: Random
classArgs:
optimize_mode: maximize
assessor:
builtinAssessorName: Medianstop
classArgs:
optimize_mode: maximize
trial:
codeDir: ../../../examples/trials/mnist-annotation
command: python3 mnist.py --batch_num 100
gpuNum: 0
useAnnotation: true
multiPhase: false
multiThread: false
trainingServicePlatform: local
authorName: nni
experimentName: default_test
maxExecDuration: 5m
maxTrialNum: 4
trialConcurrency: 2
searchSpacePath: ../../../examples/trials/mnist-keras/search_space.json
tuner:
builtinTunerName: Random
classArgs:
optimize_mode: maximize
assessor:
builtinAssessorName: Medianstop
classArgs:
optimize_mode: maximize
trial:
codeDir: ../../../examples/trials/mnist-keras
command: python3 mnist-keras.py --num_train 200 --epochs 1
gpuNum: 0
useAnnotation: false
multiPhase: false
multiThread: false
trainingServicePlatform: local
authorName: nni
experimentName: default_test
maxExecDuration: 5m
maxTrialNum: 2
trialConcurrency: 1
searchSpacePath: ./mnist_search_space.json
tuner:
builtinTunerName: Random
classArgs:
optimize_mode: maximize
assessor:
builtinAssessorName: Medianstop
classArgs:
optimize_mode: maximize
trial:
codeDir: ../../../examples/trials/mnist
command: python3 mnist.py --batch_num 100
gpuNum: 0
useAnnotation: false
multiPhase: false
multiThread: false
trainingServicePlatform: local
{
"dropout_rate":{"_type":"uniform","_value":[0.5, 0.9]},
"conv_size":{"_type":"choice","_value":[2,3,5,7]},
"hidden_size":{"_type":"choice","_value":[124, 512, 1024]},
"batch_size": {"_type":"choice", "_value": [16, 32]},
"learning_rate":{"_type":"choice","_value":[0.0001, 0.001, 0.01, 0.1]}
}
authorName: nni
experimentName: default_test
maxExecDuration: 5m
maxTrialNum: 4
trialConcurrency: 2
searchSpacePath: ../../../examples/trials/sklearn/classification/search_space.json
tuner:
builtinTunerName: Random
classArgs:
optimize_mode: maximize
assessor:
builtinAssessorName: Medianstop
classArgs:
optimize_mode: maximize
trial:
codeDir: ../../../examples/trials/sklearn/classification
command: python3 main.py
gpuNum: 0
useAnnotation: false
multiPhase: false
multiThread: false
trainingServicePlatform: local
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