You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何h2o.r2()计算结果与手动计算的R²不一致?

H2O AutoML模型h2o.r2()与手动计算R²结果不一致的原因及解决方法

在使用H2O AutoML训练回归模型时,发现h2o.r2(model, train=T)返回的R²值与手动计算结果差异显著,但简单线性模型下两者结果一致。以下是复现代码:

library(tidyverse)
# fit AML, get leader...
fit_aml_H2O = function(X_df, Y_vec) {
  library(h2o); h2o.init()
  
  df = cbind(X_df, Y_vec)
  colnames(df)[[ncol(df)]]='Y'
  df = as.h2o(df)
  aml <- h2o.automl(x=colnames(X_df), y='Y', training_frame=df, nfolds=0,
                    max_models = 15, max_runtime_secs=90)
  leader = h2o.get_best_model(aml)
  cat('R^2: ', h2o.r2(leader, train=T))
  return(leader)
}

# manually compute R^2 (verified to work)
R2 = function(Y_pred, Y_true) {
  MSE = mean((as.numeric(Y_true)-as.numeric(Y_pred))**2)
  R2 = 1-MSE/var(Y_true)
  return(R2)
}

data(iris)

X_df = iris %>% select(-Petal.Length)
Y = iris %>% select(Petal.Length)
model = fit_aml_H2O(X_df, Y)

X_df = as.h2o(X_df)
Y_pred = h2o.predict(model, newdata=X_df)$predict
(R2_train = R2(Y_pred[,1], Y[,1]))
cat('R^2 (manually computed): ', R2_train, '\n')
cat('R^2 (reported by H2O): ', h2o.r2(model,train=T), '\n')
cat('difference between manual R^2 & H2O reported R^2: ',
    abs(R2_train-h2o.r2(model,train=T)), '\n')
stopifnot(all.equal(h2o.r2(model,train=T), R2_train))

核心原因

出现差异的关键在于手动计算与H2O内部的R²公式不一致:

  • H2O的R²计算公式为:R² = 1 - (RSS / TSS),其中RSS是残差平方和(sum((Y_true - Y_pred)^2)),TSS是总平方和(sum((Y_true - mean(Y_true))^2)),基于总体方差逻辑计算。
  • 你的手动代码中,var(Y_true)是样本方差(即sum((Y_true - mean(Y_true))^2)/(n-1)),导致最终公式变为1 - (MSE) / (TSS/(n-1)),与H2O的计算逻辑出现偏差。

此外,AutoML中的复杂模型(如GBM、堆叠集成模型)训练时会自动处理特征,若手动预测时使用的数据集与模型训练集存在细微差异(比如R数据框转H2O时的类型处理),也可能放大差异,但核心矛盾是方差计算方式的不同。

解决方案

修正手动计算逻辑,与H2O的公式保持一致,同时确保使用模型训练时的原始训练集数据进行计算,避免数据不一致:

library(tidyverse)
library(h2o)

fit_aml_H2O = function(X_df, Y_vec) {
  h2o.init()
  
  df = cbind(X_df, Y_vec)
  colnames(df)[[ncol(df)]]='Y'
  df_h2o = as.h2o(df)
  aml <- h2o.automl(x=colnames(X_df), y='Y', training_frame=df_h2o, nfolds=0,
                    max_models = 15, max_runtime_secs=90)
  leader = h2o.get_best_model(aml)
  cat('R^2 (H2O reported): ', h2o.r2(leader, train=T), '\n')
  return(list(model=leader, train_data=df_h2o)) # 返回训练集H2O对象
}

# 与H2O逻辑一致的手动R²计算函数
R2_h2o_style = function(Y_pred, Y_true) {
  Y_pred_vec = as.numeric(Y_pred)
  Y_true_vec = as.numeric(Y_true)
  
  RSS = sum((Y_true_vec - Y_pred_vec)^2)
  TSS = sum((Y_true_vec - mean(Y_true_vec))^2)
  R2 = 1 - RSS/TSS
  return(R2)
}

data(iris)

X_df = iris %>% select(-Petal.Length)
Y = iris %>% select(Petal.Length)
result = fit_aml_H2O(X_df, Y)
model = result$model
train_data_h2o = result$train_data

# 使用模型训练时的原始H2O数据集预测,避免数据偏差
Y_pred = h2o.predict(model, newdata=train_data_h2o)$predict
Y_true = train_data_h2o$Y

(R2_train = R2_h2o_style(Y_pred, Y_true))
cat('R^2 (manually computed, H2O style): ', R2_train, '\n')
cat('difference between manual R^2 & H2O reported R^2: ',
    abs(R2_train - h2o.r2(model, train=T)), '\n')
stopifnot(all.equal(h2o.r2(model,train=T), R2_train, tolerance=1e-6))

修正说明

  1. 修改fit_aml_H2O函数,返回训练集的H2O对象,确保手动计算使用与模型训练完全一致的数据。
  2. 重写R2_h2o_style函数,采用H2O的1 - RSS/TSS公式,摒弃基于样本方差的计算逻辑。
  3. 直接使用训练集H2O对象进行预测,避免原R数据框转H2O时可能出现的类型或数据差异。

内容的提问来源于stack exchange,提问作者profPlum

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 23:15:20