Create AutoML SDK configuration
AutoML SDK configuration is a Java Spring-like set of definitions
for AutoML SDK components: Annotators, Feature Extractors, Post-Processors, and so on. The configuration is created declaratively using Java annotations. There are two primary annotations (similar to Spring's @Configuration and @Bean annotations accordingly):
@ModelConfigurationindicates that the annotated class is used as a source of component definitions.@Nameddeclares a component and tells that a method returns an actual component that should be registered inComponentRegistry.
The @ModelConfiguration-annotated class can have a declaration for more than one @Named. Once your classes are defined, you can use the configuration for model training.
Basic configuration
The basic configuration involves the following:
- Use
@ModelConfigurationto declare a class as a configuration instance. - Add the
@HypermodelConfigurationmain configuration class to the hyper model declaration.
@ModelConfiguration
public class ExampleConfiguration {
}
@HypermodelConfiguration(ExampleConfiguration.class)
public class ExampleHypermodel extends GenericIeHypermodel {
public ExampleHypermodel() throws Exception {
super();
}
}
Note that the example above extends the default GenericIeHypermodel configuration. You can change this during the Archetype generation by specifying the importConfigurationFromGenericModel parameter.
Component configuration
To declare one or multiple AutoML SDK components, use @Named annotation with a string value to attach a name identifier to a component. Note that @Named can return a single component or a list of components. Components are recognized based on the response type.
The following example declares three named components: basePostProcessors, featureExtractors, and a list of annotators.
import java.util.ArrayList;
import java.util.List;
import com.workfusion.vds.sdk.api.hypermodel.annotation.ModelConfiguration;
import com.workfusion.vds.sdk.api.hypermodel.annotation.Named;
import com.workfusion.vds.sdk.api.nlp.annotator.Annotator;
import com.workfusion.vds.sdk.api.nlp.fe.FeatureExtractor;
import com.workfusion.vds.sdk.api.nlp.model.Cell;
import com.workfusion.vds.sdk.api.nlp.model.Element;
import com.workfusion.vds.sdk.api.nlp.model.IeDocument;
import com.workfusion.vds.sdk.api.nlp.processing.Processor;
import com.workfusion.vds.sdk.nlp.component.annotator.EntityBoundaryAnnotator;
import com.workfusion.vds.sdk.nlp.component.annotator.tokenizer.SplitterTokenAnnotator;
import com.workfusion.vds.sdk.nlp.component.fe.ner.IsFirstNerInFocusElementFE;
import com.workfusion.vds.sdk.nlp.component.processing.grouping.RowBasedGroupingProcessor;
@ModelConfiguration
public class ExampleConfiguration {
@Named("basePostProcessors")
public Processor<IeDocument> postProcessors() {
return new RowBasedGroupingProcessor();
}
@Named("featureExtractors")
public FeatureExtractor<Element> featureExtractors() {
return new IsFirstNerInFocusElementFE(Cell.class);
}
@Named("annotators")
public List<Annotator> annotators() {
List<Annotator> annotators = new ArrayList<>();
annotators.add(new SplitterTokenAnnotator("\\s+"));
annotators.add(new EntityBoundaryAnnotator());
return annotators;
}
}
The result of this configuration is as follows:
- Post-Processor from
basePostProcessors - Feature Extractor from
featureExtractors - Two Annotators from
annotators
For advanced configuration components, refer to the section below.
ConfigurationContext
ConfigurationContext is an interface that provides a map of initial configuration parameters and a reference to a Field object. The Field value is used to specify the settings for a specific sub-model configuration, for example, to apply specific annotators only to the field of the ADDRESS type.
For Information Extraction models,@Named methods can accept a ConfigurationContext or IeConfigurationContext object.
ConfigurationContex and IeConfigurationContext contain methods to access different properties. The most common property is FieldInfo: a class for information extraction containing information about the current sub-model configuration. With the property, one @Named method can produce different sets of AutoML SDK components for different sub-models using a switch case or an if-else statement.
The following ConfigurationContext methods are considered the most useful:
For a full list of methods, refer to the ConfigurationContext documentation.
getField
getField gets the FieldInfo associated with the current sub-model configuration. For the ConfigurationContext that isn't related to a particular field, getField returns empty FieldInfo with code by default.
FieldInfo getField()
The following example contains an IeConfigurationContext object. In this case, each AutoML SDK configuration produces a different set of Annotators, depending on the code type: invoice_number or address.
import java.util.ArrayList;
import java.util.List;
import com.workfusion.vds.sdk.api.hypermodel.annotation.ModelConfiguration;
import com.workfusion.vds.sdk.api.hypermodel.annotation.Named;
import com.workfusion.vds.sdk.api.nlp.annotator.Annotator;
import com.workfusion.vds.sdk.api.nlp.configuration.IeConfigurationContext;
import com.workfusion.vds.sdk.nlp.component.annotator.EntityBoundaryAnnotator;
import com.workfusion.vds.sdk.nlp.component.annotator.ner.BaseRegexNerAnnotator;
import com.workfusion.vds.sdk.nlp.component.annotator.tokenizer.SplitterTokenAnnotator;
@ModelConfiguration
public class TrainingIeModelConfiguration {
public static final String REGEX_SPLITTER_TOKEN = "\\s+";
public static final String REGEX_ZIP = "[0-9]{5}(-[0-9]{4,5})?";
public static final String REGEX_CITY = "[A-Z][a-z]{3,18}";
public static final String REGEX_INVOICE_NUMBER = "([\\d]{10,11})";
@Named("annotators")
public List<Annotator> annotators(IeConfigurationContext context) {
List<Annotator> annotators = new ArrayList<>();
annotators.add(new SplitterTokenAnnotator(REGEX_SPLITTER_TOKEN));
annotators.add(new EntityBoundaryAnnotator());
String codeType = context.getField().getCode();
switch (codeType) {
case "invoice_number": {
annotators.add(BaseRegexNerAnnotator.getJavaPatternRegexNerAnnotator("invoice_number", REGEX_INVOICE_NUMBER));
break;
}
case "address": {
annotators.add(BaseRegexNerAnnotator.getJavaPatternRegexNerAnnotator("city", REGEX_CITY));
annotators.add(BaseRegexNerAnnotator.getJavaPatternRegexNerAnnotator("zip", REGEX_ZIP));
break;
}
}
return annotators;
}
}
For more information, refer to the FieldInfo documentation.
getResource
getResource provides a Resource instance for a specified path:
- Classpath resource, for example,
classpath:test.csv - Model resource, for example,
/ref-data/test.csv,/dictionaries/test.csv,/corrections/test.csv
Resource getResource(String path)
Here's an example of getResource usage with a dictionary:
@Named("annotators")
public List<Annotator> annotators(IeConfigurationContext context) {
List<Annotator> annotators = new ArrayList<>();
annotators.add(new AhoCorasickDictionaryNerAnnotator("company_name",
new CsvDictionaryKeywordProvider(context.getResource("/dictionaries/CompanyNames.csv"))));
return annotators;
}
InputStream
In case the Resource instance is provided by an external WorkFusion source, for example, are attached to a Manual Task or use an input stream to read data, apply the try-with-resources construct to ensure that the opened stream is closed at the end of the statement.
InputStream openInputStream()
getParameter
getParameter gets a configuration parameter (for example, parameter name) acquired externally from a WorkFusion service. This parameter can be used for advanced model configuration.
Object getParameter(String name)
For more information, refer to the getParameter() documentation.
ComponentRegistry
ComponentRegistry is an interface to provide access to @Named components as Java Objects. You can get an instance of such an object in a @Named method. Using the interface, you can obtain a component instance by ID or internal return type (for example, FeatureExtactor.class ). In some cases, this can simplify the configuration code.
ComponentRegistry default implementation contains the following methods:
For usage information, refer to the ComponentRegistry documentation.
getComponent
getComponent gets a single component based on a unique component ID.
Component getComponent(String componentName)
| Parameter | Description |
|---|---|
@componentName | @Named component annotation value |
@return | Single component |
getComponents
getComponents gets a collection of @Named components based on the return type.
Collection<Component> getComponents(Class<?> type)
| Parameter | Description |
|---|---|
@type | Component type |
@return | Collection of components |
import com.workfusion.vds.nlp.model.configuration.DefaultComponentRegistry;
import com.workfusion.vds.sdk.api.hypermodel.annotation.ModelConfiguration;
import com.workfusion.vds.sdk.api.hypermodel.annotation.Named;
import com.workfusion.vds.sdk.api.nlp.annotator.Annotator;
import com.workfusion.vds.sdk.api.nlp.configuration.IeConfigurationContext;
import com.workfusion.vds.sdk.nlp.component.annotator.ner.BaseRegexNerAnnotator;
@ModelConfiguration
public class Config {
public static final String REGEX_ZIP = "[0-9]{5}(-[0-9]{4,5})?";
public static final String REGEX_CITY = "[A-Z][a-z]{3,18}";
public static final String REGEX_INVOICE_NUMBER = "([\\d]{10,11})";
@Named("a1")
public Annotator a1() {
return BaseRegexNerAnnotator.getJavaPatternRegexNerAnnotator("invoice_number", REGEX_INVOICE_NUMBER);
}
@Named("a2")
public Annotator a2() {
return BaseRegexNerAnnotator.getJavaPatternRegexNerAnnotator("zip", REGEX_ZIP);
}
@Named("a3")
public Annotator a3() {
return BaseRegexNerAnnotator.getJavaPatternRegexNerAnnotator("city", REGEX_CITY);
}
}
Configuration import
To avoid coding a configuration from scratch, you can import one or multiple other configurations into it. In this case, the imported configuration is considered a child, and the target configuration is considered a parent. The resulting configuration contains all components from the child and parent configurations.
To import one or more configurations to another configuration, use the @Import class-level annotation together with @ModelConfiguration. The latter contains a reference to the child configuration class.
import com.workfusion.vds.sdk.api.hypermodel.annotation.Import;
import com.workfusion.vds.sdk.api.hypermodel.annotation.ModelConfiguration;
import com.workfusion.vds.sdk.api.hypermodel.annotation.Named;
import com.workfusion.vds.sdk.api.nlp.processing.Processor;
@ModelConfiguration
class PostProcessorConfig {
@Named("presentationPostProcessor")
public Processor getPostProcessors() {
return new ExamplePostProcessing1();
}
@Named("basePostProcessor")
public Processor postProcessors() {
return new ExamplePostProcessing2();
}
}
@ModelConfiguration
@Import(configurations = {
@Import.Configuration(PostProcessorConfig.class)
})
class GenericPostProcessorConfig {
@Named("basePostProcessor")
public Processor postProcessors() {
return new ExamplePostProcessing3();
}
}
The GenericPostProcessorConfig processing output comprises all named components from the child PostProcessorConfig configuration and all components from the parent GenericPostProcessorConfig one.
Both configurations contain a basePostProcessor Post-Processor. In this case, the component declared in the parent GenericPostProcessorConfig configuration overrides the component with the same name declared in the child PostProcessorConfig one.
The resulting configuration contains the following components:
-
presentationPostProcessorfromPostProcessorConfig -
basePostProcessorfromGenericPostProcessorConfig
Filtering
When importing a configuration, you can filter out certain components, for instance, sub-child configurations (if any). See the two examples below:
Example 1
There are two configurations, each containing several named components. ChildAnnotatorConfig is imported to ParentAnnotatorConfig with @Filter disabled.
import com.workfusion.vds.sdk.api.hypermodel.annotation.Import;
import com.workfusion.vds.sdk.api.hypermodel.annotation.ModelConfiguration;
import com.workfusion.vds.sdk.api.hypermodel.annotation.Named;
import com.workfusion.vds.sdk.api.nlp.annotator.Annotator;
@ModelConfiguration
class ChildAnnotatorConfig {
@Named("annotatorA")
public Annotator testAnnotatorA() {
return new AnnotatorA();
}
@Named("annotatorB")
public Annotator testAnnotatorB() {
return new AnnotatorB();
}
}
@ModelConfiguration
@Import(configurations = {@Import.Configuration(ChildAnnotatorConfig.class)})
class ParentAnnotatorConfig {
@Named("annotatorC")
public Annotator testAnnotatorC() {
return new AnnotatorC();
}
}
The output is a merge of the child and parent configurations and contains the following components:
- Optional
annotatorA - Fixed
annotatorB - Fixed
annotatorC
Example 2
There are two configurations, each containing several named components. ChildAnnotatorConfig is imported to ParentAnnotatorConfig with enabled @Filter. To enable the filter, use @Filter in the @Import annotation.
import com.workfusion.vds.sdk.api.hypermodel.annotation.Filter;
import com.workfusion.vds.sdk.api.hypermodel.annotation.Import;
import com.workfusion.vds.sdk.api.hypermodel.annotation.ModelConfiguration;
import com.workfusion.vds.sdk.api.hypermodel.annotation.Named;
import com.workfusion.vds.sdk.api.nlp.annotator.Annotator;
@ModelConfiguration
class ChildAnnotatorConfig {
@Named("annotatorA")
public Annotator testAnnotatorA() {
return new AnnotatorA();
}
@Named("annotatorB")
public Annotator testAnnotatorB() {
return new AnnotatorB();
}
}
@ModelConfiguration
@Import(configurations = {
@Import.Configuration(value = ChildAnnotatorConfig.class, exclude = {@Filter(type = AnnotatorB.class)}),
})
class ParentAnnotatorConfig {
@Named("annotatorC")
public Annotator testAnnotatorC() {
return new AnnotatorC();
}
}
The resulting configuration contains the following components:
- Fixed
AnnotatorC AnnotatorAorAnnotatorB
Advanced configuration components
Apart from standard components like Annotators, Feature Extractors, and Post-Processors, a model can contain fundamental components for advanced configuration. These components may require a deeper understanding of AutoML SDK.
Any of the following components can be declared only once. Otherwise, errors may occur.
Advanced components include:
LiblinearClassifier,Vowpal Wabbit ClassifierPipelineConfiguration
LiblinearClassifier and Vowpal Wabbit Classifier
LiblinearClassifier and VWClassifier are utility classes to store possible training arguments of Liblinear and Vowpal Wabbit.
@Named("mlConfig")
public LiblinearClassifier mlConfig(){
return new LiblinearClassifier.Builder()
.solver(Solver.L2R_L2LOSS_SVC)
.quietMode()
.build();
}
Only one classifier at a time can be used in a configuration. Otherwise, errors may occur.
For more information, refer to the LIBSVM and Vowpal Wabbit documentation.
Algorithm extensions
AutoML SDK provides extensions for LiblinearClassifier and some Python-based classifiers, such as AdaBoostClassifier, LinearSupportVectorClassifier, LogisticRegression, RandomForestClassifier. Extensions allow setting value ranges for specific algorithm parameters to select the best ones for model training.
Here's the list of extensions:
LiblinearParamGridClassifierAdaBoostParamGridClassifierLinearSupportVectorParamGridClassifierLogisticRegressionParamGridClassifierRandomForestParamGridClassifier
To use an extension, add the mlConfig annotation to your AutoML SDK configuration. Here's an example of using the LiblinearParamGridClassifier extension:
@Named("mlConfig")
public Classifier mlConfig() {
return new LiblinearParamGridClassifier.Builder()
.solver(LiblinearClassifier.Solver.L2R_L2LOSS_SVC)
.quietMode()
.cost(new RealMultipliedRange(Math.pow(2, -3), Math.pow(2, 6), 2))
.solverTypes(ImmutableList.of(
LiblinearClassifier.Solver.L2R_L2LOSS_SVC,
LiblinearClassifier.Solver.L2R_L1LOSS_SVC_DUAL,
LiblinearClassifier.Solver.L1R_L2LOSS_SVC))
.build();
}
Also, see this example of using LinearSupportVectorParamGridClassifier:
@Named("mlConfig")
public Classifier mlConfig() {
return new LinearSupportVectorParamGridClassifier.Builder()
.maxIterationsRange(new DiscreteMultipliedRange(2, 8, 2))
.seed(123)
.build();
}
ParamGridClassifier implementations have extra utilities, such as DiscreteMultipliedRange, DiscreteRange, RealMultipliedRange, RealRange to specify the ranges of parameters for different data types (Integer, Double, and so on) and steps to generate arithmetic or geometric sequence.
Below is an example of using RealMultipliedRange that produces the [1/8, 1/4, 1/2, 1, 2, 4, 8, 16, 32, 64] range:
new RealMultipliedRange(Math.pow(2, -3), Math.pow(2, 6), 2)
Here's an example of using DiscreteRange that produces the [100, 200, 300, 500] range:
new DiscreteRange(100, 500, 100)
It is recommended to use ParamGridClassifier extensions only for local model training, not for production. The selection of algorithm parameters is a time-consuming operation, which can dramatically affect model training time.
PipelineConfiguration
PipelineConfiguration is a set of parameters for building a pipeline. The defined parameters remain unchanged across all configurations created for experiments.
The following pipelineConfiguration example sets the NORMALIZE_SCORE and CASE_SENSITIVE parameters to true.
@Named("pipelineConfiguration")
public PipelineConfiguration pipelineConfiguration() {
return new PipelineConfiguration.Builder()
.parameter(GenericPipelineConfiguration.NORMALIZE_SCORE, true)
.parameter(GenericPipelineConfiguration.CASE_SENSITIVE, true)
.build();
}
For more information, refer to the PipelineConfiguration and GenericPipelineConfiguration documentation.