diff --git a/react-ui/src/enums/index.ts b/react-ui/src/enums/index.ts index 34d5b51b..bdfe9e00 100644 --- a/react-ui/src/enums/index.ts +++ b/react-ui/src/enums/index.ts @@ -118,3 +118,14 @@ export const autoMLResamplingStrategyOptions = [ { label: 'holdout', value: AutoMLResamplingStrategy.Holdout }, { label: 'crossValid', value: AutoMLResamplingStrategy.CrossValid }, ]; + +// 超参数自动寻优优化方向 +export enum hyperParameterOptimizedMode { + Min = 'min', + Max = 'max', +} + +export const hyperParameterOptimizedModeOptions = [ + { label: '越大越好', value: hyperParameterOptimizedMode.Max }, + { label: '越小越好', value: hyperParameterOptimizedMode.Min }, +]; diff --git a/react-ui/src/pages/AutoML/Create/index.tsx b/react-ui/src/pages/AutoML/Create/index.tsx index 30699063..ec016c3c 100644 --- a/react-ui/src/pages/AutoML/Create/index.tsx +++ b/react-ui/src/pages/AutoML/Create/index.tsx @@ -190,7 +190,7 @@ function CreateAutoML() { - + diff --git a/react-ui/src/pages/AutoML/Info/index.tsx b/react-ui/src/pages/AutoML/Info/index.tsx index cc5247e2..0d0ec460 100644 --- a/react-ui/src/pages/AutoML/Info/index.tsx +++ b/react-ui/src/pages/AutoML/Info/index.tsx @@ -3,9 +3,7 @@ * @Date: 2024-04-16 13:58:08 * @Description: 自主机器学习详情 */ -import KFIcon from '@/components/KFIcon'; import PageTitle from '@/components/PageTitle'; -import { CommonTabKeys } from '@/enums'; import { getAutoMLInfoReq } from '@/services/autoML'; import { safeInvoke } from '@/utils/functional'; import { to } from '@/utils/promise'; @@ -16,24 +14,10 @@ import { AutoMLData } from '../types'; import styles from './index.less'; function AutoMLInfo() { - const [activeTab, setActiveTab] = useState(CommonTabKeys.Public); const params = useParams(); const autoMLId = safeInvoke(Number)(params.id); const [autoMLInfo, setAutoMLInfo] = useState(undefined); - const tabItems = [ - { - key: CommonTabKeys.Public, - label: '基本信息', - icon: , - }, - { - key: CommonTabKeys.Private, - label: 'Trial列表', - icon: , - }, - ]; - useEffect(() => { if (autoMLId) { getAutoMLInfo(); diff --git a/react-ui/src/pages/AutoML/components/AutoMLBasic/index.tsx b/react-ui/src/pages/AutoML/components/AutoMLBasic/index.tsx index 854c6035..76d26a75 100644 --- a/react-ui/src/pages/AutoML/components/AutoMLBasic/index.tsx +++ b/react-ui/src/pages/AutoML/components/AutoMLBasic/index.tsx @@ -4,6 +4,7 @@ import { experimentStatusInfo } from '@/pages/Experiment/status'; import { type NodeStatus } from '@/types'; import { parseJsonText } from '@/utils'; import { elapsedTime } from '@/utils/date'; +import { formatDataset } from '@/utils/format'; import { Flex } from 'antd'; import classNames from 'classnames'; import { useMemo } from 'react'; @@ -15,14 +16,6 @@ import ConfigInfo, { } from '../ConfigInfo'; import styles from './index.less'; -// 格式化数据集 -const formatDataset = (dataset: { name: string; version: string }) => { - if (!dataset || !dataset.name || !dataset.version) { - return '--'; - } - return `${dataset.name}:${dataset.version}`; -}; - // 格式化优化方向 const formatOptimizeMode = (value: boolean) => { return value ? '越大越好' : '越小越好'; diff --git a/react-ui/src/pages/AutoML/components/ConfigInfo/index.tsx b/react-ui/src/pages/AutoML/components/ConfigInfo/index.tsx index 10e042e4..256f7b16 100644 --- a/react-ui/src/pages/AutoML/components/ConfigInfo/index.tsx +++ b/react-ui/src/pages/AutoML/components/ConfigInfo/index.tsx @@ -11,13 +11,15 @@ type ConfigInfoProps = { labelWidth: number; className?: string; style?: React.CSSProperties; + children?: React.ReactNode; }; -function ConfigInfo({ title, data, labelWidth, className, style }: ConfigInfoProps) { +function ConfigInfo({ title, data, labelWidth, className, style, children }: ConfigInfoProps) { return (
+ {children}
); diff --git a/react-ui/src/pages/AutoML/components/ExperimentList/index.tsx b/react-ui/src/pages/AutoML/components/ExperimentList/index.tsx index c0740eee..0da5ca9c 100644 --- a/react-ui/src/pages/AutoML/components/ExperimentList/index.tsx +++ b/react-ui/src/pages/AutoML/components/ExperimentList/index.tsx @@ -400,6 +400,7 @@ function ExperimentList({ type }: ExperimentListProps) { expandable={{ expandedRowRender: (record) => ( gotoInstanceInfo(record, item)} diff --git a/react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx b/react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx index b0bad9de..807270a8 100644 --- a/react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx +++ b/react-ui/src/pages/Dataset/components/ResourceIntro/index.tsx @@ -1,17 +1,8 @@ import BasicTableInfo, { BasicInfoData } from '@/components/BasicTableInfo'; import SubAreaTitle from '@/components/SubAreaTitle'; -import { ResourceInfoTabKeys } from '@/pages/Dataset/components/ResourceInfo'; -import { - DataSource, - DatasetData, - ModelData, - ProjectDependency, - ResourceType, - TrainTask, - resourceConfig, -} from '@/pages/Dataset/config'; +import { DatasetData, ModelData, ResourceType, resourceConfig } from '@/pages/Dataset/config'; import ModelMetrics from '@/pages/Model/components/ModelMetrics'; -import { getGitUrl } from '@/utils'; +import { formatCodeConfig, formatDatasets, formatSource, formatTrainTask } from '@/utils/format'; import classNames from 'classnames'; import styles from './index.less'; @@ -24,55 +15,6 @@ type ResourceIntroProps = { version?: string; }; -export const formatDataset = (datasets?: DatasetData[]) => { - if (!datasets || datasets.length === 0) { - return undefined; - } - return datasets.map((item) => ({ - value: item.name, - url: `${origin}/dataset/dataset/info/${item.id}?tab=${ResourceInfoTabKeys.Version}&version=${item.version}&name=${item.name}&owner=${item.owner}&identifier=${item.identifier}`, - })); -}; - -export const getProjectUrl = (project?: ProjectDependency) => { - if (!project || !project.url || !project.branch) { - return undefined; - } - const { url, branch } = project; - return getGitUrl(url, branch); -}; - -export const formatProject = (project?: ProjectDependency) => { - if (!project) { - return undefined; - } - return { - value: project.name, - url: getProjectUrl(project), - }; -}; - -export const formatTrainTask = (task?: TrainTask) => { - if (!task) { - return undefined; - } - return { - value: task.name, - url: `${origin}/pipeline/experiment/instance/${task.workflow_id}/${task.ins_id}`, - }; -}; - -export const formatSource = (source?: string) => { - if (source === DataSource.Create) { - return '用户上传'; - } else if (source === DataSource.HandExport) { - return '手动导入'; - } else if (source === DataSource.AtuoExport) { - return '实验自动导入'; - } - return source; -}; - const getDatasetDatas = (data: DatasetData): BasicInfoData[] => [ { label: '数据集名称', @@ -109,7 +51,7 @@ const getDatasetDatas = (data: DatasetData): BasicInfoData[] => [ { label: '处理代码', value: data.processing_code, - format: formatProject, + format: formatCodeConfig, ellipsis: true, }, { @@ -153,19 +95,19 @@ const getModelDatas = (data: ModelData): BasicInfoData[] => [ { label: '训练代码', value: data.project_depency, - format: formatProject, + format: formatCodeConfig, ellipsis: true, }, { label: '训练数据集', value: data.train_datasets, - format: formatDataset, + format: formatDatasets, ellipsis: true, }, { label: '测试数据集', value: data.test_datasets, - format: formatDataset, + format: formatDatasets, ellipsis: true, }, { diff --git a/react-ui/src/pages/Dataset/components/VersionCompareModal/index.tsx b/react-ui/src/pages/Dataset/components/VersionCompareModal/index.tsx index 2ee76e78..7bafdf95 100644 --- a/react-ui/src/pages/Dataset/components/VersionCompareModal/index.tsx +++ b/react-ui/src/pages/Dataset/components/VersionCompareModal/index.tsx @@ -8,11 +8,11 @@ import { resourceConfig, } from '@/pages/Dataset/config'; import { isEmpty } from '@/utils'; +import { formatSource } from '@/utils/format'; import { to } from '@/utils/promise'; import { Typography, type ModalProps } from 'antd'; import classNames from 'classnames'; import { useEffect, useMemo, useState } from 'react'; -import { formatSource } from '../ResourceIntro'; import styles from './index.less'; type CompareData = { @@ -47,10 +47,10 @@ const formatProject = (project?: ProjectDependency) => { if (!project) { return undefined; } - return project.name; + return `${project.name}:${project.branch}`; }; -export const formatTrainTask = (task?: TrainTask) => { +const formatTrainTask = (task?: TrainTask) => { if (!task) { return undefined; } diff --git a/react-ui/src/pages/HyperParameter/Create/index.tsx b/react-ui/src/pages/HyperParameter/Create/index.tsx index dfe367ba..fa5fc8b8 100644 --- a/react-ui/src/pages/HyperParameter/Create/index.tsx +++ b/react-ui/src/pages/HyperParameter/Create/index.tsx @@ -41,10 +41,9 @@ function CreateHyperparameter() { const name = isCopy ? `${name_str}-copy` : name_str; if (parameters && Array.isArray(parameters)) { parameters.forEach((item) => { - item.range = item.bounds || item.values || item.value; - delete item.bounds; - delete item.values; - delete item.value; + const paramName = getReqParamName(item.type); + item.range = item[paramName]; + item[paramName] = undefined; }); } @@ -63,8 +62,6 @@ function CreateHyperparameter() { const createExperiment = async (formData: FormData) => { // 按后台接口要求,修改参数表单数据结构,将 "value" 参数改为 "bounds"/"values"/"value" const parameters = formData['parameters']; - // const points_to_evaluate = formData['points_to_evaluate']; - // const runParameters = formData['parameters']; parameters.forEach((item) => { const paramName = getReqParamName(item.type); item[paramName] = item.range; @@ -142,7 +139,7 @@ function CreateHyperparameter() { - + diff --git a/react-ui/src/pages/HyperParameter/Info/index.tsx b/react-ui/src/pages/HyperParameter/Info/index.tsx index 8b2fded3..9a37a68f 100644 --- a/react-ui/src/pages/HyperParameter/Info/index.tsx +++ b/react-ui/src/pages/HyperParameter/Info/index.tsx @@ -3,48 +3,34 @@ * @Date: 2024-04-16 13:58:08 * @Description: 自主机器学习详情 */ -import KFIcon from '@/components/KFIcon'; import PageTitle from '@/components/PageTitle'; -import { CommonTabKeys } from '@/enums'; -import { getAutoMLInfoReq } from '@/services/autoML'; +import { getRayInfoReq } from '@/services/hyperParameter'; import { safeInvoke } from '@/utils/functional'; import { to } from '@/utils/promise'; import { useParams } from '@umijs/max'; import { useEffect, useState } from 'react'; -import AutoMLBasic from '../components/AutoMLBasic'; +import HyperParameterBasic from '../components/HyperParameterBasic'; import { HyperparameterData } from '../types'; import styles from './index.less'; -function AutoMLInfo() { - const [activeTab, setActiveTab] = useState(CommonTabKeys.Public); +function HyperparameterInfo() { const params = useParams(); - const autoMLId = safeInvoke(Number)(params.id); - const [autoMLInfo, setAutoMLInfo] = useState(undefined); - - const tabItems = [ - { - key: CommonTabKeys.Public, - label: '基本信息', - icon: , - }, - { - key: CommonTabKeys.Private, - label: 'Trial列表', - icon: , - }, - ]; + const hyperparameterId = safeInvoke(Number)(params.id); + const [hyperparameterInfo, setHyperparameterInfo] = useState( + undefined, + ); useEffect(() => { - if (autoMLId) { - getAutoMLInfo(); + if (hyperparameterId) { + getHyperparameterInfo(); } }, []); // 获取自动机器学习详情 - const getAutoMLInfo = async () => { - const [res] = await to(getAutoMLInfoReq({ id: autoMLId })); + const getHyperparameterInfo = async () => { + const [res] = await to(getRayInfoReq({ id: hyperparameterId })); if (res && res.data) { - setAutoMLInfo(res.data); + setHyperparameterInfo(res.data); } }; @@ -52,10 +38,10 @@ function AutoMLInfo() {
- +
); } -export default AutoMLInfo; +export default HyperparameterInfo; diff --git a/react-ui/src/pages/HyperParameter/Instance/index.tsx b/react-ui/src/pages/HyperParameter/Instance/index.tsx index 355ced01..8f7faa33 100644 --- a/react-ui/src/pages/HyperParameter/Instance/index.tsx +++ b/react-ui/src/pages/HyperParameter/Instance/index.tsx @@ -9,9 +9,9 @@ import { to } from '@/utils/promise'; import { useParams } from '@umijs/max'; import { Tabs } from 'antd'; import { useEffect, useRef, useState } from 'react'; -import AutoMLBasic from '../components/AutoMLBasic'; import ExperimentHistory from '../components/ExperimentHistory'; import ExperimentResult from '../components/ExperimentResult'; +import HyperParameterBasic from '../components/HyperParameterBasic'; import { AutoMLInstanceData, HyperparameterData } from '../types'; import styles from './index.less'; @@ -140,7 +140,7 @@ function AutoMLInstance() { label: '基本信息', icon: , children: ( - { - if (!dataset || !dataset.name || !dataset.version) { - return '--'; - } - return `${dataset.name}:${dataset.version}`; -}; - -// 格式化优化方向 -const formatOptimizeMode = (value: boolean) => { - return value ? '越大越好' : '越小越好'; -}; - -const formatMetricsWeight = (value: string) => { - if (!value) { - return '--'; - } - const json = parseJsonText(value); - if (!json) { - return '--'; - } - return Object.entries(json) - .map(([key, value]) => `${key}:${value}`) - .join('\n'); -}; - -type AutoMLBasicProps = { - info?: AutoMLData; - className?: string; - isInstance?: boolean; - runStatus?: NodeStatus; -}; - -function AutoMLBasic({ info, className, runStatus, isInstance = false }: AutoMLBasicProps) { - const basicDatas: BasicInfoData[] = useMemo(() => { - if (!info) { - return []; - } - - 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 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, - }, - - { - label: '数据集', - value: info.dataset, - ellipsis: true, - format: formatDataset, - }, - { - label: '预测目标列', - value: info.target_columns, - ellipsis: true, - }, - ]; - }, [info]); - - 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]); - - 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: ( - - -
- {experimentStatusInfo[runStatus?.phase]?.label} -
-
- ), - ellipsis: true, - }, - ]; - }, [runStatus]); - - return ( -
- {isInstance && runStatus && ( - - )} - {!isInstance && ( - - )} - - -
- ); -} - -export default AutoMLBasic; diff --git a/react-ui/src/pages/HyperParameter/components/ConfigInfo/index.less b/react-ui/src/pages/HyperParameter/components/ConfigInfo/index.less deleted file mode 100644 index 33fb3314..00000000 --- a/react-ui/src/pages/HyperParameter/components/ConfigInfo/index.less +++ /dev/null @@ -1,20 +0,0 @@ -.config-info { - :global { - .kf-basic-info { - width: 100%; - - &__item { - width: calc((100% - 80px) / 3); - &__label { - font-size: @font-size; - text-align: left; - text-align-last: left; - } - &__value { - min-width: 0; - font-size: @font-size; - } - } - } - } -} diff --git a/react-ui/src/pages/HyperParameter/components/ConfigInfo/index.tsx b/react-ui/src/pages/HyperParameter/components/ConfigInfo/index.tsx deleted file mode 100644 index 10e042e4..00000000 --- a/react-ui/src/pages/HyperParameter/components/ConfigInfo/index.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import BasicInfo, { type BasicInfoData } from '@/components/BasicInfo'; -import InfoGroup from '@/components/InfoGroup'; -import classNames from 'classnames'; -import styles from './index.less'; -export * from '@/components/BasicInfo/format'; -export type { BasicInfoData }; - -type ConfigInfoProps = { - title: string; - data: BasicInfoData[]; - labelWidth: number; - className?: string; - style?: React.CSSProperties; -}; - -function ConfigInfo({ title, data, labelWidth, className, style }: ConfigInfoProps) { - return ( - -
- -
-
- ); -} - -export default ConfigInfo; diff --git a/react-ui/src/pages/HyperParameter/components/CreateForm/ExecuteConfig.tsx b/react-ui/src/pages/HyperParameter/components/CreateForm/ExecuteConfig.tsx index 92e7f961..86760130 100644 --- a/react-ui/src/pages/HyperParameter/components/CreateForm/ExecuteConfig.tsx +++ b/react-ui/src/pages/HyperParameter/components/CreateForm/ExecuteConfig.tsx @@ -5,6 +5,7 @@ import ResourceSelect, { requiredValidator, } from '@/components/ResourceSelect'; import SubAreaTitle from '@/components/SubAreaTitle'; +import { hyperParameterOptimizedModeOptions } from '@/enums'; import { modalConfirm } from '@/utils/ui'; import { MinusCircleOutlined, PlusCircleOutlined } from '@ant-design/icons'; import { Button, Col, Flex, Form, Input, InputNumber, Radio, Row, Select } from 'antd'; @@ -456,10 +457,7 @@ function ExecuteConfig() { name="mode" rules={[{ required: true, message: '请选择优化方向' }]} > - - 越大越好 - 越小越好 - +
@@ -467,16 +465,16 @@ function ExecuteConfig() { - + @@ -484,16 +482,16 @@ function ExecuteConfig() { - + diff --git a/react-ui/src/pages/HyperParameter/components/CreateForm/PopParameterRange/index.less b/react-ui/src/pages/HyperParameter/components/CreateForm/PopParameterRange/index.less index 01faf3d0..8f0a0f97 100644 --- a/react-ui/src/pages/HyperParameter/components/CreateForm/PopParameterRange/index.less +++ b/react-ui/src/pages/HyperParameter/components/CreateForm/PopParameterRange/index.less @@ -1,5 +1,8 @@ .parameter-range { :global { + .ant-popover-inner { + padding: 20px 20px 12px; + } .ant-popconfirm-description { padding-top: 20px; } @@ -22,15 +25,6 @@ border-radius: 8px; cursor: pointer; - &:hover { - border-color: #4086ff; - } - - &--disabled { - background-color: rgba(0, 0, 0, 0.04); - cursor: not-allowed; - } - &__text { flex: 1; margin-right: 10px; @@ -40,8 +34,22 @@ flex: none; } - &--disabled &__icon { - color: @text-color-tertiary; + &:hover { + border-color: #4086ff; + } + + &:hover &__icon { + color: #4086ff; + } + + &&--disabled { + background-color: rgba(0, 0, 0, 0.04); + border-color: rgba(0, 0, 0, 0.04) !important; + cursor: not-allowed; + } + + &&--disabled &__icon { + color: #aaaaaa !important; } } } diff --git a/react-ui/src/pages/HyperParameter/components/CreateForm/index.less b/react-ui/src/pages/HyperParameter/components/CreateForm/index.less index fcb77fdd..8f8bae7b 100644 --- a/react-ui/src/pages/HyperParameter/components/CreateForm/index.less +++ b/react-ui/src/pages/HyperParameter/components/CreateForm/index.less @@ -92,12 +92,13 @@ .run-parameter { width: calc(41.66% + 126px); margin-bottom: 20px; - border-radius: 8px; + &__body { flex: 1; margin-right: 10px; padding: 20px 20px 0; - border: 1px dashed @border-color-base; + border: 1px dashed #dddddd; + border-radius: 8px; } &__operation { display: flex; diff --git a/react-ui/src/pages/HyperParameter/components/AutoMLBasic/index.less b/react-ui/src/pages/HyperParameter/components/HyperParameterBasic/index.less similarity index 89% rename from react-ui/src/pages/HyperParameter/components/AutoMLBasic/index.less rename to react-ui/src/pages/HyperParameter/components/HyperParameterBasic/index.less index cbd05bcc..f365aa66 100644 --- a/react-ui/src/pages/HyperParameter/components/AutoMLBasic/index.less +++ b/react-ui/src/pages/HyperParameter/components/HyperParameterBasic/index.less @@ -1,4 +1,4 @@ -.auto-ml-basic { +.hyper-parameter-basic { height: 100%; padding: 20px @content-padding; overflow-y: auto; diff --git a/react-ui/src/pages/HyperParameter/components/HyperParameterBasic/index.tsx b/react-ui/src/pages/HyperParameter/components/HyperParameterBasic/index.tsx new file mode 100644 index 00000000..167cbbe4 --- /dev/null +++ b/react-ui/src/pages/HyperParameter/components/HyperParameterBasic/index.tsx @@ -0,0 +1,207 @@ +import { hyperParameterOptimizedMode } from '@/enums'; +import ConfigInfo, { formatDate, type BasicInfoData } from '@/pages/AutoML/components/ConfigInfo'; +import { experimentStatusInfo } from '@/pages/Experiment/status'; +import { HyperparameterData } from '@/pages/HyperParameter/types'; +import { type NodeStatus } from '@/types'; +import { elapsedTime } from '@/utils/date'; +import { formatDataset, formatSelectCodeConfig } from '@/utils/format'; +import { Flex } from 'antd'; +import classNames from 'classnames'; +import { useMemo } from 'react'; +import ParameterInfo from '../ParameterInfo'; +import styles from './index.less'; + +// 格式化优化方向 +const formatOptimizeMode = (value: string) => { + return value === hyperParameterOptimizedMode.Max ? '越大越好' : '越小越好'; +}; + +type HyperParameterBasicProps = { + info?: HyperparameterData; + className?: string; + isInstance?: boolean; + runStatus?: NodeStatus; +}; + +function HyperParameterBasic({ + info, + className, + runStatus, + isInstance = false, +}: HyperParameterBasicProps) { + const basicDatas: BasicInfoData[] = useMemo(() => { + if (!info) { + return []; + } + + return [ + { + label: '实验名称', + value: info.name, + ellipsis: true, + }, + { + label: '实验描述', + value: info.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 configDatas: BasicInfoData[] = useMemo(() => { + if (!info) { + return []; + } + return [ + { + label: '代码', + value: info.code, + ellipsis: true, + format: formatSelectCodeConfig, + }, + { + label: '主函数代码文件', + value: info.main_py, + ellipsis: true, + }, + { + label: '数据集', + value: info.dataset, + ellipsis: true, + format: formatDataset, + }, + + { + label: '数据集挂载路径', + value: info.dataset_path, + ellipsis: true, + }, + { + label: '总实验次数', + value: info.num_samples, + ellipsis: true, + }, + { + label: '搜索算法', + value: info.search_alg, + ellipsis: true, + }, + { + label: '调度算法', + value: info.scheduler, + ellipsis: true, + }, + { + label: '优化方向', + value: info.mode, + ellipsis: true, + format: formatOptimizeMode, + }, + { + label: '指标', + value: info.metric, + ellipsis: true, + }, + { + label: 'CPU 数量', + value: info.cpu, + ellipsis: true, + }, + { + label: 'GPU 数量', + value: info.gpu, + ellipsis: true, + }, + ]; + }, [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: ( + + +
+ {experimentStatusInfo[runStatus?.phase]?.label} +
+
+ ), + ellipsis: true, + }, + ]; + }, [runStatus]); + + return ( +
+ {isInstance && runStatus && ( + + )} + {!isInstance && ( + + )} + + {info && } + +
+ ); +} + +export default HyperParameterBasic; diff --git a/react-ui/src/pages/HyperParameter/components/ParameterInfo/index.less b/react-ui/src/pages/HyperParameter/components/ParameterInfo/index.less new file mode 100644 index 00000000..81d6fd56 --- /dev/null +++ b/react-ui/src/pages/HyperParameter/components/ParameterInfo/index.less @@ -0,0 +1,7 @@ +.parameter-info { + &__title { + margin: 20px 0; + color: @text-color-secondary; + font-size: @font-size; + } +} diff --git a/react-ui/src/pages/HyperParameter/components/ParameterInfo/index.tsx b/react-ui/src/pages/HyperParameter/components/ParameterInfo/index.tsx new file mode 100644 index 00000000..9b415d85 --- /dev/null +++ b/react-ui/src/pages/HyperParameter/components/ParameterInfo/index.tsx @@ -0,0 +1,108 @@ +import { + getReqParamName, + type FormParameter, +} from '@/pages/HyperParameter/components/CreateForm/utils'; +import { HyperparameterData } from '@/pages/HyperParameter/types'; +import tableCellRender, { TableCellValueType } from '@/utils/table'; +import { Table, Tooltip, type TableProps } from 'antd'; +import { useMemo } from 'react'; +import styles from './index.less'; + +type ParameterInfoProps = { + info: HyperparameterData; +}; + +function ParameterInfo({ info }: ParameterInfoProps) { + const parameters = useMemo(() => { + if (!info.parameters) { + return []; + } + return info.parameters.map((item) => { + const paramName = getReqParamName(item.type); + const range = item[paramName]; + return { + ...item, + range, + }; + }); + }, [info]); + + const runParameters = useMemo(() => { + if (!info.points_to_evaluate) { + return []; + } + return info.points_to_evaluate.map((item, index) => ({ + ...item, + id: index, + })); + }, [info]); + + const columns: TableProps['columns'] = [ + { + title: '参数名称', + dataIndex: 'name', + key: 'type', + width: '40%', + render: tableCellRender(true), + ellipsis: { showTitle: false }, + }, + { + title: '参数类型', + dataIndex: 'type', + key: 'type', + width: '20%', + render: tableCellRender(true), + ellipsis: { showTitle: false }, + }, + { + title: '取值范围', + dataIndex: 'range', + key: 'range', + width: '40%', + render: tableCellRender(true, TableCellValueType.Custom, { + format: (value) => { + return JSON.stringify(value); + }, + }), + ellipsis: { showTitle: false }, + }, + ]; + + const runColumns: TableProps>['columns'] = + runParameters.length > 0 + ? Object.keys(runParameters[0]) + .filter((key) => key !== 'id') + .map((key) => { + return { + title: ( + + {key} + + ), + dataIndex: key, + key: key, + width: 150, + render: tableCellRender(true), + ellipsis: { showTitle: false }, + }; + }) + : []; + + return ( +
+
参数
+ +
手动运行参数
+
+ + ); +} + +export default ParameterInfo; diff --git a/react-ui/src/pages/HyperParameter/types.ts b/react-ui/src/pages/HyperParameter/types.ts index 3eec723c..bb412c09 100644 --- a/react-ui/src/pages/HyperParameter/types.ts +++ b/react-ui/src/pages/HyperParameter/types.ts @@ -16,7 +16,7 @@ export type FormData = { dataset: ParameterInputObject; // 数据集 dataset_path: string; // 数据集路径 main_py: string; // 主函数代码文件 - metrics: string; // 指标 + metric: string; // 指标 mode: string; // 优化方向 search_alg?: string; // 搜索算法 scheduler?: string; // 调度算法 diff --git a/react-ui/src/utils/format.ts b/react-ui/src/utils/format.ts new file mode 100644 index 00000000..579ae103 --- /dev/null +++ b/react-ui/src/utils/format.ts @@ -0,0 +1,87 @@ +import { ResourceInfoTabKeys } from '@/pages/Dataset/components/ResourceInfo'; +import { DataSource, DatasetData, ProjectDependency, TrainTask } from '@/pages/Dataset/config'; +import { getGitUrl } from '@/utils'; + +// 格式化数据集数组 +export const formatDatasets = (datasets?: DatasetData[]) => { + if (!datasets || datasets.length === 0) { + return undefined; + } + return datasets.map((item) => ({ + value: item.name, + url: `${origin}/dataset/dataset/info/${item.id}?tab=${ResourceInfoTabKeys.Introduction}&version=${item.version}&name=${item.name}&owner=${item.owner}&identifier=${item.identifier}`, + })); +}; + +// 格式化数据集 +export const formatDataset = (dataset?: DatasetData) => { + if (!dataset) { + return undefined; + } + return { + value: dataset.name, + url: `${origin}/dataset/dataset/info/${dataset.id}?tab=${ResourceInfoTabKeys.Introduction}&version=${dataset.version}&name=${dataset.name}&owner=${dataset.owner}&identifier=${dataset.identifier}`, + }; +}; + +// 获取代码配置的仓库的 url +export const getRepoUrl = (project?: ProjectDependency) => { + if (!project) { + return undefined; + } + const { url, branch } = project; + return getGitUrl(url, branch); +}; + +// 格式化代码配置 +export const formatCodeConfig = (project?: ProjectDependency) => { + if (!project) { + return undefined; + } + return { + value: project.name, + url: getRepoUrl(project), + }; +}; + +// 格式化选中的代码配置 +export const formatSelectCodeConfig = (value?: { + code_path: string; + branch: string; + showValue: string; +}) => { + if (!value) { + return undefined; + } + const { showValue, code_path, branch } = value; + return { + value: showValue, + url: getRepoUrl({ + url: code_path, + branch, + } as ProjectDependency), + }; +}; + +// 格式化训练任务(实验实例) +export const formatTrainTask = (task?: TrainTask) => { + if (!task) { + return undefined; + } + return { + value: task.name, + url: `${origin}/pipeline/experiment/instance/${task.workflow_id}/${task.ins_id}`, + }; +}; + +// 格式化数据来源 +export const formatSource = (source?: string) => { + if (source === DataSource.Create) { + return '用户上传'; + } else if (source === DataSource.HandExport) { + return '手动导入'; + } else if (source === DataSource.AtuoExport) { + return '实验自动导入'; + } + return source; +};