Browse Source

feat: 完成自动机器学习详情

pull/146/head
cp3hnu 1 year ago
parent
commit
605a0614e9
20 changed files with 616 additions and 250 deletions
  1. BIN
      react-ui/src/assets/img/dataset-config-icon.png
  2. +113
    -0
      react-ui/src/components/BasicInfo/components.tsx
  3. +48
    -0
      react-ui/src/components/BasicInfo/format.ts
  4. +4
    -118
      react-ui/src/components/BasicInfo/index.tsx
  5. +14
    -0
      react-ui/src/components/BasicInfo/types.ts
  6. +3
    -1
      react-ui/src/components/BasicTableInfo/index.tsx
  7. +11
    -1
      react-ui/src/enums/index.ts
  8. +1
    -1
      react-ui/src/pages/AutoML/Create/index.less
  9. +17
    -16
      react-ui/src/pages/AutoML/Create/index.tsx
  10. +41
    -8
      react-ui/src/pages/AutoML/List/index.tsx
  11. +6
    -0
      react-ui/src/pages/AutoML/components/AutoMLBasic/index.less
  12. +283
    -58
      react-ui/src/pages/AutoML/components/AutoMLBasic/index.tsx
  13. +2
    -1
      react-ui/src/pages/AutoML/components/ConfigInfo/index.tsx
  14. +1
    -1
      react-ui/src/pages/AutoML/components/CreateForm/DatasetConfig.tsx
  15. +13
    -14
      react-ui/src/pages/AutoML/components/CreateForm/ExecuteConfig.tsx
  16. +6
    -5
      react-ui/src/pages/AutoML/components/CreateForm/TrialConfig.tsx
  17. +14
    -1
      react-ui/src/pages/AutoML/components/CreateForm/index.less
  18. +27
    -23
      react-ui/src/pages/AutoML/types.ts
  19. +1
    -1
      react-ui/src/services/autoML.js
  20. +11
    -1
      react-ui/src/utils/index.ts

BIN
react-ui/src/assets/img/dataset-config-icon.png View File

Before After
Width: 51  |  Height: 51  |  Size: 3.4 kB

+ 113
- 0
react-ui/src/components/BasicInfo/components.tsx View File

@@ -0,0 +1,113 @@
/*
* @Author: 赵伟
* @Date: 2024-11-29 09:27:19
* @Description: 用于 BasicInfo 和 BasicTableInfo 组件的子组件
*/

import { Link } from '@umijs/max';
import { Typography } from 'antd';
import React from 'react';
import { type BasicInfoData, type BasicInfoLink } from './types';

type BasicInfoItemProps = {
data: BasicInfoData;
labelWidth: number;
classPrefix: string;
};

export function BasicInfoItem({ data, labelWidth, classPrefix }: BasicInfoItemProps) {
const { label, value, format, ellipsis } = data;
const formatValue = format ? format(value) : value;
const myClassName = `${classPrefix}__item`;
let valueComponent = undefined;
if (Array.isArray(formatValue)) {
valueComponent = (
<div className={`${myClassName}__value-container`}>
{formatValue.map((item: BasicInfoLink) => (
<BasicInfoItemValue
key={item.value}
value={item.value}
link={item.link}
url={item.url}
ellipsis={ellipsis}
classPrefix={classPrefix}
/>
))}
</div>
);
} else if (React.isValidElement(formatValue)) {
// 这个判断必须在下面的判断之前
valueComponent = (
<BasicInfoItemValue value={formatValue} ellipsis={ellipsis} classPrefix={classPrefix} />
);
} else if (typeof formatValue === 'object' && formatValue) {
valueComponent = (
<BasicInfoItemValue
value={formatValue.value}
link={formatValue.link}
url={formatValue.url}
ellipsis={ellipsis}
classPrefix={classPrefix}
/>
);
} else {
valueComponent = (
<BasicInfoItemValue value={formatValue} ellipsis={ellipsis} classPrefix={classPrefix} />
);
}
return (
<div className={myClassName} key={label}>
<div className={`${myClassName}__label`} style={{ width: labelWidth }}>
{label}
</div>
{valueComponent}
</div>
);
}

type BasicInfoItemValueProps = {
ellipsis?: boolean;
classPrefix: string;
value: string | React.ReactNode;
link?: string;
url?: string;
};

export function BasicInfoItemValue({
value,
link,
url,
ellipsis,
classPrefix,
}: BasicInfoItemValueProps) {
const myClassName = `${classPrefix}__item__value`;
let component = undefined;
if (url && value) {
component = (
<a className={`${myClassName}__link`} href={url} target="_blank" rel="noopener noreferrer">
{value}
</a>
);
} else if (link && value) {
component = (
<Link to={link} className={`${myClassName}__link`}>
{value}
</Link>
);
} else if (React.isValidElement(value)) {
return value;
} else {
component = <span className={`${myClassName}__text`}>{value ?? '--'}</span>;
}

return (
<div className={myClassName}>
<Typography.Text
ellipsis={ellipsis ? { tooltip: value } : false}
style={{ fontSize: 'inherit' }}
>
{component}
</Typography.Text>
</div>
);
}

+ 48
- 0
react-ui/src/components/BasicInfo/format.ts View File

@@ -0,0 +1,48 @@
/*
* @Author: 赵伟
* @Date: 2024-11-29 09:27:19
* @Description: 用于 BasicInfo 和 BasicTableInfo 组件的常用转化格式
*/

// 格式化日期
export { formatDate } from '@/utils/date';

/**
* 格式化字符串数组
* @param value - 字符串数组
* @returns 逗号分隔的字符串
*/
export const formatList = (value: string[] | null | undefined): string => {
if (
value === undefined ||
value === null ||
Array.isArray(value) === false ||
value.length === 0
) {
return '--';
}
return value.join(',');
};

/**
* 格式化布尔值
* @param value - 布尔值
* @returns "是" 或 "否"
*/
export const formatBoolean = (value: boolean): string => {
return value ? '是' : '否';
};

type FormatEnum = (value: string | number) => string;

/**
* 格式化枚举
* @param options - 枚举选项
* @returns 格式化枚举函数
*/
export const formatEnum = (options: { value: string | number; label: string }[]): FormatEnum => {
return (value: string | number) => {
const option = options.find((item) => item.value === value);
return option ? option.label : '--';
};
};

+ 4
- 118
react-ui/src/components/BasicInfo/index.tsx View File

@@ -1,21 +1,10 @@
import { Link } from '@umijs/max';
import { Typography } from 'antd';
import classNames from 'classnames';
import React from 'react';
import { BasicInfoItem } from './components';
import './index.less';

export type BasicInfoLink = {
value: string;
link?: string;
url?: string;
};

export type BasicInfoData = {
label: string;
value?: any;
ellipsis?: boolean;
format?: (_value?: any) => string | BasicInfoLink | BasicInfoLink[] | undefined;
};
import type { BasicInfoData, BasicInfoLink } from './types';
export * from './format';
export type { BasicInfoData, BasicInfoLink };

type BasicInfoProps = {
datas: BasicInfoData[];
@@ -24,20 +13,6 @@ type BasicInfoProps = {
labelWidth: number;
};

type BasicInfoItemProps = {
data: BasicInfoData;
labelWidth: number;
classPrefix: string;
};

type BasicInfoItemValueProps = {
ellipsis?: boolean;
classPrefix: string;
value: string | React.ReactNode;
link?: string;
url?: string;
};

export default function BasicInfo({ datas, className, style, labelWidth }: BasicInfoProps) {
return (
<div className={classNames('kf-basic-info', className)} style={style}>
@@ -52,92 +27,3 @@ export default function BasicInfo({ datas, className, style, labelWidth }: Basic
</div>
);
}

export function BasicInfoItem({ data, labelWidth, classPrefix }: BasicInfoItemProps) {
const { label, value, format, ellipsis } = data;
const formatValue = format ? format(value) : value;
const myClassName = `${classPrefix}__item`;
let valueComponent = undefined;
if (Array.isArray(formatValue)) {
valueComponent = (
<div className={`${myClassName}__value-container`}>
{formatValue.map((item: BasicInfoLink) => (
<BasicInfoItemValue
key={item.value}
value={item.value}
link={item.link}
url={item.url}
ellipsis={ellipsis}
classPrefix={classPrefix}
/>
))}
</div>
);
} else if (React.isValidElement(formatValue)) {
// 这个判断必须在下面的判断之前
valueComponent = (
<BasicInfoItemValue value={formatValue} ellipsis={ellipsis} classPrefix={classPrefix} />
);
} else if (typeof formatValue === 'object' && formatValue) {
valueComponent = (
<BasicInfoItemValue
value={formatValue.value}
link={formatValue.link}
url={formatValue.url}
ellipsis={ellipsis}
classPrefix={classPrefix}
/>
);
} else {
valueComponent = (
<BasicInfoItemValue value={formatValue} ellipsis={ellipsis} classPrefix={classPrefix} />
);
}
return (
<div className={myClassName} key={label}>
<div className={`${myClassName}__label`} style={{ width: labelWidth }}>
{label}
</div>
{valueComponent}
</div>
);
}

export function BasicInfoItemValue({
value,
link,
url,
ellipsis,
classPrefix,
}: BasicInfoItemValueProps) {
const myClassName = `${classPrefix}__item__value`;
let component = undefined;
if (url && value) {
component = (
<a className={`${myClassName}__link`} href={url} target="_blank" rel="noopener noreferrer">
{value}
</a>
);
} else if (link && value) {
component = (
<Link to={link} className={`${myClassName}__link`}>
{value}
</Link>
);
} else if (React.isValidElement(value)) {
return value;
} else {
component = <span className={`${myClassName}__text`}>{value ?? '--'}</span>;
}

return (
<div className={myClassName}>
<Typography.Text
ellipsis={ellipsis ? { tooltip: value } : false}
style={{ fontSize: 'inherit' }}
>
{component}
</Typography.Text>
</div>
);
}

+ 14
- 0
react-ui/src/components/BasicInfo/types.ts View File

@@ -0,0 +1,14 @@
// 基础信息
export type BasicInfoData = {
label: string;
value?: any;
ellipsis?: boolean;
format?: (_value?: any) => string | BasicInfoLink | BasicInfoLink[] | undefined;
};

// 值为链接的类型
export type BasicInfoLink = {
value: string;
link?: string;
url?: string;
};

+ 3
- 1
react-ui/src/components/BasicTableInfo/index.tsx View File

@@ -1,6 +1,8 @@
import classNames from 'classnames';
import { BasicInfoItem, type BasicInfoData, type BasicInfoLink } from '../BasicInfo';
import { BasicInfoItem } from '../BasicInfo/components';
import { type BasicInfoData, type BasicInfoLink } from '../BasicInfo/types';
import './index.less';
export * from '../BasicInfo/format';
export type { BasicInfoData, BasicInfoLink };

type BasicTableInfoProps = {


+ 11
- 1
react-ui/src/enums/index.ts View File

@@ -92,19 +92,29 @@ export enum AutoMLTaskType {
Regression = 'regression',
}

export const autoMLTaskTypeOptions = [
{ label: '分类', value: AutoMLTaskType.Classification },
{ label: '回归', value: AutoMLTaskType.Regression },
];

// 自动化任务集成策略
export enum AutoMLEnsembleClass {
Default = 'default',
SingleBest = 'SingleBest',
}

export const autoMLEnsembleClassOptions = [
{ label: '集成模型', value: AutoMLEnsembleClass.Default },
{ label: '单一最佳模型', value: AutoMLEnsembleClass.SingleBest },
];

// 自动化任务重采样策略
export enum AutoMLResamplingStrategy {
Holdout = 'holdout',
CrossValid = 'crossValid',
}

export const resamplingStrategyOptions = [
export const autoMLResamplingStrategyOptions = [
{ label: 'holdout', value: AutoMLResamplingStrategy.Holdout },
{ label: 'crossValid', value: AutoMLResamplingStrategy.CrossValid },
];

+ 1
- 1
react-ui/src/pages/AutoML/Create/index.less View File

@@ -1,4 +1,4 @@
.create-service-version {
.create-automl {
height: 100%;

&__content {


+ 17
- 16
react-ui/src/pages/AutoML/Create/index.tsx View File

@@ -5,9 +5,9 @@
*/
import PageTitle from '@/components/PageTitle';

import { AutoMLTaskType } from '@/enums';
import { addAutoMLReq, getDatasetInfoReq, updateAutoMLReq } from '@/services/autoML';
import { parseJsonText, trimCharacter } from '@/utils';
import { AutoMLEnsembleClass, AutoMLTaskType } from '@/enums';
import { addAutoMLReq, getAutoMLInfoReq, updateAutoMLReq } from '@/services/autoML';
import { convertEmptyStringToUndefined, parseJsonText, trimCharacter } from '@/utils';
import { safeInvoke } from '@/utils/functional';
import { to } from '@/utils/promise';
import SessionStorage from '@/utils/sessionStorage';
@@ -49,7 +49,7 @@ function CreateAutoML() {

// 获取服务详情
const getAutoMLInfo = async (id: number, isCopy = false) => {
const [res] = await to(getDatasetInfoReq({ id }));
const [res] = await to(getAutoMLInfoReq({ id }));
if (res && res.data) {
const autoMLInfo: AutoMLData = res.data;
const {
@@ -96,7 +96,7 @@ function CreateAutoML() {
}
};

// 创建版本
// 创建、更新、复制实验
const createExperiment = async (formData: FormData) => {
const include_classifier = formData['include_classifier']?.join(',');
const include_feature_preprocessor = formData['include_feature_preprocessor']?.join(',');
@@ -113,12 +113,12 @@ function CreateAutoML() {
// 根据后台要求,修改表单数据
const object = {
...omit(formData),
include_classifier,
include_feature_preprocessor,
include_regressor,
exclude_classifier,
exclude_feature_preprocessor,
exclude_regressor,
include_classifier: convertEmptyStringToUndefined(include_classifier),
include_feature_preprocessor: convertEmptyStringToUndefined(include_feature_preprocessor),
include_regressor: convertEmptyStringToUndefined(include_regressor),
exclude_classifier: convertEmptyStringToUndefined(exclude_classifier),
exclude_feature_preprocessor: convertEmptyStringToUndefined(exclude_feature_preprocessor),
exclude_regressor: convertEmptyStringToUndefined(exclude_regressor),
metrics: metrics ? JSON.stringify(metrics) : undefined,
target_columns,
};
@@ -148,7 +148,6 @@ function CreateAutoML() {
navigate(-1);
};

const disabled = id !== null || id !== undefined;
let buttonText = '新建';
let title = '新增实验';
if (id) {
@@ -157,26 +156,28 @@ function CreateAutoML() {
}

return (
<div className={styles['create-service-version']}>
<div className={styles['create-automl']}>
<PageTitle title={title}></PageTitle>
<div className={styles['create-service-version__content']}>
<div className={styles['create-automl__content']}>
<div>
<Form
name="create-service-version"
name="create-automl"
labelCol={{ flex: '160px' }}
labelAlign="left"
form={form}
onFinish={handleSubmit}
size="large"
autoComplete="off"
scrollToFirstError
initialValues={{
task_type: AutoMLTaskType.Classification,
shuffle: false,
ensemble_class: AutoMLEnsembleClass.Default,
greater_is_better: true,
}}
>
<BasicConfig />
<ExecuteConfig disabled={disabled} />
<ExecuteConfig />
<TrialConfig />
<DatasetConfig />



+ 41
- 8
react-ui/src/pages/AutoML/List/index.tsx View File

@@ -6,7 +6,7 @@
import KFIcon from '@/components/KFIcon';
import PageTitle from '@/components/PageTitle';
import { useCacheState } from '@/hooks/pageCacheState';
import { deleteAutoMLReq, getAutoMLListReq } from '@/services/autoML';
import { deleteAutoMLReq, getAutoMLListReq, runAutoMLReq } from '@/services/autoML';
import themes from '@/styles/theme.less';
import { to } from '@/utils/promise';
import SessionStorage from '@/utils/sessionStorage';
@@ -129,6 +129,24 @@ function AutoMLList() {
navigate(`/pipeline/autoML/info/${record.id}`);
};

// 启动
const startAutoML = async (record: AutoMLData) => {
const [res] = await to(runAutoMLReq(record.id));
if (res) {
message.success('操作成功');
getServiceList();
}
};

// 停止
const stopAutoML = async (record: AutoMLData) => {
const [res] = await to(runAutoMLReq(record.id));
if (res) {
message.success('操作成功');
getServiceList();
}
};

// 分页切换
const handleTableChange: TableProps<AutoMLData>['onChange'] = (
pagination,
@@ -202,13 +220,10 @@ function AutoMLList() {
{
title: '操作',
dataIndex: 'operation',
width: 400,
width: 320,
key: 'operation',
render: (_: any, record: AutoMLData) => (
<div>
<Button type="link" size="small" key="mirror" icon={<KFIcon type="icon-jingxiang" />}>
镜像
</Button>
<Button
type="link"
size="small"
@@ -227,9 +242,27 @@ function AutoMLList() {
>
复制
</Button>
<Button type="link" size="small" key="stop" icon={<KFIcon type="icon-tingzhi" />}>
停止
</Button>
{record.run_state === 'Running' ? (
<Button
type="link"
size="small"
key="stop"
icon={<KFIcon type="icon-tingzhi" />}
onClick={() => startAutoML(record)}
>
停止
</Button>
) : (
<Button
type="link"
size="small"
key="start"
icon={<KFIcon type="icon-yunhang" />}
onClick={() => stopAutoML(record)}
>
运行
</Button>
)}
<ConfigProvider
theme={{
token: {


+ 6
- 0
react-ui/src/pages/AutoML/components/AutoMLBasic/index.less View File

@@ -4,4 +4,10 @@
overflow-y: auto;
background-color: white;
border-radius: 10px;

:global {
.kf-basic-info__item__value__text {
white-space: pre;
}
}
}

+ 283
- 58
react-ui/src/pages/AutoML/components/AutoMLBasic/index.tsx View File

@@ -1,78 +1,303 @@
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 } from 'react';
import ConfigInfo, { type BasicInfoData } from '../ConfigInfo';
import CopyingText from '../CopyingText';
import StatusChart from '../StatusChart';
import { useEffect, useState } from 'react';
import ConfigInfo, {
formatBoolean,
formatDate,
formatEnum,
type BasicInfoData,
} from '../ConfigInfo';
// import CopyingText from '../CopyingText';
import { AutoMLTaskType, autoMLEnsembleClassOptions, autoMLTaskTypeOptions } from '@/enums';
import { parseJsonText } from '@/utils';
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 ? '越大越好' : '越小越好';
};

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');
};

function AutoMLBasic() {
useEffect(() => {}, []);
const params = useParams();
const id = safeInvoke(Number)(params.id);
const [basicDatas, setBasicDatas] = useState<BasicInfoData[]>([]);
const [configDatas, setConfigDatas] = useState<BasicInfoData[]>([]);

useEffect(() => {
if (id) {
getAutoMLInfo(id);
}
}, []);

const datas: 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,
},
];
// 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,
// },
// ];

// 获取服务详情
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[] = [
{
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,
},
{
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);
}
};

return (
<div className={styles['auto-ml-basic']}>
<Flex gap={15} align="stretch">
<ConfigInfo title="基本信息" data={datas} labelWidth={70} />
<StatusChart
<ConfigInfo title="基本信息" data={basicDatas} labelWidth={70} threeColumn />
{/* <StatusChart
chartData={{ Failed: 10, Pending: 20, Running: 30, Succeeded: 40, Terminated: 50 }}
/>
<ConfigInfo title="Trial 配置" data={datas} labelWidth={70} />
/> */}
</Flex>
<ConfigInfo
title="搜索配置"
data={datas}
style={{ marginTop: '16px' }}
labelWidth={70}
title="配置信息"
data={configDatas.slice(0, -3)}
labelWidth={150}
threeColumn
style={{ marginTop: '20px' }}
/>
<ConfigInfo
title="执行配置"
data={datas}
style={{ marginTop: '16px' }}
title="优化指标"
data={configDatas.slice(-3)}
labelWidth={70}
threeColumn
style={{ marginTop: '20px' }}
/>
</div>
);


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

@@ -3,7 +3,8 @@ import classNames from 'classnames';
import { useEffect } from 'react';
import ConfigTitle from '../ConfigTitle';
import styles from './index.less';
export { type BasicInfoData };
export * from '@/components/BasicInfo/format';
export type { BasicInfoData };

type ConfigInfoProps = {
title: string;


+ 1
- 1
react-ui/src/pages/AutoML/components/CreateForm/DatasetConfig.tsx View File

@@ -10,7 +10,7 @@ function DatasetConfig() {
<>
<SubAreaTitle
title="数据集配置"
image={require('@/assets/img/search-config-icon.png')}
image={require('@/assets/img/dataset-config-icon.png')}
style={{ marginTop: '20px', marginBottom: '24px' }}
></SubAreaTitle>
<Row gutter={8}>


+ 13
- 14
react-ui/src/pages/AutoML/components/CreateForm/ExecuteConfig.tsx View File

@@ -3,9 +3,11 @@ import {
AutoMLEnsembleClass,
AutoMLResamplingStrategy,
AutoMLTaskType,
resamplingStrategyOptions,
autoMLEnsembleClassOptions,
autoMLResamplingStrategyOptions,
autoMLTaskTypeOptions,
} from '@/enums';
import { Col, Form, Input, InputNumber, Radio, Row, Select, Switch } from 'antd';
import { Col, Form, InputNumber, Radio, Row, Select, Switch } from 'antd';

// 分类算法
const classificationAlgorithms = [
@@ -120,10 +122,10 @@ function ExecuteConfig() {
name="task_type"
rules={[{ required: true, message: '请选择任务类型' }]}
>
<Radio.Group onChange={() => form.resetFields(['metrics'])}>
<Radio value={AutoMLTaskType.Classification}>分类</Radio>
<Radio value={AutoMLTaskType.Regression}>回归</Radio>
</Radio.Group>
<Radio.Group
options={autoMLTaskTypeOptions}
onChange={() => form.resetFields(['metrics'])}
></Radio.Group>
</Form.Item>
</Col>
</Row>
@@ -259,10 +261,7 @@ function ExecuteConfig() {
name="ensemble_class"
tooltip="仅使用单个最佳模型还是集成模型"
>
<Radio.Group>
<Radio value={AutoMLEnsembleClass.Default}>集成模型</Radio>
<Radio value={AutoMLEnsembleClass.SingleBest}>单一最佳模型</Radio>
</Radio.Group>
<Radio.Group options={autoMLEnsembleClassOptions}></Radio.Group>
</Form.Item>
</Col>
</Row>
@@ -357,7 +356,7 @@ function ExecuteConfig() {
<Select
allowClear
placeholder="请选择重采样策略"
options={resamplingStrategyOptions}
options={autoMLResamplingStrategyOptions}
showSearch
/>
</Form.Item>
@@ -389,7 +388,7 @@ function ExecuteConfig() {

<Row gutter={8}>
<Col span={10}>
<Form.Item label="shuffle" name="shuffle" tooltip="拆分数据前是否进行 shuffle">
<Form.Item label="是否打乱" name="shuffle" tooltip="拆分数据前是否打乱顺序">
<Switch />
</Form.Item>
</Col>
@@ -444,7 +443,7 @@ function ExecuteConfig() {
</Col>
</Row>

<Row gutter={8}>
{/* <Row gutter={8}>
<Col span={10}>
<Form.Item
label="文件夹路径"
@@ -461,7 +460,7 @@ function ExecuteConfig() {
<Input placeholder="请输入文件夹路径" maxLength={64} showCount allowClear />
</Form.Item>
</Col>
</Row>
</Row> */}

{/* <Form.List name="hyper-parameter">
{(fields, { add, remove }) => (


+ 6
- 5
react-ui/src/pages/AutoML/components/CreateForm/TrialConfig.tsx View File

@@ -21,7 +21,7 @@ function TrialConfig() {
<SubAreaTitle
title="优化指标"
image={require('@/assets/img/trial-config-icon.png')}
style={{ marginBottom: '26px' }}
style={{ marginTop: '20px', marginBottom: '24px' }}
></SubAreaTitle>
<Row gutter={8}>
<Col span={10}>
@@ -37,7 +37,7 @@ function TrialConfig() {
{(fields, { add, remove }) => (
<>
{fields.map(({ key, name, ...restField }, index) => (
<Flex key={key} align="flex-start" className={styles['advanced-config']}>
<Flex key={key} align="flex-start" className={styles['metrics-weight']}>
<Form.Item
style={{ flex: 1, marginBottom: 0, minWidth: 0 }}
{...restField}
@@ -88,10 +88,11 @@ function TrialConfig() {
</Flex>
))}
{fields.length === 0 && (
<Form.Item style={{ marginBottom: 0 }}>
<Form.Item className={styles['add-weight']}>
<Button
style={{ background: 'white' }}
type="dashed"
className={styles['add-weight__button']}
color="primary"
variant="dashed"
onClick={() => add()}
block
icon={<PlusCircleOutlined />}


+ 14
- 1
react-ui/src/pages/AutoML/components/CreateForm/index.less View File

@@ -1,4 +1,4 @@
.advanced-config {
.metrics-weight {
margin-bottom: 20px;

&:last-child {
@@ -6,6 +6,19 @@
}
}

.add-weight {
margin-bottom: 0;

// 增加样式权重
& &__button {
border-color: .addAlpha(@primary-color, 0.5) [];
box-shadow: none !important;
&:hover {
border-style: solid;
}
}
}

// .command {
// width: 83.33%;
// margin-bottom: 20px;


+ 27
- 23
react-ui/src/pages/AutoML/types.ts View File

@@ -10,32 +10,32 @@ export enum OperationType {
export type FormData = {
ml_name: string; // 实验名称
ml_description: string; // 实验描述
ensemble_class?: string; // 集成构建
ensemble_nbest?: string;
ensemble_size?: number;
include_classifier?: string[];
include_feature_preprocessor?: string[];
include_regressor?: string[];
ensemble_class?: string; // 集成方式
ensemble_nbest?: string; // 集成最佳模型数量
ensemble_size?: number; // 集成模型数量
include_classifier?: string[]; // 分类算法
include_feature_preprocessor?: string[]; // 特征预处理算法
include_regressor?: string[]; // 回归算法
exclude_classifier?: string[];
exclude_feature_preprocessor?: string[];
exclude_regressor?: string[];
max_models_on_disc?: number;
memory_limit?: number;
metric_name?: string;
greater_is_better: boolean;
per_run_time_limit?: number;
resampling_strategy?: string;
scoring_functions?: string;
shuffle?: boolean;
seed?: number;
target_columns: string;
task_type: string;
test_size?: number;
train_size?: number;
time_left_for_this_task: number;
tmp_folder?: string;
metrics?: { name: string; value: number }[];
dataset: ParameterInputObject; // 模型
max_models_on_disc?: number; // 最大数量
memory_limit?: number; // 内存限制(MB)
per_run_time_limit?: number; // 时间限制(秒)
resampling_strategy?: string; // 重采样策略
folds?: number; // 交叉验证折数
scoring_functions?: string; // 计算指标
shuffle?: boolean; // 是否打乱
seed?: number; // 随机种子
task_type: string; // 任务类型
test_size?: number; // 测试集比率
train_size?: number; // 训练集比率
time_left_for_this_task: number; // 搜索时间限制(秒)
metric_name?: string; // 指标名称
greater_is_better: boolean; // 指标优化方向
metrics?: { name: string; value: number }[]; // 指标权重
dataset: ParameterInputObject; // 数据集
target_columns: string; // 预测目标列
};

export type AutoMLData = {
@@ -51,6 +51,10 @@ export type AutoMLData = {
exclude_feature_preprocessor?: string;
exclude_regressor?: string;
dataset?: string;
create_by?: string;
create_time?: string;
update_by?: string;
update_time?: string;
} & Omit<
FormData,
'metrics|dataset|include_classifier|include_feature_preprocessor|include_regressor|exclude_classifier|exclude_feature_preprocessor|exclude_regressor'


+ 1
- 1
react-ui/src/services/autoML.js View File

@@ -16,7 +16,7 @@ export function getAutoMLListReq(params) {
}

// 查询自动学习详情
export function getDatasetInfoReq(params) {
export function getAutoMLInfoReq(params) {
return request(`/api/mmp/autoML/getAutoMlDetail`, {
method: 'GET',
params,


+ 11
- 1
react-ui/src/utils/index.ts View File

@@ -198,7 +198,7 @@ export const fittingString = (str: string, maxWidth: number, fontSize: number):
* @param {any} str - the string to be checked
* @return {boolean} true if the string is empty, undefined, or null, false otherwise
*/
export const isEmptyString = (str: any): boolean => {
export const isEmpty = (str: any): boolean => {
return str === '' || str === undefined || str === null;
};

@@ -256,3 +256,13 @@ export const trimCharacter = (str: string, ch: string): string => {
const reg = new RegExp(`^${ch}|${ch}$`, 'g');
return str.trim().replace(reg, '');
};

/**
* Converts an empty string to undefined.
*
* @param {string} [value] - The string to convert.
* @return {string | undefined} The converted string or undefined.
*/
export const convertEmptyStringToUndefined = (value?: string): string | undefined => {
return value === '' ? undefined : value;
};

Loading…
Cancel
Save