| @@ -169,6 +169,11 @@ export default [ | |||||
| path: 'edit/:id', | path: 'edit/:id', | ||||
| component: './AutoML/Create/index', | component: './AutoML/Create/index', | ||||
| }, | }, | ||||
| { | |||||
| name: '实验实例', | |||||
| path: 'instance/:autoMLId/:id', | |||||
| component: './AutoML/Instance/index', | |||||
| }, | |||||
| ], | ], | ||||
| }, | }, | ||||
| ], | ], | ||||
| @@ -5,26 +5,23 @@ | |||||
| */ | */ | ||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import { CommonTabKeys } from '@/enums'; | import { CommonTabKeys } from '@/enums'; | ||||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||||
| import { getAutoMLInfoReq } from '@/services/autoML'; | |||||
| import themes from '@/styles/theme.less'; | import themes from '@/styles/theme.less'; | ||||
| import { useNavigate } from '@umijs/max'; | |||||
| import { safeInvoke } from '@/utils/functional'; | |||||
| import { to } from '@/utils/promise'; | |||||
| import { useParams } from '@umijs/max'; | |||||
| import { Tabs } from 'antd'; | import { Tabs } from 'antd'; | ||||
| import { useState } from 'react'; | |||||
| import { useEffect, useState } from 'react'; | |||||
| import AutoMLBasic from '../components/AutoMLBasic'; | import AutoMLBasic from '../components/AutoMLBasic'; | ||||
| import AutoMLTable from '../components/AutoMLTable'; | import AutoMLTable from '../components/AutoMLTable'; | ||||
| import { AutoMLData } from '../types'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| export type MirrorData = { | |||||
| id: number; | |||||
| name: string; | |||||
| description: string; | |||||
| create_time: string; | |||||
| }; | |||||
| function AutoMLInfo() { | function AutoMLInfo() { | ||||
| const navigate = useNavigate(); | |||||
| const [cacheState, setCacheState] = useCacheState(); | |||||
| const [activeTab, setActiveTab] = useState<string>(cacheState?.activeTab ?? CommonTabKeys.Public); | |||||
| const [activeTab, setActiveTab] = useState<string>(CommonTabKeys.Public); | |||||
| const params = useParams(); | |||||
| const autoMLId = safeInvoke(Number)(params.id); | |||||
| const [autoMLInfo, setAutoMLInfo] = useState<AutoMLData | undefined>(undefined); | |||||
| const tabItems = [ | const tabItems = [ | ||||
| { | { | ||||
| @@ -39,13 +36,27 @@ function AutoMLInfo() { | |||||
| }, | }, | ||||
| ]; | ]; | ||||
| useEffect(() => { | |||||
| if (autoMLId) { | |||||
| getAutoMLInfo(); | |||||
| } | |||||
| }, []); | |||||
| // 获取自动机器学习详情 | |||||
| const getAutoMLInfo = async () => { | |||||
| const [res] = await to(getAutoMLInfoReq({ id: autoMLId })); | |||||
| if (res && res.data) { | |||||
| setAutoMLInfo(res.data); | |||||
| } | |||||
| }; | |||||
| return ( | return ( | ||||
| <div className={styles['auto-ml-info']}> | <div className={styles['auto-ml-info']}> | ||||
| <div className={styles['auto-ml-info__tabs']}> | <div className={styles['auto-ml-info__tabs']}> | ||||
| <Tabs items={tabItems} activeKey={activeTab} onChange={setActiveTab} /> | <Tabs items={tabItems} activeKey={activeTab} onChange={setActiveTab} /> | ||||
| </div> | </div> | ||||
| <div className={styles['auto-ml-info__content']}> | <div className={styles['auto-ml-info__content']}> | ||||
| {activeTab === CommonTabKeys.Public && <AutoMLBasic />} | |||||
| {activeTab === CommonTabKeys.Public && <AutoMLBasic info={autoMLInfo} />} | |||||
| {activeTab === CommonTabKeys.Private && <AutoMLTable />} | {activeTab === CommonTabKeys.Private && <AutoMLTable />} | ||||
| </div> | </div> | ||||
| {activeTab === CommonTabKeys.Private && ( | {activeTab === CommonTabKeys.Private && ( | ||||
| @@ -0,0 +1,17 @@ | |||||
| .auto-ml-instance { | |||||
| 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%; | |||||
| } | |||||
| &__content { | |||||
| height: calc(100% - 60px); | |||||
| margin-top: 10px; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,118 @@ | |||||
| import KFIcon from '@/components/KFIcon'; | |||||
| import { AutoMLTaskType, ExperimentStatus } from '@/enums'; | |||||
| import LogList from '@/pages/Experiment/components/LogList'; | |||||
| import { getExperimentInsReq } from '@/services/autoML'; | |||||
| 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 AutoMLBasic from '../components/AutoMLBasic'; | |||||
| import ExperimentHistory from '../components/ExperimentHistory'; | |||||
| import ExperimentResult from '../components/ExperimentResult'; | |||||
| import { AutoMLData, AutoMLInstanceData } from '../types'; | |||||
| import styles from './index.less'; | |||||
| enum TabKeys { | |||||
| Params = 'params', | |||||
| Log = 'log', | |||||
| Result = 'result', | |||||
| History = 'history', | |||||
| } | |||||
| function AutoMLInstance() { | |||||
| const [activeTab, setActiveTab] = useState<string>(TabKeys.Params); | |||||
| const [autoMLInfo, setAutoMLInfo] = useState<AutoMLData | undefined>(undefined); | |||||
| const [instanceInfo, setInstanceInfo] = useState<AutoMLInstanceData | undefined>(undefined); | |||||
| const params = useParams(); | |||||
| // const autoMLId = safeInvoke(Number)(params.autoMLId); | |||||
| const instanceId = safeInvoke(Number)(params.id); | |||||
| useEffect(() => { | |||||
| if (instanceId) { | |||||
| getExperimentInsInfo(); | |||||
| } | |||||
| }, []); | |||||
| // 获取实验实例详情 | |||||
| const getExperimentInsInfo = async () => { | |||||
| const [res] = await to(getExperimentInsReq(instanceId)); | |||||
| if (res && res.data) { | |||||
| const info = res.data as AutoMLInstanceData; | |||||
| const { param, node_status } = info; | |||||
| // 解析配置参数 | |||||
| const paramJson = parseJsonText(param); | |||||
| if (paramJson) { | |||||
| setAutoMLInfo(paramJson); | |||||
| } | |||||
| // 进行节点状态 | |||||
| const nodeStatusJson = parseJsonText(node_status); | |||||
| if (nodeStatusJson) { | |||||
| Object.keys(nodeStatusJson).forEach((key) => { | |||||
| if (key.startsWith('auto-ml')) { | |||||
| const value = nodeStatusJson[key]; | |||||
| value.nodeId = key; | |||||
| info.nodeStatus = value; | |||||
| } | |||||
| }); | |||||
| } | |||||
| setInstanceInfo(info); | |||||
| } | |||||
| }; | |||||
| const tabItems = [ | |||||
| { | |||||
| key: TabKeys.Params, | |||||
| label: '参数信息', | |||||
| icon: <KFIcon type="icon-jibenxinxi" />, | |||||
| }, | |||||
| { | |||||
| key: TabKeys.Log, | |||||
| label: '日志', | |||||
| icon: <KFIcon type="icon-Trialliebiao" />, | |||||
| }, | |||||
| { | |||||
| key: TabKeys.Result, | |||||
| label: '实验结果', | |||||
| icon: <KFIcon type="icon-Trialliebiao" />, | |||||
| }, | |||||
| { | |||||
| key: TabKeys.History, | |||||
| label: '运行历史', | |||||
| icon: <KFIcon type="icon-Trialliebiao" />, | |||||
| }, | |||||
| ]; | |||||
| 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> | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| export default AutoMLInstance; | |||||
| @@ -143,8 +143,10 @@ function AutoMLList() { | |||||
| const startAutoML = async (record: AutoMLData) => { | const startAutoML = async (record: AutoMLData) => { | ||||
| const [res] = await to(runAutoMLReq(record.id)); | const [res] = await to(runAutoMLReq(record.id)); | ||||
| if (res) { | if (res) { | ||||
| message.success('操作成功'); | |||||
| getAutoMLList(); | |||||
| message.success('运行成功'); | |||||
| setExpandedRowKeys([record.id]); | |||||
| refreshExperimentList(); | |||||
| refreshExperimentIns(record.id); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -183,8 +185,8 @@ function AutoMLList() { | |||||
| }; | }; | ||||
| // 跳转到实验实例详情 | // 跳转到实验实例详情 | ||||
| const gotoInstanceInfo = (item, record) => { | |||||
| navigate({ pathname: `/pipeline/experiment/instance/${record.workflow_id}/${item.id}` }); | |||||
| const gotoInstanceInfo = (autoML: AutoMLData, record: ExperimentInstanceData) => { | |||||
| navigate({ pathname: `/pipeline/automl/instance/${autoML.id}/${record.id}` }); | |||||
| }; | }; | ||||
| // 刷新实验实例列表 | // 刷新实验实例列表 | ||||
| @@ -386,7 +388,7 @@ function AutoMLList() { | |||||
| <ExperimentInstance | <ExperimentInstance | ||||
| experimentInsList={experimentInsList} | experimentInsList={experimentInsList} | ||||
| experimentInsTotal={experimentInsTotal} | experimentInsTotal={experimentInsTotal} | ||||
| onClickInstance={(item) => gotoInstanceInfo(item, record)} | |||||
| onClickInstance={(item) => gotoInstanceInfo(record, item)} | |||||
| onRemove={() => { | onRemove={() => { | ||||
| refreshExperimentIns(record.id); | refreshExperimentIns(record.id); | ||||
| refreshExperimentList(); | refreshExperimentList(); | ||||
| @@ -1,19 +1,13 @@ | |||||
| import { AutoMLTaskType, autoMLEnsembleClassOptions, autoMLTaskTypeOptions } from '@/enums'; | |||||
| import { AutoMLData } from '@/pages/AutoML/types'; | import { AutoMLData } from '@/pages/AutoML/types'; | ||||
| import { getAutoMLInfoReq } from '@/services/autoML'; | |||||
| import { safeInvoke } from '@/utils/functional'; | |||||
| import { to } from '@/utils/promise'; | |||||
| import { useParams } from '@umijs/max'; | |||||
| import { Flex } from 'antd'; | |||||
| import { useEffect, useState } from 'react'; | |||||
| import { parseJsonText } from '@/utils'; | |||||
| import { useMemo } from 'react'; | |||||
| import ConfigInfo, { | import ConfigInfo, { | ||||
| formatBoolean, | formatBoolean, | ||||
| formatDate, | formatDate, | ||||
| formatEnum, | formatEnum, | ||||
| type BasicInfoData, | type BasicInfoData, | ||||
| } from '../ConfigInfo'; | } from '../ConfigInfo'; | ||||
| // import CopyingText from '../CopyingText'; | |||||
| import { AutoMLTaskType, autoMLEnsembleClassOptions, autoMLTaskTypeOptions } from '@/enums'; | |||||
| import { parseJsonText } from '@/utils'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| // 格式化数据集 | // 格式化数据集 | ||||
| @@ -42,263 +36,216 @@ const formatMetricsWeight = (value: string) => { | |||||
| .join('\n'); | .join('\n'); | ||||
| }; | }; | ||||
| function AutoMLBasic() { | |||||
| const params = useParams(); | |||||
| const id = safeInvoke(Number)(params.id); | |||||
| const [basicDatas, setBasicDatas] = useState<BasicInfoData[]>([]); | |||||
| const [configDatas, setConfigDatas] = useState<BasicInfoData[]>([]); | |||||
| type AutoMLBasicProps = { | |||||
| info?: AutoMLData; | |||||
| hasBasicInfo?: boolean; | |||||
| }; | |||||
| useEffect(() => { | |||||
| if (id) { | |||||
| getAutoMLInfo(id); | |||||
| function AutoMLBasic({ info, hasBasicInfo = true }: AutoMLBasicProps) { | |||||
| const basicDatas: BasicInfoData[] = useMemo(() => { | |||||
| if (!info) { | |||||
| return []; | |||||
| } | } | ||||
| }, []); | |||||
| // const basicDatas: BasicInfoData[] = [ | |||||
| // { | |||||
| // label: '项目名称', | |||||
| // value: '测试项目名称', | |||||
| // ellipsis: true, | |||||
| // }, | |||||
| // { | |||||
| // label: '项目名称', | |||||
| // value: '测试项目名称', | |||||
| // ellipsis: true, | |||||
| // }, | |||||
| // { | |||||
| // label: '项目名称', | |||||
| // value: '测试项目名称', | |||||
| // ellipsis: true, | |||||
| // }, | |||||
| // { | |||||
| // label: '项目名称', | |||||
| // value: '测试项目名称', | |||||
| // ellipsis: true, | |||||
| // }, | |||||
| // { | |||||
| // label: '项目名称', | |||||
| // value: '测试项目名称', | |||||
| // ellipsis: true, | |||||
| // }, | |||||
| // { | |||||
| // label: '项目名称', | |||||
| // value: '测试项目名称', | |||||
| // ellipsis: true, | |||||
| // }, | |||||
| // { | |||||
| // label: '项目名称', | |||||
| // value: '测试项目名称', | |||||
| // ellipsis: true, | |||||
| // }, | |||||
| // { | |||||
| // label: '项目名称', | |||||
| // value: <CopyingText text="测试项目名称测试项目名称测试项目名称"></CopyingText>, | |||||
| // ellipsis: false, | |||||
| // }, | |||||
| // ]; | |||||
| return [ | |||||
| { | |||||
| label: '实验名称', | |||||
| value: info.ml_name, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '实验描述', | |||||
| value: info.ml_description, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '创建人', | |||||
| value: info.create_by, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '创建时间', | |||||
| value: info.create_time, | |||||
| ellipsis: true, | |||||
| format: formatDate, | |||||
| }, | |||||
| { | |||||
| label: '更新时间', | |||||
| value: info.update_time, | |||||
| ellipsis: true, | |||||
| format: formatDate, | |||||
| }, | |||||
| ]; | |||||
| }, [info]); | |||||
| // 获取服务详情 | |||||
| const getAutoMLInfo = async (id: number) => { | |||||
| const [res] = await to(getAutoMLInfoReq({ id })); | |||||
| if (res && res.data) { | |||||
| const info: AutoMLData = res.data; | |||||
| const basicDatas: BasicInfoData[] = [ | |||||
| { | |||||
| label: '实验名称', | |||||
| value: info.ml_name, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '实验描述', | |||||
| value: info.ml_description, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '创建人', | |||||
| value: info.create_by, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '创建时间', | |||||
| value: info.create_time, | |||||
| ellipsis: true, | |||||
| format: formatDate, | |||||
| }, | |||||
| { | |||||
| label: '更新时间', | |||||
| value: info.update_time, | |||||
| ellipsis: true, | |||||
| format: formatDate, | |||||
| }, | |||||
| { | |||||
| label: '状态', | |||||
| value: info.run_state, | |||||
| ellipsis: true, | |||||
| }, | |||||
| ]; | |||||
| setBasicDatas(basicDatas); | |||||
| const configDatas: BasicInfoData[] = useMemo(() => { | |||||
| if (!info) { | |||||
| return []; | |||||
| } | |||||
| return [ | |||||
| { | |||||
| label: '任务类型', | |||||
| value: info.task_type, | |||||
| ellipsis: true, | |||||
| format: formatEnum(autoMLTaskTypeOptions), | |||||
| }, | |||||
| { | |||||
| label: '特征预处理算法', | |||||
| value: info.include_feature_preprocessor, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '排除的特征预处理算法', | |||||
| value: info.exclude_feature_preprocessor, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: info.task_type === AutoMLTaskType.Regression ? '回归算法' : '分类算法', | |||||
| value: | |||||
| info.task_type === AutoMLTaskType.Regression | |||||
| ? info.include_regressor | |||||
| : info.include_classifier, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: info.task_type === AutoMLTaskType.Regression ? '排除的回归算法' : '排除的分类算法', | |||||
| value: | |||||
| info.task_type === AutoMLTaskType.Regression | |||||
| ? info.exclude_regressor | |||||
| : info.exclude_classifier, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '集成方式', | |||||
| value: info.ensemble_class, | |||||
| ellipsis: true, | |||||
| format: formatEnum(autoMLEnsembleClassOptions), | |||||
| }, | |||||
| { | |||||
| label: '集成模型数量', | |||||
| value: info.ensemble_size, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '集成最佳模型数量', | |||||
| value: info.ensemble_nbest, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '最大数量', | |||||
| value: info.max_models_on_disc, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '内存限制(MB)', | |||||
| value: info.memory_limit, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '时间限制(秒)', | |||||
| value: info.per_run_time_limit, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '搜索时间限制(秒)', | |||||
| value: info.time_left_for_this_task, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '重采样策略', | |||||
| value: info.resampling_strategy, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '交叉验证折数', | |||||
| value: info.folds, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '是否打乱', | |||||
| value: info.shuffle, | |||||
| ellipsis: true, | |||||
| format: formatBoolean, | |||||
| }, | |||||
| { | |||||
| label: '训练集比率', | |||||
| value: info.train_size, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '测试集比率', | |||||
| value: info.test_size, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '计算指标', | |||||
| value: info.scoring_functions, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '随机种子', | |||||
| value: info.seed, | |||||
| ellipsis: true, | |||||
| }, | |||||
| const configDatas: BasicInfoData[] = [ | |||||
| { | |||||
| label: '任务类型', | |||||
| value: info.task_type, | |||||
| ellipsis: true, | |||||
| format: formatEnum(autoMLTaskTypeOptions), | |||||
| }, | |||||
| { | |||||
| label: '特征预处理算法', | |||||
| value: info.include_feature_preprocessor, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '排除的特征预处理算法', | |||||
| value: info.exclude_feature_preprocessor, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: info.task_type === AutoMLTaskType.Regression ? '回归算法' : '分类算法', | |||||
| value: | |||||
| info.task_type === AutoMLTaskType.Regression | |||||
| ? info.include_regressor | |||||
| : info.include_classifier, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: info.task_type === AutoMLTaskType.Regression ? '排除的回归算法' : '排除的分类算法', | |||||
| value: | |||||
| info.task_type === AutoMLTaskType.Regression | |||||
| ? info.exclude_regressor | |||||
| : info.exclude_classifier, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '集成方式', | |||||
| value: info.ensemble_class, | |||||
| ellipsis: true, | |||||
| format: formatEnum(autoMLEnsembleClassOptions), | |||||
| }, | |||||
| { | |||||
| label: '集成模型数量', | |||||
| value: info.ensemble_size, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '集成最佳模型数量', | |||||
| value: info.ensemble_nbest, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '最大数量', | |||||
| value: info.max_models_on_disc, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '内存限制(MB)', | |||||
| value: info.memory_limit, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '时间限制(秒)', | |||||
| value: info.per_run_time_limit, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '搜索时间限制(秒)', | |||||
| value: info.time_left_for_this_task, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '重采样策略', | |||||
| value: info.resampling_strategy, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '交叉验证折数', | |||||
| value: info.folds, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '是否打乱', | |||||
| value: info.shuffle, | |||||
| ellipsis: true, | |||||
| format: formatBoolean, | |||||
| }, | |||||
| { | |||||
| label: '训练集比率', | |||||
| value: info.train_size, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '测试集比率', | |||||
| value: info.test_size, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '计算指标', | |||||
| value: info.scoring_functions, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '随机种子', | |||||
| value: info.seed, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '数据集', | |||||
| value: info.dataset, | |||||
| ellipsis: true, | |||||
| format: formatDataset, | |||||
| }, | |||||
| { | |||||
| label: '预测目标列', | |||||
| value: info.target_columns, | |||||
| ellipsis: true, | |||||
| }, | |||||
| ]; | |||||
| }, [info]); | |||||
| { | |||||
| label: '数据集', | |||||
| value: info.dataset, | |||||
| ellipsis: true, | |||||
| format: formatDataset, | |||||
| }, | |||||
| { | |||||
| label: '预测目标列', | |||||
| value: info.target_columns, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '指标名称', | |||||
| value: info.metric_name, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '优化方向', | |||||
| value: info.greater_is_better, | |||||
| ellipsis: true, | |||||
| format: formatOptimizeMode, | |||||
| }, | |||||
| { | |||||
| label: '指标权重', | |||||
| value: info.metrics, | |||||
| ellipsis: true, | |||||
| format: formatMetricsWeight, | |||||
| }, | |||||
| ]; | |||||
| setConfigDatas(configDatas); | |||||
| const metricsData = useMemo(() => { | |||||
| if (!info) { | |||||
| return []; | |||||
| } | } | ||||
| }; | |||||
| return [ | |||||
| { | |||||
| label: '指标名称', | |||||
| value: info.metric_name, | |||||
| ellipsis: true, | |||||
| }, | |||||
| { | |||||
| label: '优化方向', | |||||
| value: info.greater_is_better, | |||||
| ellipsis: true, | |||||
| format: formatOptimizeMode, | |||||
| }, | |||||
| { | |||||
| label: '指标权重', | |||||
| value: info.metrics, | |||||
| ellipsis: true, | |||||
| format: formatMetricsWeight, | |||||
| }, | |||||
| ]; | |||||
| }, [info]); | |||||
| return ( | return ( | ||||
| <div className={styles['auto-ml-basic']}> | <div className={styles['auto-ml-basic']}> | ||||
| <Flex gap={15} align="stretch"> | |||||
| <ConfigInfo title="基本信息" data={basicDatas} labelWidth={70} threeColumn /> | |||||
| {/* <StatusChart | |||||
| chartData={{ Failed: 10, Pending: 20, Running: 30, Succeeded: 40, Terminated: 50 }} | |||||
| /> */} | |||||
| </Flex> | |||||
| {hasBasicInfo && ( | |||||
| <ConfigInfo | |||||
| title="基本信息" | |||||
| data={basicDatas} | |||||
| labelWidth={70} | |||||
| threeColumn | |||||
| style={{ marginBottom: '20px' }} | |||||
| /> | |||||
| )} | |||||
| <ConfigInfo | <ConfigInfo | ||||
| title="配置信息" | title="配置信息" | ||||
| data={configDatas.slice(0, -3)} | |||||
| data={configDatas} | |||||
| labelWidth={150} | labelWidth={150} | ||||
| threeColumn | threeColumn | ||||
| style={{ marginTop: '20px' }} | |||||
| /> | |||||
| <ConfigInfo | |||||
| title="优化指标" | |||||
| data={configDatas.slice(-3)} | |||||
| labelWidth={70} | |||||
| threeColumn | |||||
| style={{ marginTop: '20px' }} | |||||
| style={{ marginBottom: '20px' }} | |||||
| /> | /> | ||||
| <ConfigInfo title="优化指标" data={metricsData} labelWidth={70} threeColumn /> | |||||
| </div> | </div> | ||||
| ); | ); | ||||
| } | } | ||||
| @@ -12,18 +12,13 @@ | |||||
| :global { | :global { | ||||
| .kf-basic-info { | .kf-basic-info { | ||||
| gap: 15px; | |||||
| width: 100%; | width: 100%; | ||||
| &__item { | &__item { | ||||
| width: calc((100% - 15px) / 2); | |||||
| &__label { | &__label { | ||||
| font-size: @font-size; | font-size: @font-size; | ||||
| text-align: left; | text-align: left; | ||||
| text-align-last: left; | text-align-last: left; | ||||
| &::after { | |||||
| display: none; | |||||
| } | |||||
| } | } | ||||
| &__value { | &__value { | ||||
| min-width: 0; | min-width: 0; | ||||
| @@ -0,0 +1,14 @@ | |||||
| .experiment-history { | |||||
| height: 100%; | |||||
| &__content { | |||||
| height: 100%; | |||||
| margin-top: 10px; | |||||
| padding: 20px @content-padding 0; | |||||
| background-color: white; | |||||
| border-radius: 10px; | |||||
| &__table { | |||||
| height: 100%; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,132 @@ | |||||
| import { getFileReq } from '@/services/file'; | |||||
| import { to } from '@/utils/promise'; | |||||
| import tableCellRender from '@/utils/table'; | |||||
| import { Table, type TableProps } from 'antd'; | |||||
| import classNames from 'classnames'; | |||||
| import { useEffect, useState } from 'react'; | |||||
| import styles from './index.less'; | |||||
| type ExperimentHistoryProps = { | |||||
| fileUrl?: string; | |||||
| isClassification: boolean; | |||||
| }; | |||||
| type TableData = { | |||||
| id?: string; | |||||
| accuracy?: number; | |||||
| duration?: number; | |||||
| train_loss?: number; | |||||
| status?: string; | |||||
| feature?: string; | |||||
| althorithm?: string; | |||||
| }; | |||||
| function ExperimentHistory({ fileUrl, isClassification }: ExperimentHistoryProps) { | |||||
| const [tableData, setTableData] = useState<TableData[]>([]); | |||||
| useEffect(() => { | |||||
| if (fileUrl) { | |||||
| getHistoryFile(); | |||||
| } | |||||
| }, [fileUrl]); | |||||
| // 获取实验运行历史记录 | |||||
| const getHistoryFile = async () => { | |||||
| const [res] = await to(getFileReq(fileUrl)); | |||||
| if (res) { | |||||
| const data: any[] = res.data; | |||||
| const list: TableData[] = data.map((item) => { | |||||
| return { | |||||
| id: item[0]?.[0], | |||||
| accuracy: item[1]?.[5]?.accuracy, | |||||
| duration: item[1]?.[5]?.duration, | |||||
| train_loss: item[1]?.[5]?.train_loss, | |||||
| status: item[1]?.[2]?.['__enum__']?.split('.')?.[1], | |||||
| }; | |||||
| }); | |||||
| list.forEach((item) => { | |||||
| if (!item.id) return; | |||||
| const config = (res as any).configs?.[item.id]; | |||||
| item.feature = config?.['feature_preprocessor:__choice__']; | |||||
| item.althorithm = isClassification | |||||
| ? config?.['classifier:__choice__'] | |||||
| : config?.['regressor:__choice__']; | |||||
| }); | |||||
| setTableData(list); | |||||
| } | |||||
| }; | |||||
| const columns: TableProps<TableData>['columns'] = [ | |||||
| { | |||||
| title: 'ID', | |||||
| dataIndex: 'id', | |||||
| key: 'id', | |||||
| width: 80, | |||||
| render: tableCellRender(false), | |||||
| }, | |||||
| { | |||||
| title: '准确率', | |||||
| dataIndex: 'accuracy', | |||||
| key: 'accuracy', | |||||
| render: tableCellRender(true), | |||||
| ellipsis: { showTitle: false }, | |||||
| }, | |||||
| { | |||||
| title: '耗时', | |||||
| dataIndex: 'duration', | |||||
| key: 'duration', | |||||
| render: tableCellRender(true), | |||||
| ellipsis: { showTitle: false }, | |||||
| }, | |||||
| { | |||||
| title: '训练损失', | |||||
| dataIndex: 'train_loss', | |||||
| key: 'train_loss', | |||||
| render: tableCellRender(true), | |||||
| ellipsis: { showTitle: false }, | |||||
| }, | |||||
| { | |||||
| title: '特征处理', | |||||
| dataIndex: 'feature', | |||||
| key: 'feature', | |||||
| render: tableCellRender(true), | |||||
| ellipsis: { showTitle: false }, | |||||
| }, | |||||
| { | |||||
| title: '算法', | |||||
| dataIndex: 'althorithm', | |||||
| key: 'althorithm', | |||||
| render: tableCellRender(true), | |||||
| ellipsis: { showTitle: false }, | |||||
| }, | |||||
| { | |||||
| title: '状态', | |||||
| dataIndex: 'status', | |||||
| key: 'status', | |||||
| width: 120, | |||||
| render: tableCellRender(false), | |||||
| }, | |||||
| ]; | |||||
| return ( | |||||
| <div className={styles['experiment-history']}> | |||||
| <div className={styles['experiment-history__content']}> | |||||
| <div | |||||
| className={classNames( | |||||
| 'vertical-scroll-table-no-page', | |||||
| styles['experiment-history__content__table'], | |||||
| )} | |||||
| > | |||||
| <Table | |||||
| dataSource={tableData} | |||||
| columns={columns} | |||||
| pagination={false} | |||||
| scroll={{ y: 'calc(100% - 55px)' }} | |||||
| rowKey="id" | |||||
| /> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| export default ExperimentHistory; | |||||
| @@ -0,0 +1,18 @@ | |||||
| .experiment-result { | |||||
| height: 100%; | |||||
| margin-top: 10px; | |||||
| padding: 20px @content-padding 0; | |||||
| overflow-y: auto; | |||||
| background-color: white; | |||||
| border-radius: 10px; | |||||
| &__text { | |||||
| margin-bottom: 20px; | |||||
| white-space: pre-wrap; | |||||
| } | |||||
| &__image { | |||||
| display: block; | |||||
| height: 200px; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,51 @@ | |||||
| import { getFileReq } from '@/services/file'; | |||||
| import { to } from '@/utils/promise'; | |||||
| import { useEffect, useMemo, useState } from 'react'; | |||||
| import styles from './index.less'; | |||||
| type ExperimentResultProps = { | |||||
| fileUrl?: string; | |||||
| imageUrl?: string; | |||||
| }; | |||||
| function ExperimentResult({ fileUrl, imageUrl }: ExperimentResultProps) { | |||||
| const [result, setResult] = useState<string | undefined>(''); | |||||
| const images = useMemo(() => { | |||||
| if (imageUrl) { | |||||
| return imageUrl.split(',').map((item) => item.trim()); | |||||
| } | |||||
| return []; | |||||
| }, [imageUrl]); | |||||
| useEffect(() => { | |||||
| if (fileUrl) { | |||||
| getResultFile(); | |||||
| } | |||||
| }, [fileUrl]); | |||||
| // 获取实验运行历史记录 | |||||
| const getResultFile = async () => { | |||||
| const [res] = await to(getFileReq(fileUrl)); | |||||
| if (res) { | |||||
| setResult(res as any as string); | |||||
| } | |||||
| }; | |||||
| return ( | |||||
| <div className={styles['experiment-result']}> | |||||
| <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="" | |||||
| /> | |||||
| ))} | |||||
| </div> | |||||
| ); | |||||
| } | |||||
| export default ExperimentResult; | |||||
| @@ -61,6 +61,24 @@ export type AutoMLData = { | |||||
| 'metrics|dataset|include_classifier|include_feature_preprocessor|include_regressor|exclude_classifier|exclude_feature_preprocessor|exclude_regressor' | 'metrics|dataset|include_classifier|include_feature_preprocessor|include_regressor|exclude_classifier|exclude_feature_preprocessor|exclude_regressor' | ||||
| >; | >; | ||||
| export type ExperimentInstanceData = { | |||||
| // 自动机器学习实验实例 | |||||
| export type AutoMLInstanceData = { | |||||
| id: number; | id: number; | ||||
| auto_ml_id: number; | |||||
| result_path: string; | |||||
| model_path: string; | |||||
| img_path: string; | |||||
| run_history_path: string; | |||||
| state: number; | |||||
| status: string; | |||||
| node_status: string; | |||||
| node_result: string; | |||||
| param: string; | |||||
| source: string | null; | |||||
| argo_ins_name: string; | |||||
| argo_ins_ns: string; | |||||
| create_time: string; | |||||
| update_time: string; | |||||
| finish_time: string; | |||||
| nodeStatus?: { [key: string]: string }; | |||||
| }; | }; | ||||
| @@ -135,7 +135,7 @@ function LogGroup({ | |||||
| const setupSockect = () => { | const setupSockect = () => { | ||||
| let { host } = location; | let { host } = location; | ||||
| if (process.env.NODE_ENV === 'development') { | if (process.env.NODE_ENV === 'development') { | ||||
| host = '172.20.32.185:31213'; | |||||
| host = '172.20.32.181:31213'; | |||||
| } | } | ||||
| const socket = new WebSocket( | const socket = new WebSocket( | ||||
| `ws://${host}/newlog/realtimeLog?start=${start_time}&query={pod="${pod_name}"}`, | `ws://${host}/newlog/realtimeLog?start=${start_time}&query={pod="${pod_name}"}`, | ||||
| @@ -271,6 +271,7 @@ function Experiment() { | |||||
| const [res] = await to(runExperiments(id)); | const [res] = await to(runExperiments(id)); | ||||
| if (res) { | if (res) { | ||||
| message.success('运行成功'); | message.success('运行成功'); | ||||
| refreshExperimentList(); | |||||
| refreshExperimentIns(id); | refreshExperimentIns(id); | ||||
| } else { | } else { | ||||
| message.error('运行失败'); | message.error('运行失败'); | ||||
| @@ -58,11 +58,12 @@ export const requestConfig: RequestConfig = { | |||||
| const options = config as RequestOptions; | const options = config as RequestOptions; | ||||
| const skipErrorHandler = options?.skipErrorHandler; | const skipErrorHandler = options?.skipErrorHandler; | ||||
| const skipLoading = options?.skipLoading; | const skipLoading = options?.skipLoading; | ||||
| const skipValidating = options?.skipValidating; | |||||
| if (!skipLoading) { | if (!skipLoading) { | ||||
| Loading.hide(); | Loading.hide(); | ||||
| } | } | ||||
| if (status >= 200 && status < 300) { | if (status >= 200 && status < 300) { | ||||
| if (data && (data instanceof Blob || data.code === 200)) { | |||||
| if (data && (skipValidating || data instanceof Blob || data.code === 200)) { | |||||
| return response; | return response; | ||||
| } else if (data && data.code === 401) { | } else if (data && data.code === 401) { | ||||
| clearSessionToken(); | clearSessionToken(); | ||||
| @@ -0,0 +1,20 @@ | |||||
| /* | |||||
| * @Author: 赵伟 | |||||
| * @Date: 2024-11-30 11:43:26 | |||||
| * @Description: 请求文件,比如 json 文件 | |||||
| */ | |||||
| import { request } from '@umijs/max'; | |||||
| // 获取文件,不需要token,非结构化数据 | |||||
| export function getFileReq(url, config) { | |||||
| return request(url, { | |||||
| method: 'GET', | |||||
| headers: { | |||||
| isToken: false, | |||||
| }, | |||||
| skipValidating: true, | |||||
| ...config | |||||
| }); | |||||
| } | |||||