Browse Source

feat: 完成自动机器学习接口联调

pull/146/head
cp3hnu 1 year ago
parent
commit
e844fc614e
17 changed files with 995 additions and 397 deletions
  1. +6
    -1
      react-ui/config/routes.ts
  2. +24
    -0
      react-ui/src/enums/index.ts
  3. +0
    -1
      react-ui/src/pages/Authorize/index.tsx
  4. +90
    -124
      react-ui/src/pages/AutoML/Create/index.tsx
  5. +34
    -46
      react-ui/src/pages/AutoML/List/index.tsx
  6. +3
    -3
      react-ui/src/pages/AutoML/components/AutoMLTable/index.tsx
  7. +11
    -20
      react-ui/src/pages/AutoML/components/CreateForm/BasicConfig.tsx
  8. +59
    -0
      react-ui/src/pages/AutoML/components/CreateForm/DatasetConfig.tsx
  9. +434
    -21
      react-ui/src/pages/AutoML/components/CreateForm/ExecuteConfig.tsx
  10. +16
    -13
      react-ui/src/pages/AutoML/components/CreateForm/ExecuteConfigDLC.tsx
  11. +97
    -166
      react-ui/src/pages/AutoML/components/CreateForm/TrialConfig.tsx
  12. +80
    -0
      react-ui/src/pages/AutoML/components/CreateForm/UploadConfig.tsx
  13. +11
    -0
      react-ui/src/pages/AutoML/components/CreateForm/index.less
  14. +50
    -2
      react-ui/src/pages/AutoML/types.ts
  15. +55
    -0
      react-ui/src/services/autoML.js
  16. +10
    -0
      react-ui/src/utils/functional.ts
  17. +15
    -0
      react-ui/src/utils/index.ts

+ 6
- 1
react-ui/config/routes.ts View File

@@ -160,10 +160,15 @@ export default [
component: './AutoML/Info/index', component: './AutoML/Info/index',
}, },
{ {
name: '创建自动机器学习',
name: '创建实验',
path: 'create', path: 'create',
component: './AutoML/Create/index', component: './AutoML/Create/index',
}, },
{
name: '编辑实验',
path: 'edit/:id',
component: './AutoML/Create/index',
},
], ],
}, },
], ],


+ 24
- 0
react-ui/src/enums/index.ts View File

@@ -71,6 +71,7 @@ export enum DevEditorStatus {
Unknown = 'Unknown', // 未启动 Unknown = 'Unknown', // 未启动
} }


// 服务类型
export enum ServiceType { export enum ServiceType {
Video = 'video', Video = 'video',
Image = 'image', Image = 'image',
@@ -84,3 +85,26 @@ export const serviceTypeOptions = [
{ label: '音频', value: ServiceType.Audio }, { label: '音频', value: ServiceType.Audio },
{ label: '文本', value: ServiceType.Text }, { label: '文本', value: ServiceType.Text },
]; ];

// 自动化任务类型
export enum AutoMLTaskType {
Classification = 'classification',
Regression = 'regression',
}

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

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

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

+ 0
- 1
react-ui/src/pages/Authorize/index.tsx View File

@@ -22,7 +22,6 @@ function Authorize() {
code, code,
}; };
const [res] = await to(loginByOauth2Req(params)); const [res] = await to(loginByOauth2Req(params));
debugger;
if (res && res.data) { if (res && res.data) {
const { access_token, expires_in } = res.data; const { access_token, expires_in } = res.data;
setSessionToken(access_token, access_token, expires_in); setSessionToken(access_token, access_token, expires_in);


+ 90
- 124
react-ui/src/pages/AutoML/Create/index.tsx View File

@@ -4,147 +4,116 @@
* @Description: 创建服务版本 * @Description: 创建服务版本
*/ */
import PageTitle from '@/components/PageTitle'; import PageTitle from '@/components/PageTitle';
import { type ParameterInputObject } from '@/components/ResourceSelect';
import { useComputingResource } from '@/hooks/resource';
import {
createServiceVersionReq,
getServiceInfoReq,
updateServiceVersionReq,
} from '@/services/modelDeployment';
import { changePropertyName } from '@/utils';

import { AutoMLTaskType } from '@/enums';
import { addAutoMLReq, getDatasetInfoReq, updateAutoMLReq } from '@/services/autoML';
import { parseJsonText, trimCharacter } from '@/utils';
import { safeInvoke } from '@/utils/functional';
import { to } from '@/utils/promise'; import { to } from '@/utils/promise';
import SessionStorage from '@/utils/sessionStorage';
import { useNavigate, useParams } from '@umijs/max'; import { useNavigate, useParams } from '@umijs/max';
import { App, Button, Form } from 'antd'; import { App, Button, Form } from 'antd';
import { omit, pick } from 'lodash';
import { useEffect, useState } from 'react';
import { omit } from 'lodash';
import { useEffect } from 'react';
import BasicConfig from '../components/CreateForm/BasicConfig'; import BasicConfig from '../components/CreateForm/BasicConfig';
import DatasetConfig from '../components/CreateForm/DatasetConfig';
import ExecuteConfig from '../components/CreateForm/ExecuteConfig'; import ExecuteConfig from '../components/CreateForm/ExecuteConfig';
import SearchConfig from '../components/CreateForm/SearchConfig';
import TrialConfig from '../components/CreateForm/TrialConfig'; import TrialConfig from '../components/CreateForm/TrialConfig';
import { ServiceData, ServiceOperationType, ServiceVersionData } from '../types';
import { AutoMLData, FormData } from '../types';
import styles from './index.less'; import styles from './index.less';


// 表单数据
export type FormData = {
service_name: string; // 服务名称
version: string; // 服务版本
description: string; // 描述
model: ParameterInputObject; // 模型
image: ParameterInputObject; // 镜像
code_config: ParameterInputObject; // 代码
resource: string; // 资源规格
replicas: string; // 副本数量
mount_path: string; // 模型路径
env_variables: { key: string; value: string }[]; // 环境变量
};

function CreateAutoML() { function CreateAutoML() {
const navigate = useNavigate(); const navigate = useNavigate();
const [form] = Form.useForm(); const [form] = Form.useForm();
const [resourceStandardList, filterResourceStandard] = useComputingResource();
const [operationType, setOperationType] = useState(ServiceOperationType.Create);

const { message } = App.useApp(); const { message } = App.useApp();
const [serviceInfo, setServiceInfo] = useState<ServiceData | undefined>(undefined);
const [versionInfo, setVersionInfo] = useState<ServiceVersionData | undefined>(undefined);
const params = useParams(); const params = useParams();
const id = params.id;
const id = safeInvoke(Number)(params.id);


useEffect(() => { useEffect(() => {
const res:
| (ServiceVersionData & {
operationType: ServiceOperationType;
})
| undefined = SessionStorage.getItem(SessionStorage.serviceVersionInfoKey, true);
if (res) {
setOperationType(res.operationType);

setVersionInfo(res);
let model, codeConfig, envVariables;
if (res.model && typeof res.model === 'object') {
model = changePropertyName(res.model, { show_value: 'showValue' });
// 接口返回是数据没有 value 值,但是 form 需要 value
model.value = model.showValue;
}
if (res.code_config && typeof res.code_config === 'object') {
codeConfig = changePropertyName(res.code_config, { show_value: 'showValue' });
// 接口返回是数据没有 value 值,但是 form 需要 value
codeConfig.value = codeConfig.showValue;
}
if (res.env_variables && typeof res.env_variables === 'object') {
envVariables = Object.entries(res.env_variables).map(([key, value]) => ({
key,
value,
}));
}

const formData = {
...omit(res, 'model', 'code_config', 'env_variables'),
model: model,
code_config: codeConfig,
env_variables: envVariables,
};
form.setFieldsValue(formData);
if (id) {
getAutoMLInfo();
} }
return () => {
SessionStorage.removeItem(SessionStorage.serviceVersionInfoKey);
};
}, []);
}, [id]);


// 获取服务详情 // 获取服务详情
const getServiceInfo = async () => {
const [res] = await to(getServiceInfoReq(id));
const getAutoMLInfo = async () => {
const [res] = await to(getDatasetInfoReq({ id }));
if (res && res.data) { if (res && res.data) {
setServiceInfo(res.data);
form.setFieldsValue({
service_name: res.data.service_name,
});
const autoMLInfo: AutoMLData = res.data;
const {
include_classifier: include_classifier_str,
include_feature_preprocessor: include_feature_preprocessor_str,
include_regressor: include_regressor_str,
exclude_classifier: exclude_classifier_str,
exclude_feature_preprocessor: exclude_feature_preprocessor_str,
exclude_regressor: exclude_regressor_str,
metrics: metrics_str,
} = autoMLInfo;
const include_classifier = include_classifier_str?.split(',').filter(Boolean);
const include_feature_preprocessor = include_feature_preprocessor_str
?.split(',')
.filter(Boolean);
const include_regressor = include_regressor_str?.split(',').filter(Boolean);
const exclude_classifier = exclude_classifier_str?.split(',').filter(Boolean);
const exclude_feature_preprocessor = exclude_feature_preprocessor_str
?.split(',')
.filter(Boolean);
const exclude_regressor = exclude_regressor_str?.split(',').filter(Boolean);
const metricsObj = safeInvoke(parseJsonText)(metrics_str) ?? {};
const metrics = Object.entries(metricsObj).map(([key, value]) => ({
name: key,
value,
}));

const formData = {
...autoMLInfo,
include_classifier,
include_feature_preprocessor,
include_regressor,
exclude_classifier,
exclude_feature_preprocessor,
exclude_regressor,
metrics,
};

form.setFieldsValue(formData);
} }
}; };


// 创建版本 // 创建版本
const createServiceVersion = async (formData: FormData) => {
const envList = formData['env_variables'] ?? [];
const image = formData['image'];
const model = formData['model'];
const codeConfig = formData['code_config'];
const envVariables = envList.reduce((acc, cur) => {
acc[cur.key] = cur.value;
const createExperiment = async (formData: FormData) => {
const include_classifier = formData['include_classifier']?.join(',');
const include_feature_preprocessor = formData['include_feature_preprocessor']?.join(',');
const include_regressor = formData['include_regressor']?.join(',');
const exclude_classifier = formData['exclude_classifier']?.join(',');
const exclude_feature_preprocessor = formData['exclude_feature_preprocessor']?.join(',');
const exclude_regressor = formData['exclude_regressor']?.join(',');
const metrics = formData['metrics']?.reduce((acc, cur) => {
acc[cur.name] = cur.value;
return acc; return acc;
}, {} as Record<string, string>);
}, {} as Record<string, number>);
const target_columns = trimCharacter(formData['target_columns'], ',');


// 根据后台要求,修改表单数据 // 根据后台要求,修改表单数据
const object = { const object = {
...omit(formData, ['replicas', 'env_variables', 'image', 'model', 'code_config']),
replicas: Number(formData.replicas),
env_variables: envVariables,
image: image.value,
model: changePropertyName(
pick(model, ['id', 'name', 'version', 'path', 'identifier', 'owner', 'showValue']),
{ showValue: 'show_value' },
),
code_config: changePropertyName(pick(codeConfig, ['code_path', 'branch', 'showValue']), {
showValue: 'show_value',
}),
service_id: serviceInfo?.id,
...omit(formData),
include_classifier,
include_feature_preprocessor,
include_regressor,
exclude_classifier,
exclude_feature_preprocessor,
exclude_regressor,
metrics: metrics ? JSON.stringify(metrics) : undefined,
target_columns,
}; };


const params =
operationType === ServiceOperationType.Create
? object
: {
id: versionInfo?.id,
rerun: operationType === ServiceOperationType.Restart ? true : false,
deployment_name: versionInfo?.deployment_name,
...object,
};

const request =
operationType === ServiceOperationType.Create
? createServiceVersionReq
: updateServiceVersionReq;
const params = id
? {
id: id,
...object,
}
: object;


const request = id ? updateAutoMLReq : addAutoMLReq;
const [res] = await to(request(params)); const [res] = await to(request(params));
if (res) { if (res) {
message.success('操作成功'); message.success('操作成功');
@@ -154,7 +123,7 @@ function CreateAutoML() {


// 提交 // 提交
const handleSubmit = (values: FormData) => { const handleSubmit = (values: FormData) => {
console.log('values', values);
createExperiment(values);
}; };


// 取消 // 取消
@@ -162,15 +131,12 @@ function CreateAutoML() {
navigate(-1); navigate(-1);
}; };


const disabled = operationType !== ServiceOperationType.Create;
const disabled = id !== null || id !== undefined;
let buttonText = '新建'; let buttonText = '新建';
let title = '新增服务版本';
if (operationType === ServiceOperationType.Update) {
title = '更新服务版本';
let title = '新增实验';
if (id) {
title = '更新实验';
buttonText = '更新'; buttonText = '更新';
} else if (operationType === ServiceOperationType.Restart) {
title = '重启服务版本';
buttonText = '重启';
} }


return ( return (
@@ -180,22 +146,22 @@ function CreateAutoML() {
<div> <div>
<Form <Form
name="create-service-version" name="create-service-version"
labelCol={{ flex: '140px' }}
labelCol={{ flex: '160px' }}
labelAlign="left" labelAlign="left"
form={form} form={form}
onFinish={handleSubmit} onFinish={handleSubmit}
size="large" size="large"
autoComplete="off" autoComplete="off"
initialValues={{ initialValues={{
execute_type: 'DLC',
image_type: 3,
advanced_config: [{ key: '', value: '' }],
task_type: AutoMLTaskType.Classification,
shuffle: false,
greater_is_better: true,
}} }}
> >
<BasicConfig /> <BasicConfig />
<ExecuteConfig disabled={disabled} /> <ExecuteConfig disabled={disabled} />
<TrialConfig /> <TrialConfig />
<SearchConfig />
<DatasetConfig />


<Form.Item wrapperCol={{ offset: 0, span: 16 }}> <Form.Item wrapperCol={{ offset: 0, span: 16 }}>
<Button type="primary" htmlType="submit"> <Button type="primary" htmlType="submit">


+ 34
- 46
react-ui/src/pages/AutoML/List/index.tsx View File

@@ -1,15 +1,14 @@
/* /*
* @Author: 赵伟 * @Author: 赵伟
* @Date: 2024-04-16 13:58:08 * @Date: 2024-04-16 13:58:08
* @Description: 自主机器学习
* @Description: 自主机器学习列表
*/ */
import KFIcon from '@/components/KFIcon'; import KFIcon from '@/components/KFIcon';
import PageTitle from '@/components/PageTitle'; import PageTitle from '@/components/PageTitle';
import { useCacheState } from '@/hooks/pageCacheState'; import { useCacheState } from '@/hooks/pageCacheState';
import { deleteServiceReq, getServiceListReq } from '@/services/modelDeployment';
import { deleteAutoMLReq, getAutoMLListReq } from '@/services/autoML';
import themes from '@/styles/theme.less'; import themes from '@/styles/theme.less';
import { to } from '@/utils/promise'; import { to } from '@/utils/promise';
import SessionStorage from '@/utils/sessionStorage';
import tableCellRender, { TableCellValueType } from '@/utils/table'; import tableCellRender, { TableCellValueType } from '@/utils/table';
import { modalConfirm } from '@/utils/ui'; import { modalConfirm } from '@/utils/ui';
import { useNavigate } from '@umijs/max'; import { useNavigate } from '@umijs/max';
@@ -27,7 +26,7 @@ import classNames from 'classnames';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import ExecuteScheduleCell from '../components/ExecuteScheduleCell'; import ExecuteScheduleCell from '../components/ExecuteScheduleCell';
import RunStatusCell from '../components/RunStatusCell'; import RunStatusCell from '../components/RunStatusCell';
import { ServiceData, ServiceOperationType } from '../types';
import { AutoMLData } from '../types';
import styles from './index.less'; import styles from './index.less';


function AutoMLList() { function AutoMLList() {
@@ -36,7 +35,7 @@ function AutoMLList() {
const [cacheState, setCacheState] = useCacheState(); const [cacheState, setCacheState] = useCacheState();
const [searchText, setSearchText] = useState(cacheState?.searchText); const [searchText, setSearchText] = useState(cacheState?.searchText);
const [inputText, setInputText] = useState(cacheState?.searchText); const [inputText, setInputText] = useState(cacheState?.searchText);
const [tableData, setTableData] = useState<ServiceData[]>([]);
const [tableData, setTableData] = useState<AutoMLData[]>([]);
const [total, setTotal] = useState(0); const [total, setTotal] = useState(0);
const [pagination, setPagination] = useState<TablePaginationConfig>( const [pagination, setPagination] = useState<TablePaginationConfig>(
cacheState?.pagination ?? { cacheState?.pagination ?? {
@@ -54,9 +53,9 @@ function AutoMLList() {
const params: Record<string, any> = { const params: Record<string, any> = {
page: pagination.current! - 1, page: pagination.current! - 1,
size: pagination.pageSize, size: pagination.pageSize,
service_name: searchText,
ml_name: searchText,
}; };
const [res] = await to(getServiceListReq(params));
const [res] = await to(getAutoMLListReq(params));
if (res && res.data) { if (res && res.data) {
const { content = [], totalElements = 0 } = res.data; const { content = [], totalElements = 0 } = res.data;
setTableData(content); setTableData(content);
@@ -65,8 +64,8 @@ function AutoMLList() {
}; };


// 删除模型部署 // 删除模型部署
const deleteService = async (record: ServiceData) => {
const [res] = await to(deleteServiceReq(record.id));
const deleteService = async (record: AutoMLData) => {
const [res] = await to(deleteAutoMLReq(record.id));
if (res) { if (res) {
message.success('删除成功'); message.success('删除成功');
// 如果是一页的唯一数据,删除时,请求第一页的数据 // 如果是一页的唯一数据,删除时,请求第一页的数据
@@ -89,7 +88,7 @@ function AutoMLList() {
}; };


// 处理删除 // 处理删除
const handleServiceDelete = (record: ServiceData) => {
const handleAutoMLDelete = (record: AutoMLData) => {
modalConfirm({ modalConfirm({
title: '删除后,该服务将不可恢复', title: '删除后,该服务将不可恢复',
content: '是否确认删除?', content: '是否确认删除?',
@@ -99,27 +98,22 @@ function AutoMLList() {
}); });
}; };


// 创建、更新服务
const createService = (type: ServiceOperationType, record?: ServiceData) => {
SessionStorage.setItem(
SessionStorage.serviceInfoKey,
{
...record,
operationType: type,
},
true,
);

// 创建、编辑
const createService = (record?: AutoMLData) => {
setCacheState({ setCacheState({
pagination, pagination,
searchText, searchText,
}); });


navigate(`/pipeline/autoML/create`);
if (record) {
navigate(`/pipeline/autoML/edit/${record.id}`);
} else {
navigate(`/pipeline/autoML/create`);
}
}; };


// 查看详情 // 查看详情
const toDetail = (record: ServiceData) => {
const toDetail = (record: AutoMLData) => {
setCacheState({ setCacheState({
pagination, pagination,
searchText, searchText,
@@ -129,7 +123,7 @@ function AutoMLList() {
}; };


// 分页切换 // 分页切换
const handleTableChange: TableProps<ServiceData>['onChange'] = (
const handleTableChange: TableProps<AutoMLData>['onChange'] = (
pagination, pagination,
_filters, _filters,
_sorter, _sorter,
@@ -140,7 +134,7 @@ function AutoMLList() {
} }
}; };


const columns: TableProps<ServiceData>['columns'] = [
const columns: TableProps<AutoMLData>['columns'] = [
{ {
title: '序号', title: '序号',
dataIndex: 'index', dataIndex: 'index',
@@ -153,8 +147,8 @@ function AutoMLList() {
}, },
{ {
title: '实验名称', title: '实验名称',
dataIndex: 'service_name',
key: 'service_name',
dataIndex: 'ml_name',
key: 'ml_name',
width: '20%', width: '20%',
render: tableCellRender(false, TableCellValueType.Link, { render: tableCellRender(false, TableCellValueType.Link, {
onClick: toDetail, onClick: toDetail,
@@ -162,23 +156,23 @@ function AutoMLList() {
}, },
{ {
title: '实验描述', title: '实验描述',
dataIndex: 'service_type_name',
key: 'service_type_name',
dataIndex: 'ml_description',
key: 'ml_description',
width: '20%', width: '20%',
render: tableCellRender(true), render: tableCellRender(true),
ellipsis: { showTitle: false }, ellipsis: { showTitle: false },
}, },
{ {
title: '状态', title: '状态',
dataIndex: 'run_status',
key: 'run_status',
dataIndex: 'run_state',
key: 'run_state',
width: '20%', width: '20%',
render: RunStatusCell, render: RunStatusCell,
}, },
{ {
title: '实验实例执行进度', title: '实验实例执行进度',
dataIndex: 'description',
key: 'description',
dataIndex: 'progress',
key: 'progress',
render: ExecuteScheduleCell, render: ExecuteScheduleCell,
width: 180, width: 180,
}, },
@@ -203,15 +197,9 @@ function AutoMLList() {
dataIndex: 'operation', dataIndex: 'operation',
width: 400, width: 400,
key: 'operation', key: 'operation',
render: (_: any, record: ServiceData) => (
render: (_: any, record: AutoMLData) => (
<div> <div>
<Button
type="link"
size="small"
key="edit"
icon={<KFIcon type="icon-jingxiang" />}
onClick={() => createService(ServiceOperationType.Update, record)}
>
<Button type="link" size="small" key="mirror" icon={<KFIcon type="icon-jingxiang" />}>
镜像 镜像
</Button> </Button>
<Button <Button
@@ -219,14 +207,14 @@ function AutoMLList() {
size="small" size="small"
key="edit" key="edit"
icon={<KFIcon type="icon-bianji" />} icon={<KFIcon type="icon-bianji" />}
onClick={() => createService(ServiceOperationType.Update, record)}
onClick={() => createService(record)}
> >
编辑 编辑
</Button> </Button>
<Button <Button
type="link" type="link"
size="small" size="small"
key="run"
key="copy"
icon={<KFIcon type="icon-fuzhi" />} icon={<KFIcon type="icon-fuzhi" />}
onClick={() => toDetail(record)} onClick={() => toDetail(record)}
> >
@@ -235,7 +223,7 @@ function AutoMLList() {
<Button <Button
type="link" type="link"
size="small" size="small"
key="run"
key="stop"
icon={<KFIcon type="icon-tingzhi" />} icon={<KFIcon type="icon-tingzhi" />}
onClick={() => toDetail(record)} onClick={() => toDetail(record)}
> >
@@ -253,7 +241,7 @@ function AutoMLList() {
size="small" size="small"
key="remove" key="remove"
icon={<KFIcon type="icon-shanchu" />} icon={<KFIcon type="icon-shanchu" />}
onClick={() => handleServiceDelete(record)}
onClick={() => handleAutoMLDelete(record)}
> >
删除 删除
</Button> </Button>
@@ -279,7 +267,7 @@ function AutoMLList() {
<Button <Button
style={{ marginLeft: '20px' }} style={{ marginLeft: '20px' }}
type="default" type="default"
onClick={() => createService(ServiceOperationType.Create)}
onClick={() => createService()}
icon={<KFIcon type="icon-xinjian2" />} icon={<KFIcon type="icon-xinjian2" />}
> >
新建实验 新建实验


+ 3
- 3
react-ui/src/pages/AutoML/components/AutoMLTable/index.tsx View File

@@ -25,7 +25,7 @@ import { type SearchProps } from 'antd/es/input';
import classNames from 'classnames'; import classNames from 'classnames';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
// import ExecuteScheduleCell from '../components/ExecuteScheduleCell'; // import ExecuteScheduleCell from '../components/ExecuteScheduleCell';
import { ServiceData, ServiceOperationType } from '@/pages/AutoML/types';
import { OperationType, ServiceData } from '@/pages/AutoML/types';
import RunStatusCell from '../RunStatusCell'; import RunStatusCell from '../RunStatusCell';
import styles from './index.less'; import styles from './index.less';


@@ -99,7 +99,7 @@ function AutoMLTable() {
}; };


// 创建、更新服务 // 创建、更新服务
const createService = (type: ServiceOperationType, record?: ServiceData) => {
const createService = (type: OperationType, record?: ServiceData) => {
SessionStorage.setItem( SessionStorage.setItem(
SessionStorage.serviceInfoKey, SessionStorage.serviceInfoKey,
{ {
@@ -226,7 +226,7 @@ function AutoMLTable() {
size="small" size="small"
key="edit" key="edit"
icon={<KFIcon type="icon-rizhi" />} icon={<KFIcon type="icon-rizhi" />}
onClick={() => createService(ServiceOperationType.Update, record)}
onClick={() => createService(OperationType.Update, record)}
> >
日志 日志
</Button> </Button>


+ 11
- 20
react-ui/src/pages/AutoML/components/CreateForm/BasicConfig.tsx View File

@@ -1,5 +1,5 @@
import SubAreaTitle from '@/components/SubAreaTitle'; import SubAreaTitle from '@/components/SubAreaTitle';
import { Col, Form, Input, Row, Select } from 'antd';
import { Col, Form, Input, Row } from 'antd';
function BasicConfig() { function BasicConfig() {
return ( return (
<> <>
@@ -11,43 +11,34 @@ function BasicConfig() {
<Row gutter={8}> <Row gutter={8}>
<Col span={10}> <Col span={10}>
<Form.Item <Form.Item
label="服务名称"
name="service_name"
label="实验名称"
name="ml_name"
rules={[ rules={[
{ {
required: true, required: true,
message: '请输入服务名称',
},
{
pattern: /^(?!\d)[\u4e00-\u9fa5\w-]+$/,
message: '不能以数字开头,不能存在除了-_以外的特殊字符',
message: '请输入实验名称',
}, },
]} ]}
> >
<Input
placeholder="不能以数字开头,不能存在除了-_以外的特殊字符"
maxLength={256}
showCount
allowClear
/>
<Input placeholder="请输入实验名称" maxLength={64} showCount allowClear />
</Form.Item> </Form.Item>
</Col> </Col>
</Row> </Row>
<Row gutter={8}> <Row gutter={8}>
<Col span={20}> <Col span={20}>
<Form.Item <Form.Item
label="描述"
name="description"
label="实验描述"
name="ml_description"
rules={[ rules={[
{ {
required: true, required: true,
message: '请输入描述',
message: '请输入实验描述',
}, },
]} ]}
> >
<Input.TextArea <Input.TextArea
autoSize={{ minRows: 2, maxRows: 6 }} autoSize={{ minRows: 2, maxRows: 6 }}
placeholder="请输入描述"
placeholder="请输入实验描述"
maxLength={256} maxLength={256}
showCount showCount
allowClear allowClear
@@ -55,7 +46,7 @@ function BasicConfig() {
</Form.Item> </Form.Item>
</Col> </Col>
</Row> </Row>
<Row gutter={8}>
{/* <Row gutter={8}>
<Col span={10}> <Col span={10}>
<Form.Item <Form.Item
label="可见范围" label="可见范围"
@@ -77,7 +68,7 @@ function BasicConfig() {
/> />
</Form.Item> </Form.Item>
</Col> </Col>
</Row>
</Row> */}
</> </>
); );
} }


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

@@ -0,0 +1,59 @@
import ResourceSelect, {
ResourceSelectorType,
requiredValidator,
} from '@/components/ResourceSelect';
import SubAreaTitle from '@/components/SubAreaTitle';
import { Col, Form, Input, Row } from 'antd';

function DatasetConfig() {
return (
<>
<SubAreaTitle
title="数据集配置"
image={require('@/assets/img/search-config-icon.png')}
style={{ marginTop: '20px', marginBottom: '24px' }}
></SubAreaTitle>
<Row gutter={8}>
<Col span={10}>
<Form.Item
label="数据集"
name="dataset"
rules={[
{
validator: requiredValidator,
message: '请选择数据集',
},
]}
required
>
<ResourceSelect
type={ResourceSelectorType.Dataset}
placeholder="请选择数据集"
canInput={false}
size="large"
/>
</Form.Item>
</Col>
</Row>
<Row gutter={8}>
<Col span={10}>
<Form.Item
label="预测目标列"
name="target_columns"
rules={[
{
required: true,
message: '请输入预测目标列',
},
]}
tooltip="数据集 csv 文件中哪几列是预测目标列,逗号分隔"
>
<Input placeholder="请输入预测目标列" maxLength={64} showCount allowClear />
</Form.Item>
</Col>
</Row>
</>
);
}

export default DatasetConfig;

+ 434
- 21
react-ui/src/pages/AutoML/components/CreateForm/ExecuteConfig.tsx View File

@@ -1,19 +1,110 @@
import KFIcon from '@/components/KFIcon';
import SubAreaTitle from '@/components/SubAreaTitle'; import SubAreaTitle from '@/components/SubAreaTitle';
import { MinusCircleOutlined, PlusCircleOutlined } from '@ant-design/icons';
import { Button, Col, Flex, Form, Input, Radio, Row } from 'antd';
import ExecuteConfigDLC from './ExecuteConfigDLC';
import ExecuteConfigMC from './ExecuteConfigMC';
import styles from './index.less';
import {
AutoMLEnsembleClass,
AutoMLResamplingStrategy,
AutoMLTaskType,
resamplingStrategyOptions,
} from '@/enums';
import { Col, Form, Input, InputNumber, Radio, Row, Select, Switch } from 'antd';


type ExecuteConfigProps = {
disabled?: boolean;
};
// 分类算法
const classificationAlgorithms = [
'adaboost',
'bernoulli_nb',
'decision_tree',
'extra_trees',
'gaussian_nb',
'gradient_boosting',
'k_nearest_neighbors',
'lda',
'liblinear_svc',
'libsvm_svc',
'mlp',
'multinomial_nb',
'passive_aggressive',
'qda',
'random_forest',
'sgd',
].map((name) => ({ label: name, value: name }));


function ExecuteConfig({ disabled = false }: ExecuteConfigProps) {
// 回归算法
const regressorAlgorithms = [
'adaboost',
'ard_regression',
'decision_tree',
'extra_trees',
'gaussian_process',
'gradient_boosting',
'k_nearest_neighbors',
'liblinear_svr',
'libsvm_svr',
'mlp',
'random_forest',
'sgd',
].map((name) => ({ label: name, value: name }));

// 特征预处理算法
const featureAlgorithms = [
'densifier',
'extra_trees_preproc_for_classification',
'extra_trees_preproc_for_regression',
'fast_ica',
'feature_agglomeration',
'kernel_pca',
'kitchen_sinks',
'liblinear_svc_preprocessor',
'no_preprocessing',
'nystroem_sampler',
'pca',
'polynomial',
'random_trees_embedding',
'select_percentile_classification',
'select_percentile_regression',
'select_rates_classification',
'select_rates_regression',
'truncatedSVD',
].map((name) => ({ label: name, value: name }));

// 分类指标
export const classificationMetrics = [
'accuracy',
'balanced_accuracy',
'roc_auc',
'average_precision',
'log_loss',
'precision_macro',
'precision_micro',
'precision_samples',
'precision_weighted',
'recall_macro',
'recall_micro',
'recall_samples',
'recall_weighted',
'f1_macro',
'f1_micro',
'f1_samples',
'f1_weighted',
].map((name) => ({ label: name, value: name }));

// 回归指标
export const regressionMetrics = [
'mean_absolute_error',
'mean_squared_error',
'root_mean_squared_error',
'mean_squared_log_error',
'median_absolute_error',
'r2',
].map((name) => ({ label: name, value: name }));

function ExecuteConfig() {
const form = Form.useFormInstance(); const form = Form.useFormInstance();
const image_type = Form.useWatch('image_type', form);
console.log(image_type);
const task_type = Form.useWatch('task_type', form);
const include_classifier = Form.useWatch('include_classifier', form);
const exclude_classifier = Form.useWatch('exclude_classifier', form);
const include_regressor = Form.useWatch('include_regressor', form);
const exclude_regressor = Form.useWatch('exclude_regressor', form);
const include_feature_preprocessor = Form.useWatch('include_feature_preprocessor', form);
const exclude_feature_preprocessor = Form.useWatch('exclude_feature_preprocessor', form);


return ( return (
<> <>
@@ -26,27 +117,349 @@ function ExecuteConfig({ disabled = false }: ExecuteConfigProps) {
<Col span={10}> <Col span={10}>
<Form.Item <Form.Item
label="任务类型" label="任务类型"
name="execute_type"
name="task_type"
rules={[{ required: true, message: '请选择任务类型' }]} rules={[{ required: true, message: '请选择任务类型' }]}
> >
<Radio.Group> <Radio.Group>
<Radio value={'DLC'}>DLC</Radio>
<Radio value={'MaxCompute'}>MaxCompute</Radio>
<Radio value={AutoMLTaskType.Classification}>分类</Radio>
<Radio value={AutoMLTaskType.Regression}>回归</Radio>
</Radio.Group> </Radio.Group>
</Form.Item> </Form.Item>
</Col> </Col>
</Row> </Row>
<Form.Item dependencies={['execute_type']} noStyle>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="特征预处理算法"
name="include_feature_preprocessor"
tooltip="如果不选,则使用所有可能的特征预处理算法。否则,将只使用包含的特征预处理算法"
>
<Select
allowClear
placeholder="请选择特征预处理算法"
options={featureAlgorithms}
disabled={exclude_feature_preprocessor?.length > 0}
mode="multiple"
showSearch
/>
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="排除特征预处理算法"
name="exclude_feature_preprocessor"
tooltip="如果不选,则使用所有可能的特征预处理算法。否则,将排除包含的特征预处理算法"
>
<Select
allowClear
placeholder="排除特征预处理算法"
options={featureAlgorithms}
disabled={include_feature_preprocessor?.length > 0}
mode="multiple"
showSearch
/>
</Form.Item>
</Col>
</Row>

<Form.Item dependencies={['task_type']} noStyle>
{({ getFieldValue }) => { {({ getFieldValue }) => {
return getFieldValue('execute_type') === 'DLC' ? (
<ExecuteConfigDLC disabled={disabled}></ExecuteConfigDLC>
return getFieldValue('task_type') === AutoMLTaskType.Classification ? (
<>
<Row gutter={8}>
<Col span={10}>
<Form.Item
label="分类算法"
name="include_classifier"
tooltip="如果不选,则使用所有可能的分类算法。否则,将只使用包含的算法"
>
<Select
allowClear
placeholder="请选择分类算法"
options={classificationAlgorithms}
mode="multiple"
disabled={exclude_classifier?.length > 0}
showSearch
/>
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="排除分类算法"
name="exclude_classifier"
tooltip="如果不选,则使用所有可能的分类算法。否则,将排除包含的算法"
>
<Select
allowClear
placeholder="排除分类算法"
options={classificationAlgorithms}
mode="multiple"
disabled={include_classifier?.length > 0}
showSearch
/>
</Form.Item>
</Col>
</Row>
</>
) : ( ) : (
<ExecuteConfigMC disabled={disabled}></ExecuteConfigMC>
<>
<Row gutter={8}>
<Col span={10}>
<Form.Item
label="回归算法"
name="include_regressor"
tooltip="如果不选,则使用所有可能的回归算法。否则,将只使用包含的算法"
>
<Select
allowClear
placeholder="请选择回归算法"
options={regressorAlgorithms}
mode="multiple"
disabled={exclude_regressor?.length > 0}
showSearch
/>
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="排除的回归算法"
name="exclude_regressor"
tooltip="如果不选,则使用所有可能的回归算法。否则,将排除包含的算法"
>
<Select
allowClear
placeholder="排除回归算法"
options={regressorAlgorithms}
mode="multiple"
disabled={include_regressor?.length > 0}
showSearch
/>
</Form.Item>
</Col>
</Row>
</>
); );
}} }}
</Form.Item> </Form.Item>


<Form.List name="hyper-parameter">
<Row gutter={8}>
<Col span={10}>
<Form.Item
label="集成方式"
name="ensemble_class"
tooltip="仅使用单个最佳模型还是集成模型"
>
<Radio.Group>
<Radio value={AutoMLEnsembleClass.Default}>集成模型</Radio>
<Radio value={AutoMLEnsembleClass.SingleBest}>单一最佳模型</Radio>
</Radio.Group>
</Form.Item>
</Col>
</Row>

<Form.Item dependencies={['ensemble_class']} noStyle>
{({ getFieldValue }) => {
return getFieldValue('ensemble_class') === AutoMLEnsembleClass.Default ? (
<>
<Row gutter={8}>
<Col span={10}>
<Form.Item
label="集成模型数量"
name="ensemble_size"
tooltip="集成模型数量,如果设置为0,则没有集成。默认50"
>
<InputNumber placeholder="请输入集成模型数量" min={0} precision={0} />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="集成最佳模型数量"
name="ensemble_nbest"
tooltip="仅集成最佳的N个模型"
>
<InputNumber placeholder="请输入集成最佳模型数量" min={0} precision={0} />
</Form.Item>
</Col>
</Row>
</>
) : null;
}}
</Form.Item>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="最大数量"
name="max_models_on_disc"
tooltip="定义在磁盘中保存的模型的最大数量。额外的模型数量将被永久删除,它设置了一个集成可以使用多少个模型的上限。必须是大于等于1的整数,默认50"
>
<InputNumber placeholder="请输入最大数量" min={0} precision={0} />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="内存限制(MB)"
name="memory_limit"
tooltip="机器学习算法的内存限制(MB)。如果auto-sklearn试图分配超过memory_limit MB,它将停止拟合机器学习算法。默认3072"
>
<InputNumber placeholder="请输入内存限制" min={0} precision={0} />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="时间限制(秒)"
name="per_run_time_limit"
tooltip="单次调用机器学习模型的时间限制(以秒为单位)。如果机器学习算法运行超过时间限制,将终止模型拟合,默认600"
>
<InputNumber placeholder="请输入时间限制" min={0} precision={0} />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="搜索时间限制(秒)"
name="time_left_for_this_task"
tooltip="搜索合适模型的时间限制(以秒为单位)。通过增加这个值,auto-sklearn有更高的机会找到更好的模型。默认3600。"
>
<InputNumber placeholder="请输入搜索时间限制" min={0} precision={0} />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="重采样策略"
name="resampling_strategy"
tooltip="重采样策略,分为holdout和crossValid。holdout指定训练数据划分为训练集和验证集的比例。crossValid为交叉验证。"
>
<Select
allowClear
placeholder="请选择重采样策略"
options={resamplingStrategyOptions}
showSearch
/>
</Form.Item>
</Col>
</Row>

<Form.Item dependencies={['resampling_strategy']} noStyle>
{({ getFieldValue }) => {
return getFieldValue('resampling_strategy') === AutoMLResamplingStrategy.CrossValid ? (
<Row gutter={8}>
<Col span={10}>
<Form.Item
label="交叉验证折数"
name="folds"
rules={[
{
required: true,
message: '请输入交叉验证折数',
},
]}
>
<InputNumber placeholder="请输入交叉验证折数" min={0} precision={0} />
</Form.Item>
</Col>
</Row>
) : null;
}}
</Form.Item>

<Row gutter={8}>
<Col span={10}>
<Form.Item label="shuffle" name="shuffle" tooltip="拆分数据前是否进行 shuffle">
<Switch />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item label="训练集比率" name="train_size">
<InputNumber placeholder="请输入训练集比率" min={0} max={1} />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="测试集比率"
name="test_size"
tooltip="将数据划分为训练数据和测试数据,测试数据集所占比例"
>
<InputNumber placeholder="请输入测试集比率" min={0} max={1} />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item label="计算指标" name="scoring_functions" tooltip="需要计算并打印的指标">
<Select
allowClear
placeholder="请选择计算指标"
options={
task_type === AutoMLTaskType.Classification
? classificationMetrics
: regressionMetrics
}
showSearch
/>
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item label="随机种子" name="seed" tooltip="随机种子,将决定输出文件名">
<InputNumber placeholder="请输入随机种子" min={0} precision={0} />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item
label="文件夹路径"
name="tmp_folder"
tooltip="存放配置输出和日志文件的文件夹"
rules={[
{
pattern: /^\/[a-zA-Z0-9._/-]+$/,
message:
'请输入正确的文件夹路径,以 / 开头,只支持字母、数字、点、下划线、中横线、斜杠',
},
]}
>
<Input placeholder="请输入文件夹路径" maxLength={64} showCount allowClear />
</Form.Item>
</Col>
</Row>

{/* <Form.List name="hyper-parameter">
{(fields, { add, remove }) => ( {(fields, { add, remove }) => (
<> <>
<Row gutter={8}> <Row gutter={8}>
@@ -126,7 +539,7 @@ function ExecuteConfig({ disabled = false }: ExecuteConfigProps) {
</div> </div>
</> </>
)} )}
</Form.List>
</Form.List> */}
</> </>
); );
} }


+ 16
- 13
react-ui/src/pages/AutoML/components/CreateForm/ExecuteConfigDLC.tsx View File

@@ -1,5 +1,8 @@
import CodeSelect from '@/components/CodeSelect'; import CodeSelect from '@/components/CodeSelect';
import ResourceSelect, { ResourceSelectorType } from '@/components/ResourceSelect';
import ResourceSelect, {
requiredValidator,
ResourceSelectorType,
} from '@/components/ResourceSelect';
import { MinusCircleOutlined, PlusCircleOutlined } from '@ant-design/icons'; import { MinusCircleOutlined, PlusCircleOutlined } from '@ant-design/icons';
import { Button, Col, Flex, Form, Input, InputNumber, Radio, Row, Select } from 'antd'; import { Button, Col, Flex, Form, Input, InputNumber, Radio, Row, Select } from 'antd';
import styles from './index.less'; import styles from './index.less';
@@ -62,12 +65,12 @@ function ExecuteConfigDLC({ disabled = false }: ExecuteConfigDLCProps) {
<Form.Item <Form.Item
label="数据集" label="数据集"
name="model" name="model"
// rules={[
// {
// validator: requiredValidator,
// message: '请选择数据集',
// },
// ]}
rules={[
{
validator: requiredValidator,
message: '请选择数据集',
},
]}
required required
> >
<ResourceSelect <ResourceSelect
@@ -84,12 +87,12 @@ function ExecuteConfigDLC({ disabled = false }: ExecuteConfigDLCProps) {
<Form.Item <Form.Item
label="代码配置" label="代码配置"
name="code_config" name="code_config"
// rules={[
// {
// validator: requiredValidator,
// message: '请选择代码配置',
// },
// ]}
rules={[
{
validator: requiredValidator,
message: '请选择代码配置',
},
]}
required required
> >
<CodeSelect <CodeSelect


+ 97
- 166
react-ui/src/pages/AutoML/components/CreateForm/TrialConfig.tsx View File

@@ -1,188 +1,119 @@
import SubAreaTitle from '@/components/SubAreaTitle'; import SubAreaTitle from '@/components/SubAreaTitle';
import { AutoMLTaskType } from '@/enums';
import { MinusCircleOutlined, PlusCircleOutlined } from '@ant-design/icons'; import { MinusCircleOutlined, PlusCircleOutlined } from '@ant-design/icons';
import { Button, Col, Flex, Form, Input, Radio, Row, Select } from 'antd';
import { Button, Col, Flex, Form, Input, InputNumber, Radio, Row, Select } from 'antd';
import { classificationMetrics, regressionMetrics } from './ExecuteConfig';
import styles from './index.less'; import styles from './index.less';


function TrialConfig() { function TrialConfig() {
const form = Form.useFormInstance();
const task_type = Form.useWatch('task_type', form);
return ( return (
<> <>
<SubAreaTitle <SubAreaTitle
title="Trial配置"
title="优化指标"
image={require('@/assets/img/trial-config-icon.png')} image={require('@/assets/img/trial-config-icon.png')}
style={{ marginBottom: '26px' }} style={{ marginBottom: '26px' }}
></SubAreaTitle> ></SubAreaTitle>
<div className={styles['trial-metrics']}>
<Row gutter={8}>
<Col span={10}>
<Form.Item label="优化指标"></Form.Item>
</Col>
</Row>
<Row gutter={8}>
<Col span={24}>
<Form.Item
label="指标类型"
name="version"
rules={[
{
required: true,
message: '请选择指标类型',
},
]}
labelCol={{ flex: '120px' }}
>
<Select
allowClear
placeholder="请选择指标类型"
options={[]}
fieldNames={{ label: 'name', value: 'name' }}
optionFilterProp="name"
showSearch
/>
</Form.Item>
</Col>
</Row>
<Row gutter={8}>
<Col span={24}>
<Form.Item
label="计算方式"
name="version"
rules={[
{
required: true,
message: '请选择计算方式',
},
]}
labelCol={{ flex: '120px' }}
>
<Select
allowClear
placeholder="请选择计算方式"
options={[]}
fieldNames={{ label: 'name', value: 'name' }}
optionFilterProp="name"
showSearch
/>
</Form.Item>
</Col>
</Row>
<Row gutter={8}>
<Col span={24}>
<Form.Item label="指标权重" tooltip="指标权重" labelCol={{ flex: '120px' }}>
<Form.List name="metrics_weight">
{(fields, { add, remove }) => (
<>
{fields.map(({ key, name, ...restField }, index) => (
<Flex key={key} align="center" className={styles['advanced-config']}>
<Form.Item
style={{ flex: 1, marginBottom: 0 }}
{...restField}
name={[name, 'key']}
rules={[{ required: true, message: 'Missing first name' }]}
>
<Input placeholder="Key" />
</Form.Item>
<span style={{ margin: '0 8px' }}>:</span>
<Form.Item
style={{ flex: 1, marginBottom: 0 }}
{...restField}
name={[name, 'value']}
rules={[{ required: true, message: 'Missing last name' }]}
>
<Input placeholder="Value" />
</Form.Item>
<div style={{ width: '76px', marginLeft: '18px' }}>
<Row gutter={8}>
<Col span={10}>
<Form.Item label="指标名称" name="metric_name">
<Input placeholder="请输入指标名称" maxLength={64} showCount allowClear />
</Form.Item>
</Col>
</Row>

<Row gutter={8}>
<Col span={10}>
<Form.Item label="指标权重" tooltip="用户可自定义优化指标的组合">
<Form.List name="metrics">
{(fields, { add, remove }) => (
<>
{fields.map(({ key, name, ...restField }, index) => (
<Flex key={key} align="center" className={styles['advanced-config']}>
<Form.Item
style={{ flex: 1, marginBottom: 0 }}
{...restField}
name={[name, 'name']}
rules={[{ required: true, message: '请选择指标' }]}
>
<Select
allowClear
placeholder="请选择指标"
popupMatchSelectWidth={false}
options={
task_type === AutoMLTaskType.Classification
? classificationMetrics
: regressionMetrics
}
showSearch
/>
</Form.Item>
<span style={{ margin: '0 8px' }}>:</span>
<Form.Item
style={{ flex: 1, marginBottom: 0 }}
{...restField}
name={[name, 'value']}
rules={[{ required: true, message: '请输入指标权重' }]}
>
<InputNumber placeholder="请输入指标权重" min={0} precision={0} />
</Form.Item>
<div style={{ width: '76px', marginLeft: '18px' }}>
<Button
style={{
marginRight: '3px',
}}
shape="circle"
size="middle"
// disabled={fields.length === 1}
type="text"
onClick={() => remove(name)}
icon={<MinusCircleOutlined />}
></Button>
{index === fields.length - 1 && (
<Button <Button
style={{
marginRight: '3px',
}}
shape="circle" shape="circle"
size="middle" size="middle"
disabled={fields.length === 1}
type="text" type="text"
onClick={() => remove(name)}
icon={<MinusCircleOutlined />}
onClick={() => add()}
icon={<PlusCircleOutlined />}
></Button> ></Button>
{index === fields.length - 1 && (
<Button
shape="circle"
size="middle"
type="text"
onClick={() => add()}
icon={<PlusCircleOutlined />}
></Button>
)}
</div>
</Flex>
))}
{fields.length === 0 && (
<Form.Item style={{ marginBottom: 0 }}>
<Button
style={{ background: 'white' }}
type="dashed"
onClick={() => add()}
block
icon={<PlusCircleOutlined />}
>
添加指标权重
</Button>
</Form.Item>
)}
</>
)}
</Form.List>
</Form.Item>
</Col>
</Row>
<Row gutter={0}>
<Col span={24}>
<Form.Item
label="指标来源"
name="service_name"
rules={[
{
required: true,
message: '请输入指标来源',
},
]}
tooltip="指标来源"
labelCol={{ flex: '120px' }}
>
<Input placeholder="请输入指标来源" maxLength={256} showCount allowClear />
</Form.Item>
</Col>
</Row>

<Row gutter={0}>
<Col span={24}>
<Form.Item
label="优化方向"
name="execute_type"
rules={[{ required: true, message: '请选择优化方向' }]}
labelCol={{ flex: '120px' }}
>
<Radio.Group>
<Radio value={1}>越大越好</Radio>
<Radio value={2}>越小越好</Radio>
</Radio.Group>
</Form.Item>
</Col>
</Row>
</div>
)}
</div>
</Flex>
))}
{fields.length === 0 && (
<Form.Item style={{ marginBottom: 0 }}>
<Button
style={{ background: 'white' }}
type="dashed"
onClick={() => add()}
block
icon={<PlusCircleOutlined />}
>
添加指标权重
</Button>
</Form.Item>
)}
</>
)}
</Form.List>
</Form.Item>
</Col>
</Row>


<Row gutter={8}>
<Col span={10}>
<Row gutter={0}>
<Col span={24}>
<Form.Item <Form.Item
label="模型名称"
name="service_name"
rules={[
{
required: true,
message: '请输入模型名称',
},
]}
tooltip="模型名称"
label="优化方向"
name="greater_is_better"
rules={[{ required: true, message: '请选择优化方向' }]}
tooltip="指标组合优化的方向,是越大越好还是越小越好。"
> >
<Input placeholder="请输入模型名称" maxLength={256} showCount allowClear />
<Radio.Group>
<Radio value={true}>越大越好</Radio>
<Radio value={false}>越小越好</Radio>
</Radio.Group>
</Form.Item> </Form.Item>
</Col> </Col>
</Row> </Row>


+ 80
- 0
react-ui/src/pages/AutoML/components/CreateForm/UploadConfig.tsx View File

@@ -0,0 +1,80 @@
import { getAccessToken } from '@/access';
import KFIcon from '@/components/KFIcon';
import SubAreaTitle from '@/components/SubAreaTitle';
import { getFileListFromEvent } from '@/utils/ui';
import { Button, Col, Form, Input, Row, Upload, type UploadProps } from 'antd';
import { useState } from 'react';
import styles from './index.less';

function UploadConfig() {
const [uuid] = useState(Date.now());
// 上传组件参数
const uploadProps: UploadProps = {
action: '/api/mmp/autoML/upload',
headers: {
Authorization: getAccessToken() || '',
},
defaultFileList: [],
};

return (
<>
<SubAreaTitle
title="数据集配置"
image={require('@/assets/img/search-config-icon.png')}
style={{ marginTop: '20px', marginBottom: '24px' }}
></SubAreaTitle>
<Row gutter={8}>
<Col span={10}>
<Form.Item label="数据集名称" name="dataset_name">
<Input placeholder="请输入数据集名称" maxLength={64} showCount allowClear />
</Form.Item>
</Col>
</Row>
<Row gutter={8}>
<Col span={10}>
<Form.Item
label="预测目标列"
name="target_columns"
rules={[
{
required: true,
message: '请输入预测目标列',
},
]}
tooltip="数据集 csv 文件中哪几列是预测目标列,逗号分隔"
>
<Input placeholder="请输入预测目标列" maxLength={64} showCount allowClear />
</Form.Item>
</Col>
</Row>
<Form.Item
label="数据集文件"
name="fileList"
valuePropName="fileList"
getValueFromEvent={getFileListFromEvent}
labelCol={{ span: 24 }}
wrapperCol={{ span: 24 }}
rules={[
{
required: true,
message: '请上传数据集文件',
},
]}
>
<Upload {...uploadProps} data={{ uuid: uuid }} accept=".csv">
<Button
className={styles['upload-button']}
type="default"
icon={<KFIcon type="icon-shangchuan" />}
>
上传文件
</Button>
<div className={styles['upload-tip']}>只允许上传 .csv 格式文件</div>
</Upload>
</Form.Item>
</>
);
}

export default UploadConfig;

+ 11
- 0
react-ui/src/pages/AutoML/components/CreateForm/index.less View File

@@ -128,3 +128,14 @@
border: 1px dashed #e0e0e0; border: 1px dashed #e0e0e0;
border-radius: 8px; border-radius: 8px;
} }

.upload-tip {
margin-top: 5px;
color: @text-color-secondary;
font-size: 14px;
}

.upload-button {
height: 46px;
font-size: 15px;
}

+ 50
- 2
react-ui/src/pages/AutoML/types.ts View File

@@ -1,6 +1,54 @@
import { type ParameterInputObject } from '@/components/ResourceSelect';

// 操作类型 // 操作类型
export enum ServiceOperationType {
export enum OperationType {
Create = 'Create', // 创建 Create = 'Create', // 创建
Update = 'Update', // 更新 Update = 'Update', // 更新
Restart = 'Restart', // 重启
} }

// 表单数据
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[];
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; // 模型
};

export type AutoMLData = {
id: string;
progress: number;
run_state: string;
state: number;
metrics?: string;
include_classifier?: string;
include_feature_preprocessor?: string;
include_regressor?: string;
exclude_classifier?: string;
exclude_feature_preprocessor?: string;
exclude_regressor?: string;
dataset?: string;
};

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

@@ -0,0 +1,55 @@
/*
* @Author: 赵伟
* @Date: 2024-11-18 10:18:27
* @Description: 自动机器学习请求
*/

import { request } from '@umijs/max';


// 分页查询自动学习
export function getAutoMLListReq(params) {
return request(`/api/mmp/autoML`, {
method: 'GET',
params,
});
}

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

// 新增自动学习
export function addAutoMLReq(data) {
return request(`/api/mmp/autoML`, {
method: 'POST',
data,
});
}

// 编辑自动学习
export function updateAutoMLReq(data) {
return request(`/api/mmp/autoML`, {
method: 'PUT',
data,
});
}

// 删除自动学习
export function deleteAutoMLReq(id) {
return request(`/api/mmp/autoML/${id}`, {
method: 'DELETE',
});
}

// 运行自动学习
export function runAutoMLReq(id) {
return request(`/api/mmp/autoML/${id}`, {
method: 'POST',
params,
});
}

+ 10
- 0
react-ui/src/utils/functional.ts View File

@@ -4,6 +4,16 @@
* @Description: 函数式编程 * @Description: 函数式编程
*/ */


/**
* Safely invokes a function with a given value, returning the result of the
* function or the provided value if it is `undefined` or `null`.
*
* @template T - The type of the input value.
* @template M - The type of the output value.
* @param {function} fn - The function to be invoked with the input value.
* @returns {function} A function that takes a value, invokes `fn` with it if
* it's not `undefined` or `null`, and returns the result or the original value.
*/
export function safeInvoke<T, M>( export function safeInvoke<T, M>(
fn: (value: T) => M | undefined | null, fn: (value: T) => M | undefined | null,
): (value: T | undefined | null) => M | undefined | null { ): (value: T | undefined | null) => M | undefined | null {


+ 15
- 0
react-ui/src/utils/index.ts View File

@@ -241,3 +241,18 @@ export const tableSorter = (a: any, b: any) => {
} }
return 0; return 0;
}; };

/**
* Trim the given character from both ends of the given string.
*
* @param {string} ch - the character to trim
* @param {string} str - the string to trim
* @return {string} the trimmed string
*/
export const trimCharacter = (str: string, ch: string): string => {
if (str === null || str === undefined) {
return str;
}
const reg = new RegExp(`^${ch}|${ch}$`, 'g');
return str.trim().replace(reg, '');
};

Loading…
Cancel
Save