Skip to content

Commit 3a4f49d

Browse files
[ML] DF Analytics: Creation wizard part 3 (#69456)
* update clone tests * validate advanced params with explain * disable button while fetching validation data * comment out clone tests for now
1 parent 0102fb8 commit 3a4f49d

File tree

14 files changed

+284
-172
lines changed

14 files changed

+284
-172
lines changed

x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,18 @@ export enum ANALYSIS_CONFIG_TYPE {
2525
}
2626

2727
export enum ANALYSIS_ADVANCED_FIELDS {
28+
ETA = 'eta',
29+
FEATURE_BAG_FRACTION = 'feature_bag_fraction',
2830
FEATURE_INFLUENCE_THRESHOLD = 'feature_influence_threshold',
2931
GAMMA = 'gamma',
3032
LAMBDA = 'lambda',
3133
MAX_TREES = 'max_trees',
34+
METHOD = 'method',
35+
N_NEIGHBORS = 'n_neighbors',
36+
NUM_TOP_CLASSES = 'num_top_classes',
3237
NUM_TOP_FEATURE_IMPORTANCE_VALUES = 'num_top_feature_importance_values',
38+
OUTLIER_FRACTION = 'outlier_fraction',
39+
RANDOMIZE_SEED = 'randomize_seed',
3340
}
3441

3542
export enum OUTLIER_ANALYSIS_METHOD {

x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/advanced_step/advanced_step_form.tsx

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
import React, { FC, Fragment, useMemo } from 'react';
7+
import React, { FC, Fragment, useMemo, useEffect, useState } from 'react';
88
import {
99
EuiAccordion,
1010
EuiFieldNumber,
@@ -23,33 +23,51 @@ import { getModelMemoryLimitErrors } from '../../../analytics_management/hooks/u
2323
import {
2424
ANALYSIS_CONFIG_TYPE,
2525
NUM_TOP_FEATURE_IMPORTANCE_VALUES_MIN,
26+
ANALYSIS_ADVANCED_FIELDS,
2627
} from '../../../../common/analytics';
2728
import { DEFAULT_MODEL_MEMORY_LIMIT } from '../../../analytics_management/hooks/use_create_analytics_form/state';
2829
import { ANALYTICS_STEPS } from '../../page';
30+
import { fetchExplainData } from '../shared';
2931
import { ContinueButton } from '../continue_button';
3032
import { OutlierHyperParameters } from './outlier_hyper_parameters';
3133

3234
export function getNumberValue(value?: number) {
3335
return value === undefined ? '' : +value;
3436
}
3537

38+
export type AdvancedParamErrors = {
39+
[key in ANALYSIS_ADVANCED_FIELDS]?: string;
40+
};
41+
3642
export const AdvancedStepForm: FC<CreateAnalyticsStepProps> = ({
3743
actions,
3844
state,
3945
setCurrentStep,
4046
}) => {
47+
const [advancedParamErrors, setAdvancedParamErrors] = useState<AdvancedParamErrors>({});
48+
const [fetchingAdvancedParamErrors, setFetchingAdvancedParamErrors] = useState<boolean>(false);
49+
4150
const { setFormState } = actions;
4251
const { form, isJobCreated } = state;
4352
const {
4453
computeFeatureInfluence,
54+
eta,
55+
featureBagFraction,
4556
featureInfluenceThreshold,
57+
gamma,
4658
jobType,
59+
lambda,
60+
maxTrees,
61+
method,
4762
modelMemoryLimit,
4863
modelMemoryLimitValidationResult,
64+
nNeighbors,
4965
numTopClasses,
5066
numTopFeatureImportanceValues,
5167
numTopFeatureImportanceValuesValid,
68+
outlierFraction,
5269
predictionFieldName,
70+
randomizeSeed,
5371
} = form;
5472

5573
const mmlErrors = useMemo(() => getModelMemoryLimitErrors(modelMemoryLimitValidationResult), [
@@ -61,6 +79,43 @@ export const AdvancedStepForm: FC<CreateAnalyticsStepProps> = ({
6179

6280
const mmlInvalid = modelMemoryLimitValidationResult !== null;
6381

82+
const isStepInvalid =
83+
mmlInvalid ||
84+
Object.keys(advancedParamErrors).length > 0 ||
85+
fetchingAdvancedParamErrors === true;
86+
87+
useEffect(() => {
88+
setFetchingAdvancedParamErrors(true);
89+
(async function () {
90+
const { success, errorMessage } = await fetchExplainData(form);
91+
const paramErrors: AdvancedParamErrors = {};
92+
93+
if (!success) {
94+
// Check which field is invalid
95+
Object.values(ANALYSIS_ADVANCED_FIELDS).forEach((param) => {
96+
if (errorMessage.includes(`[${param}]`)) {
97+
paramErrors[param] = errorMessage;
98+
}
99+
});
100+
}
101+
setFetchingAdvancedParamErrors(false);
102+
setAdvancedParamErrors(paramErrors);
103+
})();
104+
}, [
105+
eta,
106+
featureBagFraction,
107+
featureInfluenceThreshold,
108+
gamma,
109+
lambda,
110+
maxTrees,
111+
method,
112+
nNeighbors,
113+
numTopClasses,
114+
numTopFeatureImportanceValues,
115+
outlierFraction,
116+
randomizeSeed,
117+
]);
118+
64119
const outlierDetectionAdvancedConfig = (
65120
<Fragment>
66121
<EuiFlexItem style={{ minWidth: '30%' }}>
@@ -126,6 +181,10 @@ export const AdvancedStepForm: FC<CreateAnalyticsStepProps> = ({
126181
'The minimum outlier score that a document needs to have in order to calculate its feature influence score. Value range: 0-1. Defaults to 0.1.',
127182
}
128183
)}
184+
isInvalid={
185+
advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.FEATURE_INFLUENCE_THRESHOLD] !== undefined
186+
}
187+
error={advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.FEATURE_INFLUENCE_THRESHOLD]}
129188
>
130189
<EuiFieldNumber
131190
onChange={(e) =>
@@ -315,14 +374,24 @@ export const AdvancedStepForm: FC<CreateAnalyticsStepProps> = ({
315374
>
316375
<EuiFlexGroup wrap>
317376
{jobType === ANALYSIS_CONFIG_TYPE.OUTLIER_DETECTION && (
318-
<OutlierHyperParameters actions={actions} state={state} />
377+
<OutlierHyperParameters
378+
actions={actions}
379+
state={state}
380+
advancedParamErrors={advancedParamErrors}
381+
/>
382+
)}
383+
{isRegOrClassJob && (
384+
<HyperParameters
385+
actions={actions}
386+
state={state}
387+
advancedParamErrors={advancedParamErrors}
388+
/>
319389
)}
320-
{isRegOrClassJob && <HyperParameters actions={actions} state={state} />}
321390
</EuiFlexGroup>
322391
</EuiAccordion>
323392
<EuiSpacer />
324393
<ContinueButton
325-
isDisabled={mmlInvalid}
394+
isDisabled={isStepInvalid}
326395
onClick={() => {
327396
setCurrentStep(ANALYTICS_STEPS.DETAILS);
328397
}}

x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/advanced_step/hyper_parameters.tsx

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,16 @@ import React, { FC, Fragment } from 'react';
88
import { EuiFieldNumber, EuiFlexItem, EuiFormRow } from '@elastic/eui';
99
import { i18n } from '@kbn/i18n';
1010
import { CreateAnalyticsFormProps } from '../../../analytics_management/hooks/use_create_analytics_form';
11-
import { getNumberValue } from './advanced_step_form';
11+
import { AdvancedParamErrors, getNumberValue } from './advanced_step_form';
12+
import { ANALYSIS_ADVANCED_FIELDS } from '../../../../common/analytics';
1213

1314
const MAX_TREES_LIMIT = 2000;
1415

15-
export const HyperParameters: FC<CreateAnalyticsFormProps> = ({ actions, state }) => {
16+
interface Props extends CreateAnalyticsFormProps {
17+
advancedParamErrors: AdvancedParamErrors;
18+
}
19+
20+
export const HyperParameters: FC<Props> = ({ actions, state, advancedParamErrors }) => {
1621
const { setFormState } = actions;
1722

1823
const { eta, featureBagFraction, gamma, lambda, maxTrees, randomizeSeed } = state.form;
@@ -28,6 +33,8 @@ export const HyperParameters: FC<CreateAnalyticsFormProps> = ({ actions, state }
2833
defaultMessage:
2934
'Regularization parameter to prevent overfitting on the training data set. Must be a non negative value.',
3035
})}
36+
isInvalid={advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.LAMBDA] !== undefined}
37+
error={advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.LAMBDA]}
3138
>
3239
<EuiFieldNumber
3340
aria-label={i18n.translate('xpack.ml.dataframe.analytics.create.lambdaInputAriaLabel', {
@@ -52,6 +59,8 @@ export const HyperParameters: FC<CreateAnalyticsFormProps> = ({ actions, state }
5259
helpText={i18n.translate('xpack.ml.dataframe.analytics.create.maxTreesText', {
5360
defaultMessage: 'The maximum number of trees the forest is allowed to contain.',
5461
})}
62+
isInvalid={advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.MAX_TREES] !== undefined}
63+
error={advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.MAX_TREES]}
5564
>
5665
<EuiFieldNumber
5766
aria-label={i18n.translate(
@@ -81,6 +90,8 @@ export const HyperParameters: FC<CreateAnalyticsFormProps> = ({ actions, state }
8190
defaultMessage:
8291
'Multiplies a linear penalty associated with the size of individual trees in the forest. Must be non-negative value.',
8392
})}
93+
isInvalid={advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.GAMMA] !== undefined}
94+
error={advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.GAMMA]}
8495
>
8596
<EuiFieldNumber
8697
aria-label={i18n.translate('xpack.ml.dataframe.analytics.create.gammaInputAriaLabel', {
@@ -105,6 +116,8 @@ export const HyperParameters: FC<CreateAnalyticsFormProps> = ({ actions, state }
105116
helpText={i18n.translate('xpack.ml.dataframe.analytics.create.etaText', {
106117
defaultMessage: 'The shrinkage applied to the weights. Must be between 0.001 and 1.',
107118
})}
119+
isInvalid={advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.ETA] !== undefined}
120+
error={advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.ETA]}
108121
>
109122
<EuiFieldNumber
110123
aria-label={i18n.translate('xpack.ml.dataframe.analytics.create.etaInputAriaLabel', {
@@ -130,6 +143,10 @@ export const HyperParameters: FC<CreateAnalyticsFormProps> = ({ actions, state }
130143
defaultMessage:
131144
'The fraction of features used when selecting a random bag for each candidate split.',
132145
})}
146+
isInvalid={
147+
advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.FEATURE_BAG_FRACTION] !== undefined
148+
}
149+
error={advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.FEATURE_BAG_FRACTION]}
133150
>
134151
<EuiFieldNumber
135152
aria-label={i18n.translate(
@@ -158,12 +175,14 @@ export const HyperParameters: FC<CreateAnalyticsFormProps> = ({ actions, state }
158175
<EuiFlexItem style={{ minWidth: '30%' }}>
159176
<EuiFormRow
160177
label={i18n.translate('xpack.ml.dataframe.analytics.create.randomizeSeedLabel', {
161-
defaultMessage: 'Randomized seed',
178+
defaultMessage: 'Randomize seed',
162179
})}
163180
helpText={i18n.translate('xpack.ml.dataframe.analytics.create.randomizeSeedText', {
164181
defaultMessage:
165182
'The seed to the random generator that is used to pick which documents will be used for training.',
166183
})}
184+
isInvalid={advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.RANDOMIZE_SEED] !== undefined}
185+
error={advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.RANDOMIZE_SEED]}
167186
>
168187
<EuiFieldNumber
169188
aria-label={i18n.translate(

x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/advanced_step/outlier_hyper_parameters.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@
77
import React, { FC, Fragment } from 'react';
88
import { EuiFieldNumber, EuiFlexItem, EuiFormRow, EuiSelect } from '@elastic/eui';
99
import { i18n } from '@kbn/i18n';
10-
import { OUTLIER_ANALYSIS_METHOD } from '../../../../common/analytics';
10+
import { OUTLIER_ANALYSIS_METHOD, ANALYSIS_ADVANCED_FIELDS } from '../../../../common/analytics';
1111
import { CreateAnalyticsFormProps } from '../../../analytics_management/hooks/use_create_analytics_form';
12-
import { getNumberValue } from './advanced_step_form';
12+
import { AdvancedParamErrors, getNumberValue } from './advanced_step_form';
1313

14-
export const OutlierHyperParameters: FC<CreateAnalyticsFormProps> = ({ actions, state }) => {
14+
interface Props extends CreateAnalyticsFormProps {
15+
advancedParamErrors: AdvancedParamErrors;
16+
}
17+
18+
export const OutlierHyperParameters: FC<Props> = ({ actions, state, advancedParamErrors }) => {
1519
const { setFormState } = actions;
1620

1721
const { method, nNeighbors, outlierFraction, standardizationEnabled } = state.form;
@@ -27,6 +31,8 @@ export const OutlierHyperParameters: FC<CreateAnalyticsFormProps> = ({ actions,
2731
defaultMessage:
2832
'Sets the method that outlier detection uses. If not set, uses an ensemble of different methods and normalises and combines their individual outlier scores to obtain the overall outlier score. We recommend to use the ensemble method',
2933
})}
34+
isInvalid={advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.METHOD] !== undefined}
35+
error={advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.METHOD]}
3036
>
3137
<EuiSelect
3238
options={Object.values(OUTLIER_ANALYSIS_METHOD).map((outlierMethod) => ({
@@ -51,6 +57,8 @@ export const OutlierHyperParameters: FC<CreateAnalyticsFormProps> = ({ actions,
5157
defaultMessage:
5258
'The value for how many nearest neighbors each method of outlier detection will use to calculate its outlier score. When not set, different values will be used for different ensemble members. Must be a positive integer',
5359
})}
60+
isInvalid={advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.N_NEIGHBORS] !== undefined}
61+
error={advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.N_NEIGHBORS]}
5462
>
5563
<EuiFieldNumber
5664
aria-label={i18n.translate(
@@ -79,6 +87,8 @@ export const OutlierHyperParameters: FC<CreateAnalyticsFormProps> = ({ actions,
7987
defaultMessage:
8088
'Sets the proportion of the data set that is assumed to be outlying prior to outlier detection.',
8189
})}
90+
isInvalid={advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.OUTLIER_FRACTION] !== undefined}
91+
error={advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.OUTLIER_FRACTION]}
8292
>
8393
<EuiFieldNumber
8494
aria-label={i18n.translate(

x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/analysis_fields_table.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ export const MemoizedAnalysisFieldsTable: FC<{
181181
</EuiCallOut>
182182
)}
183183
{tableItems.length > 0 && (
184-
<EuiPanel paddingSize="m">
184+
<EuiPanel paddingSize="m" data-test-subj="mlAnalyticsCreateJobWizardExcludesSelect">
185185
<CustomSelectionTable
186186
data-test-subj="mlAnalyticsCreationAnalysisFieldsTable"
187187
checkboxDisabledCheck={checkboxDisabledCheck}

0 commit comments

Comments
 (0)