| @@ -94,7 +94,7 @@ export default [ | |||||
| { | { | ||||
| name: '实验训练', | name: '实验训练', | ||||
| path: ':workflowId/:id', | path: ':workflowId/:id', | ||||
| component: './Experiment/training/index', | |||||
| component: './Experiment/Info/index', | |||||
| }, | }, | ||||
| { | { | ||||
| name: '实验对比', | name: '实验对比', | ||||
| @@ -112,18 +112,18 @@ export default [ | |||||
| { | { | ||||
| name: '开发环境', | name: '开发环境', | ||||
| path: '', | path: '', | ||||
| component: './DevelopmentEnvironment/List', | |||||
| }, | |||||
| { | |||||
| name: '创建编辑器', | |||||
| path: 'create', | |||||
| component: './DevelopmentEnvironment/Create', | |||||
| }, | |||||
| { | |||||
| name: '编辑器', | |||||
| path: 'editor', | |||||
| component: './DevelopmentEnvironment/Editor', | component: './DevelopmentEnvironment/Editor', | ||||
| }, | }, | ||||
| // { | |||||
| // name: '创建编辑器', | |||||
| // path: 'create', | |||||
| // component: './DevelopmentEnvironment/Create', | |||||
| // }, | |||||
| // { | |||||
| // name: '编辑器', | |||||
| // path: 'editor', | |||||
| // component: './DevelopmentEnvironment/Editor', | |||||
| // }, | |||||
| ], | ], | ||||
| }, | }, | ||||
| { | { | ||||
| @@ -0,0 +1,37 @@ | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2024-04-28 14:18:11 | |||||
| * @Description: 自定义 Table 数组类单元格 | |||||
| */ | |||||
| import { Tooltip } from 'antd'; | |||||
| function ArrayTableCell(ellipsis: boolean = false, property?: string) { | |||||
| return (value?: any | null) => { | |||||
| if ( | |||||
| value === undefined || | |||||
| value === null || | |||||
| Array.isArray(value) === false || | |||||
| value.length === 0 | |||||
| ) { | |||||
| return <span>--</span>; | |||||
| } | |||||
| let list = value; | |||||
| if (property && typeof value[0] === 'object') { | |||||
| list = value.map((item) => item[property]); | |||||
| } | |||||
| const text = list.join(','); | |||||
| if (ellipsis) { | |||||
| return ( | |||||
| <Tooltip title={text} placement="topLeft" overlayStyle={{ maxWidth: '400px' }}> | |||||
| <span>{text}</span>; | |||||
| </Tooltip> | |||||
| ); | |||||
| } else { | |||||
| return <span>{text}</span>; | |||||
| } | |||||
| }; | |||||
| } | |||||
| export default ArrayTableCell; | |||||
| @@ -6,13 +6,13 @@ | |||||
| import { Tooltip } from 'antd'; | import { Tooltip } from 'antd'; | ||||
| function renderCell(text?: string | null) { | |||||
| function renderCell(text?: any | null) { | |||||
| return <span>{text ?? '--'}</span>; | return <span>{text ?? '--'}</span>; | ||||
| } | } | ||||
| function CommonTableCell(ellipsis: boolean = false) { | function CommonTableCell(ellipsis: boolean = false) { | ||||
| if (ellipsis) { | if (ellipsis) { | ||||
| return (text?: string | null) => ( | |||||
| return (text?: any | null) => ( | |||||
| <Tooltip title={text} placement="topLeft" overlayStyle={{ maxWidth: '400px' }}> | <Tooltip title={text} placement="topLeft" overlayStyle={{ maxWidth: '400px' }}> | ||||
| {renderCell(text)} | {renderCell(text)} | ||||
| </Tooltip> | </Tooltip> | ||||
| @@ -1,9 +1,36 @@ | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2024-06-07 11:22:28 | |||||
| * @Description: 接口返回的枚举值和共用的枚举值定义在这里 | |||||
| */ | |||||
| // 公开还是私有 TabKey | // 公开还是私有 TabKey | ||||
| export enum CommonTabKeys { | export enum CommonTabKeys { | ||||
| Private = 'Private', // 私有 | Private = 'Private', // 私有 | ||||
| Public = 'Public', // 公开 | Public = 'Public', // 公开 | ||||
| } | } | ||||
| // 实验状态 | |||||
| export enum ExperimentStatus { | |||||
| Running = 'Running', // 运行中 | |||||
| Succeeded = 'Succeeded', // 成功 | |||||
| Pending = 'Pending', // 启动中 | |||||
| Failed = 'Failed', // 失败 | |||||
| Error = 'Error', // 错误 | |||||
| Terminated = 'Terminated', // 终止 | |||||
| Skipped = 'Skipped', // 跳过 | |||||
| Omitted = 'Omitted', // 忽略 | |||||
| } | |||||
| // TensorBoard 状态 | |||||
| export enum TensorBoardStatus { | |||||
| Unknown = 'Unknown', // 未知 | |||||
| Pending = 'Pending', // 启动中 | |||||
| Running = 'Running', // 运行中 | |||||
| Terminated = 'Terminated', // 未启动或者已终止 | |||||
| Failed = 'Failed', // 失败 | |||||
| } | |||||
| // 镜像版本状态 | // 镜像版本状态 | ||||
| export enum MirrorVersionStatus { | export enum MirrorVersionStatus { | ||||
| Available = 'available', // 可用 | Available = 'available', // 可用 | ||||
| @@ -4,7 +4,7 @@ | |||||
| * @Description: 覆盖 antd 样式 | * @Description: 覆盖 antd 样式 | ||||
| */ | */ | ||||
| // 设置 Table 可以滑动 | |||||
| // 设置 Table 可以滑动,带分页 | |||||
| .vertical-scroll-table { | .vertical-scroll-table { | ||||
| .ant-table-wrapper { | .ant-table-wrapper { | ||||
| height: 100%; | height: 100%; | ||||
| @@ -30,6 +30,32 @@ | |||||
| } | } | ||||
| } | } | ||||
| // 设置 Table 可以滑动,没有分页 | |||||
| .vertical-scroll-table-no-page { | |||||
| .ant-table-wrapper { | |||||
| height: 100%; | |||||
| .ant-spin-nested-loading { | |||||
| height: 100%; | |||||
| .ant-spin-container { | |||||
| height: 100%; | |||||
| .ant-table { | |||||
| height: 100%; | |||||
| .ant-table-container { | |||||
| height: 100%; | |||||
| .ant-table-body { | |||||
| overflow-y: auto !important; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| // Tabs 样式 | // Tabs 样式 | ||||
| // 删除底部白色横线 | // 删除底部白色横线 | ||||
| .ant-tabs { | .ant-tabs { | ||||
| @@ -8,10 +8,11 @@ import { ResourceData, ResourceType, resourceConfig } from '../../config'; | |||||
| import ResourceVersion from '../ResourceVersion'; | import ResourceVersion from '../ResourceVersion'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| // 这里值小写是因为值会写在 url 中 | |||||
| export enum ResourceInfoTabKeys { | export enum ResourceInfoTabKeys { | ||||
| Introduction = 'introduction', | |||||
| Version = 'version', | |||||
| Evolution = 'evolution', | |||||
| Introduction = 'introduction', // 简介 | |||||
| Version = 'version', // 版本 | |||||
| Evolution = 'evolution', // 演化 | |||||
| } | } | ||||
| type ResourceIntroProps = { | type ResourceIntroProps = { | ||||
| @@ -17,5 +17,17 @@ | |||||
| padding: 20px 30px 0; | padding: 20px 30px 0; | ||||
| background-color: white; | background-color: white; | ||||
| border-radius: 10px; | border-radius: 10px; | ||||
| :global { | |||||
| .ant-table-container { | |||||
| border: none !important; | |||||
| } | |||||
| .ant-table-tbody { | |||||
| .ant-table-cell { | |||||
| border-right: none !important; | |||||
| border-left: none !important; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,32 +1,50 @@ | |||||
| import CommonTableCell from '@/components/CommonTableCell'; | |||||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||||
| import { getExpEvaluateInfosReq, getExpTrainInfosReq } from '@/services/experiment'; | |||||
| // import { useCacheState } from '@/hooks/pageCacheState'; | |||||
| import { | |||||
| getExpEvaluateInfosReq, | |||||
| getExpMetricsReq, | |||||
| getExpTrainInfosReq, | |||||
| } from '@/services/experiment'; | |||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import tableCellRender, { arrayFormatter, dateFormatter } from '@/utils/table'; | |||||
| import { useSearchParams } from '@umijs/max'; | import { useSearchParams } from '@umijs/max'; | ||||
| import { Button, Table, TablePaginationConfig, TableProps } from 'antd'; | |||||
| import { App, Button, Table, /*TablePaginationConfig,*/ TableProps } from 'antd'; | |||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { useEffect, useState } from 'react'; | |||||
| import { useEffect, useMemo, useState } from 'react'; | |||||
| import ExperimentStatusCell from '../components/ExperimentStatusCell'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| export enum ComparisonType { | export enum ComparisonType { | ||||
| Train = 'train', // 训练 | |||||
| Evaluate = 'evaluate', // 评估 | |||||
| Train = 'Train', // 训练 | |||||
| Evaluate = 'Evaluate', // 评估 | |||||
| } | } | ||||
| type TableData = { | |||||
| experiment_ins_id: number; | |||||
| run_id: string; | |||||
| dataset: string[]; | |||||
| start_time: string; | |||||
| status: string; | |||||
| metrics_names: string[]; | |||||
| metrics: Record<string, number>; | |||||
| params_names: string[]; | |||||
| params: Record<string, string>; | |||||
| }; | |||||
| function ExperimentComparison() { | function ExperimentComparison() { | ||||
| const [searchParams] = useSearchParams(); | const [searchParams] = useSearchParams(); | ||||
| const comparisonType = searchParams.get('type'); | const comparisonType = searchParams.get('type'); | ||||
| const experimentId = searchParams.get('experimentId'); | |||||
| const [tableData, setTableData] = useState([]); | |||||
| const [cacheState, setCacheState] = useCacheState(); | |||||
| const [total, setTotal] = useState(0); | |||||
| const experimentId = searchParams.get('id'); | |||||
| const [tableData, setTableData] = useState<TableData[]>([]); | |||||
| // const [cacheState, setCacheState] = useCacheState(); | |||||
| // const [total, setTotal] = useState(0); | |||||
| const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]); | const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]); | ||||
| const [pagination, setPagination] = useState<TablePaginationConfig>( | |||||
| cacheState?.pagination ?? { | |||||
| current: 1, | |||||
| pageSize: 10, | |||||
| }, | |||||
| ); | |||||
| const { message } = App.useApp(); | |||||
| // const [pagination, setPagination] = useState<TablePaginationConfig>( | |||||
| // cacheState?.pagination ?? { | |||||
| // current: 1, | |||||
| // pageSize: 10, | |||||
| // }, | |||||
| // ); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| getComparisonData(); | getComparisonData(); | ||||
| @@ -38,20 +56,39 @@ function ExperimentComparison() { | |||||
| comparisonType === ComparisonType.Train ? getExpTrainInfosReq : getExpEvaluateInfosReq; | comparisonType === ComparisonType.Train ? getExpTrainInfosReq : getExpEvaluateInfosReq; | ||||
| const [res] = await to(request(experimentId)); | const [res] = await to(request(experimentId)); | ||||
| if (res && res.data) { | if (res && res.data) { | ||||
| const { content = [], totalElements = 0 } = res.data; | |||||
| setTableData(content); | |||||
| setTotal(totalElements); | |||||
| // const { content = [], totalElements = 0 } = res.data; | |||||
| setTableData(res.data); | |||||
| // setTotal(totalElements); | |||||
| } | } | ||||
| }; | }; | ||||
| // 分页切换 | |||||
| const handleTableChange: TableProps['onChange'] = (pagination, filters, sorter, { action }) => { | |||||
| if (action === 'paginate') { | |||||
| setPagination(pagination); | |||||
| // 获取对比 url | |||||
| const getExpMetrics = async () => { | |||||
| const [res] = await to(getExpMetricsReq(selectedRowKeys)); | |||||
| if (res && res.data) { | |||||
| const url = res.data; | |||||
| window.open(url, '_blank'); | |||||
| } | } | ||||
| // console.log(pagination, filters, sorter, action); | |||||
| }; | }; | ||||
| // 对比按钮 click | |||||
| const hanldeComparisonClick = () => { | |||||
| if (selectedRowKeys.length < 2) { | |||||
| message.error('请至少选择两项进行对比'); | |||||
| return; | |||||
| } | |||||
| getExpMetrics(); | |||||
| }; | |||||
| // 分页切换 | |||||
| // const handleTableChange: TableProps['onChange'] = (pagination, filters, sorter, { action }) => { | |||||
| // if (action === 'paginate') { | |||||
| // setPagination(pagination); | |||||
| // } | |||||
| // // console.log(pagination, filters, sorter, action); | |||||
| // }; | |||||
| // 选择行 | |||||
| const rowSelection: TableProps['rowSelection'] = { | const rowSelection: TableProps['rowSelection'] = { | ||||
| type: 'checkbox', | type: 'checkbox', | ||||
| selectedRowKeys, | selectedRowKeys, | ||||
| @@ -61,148 +98,96 @@ function ExperimentComparison() { | |||||
| }, | }, | ||||
| }; | }; | ||||
| const columns: TableProps['columns'] = [ | |||||
| { | |||||
| title: '基本信息', | |||||
| children: [ | |||||
| { | |||||
| title: '实例ID', | |||||
| dataIndex: 'name', | |||||
| key: 'name', | |||||
| width: '30%', | |||||
| render: CommonTableCell(), | |||||
| }, | |||||
| { | |||||
| title: '运行时间', | |||||
| dataIndex: 'name', | |||||
| key: 'name', | |||||
| width: '30%', | |||||
| render: CommonTableCell(), | |||||
| }, | |||||
| { | |||||
| title: '运行状态', | |||||
| dataIndex: 'name', | |||||
| key: 'name', | |||||
| width: '30%', | |||||
| render: CommonTableCell(), | |||||
| }, | |||||
| { | |||||
| title: '训练数据集', | |||||
| dataIndex: 'name', | |||||
| key: 'name', | |||||
| width: '30%', | |||||
| render: CommonTableCell(), | |||||
| }, | |||||
| { | |||||
| title: '增量训练', | |||||
| dataIndex: 'name', | |||||
| key: 'name', | |||||
| width: '30%', | |||||
| render: CommonTableCell(), | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| { | |||||
| title: '训练参数', | |||||
| children: [ | |||||
| { | |||||
| title: 'batchsize', | |||||
| dataIndex: 'name', | |||||
| key: 'name', | |||||
| width: '30%', | |||||
| render: CommonTableCell(), | |||||
| }, | |||||
| { | |||||
| title: 'config', | |||||
| dataIndex: 'name', | |||||
| key: 'name', | |||||
| width: '30%', | |||||
| render: CommonTableCell(), | |||||
| }, | |||||
| { | |||||
| title: 'epoch', | |||||
| dataIndex: 'name', | |||||
| key: 'name', | |||||
| width: '30%', | |||||
| render: CommonTableCell(), | |||||
| }, | |||||
| { | |||||
| title: 'lr', | |||||
| dataIndex: 'name', | |||||
| key: 'name', | |||||
| width: '30%', | |||||
| render: CommonTableCell(), | |||||
| }, | |||||
| { | |||||
| title: 'warmup_iters', | |||||
| dataIndex: 'name', | |||||
| key: 'name', | |||||
| width: '30%', | |||||
| render: CommonTableCell(), | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| { | |||||
| title: '训练指标', | |||||
| children: [ | |||||
| { | |||||
| title: 'metrc_name', | |||||
| dataIndex: 'name', | |||||
| key: 'name', | |||||
| width: '30%', | |||||
| render: CommonTableCell(), | |||||
| }, | |||||
| { | |||||
| title: 'test_1', | |||||
| dataIndex: 'name', | |||||
| key: 'name', | |||||
| width: '30%', | |||||
| render: CommonTableCell(), | |||||
| }, | |||||
| { | |||||
| title: 'test_2', | |||||
| dataIndex: 'name', | |||||
| key: 'name', | |||||
| width: '30%', | |||||
| render: CommonTableCell(), | |||||
| }, | |||||
| { | |||||
| title: 'test_3', | |||||
| dataIndex: 'name', | |||||
| key: 'name', | |||||
| width: '30%', | |||||
| render: CommonTableCell(), | |||||
| }, | |||||
| { | |||||
| title: 'test_4', | |||||
| dataIndex: 'name', | |||||
| key: 'name', | |||||
| width: '30%', | |||||
| render: CommonTableCell(), | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| ]; | |||||
| const columns: TableProps['columns'] = useMemo(() => { | |||||
| const first: TableData | undefined = tableData[0]; | |||||
| return [ | |||||
| { | |||||
| title: '基本信息', | |||||
| children: [ | |||||
| { | |||||
| title: '实例 ID', | |||||
| dataIndex: 'experiment_ins_id', | |||||
| key: 'experiment_ins_id', | |||||
| width: '20%', | |||||
| render: tableCellRender(), | |||||
| }, | |||||
| { | |||||
| title: '运行时间', | |||||
| dataIndex: 'start_time', | |||||
| key: 'start_time', | |||||
| width: 180, | |||||
| render: tableCellRender(false, dateFormatter), | |||||
| }, | |||||
| { | |||||
| title: '运行状态', | |||||
| dataIndex: 'status', | |||||
| key: 'status', | |||||
| width: '20%', | |||||
| render: ExperimentStatusCell, | |||||
| }, | |||||
| { | |||||
| title: '训练数据集', | |||||
| dataIndex: 'dataset', | |||||
| key: 'dataset', | |||||
| width: '20%', | |||||
| render: tableCellRender(true, arrayFormatter()), | |||||
| ellipsis: { showTitle: false }, | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| { | |||||
| title: '训练参数', | |||||
| children: first?.params_names.map((name) => ({ | |||||
| title: name, | |||||
| dataIndex: ['params', name], | |||||
| key: name, | |||||
| width: '20%', | |||||
| render: tableCellRender(true), | |||||
| ellipsis: { showTitle: false }, | |||||
| })), | |||||
| }, | |||||
| { | |||||
| title: '训练指标', | |||||
| children: first?.metrics_names.map((name) => ({ | |||||
| title: name, | |||||
| dataIndex: ['metrics', name], | |||||
| key: name, | |||||
| width: '20%', | |||||
| render: tableCellRender(true), | |||||
| ellipsis: { showTitle: false }, | |||||
| })), | |||||
| }, | |||||
| ]; | |||||
| }, [tableData]); | |||||
| return ( | return ( | ||||
| <div className={styles['experiment-comparison']}> | <div className={styles['experiment-comparison']}> | ||||
| <div className={styles['experiment-comparison__header']}> | <div className={styles['experiment-comparison__header']}> | ||||
| <Button type="default">可视化对比</Button> | |||||
| <Button type="default" onClick={hanldeComparisonClick}> | |||||
| 可视化对比 | |||||
| </Button> | |||||
| </div> | </div> | ||||
| <div className={classNames('vertical-scroll-table', styles['experiment-comparison__table'])}> | |||||
| <div | |||||
| className={classNames( | |||||
| 'vertical-scroll-table-no-page', | |||||
| styles['experiment-comparison__table'], | |||||
| )} | |||||
| > | |||||
| <Table | <Table | ||||
| dataSource={tableData} | dataSource={tableData} | ||||
| columns={columns} | columns={columns} | ||||
| rowSelection={rowSelection} | rowSelection={rowSelection} | ||||
| scroll={{ y: 'calc(100% - 55px)' }} | scroll={{ y: 'calc(100% - 55px)' }} | ||||
| pagination={{ | |||||
| ...pagination, | |||||
| total: total, | |||||
| showSizeChanger: true, | |||||
| showQuickJumper: true, | |||||
| }} | |||||
| onChange={handleTableChange} | |||||
| rowKey="id" | |||||
| pagination={false} | |||||
| bordered={true} | |||||
| // pagination={{ | |||||
| // ...pagination, | |||||
| // total: total, | |||||
| // showSizeChanger: true, | |||||
| // showQuickJumper: true, | |||||
| // }} | |||||
| // onChange={handleTableChange} | |||||
| rowKey="run_id" | |||||
| /> | /> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -0,0 +1,12 @@ | |||||
| .experiment-status-cell { | |||||
| height: 100%; | |||||
| &__label { | |||||
| display: none; | |||||
| } | |||||
| } | |||||
| .experiment-status-cell:hover { | |||||
| .experiment-status-cell__label { | |||||
| display: inline; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,28 @@ | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2024-04-18 18:35:41 | |||||
| * @Description: 实验状态 | |||||
| */ | |||||
| import { ExperimentStatus } from '@/enums'; | |||||
| import { experimentStatusInfo as statusInfo } from '@/pages/Experiment/status'; | |||||
| import styles from './index.less'; | |||||
| function ExperimentStatusCell(status?: ExperimentStatus | null) { | |||||
| if (status === null || status === undefined || !statusInfo[status]) { | |||||
| return <span>--</span>; | |||||
| } | |||||
| return ( | |||||
| <div className={styles['experiment-status-cell']}> | |||||
| <img style={{ width: '17px', marginRight: '7px' }} src={statusInfo[status]?.icon} /> | |||||
| <span | |||||
| style={{ color: statusInfo[status]?.color }} | |||||
| className={styles['experiment-status-cell__label']} | |||||
| > | |||||
| {statusInfo[status]?.label} | |||||
| </span> | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| export default ExperimentStatusCell; | |||||
| @@ -4,9 +4,9 @@ | |||||
| * @Description: 日志组件 | * @Description: 日志组件 | ||||
| */ | */ | ||||
| import { ExperimentStatus } from '@/enums'; | |||||
| import { useStateRef } from '@/hooks'; | import { useStateRef } from '@/hooks'; | ||||
| import { ExperimentStatus } from '@/pages/Experiment/status'; | |||||
| import { ExperimentLog } from '@/pages/Experiment/training/props'; | |||||
| import { ExperimentLog } from '@/pages/Experiment/Info/props'; | |||||
| import { getExperimentPodsLog } from '@/services/experiment/index.js'; | import { getExperimentPodsLog } from '@/services/experiment/index.js'; | ||||
| import { DoubleRightOutlined, DownOutlined, UpOutlined } from '@ant-design/icons'; | import { DoubleRightOutlined, DownOutlined, UpOutlined } from '@ant-design/icons'; | ||||
| import { Button } from 'antd'; | import { Button } from 'antd'; | ||||
| @@ -47,7 +47,7 @@ function LogGroup({ | |||||
| const [logList, setLogList, logListRef] = useStateRef<Log[]>([]); | const [logList, setLogList, logListRef] = useStateRef<Log[]>([]); | ||||
| const [completed, setCompleted] = useState(false); | const [completed, setCompleted] = useState(false); | ||||
| // eslint-disable-next-line @typescript-eslint/no-unused-vars | // eslint-disable-next-line @typescript-eslint/no-unused-vars | ||||
| const [isMouseDown, setIsMouseDown, isMouseDownRef] = useStateRef(false); | |||||
| const [_isMouseDown, setIsMouseDown, isMouseDownRef] = useStateRef(false); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| scrollToBottom(false); | scrollToBottom(false); | ||||
| @@ -1,5 +1,5 @@ | |||||
| import { ExperimentStatus } from '@/pages/Experiment/status'; | |||||
| import { ExperimentLog } from '@/pages/Experiment/training/props'; | |||||
| import { ExperimentStatus } from '@/enums'; | |||||
| import { ExperimentLog } from '@/pages/Experiment/Info/props'; | |||||
| import LogGroup from '../LogGroup'; | import LogGroup from '../LogGroup'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| @@ -5,16 +5,15 @@ import classNames from 'classnames'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| // import stopImg from '@/assets/img/tensor-board-stop.png'; | // import stopImg from '@/assets/img/tensor-board-stop.png'; | ||||
| import terminatedImg from '@/assets/img/tensor-board-terminated.png'; | import terminatedImg from '@/assets/img/tensor-board-terminated.png'; | ||||
| import { TensorBoardStatus } from '@/enums'; | |||||
| export enum TensorBoardStatusEnum { | |||||
| Unknown = 'Unknown', // 未知 | |||||
| Pending = 'Pending', // 启动中 | |||||
| Running = 'Running', // 运行中 | |||||
| Terminated = 'Terminated', // 未启动或者已终止 | |||||
| Failed = 'Failed', // 失败 | |||||
| } | |||||
| type TensorBoardStatusInfo = { | |||||
| label: string; | |||||
| icon: string; | |||||
| classname: string; | |||||
| }; | |||||
| const statusConfig = { | |||||
| const statusConfig: Record<TensorBoardStatus, TensorBoardStatusInfo> = { | |||||
| Unknown: { | Unknown: { | ||||
| label: '未知', | label: '未知', | ||||
| icon: terminatedImg, | icon: terminatedImg, | ||||
| @@ -43,12 +42,12 @@ const statusConfig = { | |||||
| }; | }; | ||||
| type TensorBoardStatusProps = { | type TensorBoardStatusProps = { | ||||
| status: TensorBoardStatusEnum; | |||||
| status: TensorBoardStatus; | |||||
| onClick: () => void; | onClick: () => void; | ||||
| }; | }; | ||||
| function TensorBoardStatus({ | |||||
| status = TensorBoardStatusEnum.Unknown, | |||||
| function TensorBoardStatusCell({ | |||||
| status = TensorBoardStatus.Unknown, | |||||
| onClick, | onClick, | ||||
| }: TensorBoardStatusProps) { | }: TensorBoardStatusProps) { | ||||
| return ( | return ( | ||||
| @@ -64,7 +63,7 @@ function TensorBoardStatus({ | |||||
| {statusConfig[status].icon ? ( | {statusConfig[status].icon ? ( | ||||
| <> | <> | ||||
| <div style={{ margin: '0 6px' }}>|</div> | <div style={{ margin: '0 6px' }}>|</div> | ||||
| {status === TensorBoardStatusEnum.Pending ? ( | |||||
| {status === TensorBoardStatus.Pending ? ( | |||||
| <LoadingOutlined className={styles['tensorBoard-status__icon']} /> | <LoadingOutlined className={styles['tensorBoard-status__icon']} /> | ||||
| ) : ( | ) : ( | ||||
| <img | <img | ||||
| @@ -79,4 +78,4 @@ function TensorBoardStatus({ | |||||
| ); | ); | ||||
| } | } | ||||
| export default TensorBoardStatus; | |||||
| export default TensorBoardStatusCell; | |||||
| @@ -1,5 +1,6 @@ | |||||
| import CommonTableCell from '@/components/CommonTableCell'; | import CommonTableCell from '@/components/CommonTableCell'; | ||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import { TensorBoardStatus } from '@/enums'; | |||||
| import { | import { | ||||
| deleteExperimentById, | deleteExperimentById, | ||||
| deleteQueryByExperimentInsId, | deleteQueryByExperimentInsId, | ||||
| @@ -24,7 +25,7 @@ import { useEffect, useRef, useState } from 'react'; | |||||
| import { useNavigate } from 'react-router-dom'; | import { useNavigate } from 'react-router-dom'; | ||||
| import { ComparisonType } from './Comparison'; | import { ComparisonType } from './Comparison'; | ||||
| import AddExperimentModal from './components/AddExperimentModal'; | import AddExperimentModal from './components/AddExperimentModal'; | ||||
| import TensorBoardStatus, { TensorBoardStatusEnum } from './components/TensorBoardStatus'; | |||||
| import TensorBoardStatusCell from './components/TensorBoardStatus'; | |||||
| import Styles from './index.less'; | import Styles from './index.less'; | ||||
| import { experimentStatusInfo } from './status'; | import { experimentStatusInfo } from './status'; | ||||
| @@ -260,12 +261,12 @@ function Experiment() { | |||||
| const handleTensorboard = async (experimentIn) => { | const handleTensorboard = async (experimentIn) => { | ||||
| if ( | if ( | ||||
| experimentIn.tensorBoardStatus === TensorBoardStatusEnum.Terminated || | |||||
| experimentIn.tensorBoardStatus === TensorBoardStatusEnum.Failed | |||||
| experimentIn.tensorBoardStatus === TensorBoardStatus.Terminated || | |||||
| experimentIn.tensorBoardStatus === TensorBoardStatus.Failed | |||||
| ) { | ) { | ||||
| await runTensorBoard(experimentIn); | await runTensorBoard(experimentIn); | ||||
| } else if ( | } else if ( | ||||
| experimentIn.tensorBoardStatus === TensorBoardStatusEnum.Running && | |||||
| experimentIn.tensorBoardStatus === TensorBoardStatus.Running && | |||||
| experimentIn.tensorboardUrl | experimentIn.tensorboardUrl | ||||
| ) { | ) { | ||||
| window.open(experimentIn.tensorboardUrl, '_blank'); | window.open(experimentIn.tensorboardUrl, '_blank'); | ||||
| @@ -457,12 +458,12 @@ function Experiment() { | |||||
| </a> | </a> | ||||
| <div className={Styles.tensorBoard}> | <div className={Styles.tensorBoard}> | ||||
| {item.nodes_result?.tensorboard_log ? ( | {item.nodes_result?.tensorboard_log ? ( | ||||
| <TensorBoardStatus | |||||
| <TensorBoardStatusCell | |||||
| status={item.tensorBoardStatus} | status={item.tensorBoardStatus} | ||||
| onClick={() => handleTensorboard(item)} | onClick={() => handleTensorboard(item)} | ||||
| ></TensorBoardStatus> | |||||
| ></TensorBoardStatusCell> | |||||
| ) : ( | ) : ( | ||||
| '-' | |||||
| '--' | |||||
| )} | )} | ||||
| </div> | </div> | ||||
| <div className={Styles.description}> | <div className={Styles.description}> | ||||
| @@ -1,23 +1,13 @@ | |||||
| import { ExperimentStatus } from '@/enums'; | |||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| export interface StatusInfo { | |||||
| export interface ExperimentStatusInfo { | |||||
| label: string; | label: string; | ||||
| color: string; | color: string; | ||||
| icon: string; | icon: string; | ||||
| } | } | ||||
| export enum ExperimentStatus { | |||||
| Running = 'Running', | |||||
| Succeeded = 'Succeeded', | |||||
| Pending = 'Pending', | |||||
| Failed = 'Failed', | |||||
| Error = 'Error', | |||||
| Terminated = 'Terminated', | |||||
| Skipped = 'Skipped', | |||||
| Omitted = 'Omitted', | |||||
| } | |||||
| export const experimentStatusInfo: Record<ExperimentStatus, StatusInfo | undefined> = { | |||||
| export const experimentStatusInfo: Record<ExperimentStatus, ExperimentStatusInfo> = { | |||||
| Running: { | Running: { | ||||
| label: '运行中', | label: '运行中', | ||||
| color: themes.primaryColor, | color: themes.primaryColor, | ||||
| @@ -17,9 +17,9 @@ import { ModelDeploymentData } from '../types'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| export enum ModelDeploymentTabKey { | export enum ModelDeploymentTabKey { | ||||
| Predict = 'Predict', | |||||
| Guide = 'Guide', | |||||
| Log = 'Log', | |||||
| Predict = 'Predict', // 预测 | |||||
| Guide = 'Guide', // 调用指南 | |||||
| Log = 'Log', // 服务日志 | |||||
| } | } | ||||
| function ModelDeploymentInfo() { | function ModelDeploymentInfo() { | ||||
| @@ -166,7 +166,7 @@ function ModelDeployment() { | |||||
| }; | }; | ||||
| // 分页切换 | // 分页切换 | ||||
| const handleTableChange: TableProps['onChange'] = (pagination, filters, sorter, { action }) => { | |||||
| const handleTableChange: TableProps['onChange'] = (pagination, _filters, _sorter, { action }) => { | |||||
| if (action === 'paginate') { | if (action === 'paginate') { | ||||
| setPagination(pagination); | setPagination(pagination); | ||||
| } | } | ||||
| @@ -179,7 +179,7 @@ function ModelDeployment() { | |||||
| dataIndex: 'index', | dataIndex: 'index', | ||||
| key: 'index', | key: 'index', | ||||
| width: '20%', | width: '20%', | ||||
| render(text, record, index) { | |||||
| render(_text, _record, index) { | |||||
| return <span>{(pagination.current! - 1) * pagination.pageSize! + index + 1}</span>; | return <span>{(pagination.current! - 1) * pagination.pageSize! + index + 1}</span>; | ||||
| }, | }, | ||||
| }, | }, | ||||
| @@ -26,7 +26,7 @@ export type ModelDeploymentData = { | |||||
| // 操作类型 | // 操作类型 | ||||
| export enum ModelDeploymentOperationType { | export enum ModelDeploymentOperationType { | ||||
| Create = 'Create', | |||||
| Update = 'Update', | |||||
| Restart = 'Restart', | |||||
| Create = 'Create', // 创建 | |||||
| Update = 'Update', // 更新 | |||||
| Restart = 'Restart', // 重启 | |||||
| } | } | ||||
| @@ -126,7 +126,15 @@ export function getExpEvaluateInfosReq(experimentId) { | |||||
| // 获取当前实验的模型训练指标信息 | // 获取当前实验的模型训练指标信息 | ||||
| export function getExpTrainInfosReq(experimentId) { | export function getExpTrainInfosReq(experimentId) { | ||||
| return request(`/api/mmp//aim/getExpTrainInfos/${experimentId}`, { | |||||
| return request(`/api/mmp/aim/getExpTrainInfos/${experimentId}`, { | |||||
| method: 'GET', | method: 'GET', | ||||
| }); | }); | ||||
| } | } | ||||
| // 获取当前实验的指标对比地址 | |||||
| export function getExpMetricsReq(data) { | |||||
| return request(`/api/mmp/aim/getExpMetrics`, { | |||||
| method: 'POST', | |||||
| data | |||||
| }); | |||||
| } | |||||
| @@ -4,7 +4,7 @@ | |||||
| * @Description: 定义全局类型,比如无关联的页面都需要要的类型 | * @Description: 定义全局类型,比如无关联的页面都需要要的类型 | ||||
| */ | */ | ||||
| import { ExperimentStatus } from '@/pages/Experiment/status'; | |||||
| import { ExperimentStatus } from '@/enums'; | |||||
| // 流水线全局参数 | // 流水线全局参数 | ||||
| export type PipelineGlobalParam = { | export type PipelineGlobalParam = { | ||||
| @@ -0,0 +1,68 @@ | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2024-06-26 10:05:52 | |||||
| * @Description: 列表自定义 render | |||||
| */ | |||||
| import { formatDate } from '@/utils/date'; | |||||
| import { Tooltip } from 'antd'; | |||||
| import dayjs from 'dayjs'; | |||||
| type TableCellFormatter = (value?: any | null) => string | undefined | null; | |||||
| // 字符串转换函数 | |||||
| export const stringFormatter: TableCellFormatter = (value?: any | null) => { | |||||
| return value; | |||||
| }; | |||||
| // 日期转换函数 | |||||
| export const dateFormatter: TableCellFormatter = (value?: any | null) => { | |||||
| if (value === undefined || value === null || value === '') { | |||||
| return null; | |||||
| } | |||||
| if (!dayjs(value).isValid()) { | |||||
| return null; | |||||
| } | |||||
| return formatDate(value); | |||||
| }; | |||||
| // 数组转换函数 | |||||
| export function arrayFormatter(property?: string) { | |||||
| return (value?: any | null): ReturnType<TableCellFormatter> => { | |||||
| if ( | |||||
| value === undefined || | |||||
| value === null || | |||||
| Array.isArray(value) === false || | |||||
| value.length === 0 | |||||
| ) { | |||||
| return null; | |||||
| } | |||||
| let list = value; | |||||
| if (property && typeof value[0] === 'object') { | |||||
| list = value.map((item) => item[property]); | |||||
| } | |||||
| return list.join(','); | |||||
| }; | |||||
| } | |||||
| function tableCellRender(ellipsis: boolean = false, format: TableCellFormatter = stringFormatter) { | |||||
| return (value?: any | null) => { | |||||
| const text = format(value); | |||||
| if (ellipsis && text) { | |||||
| return ( | |||||
| <Tooltip title={text} placement="topLeft" overlayStyle={{ maxWidth: '400px' }}> | |||||
| {renderCell(text)} | |||||
| </Tooltip> | |||||
| ); | |||||
| } else { | |||||
| return renderCell(text); | |||||
| } | |||||
| }; | |||||
| } | |||||
| function renderCell(text?: any | null) { | |||||
| return <span>{text ?? '--'}</span>; | |||||
| } | |||||
| export default tableCellRender; | |||||
| @@ -23,8 +23,6 @@ import java.util.stream.Collectors; | |||||
| public class AimServiceImpl implements AimService { | public class AimServiceImpl implements AimService { | ||||
| @Resource | @Resource | ||||
| private ExperimentInsService experimentInsService; | private ExperimentInsService experimentInsService; | ||||
| @Resource | |||||
| private ModelDependencyService modelDependencyService; | |||||
| @Value("${aim.url}") | @Value("${aim.url}") | ||||
| private String aimUrl; | private String aimUrl; | ||||
| @@ -44,7 +42,7 @@ public class AimServiceImpl implements AimService { | |||||
| @Override | @Override | ||||
| public String getExpMetrics(List<String> runIds) throws Exception { | public String getExpMetrics(List<String> runIds) throws Exception { | ||||
| String decode = AIM64EncoderUtil.decode(runIds); | String decode = AIM64EncoderUtil.decode(runIds); | ||||
| return aimUrl+"/api/runs/search/run?query="+decode; | |||||
| return aimUrl+"/metrics?select="+decode; | |||||
| } | } | ||||
| private List<InsMetricInfoVo> getAimRunInfos(boolean isTrain,Integer experimentId) throws Exception { | private List<InsMetricInfoVo> getAimRunInfos(boolean isTrain,Integer experimentId) throws Exception { | ||||
| @@ -56,6 +54,7 @@ public class AimServiceImpl implements AimService { | |||||
| String url = aimProxyUrl+"/api/runs/search/run?query="+encodedUrlString; | String url = aimProxyUrl+"/api/runs/search/run?query="+encodedUrlString; | ||||
| String s = HttpUtils.sendGetRequest(url); | String s = HttpUtils.sendGetRequest(url); | ||||
| List<Map<String, Object>> response = JacksonUtil.parseJSONStr2MapList(s); | List<Map<String, Object>> response = JacksonUtil.parseJSONStr2MapList(s); | ||||
| System.out.println("response: "+JacksonUtil.toJSONString(response)); | |||||
| if (response == null || response.size() == 0){ | if (response == null || response.size() == 0){ | ||||
| return new ArrayList<>(); | return new ArrayList<>(); | ||||
| } | } | ||||
| @@ -103,20 +102,15 @@ public class AimServiceImpl implements AimService { | |||||
| aimRunInfo.setStatus(ins.getStatus()); | aimRunInfo.setStatus(ins.getStatus()); | ||||
| aimRunInfo.setStartTime(ins.getCreateTime()); | aimRunInfo.setStartTime(ins.getCreateTime()); | ||||
| Map<String, Object> metricRecordMap = JacksonUtil.parseJSONStr2Map(metricRecordString); | Map<String, Object> metricRecordMap = JacksonUtil.parseJSONStr2Map(metricRecordString); | ||||
| //metricRecord 格式为{"train":[{"task_id":"model-train-35303690","run_id":"5560d78f54314672b60304c8d6ba03b8","experiment_name":"experiment-30-train"}],"evaluate":[{"task_id":"model-train-35303690","run_id":"5560d78f54314672b60304c8d6ba03b8","experiment_name":"experiment-30-train"}]} | |||||
| //遍历metricRecord,找到当前task_id对应的ModelDependency | |||||
| if (isTrain){ | if (isTrain){ | ||||
| List<Map<String, Object>> trainList = (List<Map<String, Object>>) metricRecordMap.get("train"); | |||||
| List<String> trainDateSet = getTrainDateSet(trainList, ins.getId(), isTrain); | |||||
| aimRunInfo.setDataset(trainDateSet); | |||||
| List<Map<String, Object>> records = (List<Map<String, Object>>) metricRecordMap.get("train"); | |||||
| List<String> datasetList = getTrainDateSet(records, aimrunId); | |||||
| aimRunInfo.setDataset(datasetList); | |||||
| }else { | }else { | ||||
| List<Map<String, Object>> trainList = (List<Map<String, Object>>) metricRecordMap.get("evaluate"); | |||||
| List<String> trainDateSet = getTrainDateSet(trainList, ins.getId(), isTrain); | |||||
| aimRunInfo.setDataset(trainDateSet); | |||||
| List<Map<String, Object>> records = (List<Map<String, Object>>) metricRecordMap.get("evaluate"); | |||||
| List<String> datasetList = getTrainDateSet(records, aimrunId); | |||||
| aimRunInfo.setDataset(datasetList); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| aimRunInfoList.add(aimRunInfo); | aimRunInfoList.add(aimRunInfo); | ||||
| @@ -143,33 +137,21 @@ public class AimServiceImpl implements AimService { | |||||
| } | } | ||||
| private List<String> getTrainDateSet(List<Map<String, Object>> trainList,Integer expInsId,boolean isTrain){ | |||||
| if (trainList == null || trainList.size() == 0){ | |||||
| return new ArrayList<>(); | |||||
| } | |||||
| private List<String> getTrainDateSet(List<Map<String, Object>> records, String aimrunId){ | |||||
| List<String> datasetList = new ArrayList<>(); | List<String> datasetList = new ArrayList<>(); | ||||
| for (Map<String, Object> trainMap : trainList) { | |||||
| String task_id = (String) trainMap.get("task_id"); | |||||
| //modelDependency取到数据集文件 | |||||
| ModelDependency modelDependency = modelDependencyService.queryByInsAndTrainTaskId(expInsId, task_id); | |||||
| //把数据集文件组装成String后放进List | |||||
| String datasetString = ""; | |||||
| if (isTrain){ | |||||
| datasetString = modelDependency.getTrainDataset(); | |||||
| }else { | |||||
| datasetString = modelDependency.getTestDataset(); | |||||
| } | |||||
| List<Map<String, Object>> datasetListMap = JacksonUtil.parseJSONStr2MapList(datasetString); | |||||
| if (datasetListMap != null && datasetListMap.size() > 0){ | |||||
| for (Map<String, Object> datasetMap : datasetListMap) { | |||||
| //[{"dataset_id":20,"dataset_version":"v0.1.0","dataset_name":"手写体识别模型依赖测试训练数据集"}] | |||||
| String datasetName = (String) datasetMap.get("dataset_name")+":"+(String) datasetMap.get("dataset_version"); | |||||
| for (Map<String, Object> record : records) { | |||||
| if (StringUtils.equals(aimrunId, (String)record.get("run_id"))) { | |||||
| List<Map<String, Object>> datasets = (List<Map<String, Object>>) record.get("datasets"); | |||||
| if (datasets == null || datasets.size() == 0){ | |||||
| continue; | |||||
| } | |||||
| for (Map<String, Object> dataset : datasets){ | |||||
| String datasetName = (String) dataset.get("dataset_name")+":"+(String) dataset.get("dataset_version"); | |||||
| datasetList.add(datasetName); | datasetList.add(datasetName); | ||||
| } | } | ||||
| break; | |||||
| } | } | ||||
| } | } | ||||
| return datasetList; | return datasetList; | ||||
| } | } | ||||
| } | } | ||||
| @@ -251,16 +251,14 @@ public class ExperimentServiceImpl implements ExperimentService { | |||||
| if (data == null || MapUtils.isEmpty(data)) { | if (data == null || MapUtils.isEmpty(data)) { | ||||
| throw new RuntimeException("Failed to run workflow."); | throw new RuntimeException("Failed to run workflow."); | ||||
| } | } | ||||
| //获取训练参数 | |||||
| Map<String, Object> metricRecord = (Map<String, Object>) runResMap.get("metric_record"); | |||||
| Map<String, Object> metadata = (Map<String, Object>) data.get("metadata"); | Map<String, Object> metadata = (Map<String, Object>) data.get("metadata"); | ||||
| // 插入记录到实验实例表 | // 插入记录到实验实例表 | ||||
| ExperimentIns experimentIns = new ExperimentIns(); | ExperimentIns experimentIns = new ExperimentIns(); | ||||
| if (metricRecord != null){ | |||||
| experimentIns.setMetricRecord(JacksonUtil.toJSONString(metricRecord)); | |||||
| } | |||||
| //获取训练参数 | |||||
| experimentIns.setExperimentId(experiment.getId()); | experimentIns.setExperimentId(experiment.getId()); | ||||
| experimentIns.setArgoInsNs((String) metadata.get("namespace")); | experimentIns.setArgoInsNs((String) metadata.get("namespace")); | ||||
| experimentIns.setArgoInsName((String) metadata.get("name")); | experimentIns.setArgoInsName((String) metadata.get("name")); | ||||
| @@ -271,14 +269,22 @@ public class ExperimentServiceImpl implements ExperimentService { | |||||
| //替换argoInsName | //替换argoInsName | ||||
| String outputString = JsonUtils.mapToJson(output); | String outputString = JsonUtils.mapToJson(output); | ||||
| experimentIns.setNodesResult(outputString.replace("{{workflow.name}}", (String) metadata.get("name"))); | experimentIns.setNodesResult(outputString.replace("{{workflow.name}}", (String) metadata.get("name"))); | ||||
| //插入ExperimentIns表中 | |||||
| ExperimentIns insert = experimentInsService.insert(experimentIns); | |||||
| //插入到模型依赖关系表 | |||||
| //得到dependendcy | //得到dependendcy | ||||
| Map<String, Object> converMap2 = JsonUtils.jsonToMap(JacksonUtil.replaceInAarry(convertRes, params)); | Map<String, Object> converMap2 = JsonUtils.jsonToMap(JacksonUtil.replaceInAarry(convertRes, params)); | ||||
| Map<String ,Object> dependendcy = (Map<String, Object>)converMap2.get("model_dependency"); | Map<String ,Object> dependendcy = (Map<String, Object>)converMap2.get("model_dependency"); | ||||
| Map<String ,Object> trainInfo = (Map<String, Object>)converMap2.get("component_info"); | Map<String ,Object> trainInfo = (Map<String, Object>)converMap2.get("component_info"); | ||||
| Map<String, Object> metricRecord = (Map<String, Object>) runResMap.get("metric_record"); | |||||
| if (metricRecord != null){ | |||||
| //把训练用的数据集也放进去 | |||||
| addDatesetToMetric(metricRecord, trainInfo); | |||||
| experimentIns.setMetricRecord(JacksonUtil.toJSONString(metricRecord)); | |||||
| } | |||||
| //插入ExperimentIns表中 | |||||
| ExperimentIns insert = experimentInsService.insert(experimentIns); | |||||
| //插入到模型依赖关系表 | |||||
| if (dependendcy != null && trainInfo != null){ | if (dependendcy != null && trainInfo != null){ | ||||
| insertModelDependency(dependendcy,trainInfo,insert.getId(),experiment.getName()); | insertModelDependency(dependendcy,trainInfo,insert.getId(),experiment.getName()); | ||||
| } | } | ||||
| @@ -289,6 +295,37 @@ public class ExperimentServiceImpl implements ExperimentService { | |||||
| experiment.setExperimentInsList(updatedExperimentInsList); | experiment.setExperimentInsList(updatedExperimentInsList); | ||||
| return experiment; | return experiment; | ||||
| } | } | ||||
| private void addDatesetToMetric(Map<String, Object> metricRecord, Map<String, Object> trainInfo) { | |||||
| processMetricPart(metricRecord, trainInfo, "train", "model_train"); | |||||
| processMetricPart(metricRecord, trainInfo, "evaluate", "model_evaluate"); | |||||
| } | |||||
| private void processMetricPart(Map<String, Object> metricRecord, Map<String, Object> trainInfo, String metricKey, String trainInfoKey) { | |||||
| List<Map<String, Object>> metricList = (List<Map<String, Object>>) metricRecord.get(metricKey); | |||||
| if (metricList != null) { | |||||
| for (Map<String, Object> metricRecordItem : metricList) { | |||||
| String taskId = (String) metricRecordItem.get("task_id"); | |||||
| Map<String, Object> trainInfoPart = (Map<String, Object>) trainInfo.get(trainInfoKey); | |||||
| if (trainInfoPart != null) { | |||||
| Map<String, Object> trainInfoDetails = (Map<String, Object>) trainInfoPart.get(taskId); | |||||
| if (trainInfoDetails != null) { | |||||
| List<Map<String, Object>> datasets = (List<Map<String, Object>>) trainInfoDetails.get("datasets"); | |||||
| if (datasets != null) { | |||||
| //查询名字再回填 | |||||
| for (int i = 0; i < datasets.size(); i++) { | |||||
| Dataset dataset = datasetService.queryById((Integer) datasets.get(i).get("dataset_id")); | |||||
| datasets.get(i).put("dataset_name", dataset.getName()); | |||||
| } | |||||
| metricRecordItem.put("datasets", datasets); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| private void insertModelDependency(Map<String ,Object> dependendcy,Map<String ,Object> trainInfo, Integer experimentInsId, String experimentName) throws Exception { | private void insertModelDependency(Map<String ,Object> dependendcy,Map<String ,Object> trainInfo, Integer experimentInsId, String experimentName) throws Exception { | ||||
| Iterator<Map.Entry<String, Object>> dependendcyIterator = dependendcy.entrySet().iterator(); | Iterator<Map.Entry<String, Object>> dependendcyIterator = dependendcy.entrySet().iterator(); | ||||
| Map<String, Object> modelTrain = (Map<String, Object>) trainInfo.get("model_train"); | Map<String, Object> modelTrain = (Map<String, Object>) trainInfo.get("model_train"); | ||||
| @@ -306,7 +306,7 @@ | |||||
| global_param = #{experimentIns.globalParam}, | global_param = #{experimentIns.globalParam}, | ||||
| </if> | </if> | ||||
| <if test="experimentIns.metricRecord != null and experimentIns.metricRecord != ''"> | <if test="experimentIns.metricRecord != null and experimentIns.metricRecord != ''"> | ||||
| and metric_record = #{experimentIns.metricRecord} | |||||
| metric_record = #{experimentIns.metricRecord}, | |||||
| </if> | </if> | ||||
| <if test="experimentIns.startTime != null"> | <if test="experimentIns.startTime != null"> | ||||
| start_time = #{experimentIns.startTime}, | start_time = #{experimentIns.startTime}, | ||||