Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion openml-h2o/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
<description>Contains classes and logic related with the import of H2O models.</description>

<properties>
<h2o.version>3.36.0.3</h2o.version>
<h2o.version>3.46.0.11</h2o.version>
</properties>

<dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,6 @@ public enum H2OAlgorithm implements MLAlgorithmEnum {

/**
* Generalized Linear Models (GLM) estimate regression models for outcomes following exponential distributions.
* <p>
* We enforce it to always have a family parameter Binomial that causes it to be a classification algorithm.
*/
GENERALIZED_LINEAR_MODEL(createDescriptor(
"Generalized Linear Modeling",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ public final class H2OGeneralizedLinearModelUtils extends AbstractSupervisedH2OA
// H2O UI excludes these in a hard-coded way...so do we.
.filter(modelParameter -> !"tweedie_link_power".equals(modelParameter.getName()) &&
!"tweedie_variance_power".equals(modelParameter.getName()) &&
!"nlambdas".equals(modelParameter.getName()) && !"early_stopping".equals(modelParameter.getName())
!"nlambdas".equals(modelParameter.getName()) && !"early_stopping".equals(modelParameter.getName()) &&
!"influence".equals(modelParameter.getName())
Comment thread
jpalmeida416 marked this conversation as resolved.
)
.collect(Collectors.toSet());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,29 @@ private static ModelParameter getModelParameter(final Class<? extends water.bind
if (apiAnnot.values().length > 0) {
final Set<String> possibleValues = Arrays.stream(apiAnnot.values()).collect(Collectors.toSet());
final Enum<?> defaultValue = getDefaultChoiceValue(paramsClass, fieldName);
paramType = new ChoiceFieldType(possibleValues, defaultValue.name());
String defaultValueName = defaultValue != null ? defaultValue.name() : "";
Comment thread
artpdr marked this conversation as resolved.
if (!possibleValues.contains(defaultValueName)) {
final String finalDefaultName = defaultValueName;

// Check if there is a match
final Optional<String> match = possibleValues.stream()
.filter(val -> val.equalsIgnoreCase(finalDefaultName))
.findFirst();

if (match.isPresent()) {
defaultValueName = match.get();
} else if (!possibleValues.isEmpty()) {
// Fallback to the first allowed choice if the default is missing or null
defaultValueName = possibleValues.iterator().next();
logger.warn(
"Default value name `{}` is not present on possible values set `{}`. Falling back to `{}`",
finalDefaultName,
possibleValues,
defaultValueName
);
}
}
paramType = new ChoiceFieldType(possibleValues, defaultValueName);

} else if (fieldType.equals(String.class)) {
final String defaultValue = getDefaultStringValue(paramsClass, fieldName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,9 @@ public static Map<String, String> getGlm() {
.put("lambda", "1")
.put("lambda_search", "true")
.put("standardize", "true")
.put("non_negative", "true")
// non_negative does not work with multinomial/ordinal families and GLM
// fails on init if set to true (see https://github.com/h2oai/h2o-3/issues/7055)
.put("non_negative", "false")
Comment thread
artpdr marked this conversation as resolved.
.put("obj_reg", "-1")
.put("theta", "1e-10") // default value
.put("HGLM", "false") // default value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import com.feedzai.openml.h2o.algos.mocks.RegularParameters;
import com.feedzai.openml.h2o.params.ParametersBuilderUtil;
import com.feedzai.openml.provider.descriptor.ModelParameter;
import com.feedzai.openml.provider.descriptor.fieldtype.ChoiceFieldType;

import org.junit.Test;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -177,4 +179,96 @@ private ListAppender<ILoggingEvent> appendLogger(final Class<?> clazz) {
classLogger.addAppender(listAppender);
return listAppender;
}

/**
* Tests that a case-mismatched enum name is still resolved.
*/
@Test
public void choiceFieldCaseMismatchCorrection() {
final Set<ModelParameter> parameters = ParametersBuilderUtil.getParametersFor(
MockCaseMismatchSchema.class,
MockCaseMismatchBinding.class
);

assertThat(parameters).as("The choice parameter descriptor should be resolved successfully").hasSize(1);

final ModelParameter parameter = parameters.iterator().next();

assertThat(parameter.getName()).as("The parameter name should match the target field").isEqualTo("dummyField");

final ChoiceFieldType fieldType = (ChoiceFieldType) parameter.getFieldType();

assertThat(fieldType.getDefaultValue()).as("The enum 'VALUE_ONE' should match the schema's 'value_one' choice")
.isEqualTo("value_one");
}

/**
* Tests that when an enum field evaluates to null or does not match any valid choices, we fall back to the first
* available choice.
*/
@Test
public void choiceFieldNullOrMissingFallback() {
final ListAppender<ILoggingEvent> listAppender = appendLogger(ParametersBuilderUtil.class);

final Set<ModelParameter> parameters = ParametersBuilderUtil.getParametersFor(
MockFallbackSchema.class,
MockFallbackBinding.class
);

assertThat(parameters).as("The parameter descriptor should be built using a fallback strategy").hasSize(1);

final ModelParameter parameter = parameters.iterator().next();

assertThat(parameter.getName()).as("The parameter name should match the target field").isEqualTo("dummyField");

final ChoiceFieldType fieldType = (ChoiceFieldType) parameter.getFieldType();

assertThat(fieldType.getDefaultValue()).as(
"A null value should fall back safely to the first choice in the schema").isEqualTo("value_one");

final List<ILoggingEvent> loggingList = listAppender.list;
assertThat(loggingList).as("A fallback must log an explicit warning").isNotEmpty();

final ILoggingEvent warningEvent = loggingList.get(0);
assertThat(warningEvent.getLevel()).isEqualTo(Level.WARN);
assertThat(warningEvent.getMessage()).as("The warning message should detail the fallback replacement")
.containsIgnoringCase("is not present on possible values set");
}

/**
* Dummy enum to simulate parameter choices.
*/
public enum DummyEnum {
VALUE_ONE, VALUE_TWO;
}

/**
* Schema where the annotation choices are lowercase.
*/
public static class MockCaseMismatchSchema extends water.api.schemas3.ModelParametersSchemaV3 {
@water.api.API(help = "Case mismatch", values = {"value_one", "value_two"})
public DummyEnum dummyField;
}

/**
* Fallback schema to be used when client binding POJO is null.
*/
public static class MockFallbackSchema extends water.api.schemas3.ModelParametersSchemaV3 {
@water.api.API(help = "Fallback", values = {"value_one"})
public DummyEnum dummyField;
}

/**
* Client binding POJO where the runtime field resolves to uppercase.
*/
public static class MockCaseMismatchBinding extends water.bindings.pojos.ModelParametersSchemaV3 {
public DummyEnum dummyField = DummyEnum.VALUE_ONE;
}

/**
* Client binding POJO where the enum initializes as null.
*/
public static class MockFallbackBinding extends water.bindings.pojos.ModelParametersSchemaV3 {
public DummyEnum dummyField = null;
}
}
Loading