Skip to content
Open
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
8 changes: 5 additions & 3 deletions h2o-algos/src/main/java/hex/api/MakeGLMModelHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ public GLMModelV3 make_model(int version, MakeGLMModelV3 args){

public GLMModelV3 make_unrestricted_model(int version, MakeUnrestrictedGLMModelV3 args){
GLMModel model = DKV.getGet(args.model.key());
if(model == null)
if (model == null)
throw new IllegalArgumentException("Missing source model " + args.model);
if(model._parms._control_variables == null){
throw new IllegalArgumentException("Source model is not trained with control variables.");
if (model._parms._control_variables == null && !model._parms._remove_offset_effects){
throw new IllegalArgumentException("Source model is not trained with control variables or remove offset effects.");
}
Key generatedKey = Key.make(model._key.toString()+"_unrestricted_model");
Key key = args.dest != null ? Key.make(args.dest) : generatedKey;
Expand All @@ -73,7 +73,9 @@ public GLMModelV3 make_unrestricted_model(int version, MakeUnrestrictedGLMModelV
Double.NaN, Double.NaN, -1);
m.setInputParms(inputParms);
m._input_parms._control_variables = null;
m._input_parms._remove_offset_effects = false;
m._parms._control_variables = null;
m._parms._remove_offset_effects = false;
DataInfo dinfo = model.dinfo();
dinfo.setPredictorTransform(TransformType.NONE);
m._output = new GLMOutput(model.dinfo(), model._output._names, model._output._column_types, model._output._domains,
Expand Down
96 changes: 54 additions & 42 deletions h2o-algos/src/main/java/hex/glm/GLM.java
Original file line number Diff line number Diff line change
Expand Up @@ -1430,7 +1430,7 @@ private void restoreScoringHistoryFromCheckpoint() {
else {
_scoringHistory.restoreFromCheckpoint(scoringHistory, colHeadersIndex);
}
if (_model._parms._control_variables != null) {
if (_model._parms._control_variables != null || _model._parms._remove_offset_effects) {
TwoDimTable scoringHistoryControlVal = _model._output._scoring_history_unrestricted_model;
_scoringHistoryUnrestrictedModel.restoreFromCheckpoint(scoringHistoryControlVal, colHeadersIndex);
}
Expand Down Expand Up @@ -3384,19 +3384,21 @@ private void scoreAndUpdateModel() {
Frame train = DKV.<Frame>getGet(_parms._train); // need to keep this frame to get scoring metrics back
_model.score(_parms.train(), null, CFuncRef.from(_parms._custom_metric_func)).delete();
scorePostProcessing(train, t1);
if (_model._parms._control_variables != null){
if (_model._parms._control_variables != null || _model._parms._remove_offset_effects){
try {
_model._useControlVariables = true;
_model._useControlVariables = _model._parms._control_variables != null;
_model._useRemoveOffsetEffects = _model._parms._remove_offset_effects;
long t2 = System.currentTimeMillis();
_model.score(train, null, CFuncRef.from(_parms._custom_metric_func)).delete();
scorePostProcessingControlVal(train, t2);
scorePostProcessingRestrictedModel(train, t2);
} finally {
_model._useControlVariables = false;
_model._useRemoveOffsetEffects = false;
}
}
}

private void scorePostProcessingControlVal(Frame train, long t1) {
private void scorePostProcessingRestrictedModel(Frame train, long t1) {
ModelMetrics mtrain = ModelMetrics.getFromDKV(_model, train); // updated by model.scoreAndUpdateModel
long t2 = System.currentTimeMillis();
if (mtrain != null) {
Expand All @@ -3408,34 +3410,41 @@ private void scorePostProcessingControlVal(Frame train, long t1) {
} else {
Log.info(LogMsg("ModelMetrics mtrain is null"));
}
Log.info(LogMsg("Control values training metrics computed in " + (t2 - t1) + "ms"));
Log.info(LogMsg("Restricted model training metrics computed in " + (t2 - t1) + "ms"));
if (_valid != null) {
Frame valid = DKV.<Frame>getGet(_parms._valid);
try {
_model._useControlVariables = true;
_model.score(_parms.valid(), null, CFuncRef.from(_parms._custom_metric_func)).delete();
} finally {
_model._useControlVariables = true;
}
_model.score(_parms.valid(), null, CFuncRef.from(_parms._custom_metric_func)).delete();
_model._output._validation_metrics = ModelMetrics.getFromDKV(_model, valid); //updated by model.scoreAndUpdateModel
ScoreKeeper validScore = new ScoreKeeper(Double.NaN);
validScore.fillFrom(_model._output._validation_metrics);
}
_model.addScoringInfo(_parms, nclasses(), t2, _state._iter); // add to scoringInfo for early stopping

if (_parms._generate_scoring_history) { // update scoring history with deviance train and valid if available
double[] betaContrVal = _model._output.getControlValBeta(_state.expandBeta(_state.beta()).clone());
GLMResDevTask task = new GLMResDevTask(_job._key, _dinfo, _parms, betaContrVal).doAll(_dinfo._adaptedFrame);
double objectiveControlVal = _state.objective(betaContrVal, task._likelihood);

if ((mtrain != null) && (_valid != null)) {
_scoringHistory.addIterationScore(true, true, _state._iter, task._likelihood,
objectiveControlVal, _state.deviance(task._likelihood), ((GLMMetrics) _model._output._validation_metrics).residual_deviance(),
mtrain._nobs, _model._output._validation_metrics._nobs, _state.lambda(), _state.alpha());
} else { // only doing training deviance
_scoringHistory.addIterationScore(true, false, _state._iter, task._likelihood,
objectiveControlVal, _state.deviance(task._likelihood), Double.NaN, mtrain._nobs, 1, _state.lambda(),
_state.alpha());
if(_model._useControlVariables) {
double[] betaContrVal = _model._output.getControlValBeta(_state.expandBeta(_state.beta()).clone());
GLMResDevTask task = new GLMResDevTask(_job._key, _dinfo, _parms, betaContrVal).doAll(_dinfo._adaptedFrame);
double objectiveControlVal = _state.objective(betaContrVal, task._likelihood);

if ((mtrain != null) && (_valid != null)) {
_scoringHistory.addIterationScore(true, true, _state._iter, task._likelihood,
objectiveControlVal, _state.deviance(task._likelihood), ((GLMMetrics) _model._output._validation_metrics).residual_deviance(),
mtrain._nobs, _model._output._validation_metrics._nobs, _state.lambda(), _state.alpha());
} else { // only doing training deviance
_scoringHistory.addIterationScore(true, false, _state._iter, task._likelihood,
objectiveControlVal, _state.deviance(task._likelihood), Double.NaN, mtrain._nobs, 1, _state.lambda(),
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable mtrain may be null at this access as suggested by this null guard.
Variable mtrain may be null at this access as suggested by this null guard.
Variable mtrain may be null at this access as suggested by this null guard.

Copilot uses AI. Check for mistakes.
_state.alpha());
}
} else if (_model._useRemoveOffsetEffects) {
if ((mtrain != null) && (_valid != null)) {
_scoringHistory.addIterationScore(true, true, _state._iter, _state.likelihood(),
_state.objective(), _state.deviance(), ((GLMMetrics) _model._output._validation_metrics).residual_deviance(),
mtrain._nobs, _model._output._validation_metrics._nobs, _state.lambda(), _state.alpha());
} else { // only doing training deviance
_scoringHistory.addIterationScore(true, false, _state._iter, _state.likelihood(),
_state.objective(), _state.deviance(), Double.NaN, mtrain._nobs, 1, _state.lambda(),
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable mtrain may be null at this access as suggested by this null guard.
Variable mtrain may be null at this access as suggested by this null guard.
Variable mtrain may be null at this access as suggested by this null guard.

Copilot uses AI. Check for mistakes.
_state.alpha());
}
}
_job.update(_workPerIteration, _state.toString());
}
Expand All @@ -3447,7 +3456,7 @@ private void scorePostProcessing(Frame train, long t1) {
ModelMetrics mtrain = ModelMetrics.getFromDKV(_model, train); // updated by model.scoreAndUpdateModel
long t2 = System.currentTimeMillis();
if (mtrain != null) {
if (_model._parms._control_variables != null){
if (_model._parms._control_variables != null || _model._parms._remove_offset_effects){
_model._output._training_metrics_unrestricted_model = mtrain;
_model._output._training_time_ms = t2 - _model._output._start_time; // remember training time
} else {
Expand All @@ -3464,15 +3473,15 @@ private void scorePostProcessing(Frame train, long t1) {
if (_valid != null) {
Frame valid = DKV.<Frame>getGet(_parms._valid);
_model.score(_parms.valid(), null, CFuncRef.from(_parms._custom_metric_func)).delete();
if(_model._parms._control_variables != null){
if(_model._parms._control_variables != null || _model._parms._remove_offset_effects){
_model._output._validation_metrics_unrestricted_model = ModelMetrics.getFromDKV(_model, valid);
} else {
_model._output._validation_metrics = ModelMetrics.getFromDKV(_model, valid); //updated by model.scoreAndUpdateModel
}
ScoreKeeper validScore = new ScoreKeeper(Double.NaN);
validScore.fillFrom(_model._output._validation_metrics);
}
if(_model._parms._control_variables != null) {
if(_model._parms._control_variables != null || _model._parms._remove_offset_effects) {
_model.addUnrestrictedModelScoringInfo(_parms, nclasses(), t2, _state._iter);
} else {
_model.addScoringInfo(_parms, nclasses(), t2, _state._iter);
Expand All @@ -3495,7 +3504,7 @@ private void scorePostProcessing(Frame train, long t1) {
_model._output._validation_metrics._nobs;
_lambdaSearchScoringHistory.addLambdaScore(_state._iter, ArrayUtils.countNonzeros(_state.beta()),
_state.lambda(), trainDev, validDev, xval_deviance, xval_se, _state.alpha());
} else if(_model._parms._control_variables != null){
} else if(_model._parms._control_variables != null || _model._parms._remove_offset_effects){
_scoringHistoryUnrestrictedModel.addIterationScore(true, true, _state._iter, _state.likelihood(),
_state.objective(), _state.deviance(), ((GLMMetrics) _model._output._validation_metrics_unrestricted_model).residual_deviance(),
mtrain._nobs, _model._output._validation_metrics_unrestricted_model._nobs, _state.lambda(), _state.alpha());
Expand All @@ -3509,7 +3518,7 @@ private void scorePostProcessing(Frame train, long t1) {
_lambdaSearchScoringHistory.addLambdaScore(_state._iter, ArrayUtils.countNonzeros(_state.beta()),
_state.lambda(), _state.deviance() / mtrain._nobs, Double.NaN, xval_deviance,
xval_se, _state.alpha());
} else if(_model._parms._control_variables != null) {
} else if(_model._parms._control_variables != null || _model._parms._remove_offset_effects) {
_scoringHistoryUnrestrictedModel.addIterationScore(true, false, _state._iter, _state.likelihood(),
_state.objective(), _state.deviance(), Double.NaN, mtrain._nobs, 1, _state.lambda(),
_state.alpha());
Expand All @@ -3523,7 +3532,7 @@ private void scorePostProcessing(Frame train, long t1) {
}
if (_parms._lambda_search) {
_model._output._scoring_history = _lambdaSearchScoringHistory.to2dTable();
} else if(_model._parms._control_variables != null){
} else if(_model._parms._control_variables != null || _model._parms._remove_offset_effects){
_model._output._scoring_history_unrestricted_model = _scoringHistoryUnrestrictedModel.to2dTable(_parms, _xval_deviances_generate_SH,
_xval_sd_generate_SH);
} else {
Expand Down Expand Up @@ -3846,24 +3855,24 @@ private void doCompute() {
if (_parms._generate_variable_inflation_factors) {
_model._output._vif_predictor_names = _model.buildVariableInflationFactors(_train, _dinfo);
}// build variable inflation factors for numerical predictors
if(_model._parms._control_variables != null) {
// create combination of scoring history with control variables enabled and disabled
// keep unrestricted model scoring history in _model._output._control_val_scoring_history
if(_model._parms._control_variables != null || _model._parms._remove_offset_effects) {
// create combination of scoring history with control variables or remove offset effect enabled and disabled
// keep unrestricted model scoring history in _model._output._scoring_history_unrestricted_model

TwoDimTable scoringHistoryEarlyStop = ScoringInfo.createScoringHistoryTable(_model.getScoringInfo(),
(null != _parms._valid), false, _model._output.getModelCategory(), false, _parms.hasCustomMetricFunc());
TwoDimTable scoringHistoryEarlyStopControlVal = ScoringInfo.createScoringHistoryTable(_model.getUnrestrictedModelScoringInfo(),
TwoDimTable scoringHistoryEarlyStopRestricted = ScoringInfo.createScoringHistoryTable(_model.getUnrestrictedModelScoringInfo(),
(null != _parms._valid), false, _model._output.getModelCategory(), false, _parms.hasCustomMetricFunc());
scoringHistoryEarlyStopControlVal.setTableHeader("Scoring history with control variables enabled");
ScoreKeeper.StoppingMetric sm = _model._parms._stopping_metric.name().equals("AUTO") ? _model._output.isClassifier() ?
ScoreKeeper.StoppingMetric.logloss : ScoreKeeper.StoppingMetric.deviance : _model._parms._stopping_metric;
_model._output._scoring_history = combineScoringHistoryControlVariables(_model._output._scoring_history, _model._output._scoring_history_unrestricted_model,
scoringHistoryEarlyStop, scoringHistoryEarlyStopControlVal, sm, null != _parms._valid);
_model._output._scoring_history_unrestricted_model = combineScoringHistory(_model._output._scoring_history_unrestricted_model, scoringHistoryEarlyStopControlVal);
_model._output._scoring_history = combineScoringHistoryRestricted(_model._output._scoring_history, _model._output._scoring_history_unrestricted_model,
scoringHistoryEarlyStop, scoringHistoryEarlyStopRestricted, sm, null != _parms._valid);
_model._output._scoring_history_unrestricted_model = combineScoringHistory(_model._output._scoring_history_unrestricted_model, scoringHistoryEarlyStopRestricted);
_model._output._scoring_history_unrestricted_model.setTableHeader(_model._output._scoring_history_unrestricted_model.getTableHeader()+" unrestricted model");
// set control variables flag to true for scoring after training
_model._useControlVariables = true;
_model._output._varimp = _model._output.calculateVarimp(true);
// set control variables and remove offset effects flag to true for scoring after training
_model._useControlVariables = _model._parms._control_variables != null;
_model._useRemoveOffsetEffects = _model._parms._remove_offset_effects;
_model._output._varimp = _model._output.calculateVarimp(_model._useControlVariables);
_model._output._variable_importances_unrestricted_model = calcVarImp(_model._output.calculateVarimp(false));
_model._output._variable_importances_unrestricted_model.setTableHeader(_model._output._variable_importances_unrestricted_model.getTableHeader()+" unrestricted model");
_model._output._variable_importances = calcVarImp(_model._output._varimp);
Expand Down Expand Up @@ -4063,6 +4072,9 @@ protected void updateProgress(boolean canScore) {
GLMResDevTask task = new GLMResDevTask(_job._key,_dinfo,_parms, betaContrVal).doAll(_state._dinfo._adaptedFrame);
double objectiveControlVal = _state.objective(betaContrVal, task._likelihood);
_scoringHistory.addIterationScore(_state._iter, task._likelihood, objectiveControlVal);
} else if (_model._parms._remove_offset_effects) {
_scoringHistoryUnrestrictedModel.addIterationScore(_state._iter, _state.likelihood(), _state.objective());
_scoringHistory.addIterationScore(_state._iter, _state.likelihood(), _state.objective());
} else {
_scoringHistory.addIterationScore(_state._iter, _state.likelihood(), _state.objective());
}
Expand All @@ -4079,7 +4091,7 @@ protected void updateProgress(boolean canScore) {
}

private boolean updateEarlyStop() {
ScoreKeeper[] sk = _parms._control_variables != null ? _model.unrestritedModelScoreKeepers() : _model.scoreKeepers();
ScoreKeeper[] sk = _parms._control_variables != null || _parms._remove_offset_effects ? _model.unrestritedModelScoreKeepers() : _model.scoreKeepers();
return _earlyStop || ScoreKeeper.stopEarly(sk,
_parms._stopping_rounds, ScoreKeeper.ProblemType.forSupervised(_nclass > 1), _parms._stopping_metric,
_parms._stopping_tolerance, "model's last", true);
Expand Down
Loading