Browse Source

feat: 超参数寻优-trail状态

pull/174/head
cp3hnu 11 months ago
parent
commit
2ba233d3d9
14 changed files with 303 additions and 26 deletions
  1. +3
    -0
      react-ui/src/app.tsx
  2. +20
    -0
      react-ui/src/enums/index.ts
  3. +1
    -1
      react-ui/src/pages/AutoML/Instance/index.tsx
  4. +2
    -1
      react-ui/src/pages/AutoML/components/ExperimentHistory/index.tsx
  5. +3
    -0
      react-ui/src/pages/AutoML/components/TrialStatusCell/index.less
  6. +67
    -0
      react-ui/src/pages/AutoML/components/TrialStatusCell/index.tsx
  7. +2
    -2
      react-ui/src/pages/Experiment/Comparison/index.tsx
  8. +1
    -1
      react-ui/src/pages/HyperParameter/Instance/index.tsx
  9. +21
    -5
      react-ui/src/pages/HyperParameter/components/ExperimentHistory/index.less
  10. +100
    -10
      react-ui/src/pages/HyperParameter/components/ExperimentHistory/index.tsx
  11. +3
    -0
      react-ui/src/pages/HyperParameter/components/TrialStatusCell/index.less
  12. +67
    -0
      react-ui/src/pages/HyperParameter/components/TrialStatusCell/index.tsx
  13. +6
    -6
      react-ui/src/pages/HyperParameter/types.ts
  14. +7
    -0
      react-ui/src/services/hyperParameter/index.js

+ 3
- 0
react-ui/src/app.tsx View File

@@ -245,6 +245,9 @@ export const antd: RuntimeAntdConfig = (memo) => {
linkColor: 'rgba(29, 29, 32, 0.7)',
separatorColor: 'rgba(29, 29, 32, 0.7)',
};
memo.theme.components.Tree = {
directoryNodeSelectedBg: 'rgba(22, 100, 255, 0.7)',
};

memo.theme.cssVar = true;
// memo.theme.hashed = false;


+ 20
- 0
react-ui/src/enums/index.ts View File

@@ -129,3 +129,23 @@ export const hyperParameterOptimizedModeOptions = [
{ label: '越大越好', value: hyperParameterOptimizedMode.Max },
{ label: '越小越好', value: hyperParameterOptimizedMode.Min },
];

// 超参数 Trail 运行状态
export enum HyperParameterTrailStatus {
PENDING = 'PENDING', // 挂起
RUNNING = 'RUNNING', // 运行中
TERMINATED = 'TERMINATED', // 成功
ERROR = 'ERROR', // 错误
PAUSED = 'PAUSED', // 暂停
RESTORING = 'RESTORING', // 恢复中
}

// 自动 Trail 运行状态
export enum AutoMLTrailStatus {
TIMEOUT = 'TIMEOUT', // 超时
SUCCESS = 'SUCCESS', // 成功
FAILURE = 'FAILURE', // 失败
CRASHED = 'CRASHED', // 崩溃
STOP = 'STOP', // 停止
CANCELLED = 'CANCELLED', // 取消
}

+ 1
- 1
react-ui/src/pages/AutoML/Instance/index.tsx View File

@@ -186,7 +186,7 @@ function AutoMLInstance() {
},
{
key: TabKeys.History,
label: 'Trial 列表',
label: '试验列表',
icon: <KFIcon type="icon-Trialliebiao" />,
children: (
<ExperimentHistory


+ 2
- 1
react-ui/src/pages/AutoML/components/ExperimentHistory/index.tsx View File

@@ -4,6 +4,7 @@ import tableCellRender from '@/utils/table';
import { Table, type TableProps } from 'antd';
import classNames from 'classnames';
import { useEffect, useState } from 'react';
import TrialStatusCell from '../TrialStatusCell';
import styles from './index.less';

type ExperimentHistoryProps = {
@@ -103,7 +104,7 @@ function ExperimentHistory({ fileUrl, isClassification }: ExperimentHistoryProps
dataIndex: 'status',
key: 'status',
width: 120,
render: tableCellRender(false),
render: TrialStatusCell,
},
];



+ 3
- 0
react-ui/src/pages/AutoML/components/TrialStatusCell/index.less View File

@@ -0,0 +1,3 @@
.trial-status-cell {
height: 100%;
}

+ 67
- 0
react-ui/src/pages/AutoML/components/TrialStatusCell/index.tsx View File

@@ -0,0 +1,67 @@
/*
* @Author: 赵伟
* @Date: 2024-04-18 18:35:41
* @Description: 实验状态
*/

import { AutoMLTrailStatus } from '@/enums';
import { ExperimentStatusInfo } from '@/pages/Experiment/status';
import themes from '@/styles/theme.less';
import styles from './index.less';

export const statusInfo: Record<AutoMLTrailStatus, ExperimentStatusInfo> = {
[AutoMLTrailStatus.SUCCESS]: {
label: '成功',
color: themes.successColor,
icon: '/assets/images/experiment-status/success-icon.png',
},
[AutoMLTrailStatus.TIMEOUT]: {
label: '超时',
color: themes.pendingColor,
icon: '/assets/images/experiment-status/pending-icon.png',
},
[AutoMLTrailStatus.FAILURE]: {
label: '失败',
color: themes.errorColor,
icon: '/assets/images/experiment-status/fail-icon.png',
},
[AutoMLTrailStatus.CRASHED]: {
label: '崩溃',
color: themes.errorColor,
icon: '/assets/images/experiment-status/fail-icon.png',
},
[AutoMLTrailStatus.CANCELLED]: {
label: '取消',
color: themes.abortColor,
icon: '/assets/images/experiment-status/omitted-icon.png',
},
[AutoMLTrailStatus.STOP]: {
label: '停止',
color: themes.textColor,
icon: '/assets/images/experiment-status/omitted-icon.png',
},
};

function TrialStatusCell(status?: AutoMLTrailStatus | null) {
if (status === null || status === undefined) {
return <span>--</span>;
}
return (
<div className={styles['trial-status-cell']}>
{/* <img
style={{ width: '17px', marginRight: '7px' }}
src={statusInfo[status]?.icon}
draggable={false}
alt=""
/> */}
<span
style={{ color: statusInfo[status] ? statusInfo[status].color : themes.textColor }}
className={styles['trial-status-cell__label']}
>
{statusInfo[status] ? statusInfo[status].label : status}
</span>
</div>
);
}

export default TrialStatusCell;

+ 2
- 2
react-ui/src/pages/Experiment/Comparison/index.tsx View File

@@ -77,7 +77,7 @@ function ExperimentComparison() {
};

// 对比按钮 click
const hanldeComparisonClick = () => {
const handleComparisonClick = () => {
if (selectedRowKeys.length < 2) {
message.error('请至少选择两项进行对比');
return;
@@ -202,7 +202,7 @@ function ExperimentComparison() {
return (
<div className={styles['experiment-comparison']}>
<div className={styles['experiment-comparison__header']}>
<Button type="default" onClick={hanldeComparisonClick}>
<Button type="default" onClick={handleComparisonClick}>
可视化对比
</Button>
</div>


+ 1
- 1
react-ui/src/pages/HyperParameter/Instance/index.tsx View File

@@ -191,7 +191,7 @@ function HyperParameterInstance() {
},
{
key: TabKeys.History,
label: 'Trial 列表',
label: '寻优列表',
icon: <KFIcon type="icon-Trialliebiao" />,
children: <ExperimentHistory trialList={instanceInfo?.trial_list} />,
},


+ 21
- 5
react-ui/src/pages/HyperParameter/components/ExperimentHistory/index.less View File

@@ -8,7 +8,8 @@
border-radius: 10px;

&__table {
height: 100%;
height: calc(100% - 52px);
margin-top: 20px;
}

:global {
@@ -43,16 +44,31 @@
&__best-tag {
margin-left: 8px;
padding: 1px 10px;
color: @primary-color;
color: @success-color;
font-weight: normal;
font-size: 13px;
background-color: .addAlpha(@primary-color, 0.1) [];
border: 1px solid .addAlpha(@primary-color, 0.5) [];
background-color: .addAlpha(@success-color, 0.1) [];
// border: 1px solid .addAlpha(@success-color, 0.5) [];
border-radius: 2px;
}
}

.table-best-row {
color: @primary-color;
color: @success-color;
font-weight: bold;
}

.trail-result {
:global {
.ant-tree-node-selected {
.trail-result__icon {
color: white;
}
}

.trail-result__icon {
margin-left: 8px;
color: @primary-color;
}
}
}

+ 100
- 10
react-ui/src/pages/HyperParameter/components/ExperimentHistory/index.tsx View File

@@ -1,23 +1,33 @@
import InfoGroup from '@/components/InfoGroup';
import KFIcon from '@/components/KFIcon';
import { HyperParameterFileList, HyperParameterTrialList } from '@/pages/HyperParameter/types';
import TrialStatusCell from '@/pages/HyperParameter/components/TrialStatusCell';
import { HyperParameterFile, HyperParameterTrial } from '@/pages/HyperParameter/types';
import { getExpMetricsReq } from '@/services/hyperParameter';
import { downLoadZip } from '@/utils/downloadfile';
import { to } from '@/utils/promise';
import tableCellRender, { TableCellValueType } from '@/utils/table';
import { Button, Table, Tooltip, type TableProps } from 'antd';
import { App, Button, Table, Tooltip, Tree, type TableProps, type TreeDataNode } from 'antd';
import classNames from 'classnames';
import { useState } from 'react';
import styles from './index.less';

const { DirectoryTree } = Tree;

type ExperimentHistoryProps = {
trialList?: HyperParameterTrialList[];
trialList?: HyperParameterTrial[];
};

function ExperimentHistory({ trialList = [] }: ExperimentHistoryProps) {
const first: HyperParameterTrialList | undefined = trialList[0];
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const { message } = App.useApp();

const first: HyperParameterTrial | undefined = trialList[0];
const config: Record<string, any> = first?.config ?? {};
const metricAnalysis: Record<string, any> = first?.metric_analysis ?? {};
const paramsNames = Object.keys(config);
const metricNames = Object.keys(metricAnalysis);

const trialColumns: TableProps<HyperParameterTrialList>['columns'] = [
const trialColumns: TableProps<HyperParameterTrial>['columns'] = [
{
title: '序号',
dataIndex: 'index',
@@ -55,7 +65,7 @@ function ExperimentHistory({ trialList = [] }: ExperimentHistoryProps) {
dataIndex: 'status',
key: 'status',
width: 120,
render: tableCellRender(false),
render: TrialStatusCell,
},
];

@@ -105,7 +115,7 @@ function ExperimentHistory({ trialList = [] }: ExperimentHistoryProps) {
});
}

const fileColumns: TableProps<HyperParameterFileList>['columns'] = [
const fileColumns: TableProps<HyperParameterFile>['columns'] = [
{
title: '文件名称',
dataIndex: 'name',
@@ -124,7 +134,7 @@ function ExperimentHistory({ trialList = [] }: ExperimentHistoryProps) {
dataIndex: 'option',
width: 160,
key: 'option',
render: (_: any, record: HyperParameterFileList) => {
render: (_: any, record: HyperParameterFile) => {
return (
<Button
type="link"
@@ -146,13 +156,92 @@ function ExperimentHistory({ trialList = [] }: ExperimentHistoryProps) {
},
];

const expandedRowRender = (record: HyperParameterTrialList) => (
const expandedRowRender = (record: HyperParameterTrial) => (
<Table columns={fileColumns} dataSource={[record.file]} pagination={false} rowKey="name" />
);

const expandedRowRender2 = (record: HyperParameterTrial) => {
const filesToTreeData = (
files: HyperParameterFile[],
parent?: HyperParameterFile,
): TreeDataNode[] =>
files.map((file) => {
const key = parent ? `${parent.name}/${file.name}` : file.name;
return {
...file,
key,
title: file.name,
children: file.children ? filesToTreeData(file.children, file) : undefined,
};
});

const treeData: TreeDataNode[] = filesToTreeData([record.file]);
return (
<InfoGroup title="寻优结果" className={styles['trail-result']}>
<DirectoryTree
// @ts-ignore
treeData={treeData}
defaultExpandAll
titleRender={(record: TreeDataNode & HyperParameterFile) => {
const label = record.title + (record.isFile ? `(${record.size})` : '');
return (
<>
<span style={{ fontSize: 14 }}>{label}</span>
<KFIcon
type="icon-xiazai"
className="trail-result__icon"
onClick={(e) => {
e.stopPropagation();
downLoadZip(
record.isFile
? `/api/mmp/minioStorage/downloadFile`
: `/api/mmp/minioStorage/download`,
{ path: record.url },
);
}}
/>
</>
);
}}
/>
</InfoGroup>
);
};

// 选择行
const rowSelection: TableProps<HyperParameterTrial>['rowSelection'] = {
type: 'checkbox',
columnWidth: 48,
fixed: 'left',
selectedRowKeys,
onChange: (selectedRowKeys: React.Key[]) => {
setSelectedRowKeys(selectedRowKeys);
},
};

const handleComparisonClick = () => {
if (selectedRowKeys.length < 1) {
message.error('请至少选择一项');
return;
}
getExpMetrics();
};

// 获取对比 url
const getExpMetrics = async () => {
const [res] = await to(getExpMetricsReq(selectedRowKeys));
if (res && res.data) {
const url = res.data;
window.open(url, '_blank');
}
};

return (
<div className={styles['experiment-history']}>
<div className={styles['experiment-history__content']}>
<Button type="default" onClick={handleComparisonClick}>
可视化对比
</Button>
<div
className={classNames(
'vertical-scroll-table-no-page',
@@ -167,7 +256,8 @@ function ExperimentHistory({ trialList = [] }: ExperimentHistoryProps) {
bordered={true}
scroll={{ y: 'calc(100% - 110px)', x: '100%' }}
rowKey="trial_id"
expandable={{ expandedRowRender }}
expandable={{ expandedRowRender: expandedRowRender2 }}
rowSelection={rowSelection}
/>
</div>
</div>


+ 3
- 0
react-ui/src/pages/HyperParameter/components/TrialStatusCell/index.less View File

@@ -0,0 +1,3 @@
.trial-status-cell {
height: 100%;
}

+ 67
- 0
react-ui/src/pages/HyperParameter/components/TrialStatusCell/index.tsx View File

@@ -0,0 +1,67 @@
/*
* @Author: 赵伟
* @Date: 2024-04-18 18:35:41
* @Description: 实验状态
*/

import { HyperParameterTrailStatus } from '@/enums';
import { ExperimentStatusInfo } from '@/pages/Experiment/status';
import themes from '@/styles/theme.less';
import styles from './index.less';

export const statusInfo: Record<HyperParameterTrailStatus, ExperimentStatusInfo> = {
[HyperParameterTrailStatus.RUNNING]: {
label: '运行中',
color: themes.primaryColor,
icon: '/assets/images/experiment-status/running-icon.png',
},
[HyperParameterTrailStatus.TERMINATED]: {
label: '成功',
color: themes.successColor,
icon: '/assets/images/experiment-status/success-icon.png',
},
[HyperParameterTrailStatus.PENDING]: {
label: '挂起',
color: themes.pendingColor,
icon: '/assets/images/experiment-status/pending-icon.png',
},
[HyperParameterTrailStatus.ERROR]: {
label: '失败',
color: themes.errorColor,
icon: '/assets/images/experiment-status/fail-icon.png',
},
[HyperParameterTrailStatus.PAUSED]: {
label: '暂停',
color: themes.abortColor,
icon: '/assets/images/experiment-status/omitted-icon.png',
},
[HyperParameterTrailStatus.RESTORING]: {
label: '恢复中',
color: themes.textColor,
icon: '/assets/images/experiment-status/omitted-icon.png',
},
};

function TrialStatusCell(status?: HyperParameterTrailStatus | null) {
if (status === null || status === undefined) {
return <span>--</span>;
}
return (
<div className={styles['trial-status-cell']}>
{/* <img
style={{ width: '17px', marginRight: '7px' }}
src={statusInfo[status]?.icon}
draggable={false}
alt=""
/> */}
<span
style={{ color: statusInfo[status] ? statusInfo[status].color : themes.textColor }}
className={styles['trial-status-cell__label']}
>
{statusInfo[status] ? statusInfo[status].label : status}
</span>
</div>
);
}

export default TrialStatusCell;

+ 6
- 6
react-ui/src/pages/HyperParameter/types.ts View File

@@ -59,11 +59,11 @@ export type HyperParameterInstanceData = {
update_time: string;
finish_time: string;
nodeStatus?: NodeStatus; // json之后的节点状态
trial_list?: HyperParameterTrialList[];
file_list?: HyperParameterFileList[];
trial_list?: HyperParameterTrial[];
file_list?: HyperParameterFile[];
};

export type HyperParameterTrialList = {
export type HyperParameterTrial = {
trial_id?: string;
training_iteration?: number;
time?: number;
@@ -71,14 +71,14 @@ export type HyperParameterTrialList = {
config?: Record<string, any>;
metric_analysis?: Record<string, any>;
metric: string;
file: HyperParameterFileList;
file: HyperParameterFile;
is_best?: boolean;
};

export type HyperParameterFileList = {
export type HyperParameterFile = {
name: string;
size: string;
url: string;
isFile: boolean;
children?: HyperParameterFileList[];
children: HyperParameterFile[];
};

+ 7
- 0
react-ui/src/services/hyperParameter/index.js View File

@@ -91,3 +91,10 @@ export function batchDeleteRayInsReq(data) {
});
}

// 获取当前实验的指标对比地址
export function getExpMetricsReq(data) {
return request(`/api/mmp/rayIns/getExpMetrics`, {
method: 'POST',
data
});
}

Loading…
Cancel
Save