| @@ -50,7 +50,7 @@ export async function getInitialState(): Promise<GlobalInitialState> { | |||
| // 如果不是登录页面,执行 | |||
| const { location } = history; | |||
| console.log('getInitialState', needAuth(location.pathname)); | |||
| // console.log('getInitialState', needAuth(location.pathname)); | |||
| if (needAuth(location.pathname)) { | |||
| const currentUser = await fetchUserInfo(); | |||
| return { | |||
| @@ -163,7 +163,7 @@ export const layout: RuntimeConfig['layout'] = ({ initialState }) => { | |||
| export const onRouteChange: RuntimeConfig['onRouteChange'] = async (e) => { | |||
| const { location } = e; | |||
| const menus = getRemoteMenu(); | |||
| console.log('onRouteChange', menus); | |||
| // console.log('onRouteChange', menus); | |||
| if (menus === null && needAuth(location.pathname)) { | |||
| history.go(0); | |||
| } | |||
| @@ -174,12 +174,12 @@ export const patchRoutes: RuntimeConfig['patchRoutes'] = (e) => { | |||
| }; | |||
| export const patchClientRoutes: RuntimeConfig['patchClientRoutes'] = (e) => { | |||
| console.log('patchClientRoutes', e); | |||
| // console.log('patchClientRoutes', e); | |||
| patchRouteWithRemoteMenus(e.routes); | |||
| }; | |||
| export function render(oldRender: () => void) { | |||
| console.log('render'); | |||
| // console.log('render'); | |||
| const token = getAccessToken(); | |||
| if (!token || token?.length === 0) { | |||
| oldRender(); | |||
| @@ -18,9 +18,9 @@ export enum AvailableRange { | |||
| // 实验状态 | |||
| export enum ExperimentStatus { | |||
| Pending = 'Pending', // 启动中 | |||
| Running = 'Running', // 运行中 | |||
| Succeeded = 'Succeeded', // 成功 | |||
| Pending = 'Pending', // 启动中 | |||
| Failed = 'Failed', // 失败 | |||
| Error = 'Error', // 错误 | |||
| Terminated = 'Terminated', // 终止 | |||
| @@ -43,7 +43,7 @@ body { | |||
| } | |||
| .ant-pro-layout .ant-pro-sider-menu { | |||
| padding-top: 40px; | |||
| padding-top: 15px; | |||
| } | |||
| .ant-pro-global-header-logo-mix { | |||
| padding-left: 12px; | |||
| @@ -104,10 +104,15 @@ function CreateAutoML() { | |||
| const exclude_classifier = formData['exclude_classifier']?.join(','); | |||
| const exclude_feature_preprocessor = formData['exclude_feature_preprocessor']?.join(','); | |||
| const exclude_regressor = formData['exclude_regressor']?.join(','); | |||
| const metrics = formData['metrics']?.reduce((acc, cur) => { | |||
| acc[cur.name] = cur.value; | |||
| return acc; | |||
| }, {} as Record<string, number>); | |||
| const formMetrics = formData['metrics']; | |||
| const metrics = | |||
| formMetrics && Array.isArray(formMetrics) && formMetrics.length > 0 | |||
| ? formMetrics.reduce((acc, cur) => { | |||
| acc[cur.name] = cur.value; | |||
| return acc; | |||
| }, {} as Record<string, number>) | |||
| : undefined; | |||
| const target_columns = trimCharacter(formData['target_columns'], ','); | |||
| // 根据后台要求,修改表单数据 | |||
| @@ -174,6 +179,16 @@ function CreateAutoML() { | |||
| shuffle: false, | |||
| ensemble_class: AutoMLEnsembleClass.Default, | |||
| greater_is_better: true, | |||
| ensemble_size: 50, | |||
| ensemble_nbest: 50, | |||
| max_models_on_disc: 50, | |||
| memory_limit: 3072, | |||
| per_run_time_limit: 600, | |||
| time_left_for_this_task: 3600, | |||
| resampling_strategy: 'holdout', | |||
| test_size: 0.25, | |||
| train_size: 0.67, | |||
| seed: 1, | |||
| }} | |||
| > | |||
| <BasicConfig /> | |||
| @@ -4,16 +4,14 @@ | |||
| * @Description: 自主机器学习详情 | |||
| */ | |||
| import KFIcon from '@/components/KFIcon'; | |||
| import PageTitle from '@/components/PageTitle'; | |||
| import { CommonTabKeys } from '@/enums'; | |||
| import { getAutoMLInfoReq } from '@/services/autoML'; | |||
| import themes from '@/styles/theme.less'; | |||
| import { safeInvoke } from '@/utils/functional'; | |||
| import { to } from '@/utils/promise'; | |||
| import { useParams } from '@umijs/max'; | |||
| import { Tabs } from 'antd'; | |||
| import { useEffect, useState } from 'react'; | |||
| import AutoMLBasic from '../components/AutoMLBasic'; | |||
| import AutoMLTable from '../components/AutoMLTable'; | |||
| import { AutoMLData } from '../types'; | |||
| import styles from './index.less'; | |||
| @@ -52,19 +50,10 @@ function AutoMLInfo() { | |||
| return ( | |||
| <div className={styles['auto-ml-info']}> | |||
| <div className={styles['auto-ml-info__tabs']}> | |||
| <Tabs items={tabItems} activeKey={activeTab} onChange={setActiveTab} /> | |||
| </div> | |||
| <PageTitle title="实验详情"></PageTitle> | |||
| <div className={styles['auto-ml-info__content']}> | |||
| {activeTab === CommonTabKeys.Public && <AutoMLBasic info={autoMLInfo} />} | |||
| {activeTab === CommonTabKeys.Private && <AutoMLTable />} | |||
| <AutoMLBasic info={autoMLInfo} /> | |||
| </div> | |||
| {activeTab === CommonTabKeys.Private && ( | |||
| <div className={styles['auto-ml-info__tips']}> | |||
| <KFIcon type="icon-tishi" color={themes['warningColor']} font={17} /> | |||
| <span style={{ marginLeft: '4px' }}>Trial是一次独立的尝试,他会使用某组超参来运行</span> | |||
| </div> | |||
| )} | |||
| </div> | |||
| ); | |||
| } | |||
| @@ -2,16 +2,41 @@ | |||
| height: 100%; | |||
| &__tabs { | |||
| height: 50px; | |||
| padding-left: 25px; | |||
| background-image: url(@/assets/img/page-title-bg.png); | |||
| background-repeat: no-repeat; | |||
| background-position: top center; | |||
| background-size: 100% 100%; | |||
| height: 100%; | |||
| :global { | |||
| .ant-tabs-nav-list { | |||
| width: 100%; | |||
| height: 50px; | |||
| padding-left: 15px; | |||
| background-image: url(@/assets/img/page-title-bg.png); | |||
| background-repeat: no-repeat; | |||
| background-position: top center; | |||
| background-size: 100% 100%; | |||
| } | |||
| .ant-tabs-content-holder { | |||
| height: calc(100% - 50px); | |||
| .ant-tabs-content { | |||
| height: 100%; | |||
| .ant-tabs-tabpane { | |||
| height: 100%; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| &__basic { | |||
| height: calc(100% - 10px); | |||
| margin-top: 10px; | |||
| } | |||
| &__content { | |||
| height: calc(100% - 60px); | |||
| &__log { | |||
| height: calc(100% - 10px); | |||
| margin-top: 10px; | |||
| padding: 20px calc(@content-padding - 8px); | |||
| overflow-y: visible; | |||
| background-color: white; | |||
| border-radius: 10px; | |||
| } | |||
| } | |||
| @@ -2,12 +2,13 @@ import KFIcon from '@/components/KFIcon'; | |||
| import { AutoMLTaskType, ExperimentStatus } from '@/enums'; | |||
| import LogList from '@/pages/Experiment/components/LogList'; | |||
| import { getExperimentInsReq } from '@/services/autoML'; | |||
| import { NodeStatus } from '@/types'; | |||
| import { parseJsonText } from '@/utils'; | |||
| import { safeInvoke } from '@/utils/functional'; | |||
| import { to } from '@/utils/promise'; | |||
| import { useParams } from '@umijs/max'; | |||
| import { Tabs } from 'antd'; | |||
| import { useEffect, useState } from 'react'; | |||
| import { useEffect, useRef, useState } from 'react'; | |||
| import AutoMLBasic from '../components/AutoMLBasic'; | |||
| import ExperimentHistory from '../components/ExperimentHistory'; | |||
| import ExperimentResult from '../components/ExperimentResult'; | |||
| @@ -28,11 +29,15 @@ function AutoMLInstance() { | |||
| const params = useParams(); | |||
| // const autoMLId = safeInvoke(Number)(params.autoMLId); | |||
| const instanceId = safeInvoke(Number)(params.id); | |||
| const evtSourceRef = useRef<EventSource | null>(null); | |||
| useEffect(() => { | |||
| if (instanceId) { | |||
| getExperimentInsInfo(); | |||
| } | |||
| return () => { | |||
| closeSSE(); | |||
| }; | |||
| }, []); | |||
| // 获取实验实例详情 | |||
| @@ -40,7 +45,7 @@ function AutoMLInstance() { | |||
| const [res] = await to(getExperimentInsReq(instanceId)); | |||
| if (res && res.data) { | |||
| const info = res.data as AutoMLInstanceData; | |||
| const { param, node_status } = info; | |||
| const { param, node_status, argo_ins_name, argo_ins_ns, status } = info; | |||
| // 解析配置参数 | |||
| const paramJson = parseJsonText(param); | |||
| if (paramJson) { | |||
| @@ -52,65 +57,142 @@ function AutoMLInstance() { | |||
| Object.keys(nodeStatusJson).forEach((key) => { | |||
| if (key.startsWith('auto-ml')) { | |||
| const value = nodeStatusJson[key]; | |||
| value.nodeId = key; | |||
| info.nodeStatus = value; | |||
| } | |||
| }); | |||
| } | |||
| setInstanceInfo(info); | |||
| // 运行中或者等待中,开启 SSE | |||
| if (status === ExperimentStatus.Pending || status === ExperimentStatus.Running) { | |||
| setupSSE(argo_ins_name, argo_ins_ns); | |||
| } | |||
| } | |||
| }; | |||
| const tabItems = [ | |||
| const setupSSE = (name: string, namespace: string) => { | |||
| let { origin } = location; | |||
| if (process.env.NODE_ENV === 'development') { | |||
| origin = 'http://172.20.32.181:31213'; | |||
| } | |||
| const params = encodeURIComponent(`metadata.namespace=${namespace},metadata.name=${name}`); | |||
| const evtSource = new EventSource( | |||
| `${origin}/api/v1/realtimeStatus?listOptions.fieldSelector=${params}`, | |||
| { withCredentials: false }, | |||
| ); | |||
| evtSource.onmessage = (event) => { | |||
| const data = event?.data; | |||
| if (!data) { | |||
| return; | |||
| } | |||
| const dataJson = parseJsonText(data); | |||
| if (dataJson) { | |||
| const nodes = dataJson?.result?.object?.status?.nodes; | |||
| if (nodes) { | |||
| const statusData = Object.values(nodes).find((node: any) => | |||
| node.displayName.startsWith('auto-ml'), | |||
| ) as NodeStatus; | |||
| if (statusData) { | |||
| setInstanceInfo((prev) => ({ | |||
| ...(prev as AutoMLInstanceData), | |||
| nodeStatus: statusData, | |||
| })); | |||
| // 实验结束,关闭 SSE | |||
| if ( | |||
| statusData.phase !== ExperimentStatus.Pending && | |||
| statusData.phase !== ExperimentStatus.Running | |||
| ) { | |||
| closeSSE(); | |||
| getExperimentInsInfo(); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| }; | |||
| evtSource.onerror = (error) => { | |||
| console.error('SSE error: ', error); | |||
| }; | |||
| evtSourceRef.current = evtSource; | |||
| }; | |||
| const closeSSE = () => { | |||
| if (evtSourceRef.current) { | |||
| evtSourceRef.current.close(); | |||
| evtSourceRef.current = null; | |||
| } | |||
| }; | |||
| const basicTabItems = [ | |||
| { | |||
| key: TabKeys.Params, | |||
| label: '参数信息', | |||
| label: '基本信息', | |||
| icon: <KFIcon type="icon-jibenxinxi" />, | |||
| children: ( | |||
| <AutoMLBasic | |||
| className={styles['auto-ml-instance__basic']} | |||
| info={autoMLInfo} | |||
| runStatus={instanceInfo?.nodeStatus} | |||
| isInstance | |||
| /> | |||
| ), | |||
| }, | |||
| { | |||
| key: TabKeys.Log, | |||
| label: '日志', | |||
| icon: <KFIcon type="icon-Trialliebiao" />, | |||
| icon: <KFIcon type="icon-rizhi" />, | |||
| children: ( | |||
| <div className={styles['auto-ml-instance__log']}> | |||
| {instanceInfo && instanceInfo.nodeStatus && ( | |||
| <LogList | |||
| instanceName={instanceInfo.argo_ins_name} | |||
| instanceNamespace={instanceInfo.argo_ins_ns} | |||
| pipelineNodeId={instanceInfo.nodeStatus.displayName} | |||
| workflowId={instanceInfo.nodeStatus.id} | |||
| instanceNodeStartTime={instanceInfo.nodeStatus.startedAt} | |||
| instanceNodeStatus={instanceInfo.nodeStatus.phase as ExperimentStatus} | |||
| ></LogList> | |||
| )} | |||
| </div> | |||
| ), | |||
| }, | |||
| ]; | |||
| const resultTabItems = [ | |||
| { | |||
| key: TabKeys.Result, | |||
| label: '实验结果', | |||
| icon: <KFIcon type="icon-Trialliebiao" />, | |||
| icon: <KFIcon type="icon-shiyanjieguo1" />, | |||
| children: ( | |||
| <ExperimentResult fileUrl={instanceInfo?.result_path} imageUrl={instanceInfo?.img_path} /> | |||
| ), | |||
| }, | |||
| { | |||
| key: TabKeys.History, | |||
| label: '运行历史', | |||
| label: 'Trial列表', | |||
| icon: <KFIcon type="icon-Trialliebiao" />, | |||
| children: ( | |||
| <ExperimentHistory | |||
| fileUrl={instanceInfo?.run_history_path} | |||
| isClassification={autoMLInfo?.task_type === AutoMLTaskType.Classification} | |||
| /> | |||
| ), | |||
| }, | |||
| ]; | |||
| const tabItems = | |||
| instanceInfo?.status === ExperimentStatus.Succeeded | |||
| ? [...basicTabItems, ...resultTabItems] | |||
| : basicTabItems; | |||
| return ( | |||
| <div className={styles['auto-ml-instance']}> | |||
| <div className={styles['auto-ml-instance__tabs']}> | |||
| <Tabs items={tabItems} activeKey={activeTab} onChange={setActiveTab} /> | |||
| </div> | |||
| <div className={styles['auto-ml-instance__content']}> | |||
| {activeTab === TabKeys.Params && <AutoMLBasic info={autoMLInfo} hasBasicInfo={false} />} | |||
| {activeTab === TabKeys.Log && instanceInfo && instanceInfo.nodeStatus && ( | |||
| <LogList | |||
| instanceName={instanceInfo.argo_ins_name} | |||
| instanceNamespace={instanceInfo.argo_ins_ns} | |||
| pipelineNodeId={instanceInfo.nodeStatus.nodeId} | |||
| workflowId={instanceInfo.nodeStatus.id} | |||
| instanceNodeStartTime={instanceInfo.nodeStatus.startedAt} | |||
| instanceNodeStatus={instanceInfo.nodeStatus.phase as ExperimentStatus} | |||
| ></LogList> | |||
| )} | |||
| {activeTab === TabKeys.Result && ( | |||
| <ExperimentResult fileUrl={instanceInfo?.result_path} imageUrl={instanceInfo?.img_path} /> | |||
| )} | |||
| {activeTab === TabKeys.History && ( | |||
| <ExperimentHistory | |||
| fileUrl={instanceInfo?.run_history_path} | |||
| isClassification={autoMLInfo?.task_type === AutoMLTaskType.Classification} | |||
| /> | |||
| )} | |||
| </div> | |||
| <Tabs | |||
| className={styles['auto-ml-instance__tabs']} | |||
| items={tabItems} | |||
| activeKey={activeTab} | |||
| onChange={setActiveTab} | |||
| /> | |||
| </div> | |||
| ); | |||
| } | |||
| @@ -1,6 +1,11 @@ | |||
| import { AutoMLTaskType, autoMLEnsembleClassOptions, autoMLTaskTypeOptions } from '@/enums'; | |||
| import { AutoMLData } from '@/pages/AutoML/types'; | |||
| import { experimentStatusInfo } from '@/pages/Experiment/status'; | |||
| import { type NodeStatus } from '@/types'; | |||
| import { parseJsonText } from '@/utils'; | |||
| import { elapsedTime } from '@/utils/date'; | |||
| import { Flex } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import { useMemo } from 'react'; | |||
| import ConfigInfo, { | |||
| formatBoolean, | |||
| @@ -38,10 +43,12 @@ const formatMetricsWeight = (value: string) => { | |||
| type AutoMLBasicProps = { | |||
| info?: AutoMLData; | |||
| hasBasicInfo?: boolean; | |||
| className?: string; | |||
| isInstance?: boolean; | |||
| runStatus?: NodeStatus; | |||
| }; | |||
| function AutoMLBasic({ info, hasBasicInfo = true }: AutoMLBasicProps) { | |||
| function AutoMLBasic({ info, className, runStatus, isInstance = false }: AutoMLBasicProps) { | |||
| const basicDatas: BasicInfoData[] = useMemo(() => { | |||
| if (!info) { | |||
| return []; | |||
| @@ -142,7 +149,7 @@ function AutoMLBasic({ info, hasBasicInfo = true }: AutoMLBasicProps) { | |||
| ellipsis: true, | |||
| }, | |||
| { | |||
| label: '时间限制(秒)', | |||
| label: '单次时间限制(秒)', | |||
| value: info.per_run_time_limit, | |||
| ellipsis: true, | |||
| }, | |||
| @@ -227,9 +234,59 @@ function AutoMLBasic({ info, hasBasicInfo = true }: AutoMLBasicProps) { | |||
| ]; | |||
| }, [info]); | |||
| const instanceDatas = useMemo(() => { | |||
| if (!runStatus) { | |||
| return []; | |||
| } | |||
| return [ | |||
| { | |||
| label: '启动时间', | |||
| value: formatDate(runStatus.startedAt), | |||
| ellipsis: true, | |||
| }, | |||
| { | |||
| label: '执行时长', | |||
| value: elapsedTime(runStatus.startedAt, runStatus.finishedAt), | |||
| ellipsis: true, | |||
| }, | |||
| { | |||
| label: '状态', | |||
| value: ( | |||
| <Flex align="center"> | |||
| <img | |||
| style={{ width: '17px', marginRight: '7px' }} | |||
| src={experimentStatusInfo[runStatus.phase]?.icon} | |||
| draggable={false} | |||
| alt="" | |||
| /> | |||
| <div | |||
| style={{ | |||
| color: experimentStatusInfo[runStatus?.phase]?.color, | |||
| fontSize: '15px', | |||
| lineHeight: 1.6, | |||
| }} | |||
| > | |||
| {experimentStatusInfo[runStatus?.phase]?.label} | |||
| </div> | |||
| </Flex> | |||
| ), | |||
| ellipsis: true, | |||
| }, | |||
| ]; | |||
| }, [runStatus]); | |||
| return ( | |||
| <div className={styles['auto-ml-basic']}> | |||
| {hasBasicInfo && ( | |||
| <div className={classNames(styles['auto-ml-basic'], className)}> | |||
| {isInstance && runStatus ? ( | |||
| <ConfigInfo | |||
| title="运行信息" | |||
| data={instanceDatas} | |||
| labelWidth={70} | |||
| threeColumn | |||
| style={{ marginBottom: '20px' }} | |||
| /> | |||
| ) : ( | |||
| <ConfigInfo | |||
| title="基本信息" | |||
| data={basicDatas} | |||
| @@ -1,13 +1,13 @@ | |||
| .config-info { | |||
| flex: 1; | |||
| min-width: 0; | |||
| border: 1px solid @border-color-base; | |||
| border-radius: 4px; | |||
| &__content { | |||
| padding: 20px; | |||
| padding: 20px @content-padding; | |||
| background-color: white; | |||
| border: 1px solid @border-color-base; | |||
| border-radius: 0 0 4px 4px; | |||
| } | |||
| :global { | |||
| @@ -8,6 +8,7 @@ | |||
| rgba(22, 100, 255, 0.04) 100% | |||
| ); | |||
| border: 1px solid #e8effb; | |||
| border-radius: 4px 4px 0 0; | |||
| &__img { | |||
| width: 16px; | |||
| @@ -315,7 +315,7 @@ function ExecuteConfig() { | |||
| <Form.Item | |||
| label="内存限制(MB)" | |||
| name="memory_limit" | |||
| tooltip="机器学习算法的内存限制(MB)。如果auto-sklearn试图分配超过memory_limit MB,它将停止拟合机器学习算法。默认3072" | |||
| tooltip="机器学习算法的内存限制(MB)。如果自动机器学习试图分配超过memory_limit MB,它将停止拟合机器学习算法。默认3072" | |||
| > | |||
| <InputNumber placeholder="请输入内存限制" min={0} precision={0} /> | |||
| </Form.Item> | |||
| @@ -325,7 +325,7 @@ function ExecuteConfig() { | |||
| <Row gutter={8}> | |||
| <Col span={10}> | |||
| <Form.Item | |||
| label="时间限制(秒)" | |||
| label="单次时间限制(秒)" | |||
| name="per_run_time_limit" | |||
| tooltip="单次调用机器学习模型的时间限制(以秒为单位)。如果机器学习算法运行超过时间限制,将终止模型拟合,默认600" | |||
| > | |||
| @@ -339,13 +339,56 @@ function ExecuteConfig() { | |||
| <Form.Item | |||
| label="搜索时间限制(秒)" | |||
| name="time_left_for_this_task" | |||
| tooltip="搜索合适模型的时间限制(以秒为单位)。通过增加这个值,auto-sklearn有更高的机会找到更好的模型。默认3600。" | |||
| tooltip="搜索合适模型的时间限制(以秒为单位)。通过增加这个值,自动机器学习有更高的机会找到更好的模型。默认3600。" | |||
| > | |||
| <InputNumber placeholder="请输入搜索时间限制" min={0} precision={0} /> | |||
| </Form.Item> | |||
| </Col> | |||
| </Row> | |||
| <Row gutter={8}> | |||
| <Col span={10}> | |||
| <Form.Item | |||
| label="测试集比率" | |||
| name="test_size" | |||
| tooltip="将数据划分为训练数据和测试数据,测试数据集所占比例,0到1之间" | |||
| > | |||
| <InputNumber placeholder="请输入测试集比率" min={0} max={1} /> | |||
| </Form.Item> | |||
| </Col> | |||
| </Row> | |||
| <Row gutter={8}> | |||
| <Col span={10}> | |||
| <Form.Item label="计算指标" name="scoring_functions" tooltip="需要计算并打印的指标"> | |||
| <Select | |||
| allowClear | |||
| placeholder="请选择计算指标" | |||
| options={ | |||
| task_type === AutoMLTaskType.Classification | |||
| ? classificationMetrics | |||
| : regressionMetrics | |||
| } | |||
| showSearch | |||
| /> | |||
| </Form.Item> | |||
| </Col> | |||
| </Row> | |||
| <Row gutter={8}> | |||
| <Col span={10}> | |||
| <Form.Item label="随机种子" name="seed" tooltip="随机种子,将决定输出文件名"> | |||
| <InputNumber placeholder="请输入随机种子" min={0} precision={0} /> | |||
| </Form.Item> | |||
| </Col> | |||
| </Row> | |||
| <SubAreaTitle | |||
| title="重采样策略" | |||
| image={require('@/assets/img/resample-icon.png')} | |||
| style={{ marginTop: '20px', marginBottom: '24px' }} | |||
| ></SubAreaTitle> | |||
| <Row gutter={8}> | |||
| <Col span={10}> | |||
| <Form.Item | |||
| @@ -399,50 +442,13 @@ function ExecuteConfig() { | |||
| <Form.Item | |||
| label="训练集比率" | |||
| name="train_size" | |||
| tooltip="将数据划分为训练数据和测试数据,训练数据集所占比例,0到1之间" | |||
| tooltip="重采样划分训练集和验证集,训练集的比率,0到1之间" | |||
| > | |||
| <InputNumber placeholder="请输入训练集比率" min={0} max={1} /> | |||
| </Form.Item> | |||
| </Col> | |||
| </Row> | |||
| <Row gutter={8}> | |||
| <Col span={10}> | |||
| <Form.Item | |||
| label="测试集比率" | |||
| name="test_size" | |||
| tooltip="将数据划分为训练数据和测试数据,测试数据集所占比例,0到1之间" | |||
| > | |||
| <InputNumber placeholder="请输入测试集比率" min={0} max={1} /> | |||
| </Form.Item> | |||
| </Col> | |||
| </Row> | |||
| <Row gutter={8}> | |||
| <Col span={10}> | |||
| <Form.Item label="计算指标" name="scoring_functions" tooltip="需要计算并打印的指标"> | |||
| <Select | |||
| allowClear | |||
| placeholder="请选择计算指标" | |||
| options={ | |||
| task_type === AutoMLTaskType.Classification | |||
| ? classificationMetrics | |||
| : regressionMetrics | |||
| } | |||
| showSearch | |||
| /> | |||
| </Form.Item> | |||
| </Col> | |||
| </Row> | |||
| <Row gutter={8}> | |||
| <Col span={10}> | |||
| <Form.Item label="随机种子" name="seed" tooltip="随机种子,将决定输出文件名"> | |||
| <InputNumber placeholder="请输入随机种子" min={0} precision={0} /> | |||
| </Form.Item> | |||
| </Col> | |||
| </Row> | |||
| {/* <Row gutter={8}> | |||
| <Col span={10}> | |||
| <Form.Item | |||
| @@ -7,7 +7,7 @@ | |||
| } | |||
| .add-weight { | |||
| margin-bottom: 0; | |||
| margin-bottom: 0 !important; | |||
| // 增加样式权重 | |||
| & &__button { | |||
| @@ -18,137 +18,3 @@ | |||
| } | |||
| } | |||
| } | |||
| // .command { | |||
| // width: 83.33%; | |||
| // margin-bottom: 20px; | |||
| // border: 1px solid rgba(234, 234, 234, 0.8); | |||
| // border-radius: 4px; | |||
| // &__header { | |||
| // height: 50px; | |||
| // padding-left: 8px; | |||
| // color: @text-color; | |||
| // font-size: @font-size; | |||
| // background: #f8f8f9; | |||
| // border-radius: 4px 4px 0px 0px; | |||
| // &__name { | |||
| // flex: none; | |||
| // width: 100px; | |||
| // } | |||
| // &__command { | |||
| // flex: 1; | |||
| // margin-right: 15px; | |||
| // } | |||
| // &__operation { | |||
| // flex: none; | |||
| // width: 100px; | |||
| // } | |||
| // } | |||
| // &__body { | |||
| // padding: 8px; | |||
| // border-bottom: 1px solid rgba(234, 234, 234, 0.8); | |||
| // &:last-child { | |||
| // border-bottom: none; | |||
| // } | |||
| // &__name { | |||
| // flex: none; | |||
| // width: 100px; | |||
| // } | |||
| // &__command { | |||
| // flex: 1; | |||
| // margin-right: 15px; | |||
| // margin-bottom: 0 !important; | |||
| // } | |||
| // &__operation { | |||
| // flex: none; | |||
| // width: 100px; | |||
| // } | |||
| // } | |||
| // &__add { | |||
| // display: flex; | |||
| // align-items: center; | |||
| // justify-content: center; | |||
| // padding: 15px 0; | |||
| // } | |||
| // } | |||
| // .hyper-parameter { | |||
| // width: 83.33%; | |||
| // margin-bottom: 20px; | |||
| // border: 1px solid rgba(234, 234, 234, 0.8); | |||
| // border-radius: 4px; | |||
| // &__header { | |||
| // height: 50px; | |||
| // padding-left: 8px; | |||
| // color: @text-color; | |||
| // font-size: @font-size; | |||
| // background: #f8f8f9; | |||
| // border-radius: 4px 4px 0px 0px; | |||
| // &__name, | |||
| // &__type, | |||
| // &__space { | |||
| // flex: 1; | |||
| // margin-right: 15px; | |||
| // } | |||
| // &__operation { | |||
| // flex: none; | |||
| // width: 100px; | |||
| // } | |||
| // } | |||
| // &__body { | |||
| // padding: 8px; | |||
| // border-bottom: 1px solid rgba(234, 234, 234, 0.8); | |||
| // &:last-child { | |||
| // border-bottom: none; | |||
| // } | |||
| // &__name, | |||
| // &__type, | |||
| // &__space { | |||
| // flex: 1; | |||
| // margin-right: 15px; | |||
| // margin-bottom: 0 !important; | |||
| // } | |||
| // &__operation { | |||
| // flex: none; | |||
| // width: 100px; | |||
| // } | |||
| // } | |||
| // &__add { | |||
| // display: flex; | |||
| // align-items: center; | |||
| // justify-content: center; | |||
| // padding: 15px 0; | |||
| // } | |||
| // } | |||
| // .trial-metrics { | |||
| // width: calc(41.67% - 6px); | |||
| // margin-bottom: 20px; | |||
| // padding: 0 20px; | |||
| // border: 1px dashed #e0e0e0; | |||
| // border-radius: 8px; | |||
| // } | |||
| // .upload-tip { | |||
| // margin-top: 5px; | |||
| // color: @text-color-secondary; | |||
| // font-size: 14px; | |||
| // } | |||
| // .upload-button { | |||
| // height: 46px; | |||
| // font-size: 15px; | |||
| // } | |||
| @@ -1,9 +1,9 @@ | |||
| .experiment-history { | |||
| height: 100%; | |||
| height: calc(100% - 10px); | |||
| margin-top: 10px; | |||
| &__content { | |||
| height: 100%; | |||
| margin-top: 10px; | |||
| padding: 20px @content-padding 0; | |||
| padding: 20px @content-padding; | |||
| background-color: white; | |||
| border-radius: 10px; | |||
| @@ -1,18 +1,39 @@ | |||
| .experiment-result { | |||
| height: 100%; | |||
| height: calc(100% - 10px); | |||
| margin-top: 10px; | |||
| padding: 20px @content-padding 0; | |||
| padding: 20px @content-padding; | |||
| overflow-y: auto; | |||
| background-color: white; | |||
| border-radius: 10px; | |||
| &__text { | |||
| margin-bottom: 20px; | |||
| width: 100%; | |||
| height: 460px; | |||
| margin-bottom: 16px; | |||
| padding: 20px @content-padding; | |||
| overflow: auto; | |||
| white-space: pre-wrap; | |||
| border: 1px solid @border-color-base; | |||
| border-radius: 0 0 4px 4px; | |||
| } | |||
| &__image { | |||
| display: block; | |||
| height: 200px; | |||
| &__image-container { | |||
| display: flex; | |||
| align-items: flex-start; | |||
| width: 100%; | |||
| padding: 20px @content-padding; | |||
| overflow-x: auto; | |||
| border: 1px solid @border-color-base; | |||
| border-radius: 0 0 4px 4px; | |||
| &__image { | |||
| height: 248px; | |||
| margin-right: 20px; | |||
| border: 1px solid rgba(96, 107, 122, 0.3); | |||
| &:last-child { | |||
| margin-right: 0; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -1,6 +1,7 @@ | |||
| import { getFileReq } from '@/services/file'; | |||
| import { to } from '@/utils/promise'; | |||
| import { useEffect, useMemo, useState } from 'react'; | |||
| import ConfigTitle from '../ConfigTitle'; | |||
| import styles from './index.less'; | |||
| type ExperimentResultProps = { | |||
| @@ -34,16 +35,20 @@ function ExperimentResult({ fileUrl, imageUrl }: ExperimentResultProps) { | |||
| return ( | |||
| <div className={styles['experiment-result']}> | |||
| <ConfigTitle title="实验结果"></ConfigTitle> | |||
| <div className={styles['experiment-result__text']}>{result}</div> | |||
| {images.map((item, index) => ( | |||
| <img | |||
| key={index} | |||
| className={styles['experiment-result__image']} | |||
| src={item} | |||
| draggable={false} | |||
| alt="" | |||
| /> | |||
| ))} | |||
| <ConfigTitle title="可视化结果"></ConfigTitle> | |||
| <div className={styles['experiment-result__image-container']}> | |||
| {images.map((item, index) => ( | |||
| <img | |||
| key={index} | |||
| className={styles['experiment-result__image-container__image']} | |||
| src={item} | |||
| draggable={false} | |||
| alt="" | |||
| /> | |||
| ))} | |||
| </div> | |||
| </div> | |||
| ); | |||
| } | |||
| @@ -1,4 +1,5 @@ | |||
| import { type ParameterInputObject } from '@/components/ResourceSelect'; | |||
| import { type NodeStatus } from '@/types'; | |||
| // 操作类型 | |||
| export enum OperationType { | |||
| @@ -80,5 +81,5 @@ export type AutoMLInstanceData = { | |||
| create_time: string; | |||
| update_time: string; | |||
| finish_time: string; | |||
| nodeStatus?: { [key: string]: string }; | |||
| nodeStatus?: NodeStatus; | |||
| }; | |||
| @@ -39,4 +39,10 @@ | |||
| margin-right: 6px; | |||
| border-radius: 50%; | |||
| } | |||
| &__log { | |||
| height: 100%; | |||
| padding: 8px; | |||
| background: white; | |||
| } | |||
| } | |||
| @@ -48,14 +48,16 @@ const ExperimentDrawer = ({ | |||
| key: '1', | |||
| label: '日志详情', | |||
| children: ( | |||
| <LogList | |||
| instanceName={instanceName} | |||
| instanceNamespace={instanceNamespace} | |||
| pipelineNodeId={instanceNodeData.id} | |||
| workflowId={workflowId} | |||
| instanceNodeStartTime={instanceNodeStartTime} | |||
| instanceNodeStatus={instanceNodeStatus} | |||
| ></LogList> | |||
| <div className={styles['experiment-drawer__log']}> | |||
| <LogList | |||
| instanceName={instanceName} | |||
| instanceNamespace={instanceNamespace} | |||
| pipelineNodeId={instanceNodeData.id} | |||
| workflowId={workflowId} | |||
| instanceNodeStartTime={instanceNodeStartTime} | |||
| instanceNodeStatus={instanceNodeStatus} | |||
| ></LogList> | |||
| </div> | |||
| ), | |||
| icon: <ProfileOutlined />, | |||
| }, | |||
| @@ -90,7 +90,7 @@ function LogGroup({ | |||
| start_time: startTime, | |||
| }; | |||
| const res = await getExperimentPodsLog(params); | |||
| const { log_detail } = res.data; | |||
| const { log_detail } = res.data || {}; | |||
| if (log_detail) { | |||
| setLogList((oldList) => oldList.concat(log_detail)); | |||
| @@ -1,7 +1,7 @@ | |||
| .log-list { | |||
| height: 100%; | |||
| padding: 8px; | |||
| overflow-y: auto; | |||
| background: #19253b; | |||
| &__empty { | |||
| padding: 15px; | |||
| @@ -12,4 +12,8 @@ | |||
| word-break: break-all; | |||
| background: #19253b; | |||
| } | |||
| &::-webkit-scrollbar-thumb { | |||
| background: rgba(255, 255, 255, 0.5); | |||
| } | |||
| } | |||
| @@ -114,3 +114,13 @@ export type ComputingResource = { | |||
| standard: string; | |||
| create_by: string; | |||
| }; | |||
| // 实验运行节点状态 | |||
| export type NodeStatus = { | |||
| id: string; // workflow Id | |||
| displayName: string; | |||
| name: string; | |||
| phase: ExperimentStatus; | |||
| startedAt: string; | |||
| finishedAt: string; | |||
| }; | |||