mlr3中逻辑回归模型的残差计算方式探究
问题描述
使用mlr3包构建逻辑回归模型后,提取classifier_log_reg$model$residuals得到的残差,与手动计算的Pearson残差、Deviance残差均不匹配;且该残差与「实际值-预测值」的简单差值存在系统性关联,调整后呈现类Sigmoid关系,但用nls拟合标准Sigmoid曲线效果不佳。
复现代码
模型训练代码
library(mlr3) library(tidyverse) # 创建模拟数据 data <- data.frame( predictor = c(rnorm(50, mean = 0), rnorm(50, mean = 1)), dependant = as.factor(c(rep(0,50), rep(1, 50))) ) # 定义并训练逻辑回归模型 classifier_log_reg <- mlr_learners$get("classif.log_reg") task <- mlr3::TaskClassif$new(id = "my_data", backend = data, target = "dependant", positive = "1") classifier_log_reg$train(task, row_ids = 1:100)
手动计算残差的函数
pearson_residuals <- function(p, actual) { # 计算二项分布的标准差 std_dev <- sqrt(p * (1 - p)) # 避免除以0 std_dev[std_dev == 0] <- .Machine$double.eps # Pearson残差 residuals <- (actual - p) / std_dev return(residuals) } deviance_residuals <- function(p, actual) { # 避免log(0) p <- ifelse(p == 0, .Machine$double.eps, ifelse(p == 1, 1 - .Machine$double.eps, p)) # Deviance残差 residuals <- sign(actual - p) * sqrt(-2 * (actual * log(p) + (1 - actual) * log(1 - p))) return(residuals) }
残差关联分析代码
# 从模型提取残差 residuals <- classifier_log_reg$model$residuals # 手动计算实际值-预测值的差值 classifier_log_reg$predict_type <- 'prob' predictions <- classifier_log_reg$predict(task, row_ids = 1:100) predictions <- predictions$data$prob %>% as.data.frame() %>% pull(1) actual <- data$dependant %>% as.character() %>% as.numeric() my_resid <- actual - predictions # 调整后构建数据框并绘图 df <- data.frame( x = residuals - (actual * 2) + 1, y = (my_resid + 1) / 2, actual = actual ) ggplot(df) + geom_point(aes(x = x, y = y))
Sigmoid拟合尝试代码
sigmoid <- function(x, L, k, x0) { L / (1 + exp(-k * (x - x0))) } model <- nls(y ~ sigmoid(x, L, k, x0), data = df, start = list(L = 1, k = 1, x0 = 1), control = nls.control(maxiter = 100)) df$fitted <- predict(model, df) ggplot(df) + geom_point(aes(x = fitted, y = y))
核心解答
1. mlr3逻辑回归残差的底层计算逻辑
mlr3的classif.log_reg学习器底层调用R基础包的glm()函数,使用binomial(link = "logit")族。你提取的classifier_log_reg$model$residuals是工作残差(Working Residuals),这是glm在迭代加权最小二乘拟合过程中使用的残差,计算公式为:
$$\text{Working Residuals} = \frac{y - \mu}{g'(\eta)}$$
其中:
- $y$:实际响应值(0或1)
- $\mu$:模型预测的事件发生概率(即你手动计算的
predictions) - $\eta$:线性预测值(logit尺度,$\eta = \ln(\mu/(1-\mu))$)
- $g'(\eta)$:logit链接函数的导数,即$g'(\eta) = \mu(1-\mu)$
代入后简化为:
$$\text{Working Residuals} = \frac{y - \mu}{\mu(1-\mu)}$$
2. 残差间的真实关系
你手动计算的my_resid = y - \mu,因此工作残差与my_resid的直接关系为:
$$\text{model_residuals} = \frac{\text{my_resid}}{\mu(1-\mu)}$$
你观察到的类Sigmoid关系,本质是因为$\mu$(预测概率)与自变量predictor本身遵循Sigmoid函数关系($\mu = 1/(1+e^{-(\beta_0 + \beta_1 \text{predictor})})$),经过你对x和y的变换后,间接呈现出类Sigmoid的关联,但这并非x与y直接的Sigmoid函数关系,因此用nls拟合标准Sigmoid曲线效果不佳。
3. 如何获取常见残差类型
如果需要获取Pearson残差或Deviance残差,可以直接对glm对象调用residuals()函数并指定类型:
# 获取Pearson残差 pearson_resid <- residuals(classifier_log_reg$model, type = "pearson") # 获取Deviance残差 deviance_resid <- residuals(classifier_log_reg$model, type = "deviance") # 获取响应残差(y - mu) response_resid <- residuals(classifier_log_reg$model, type = "response")
这些结果会和你手动计算的对应残差值完全匹配。
内容的提问来源于stack exchange,提问作者Inta Jones

