Menu

R 语言逻辑回归:glm() 与 family = binomial

用 glm(family = binomial) 为是/否结果建模:读懂 summary,用 exp() 把对数优势系数转换成优势比,并以正确的方式得到预测概率。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

当结果是“是/否”时

线性回归预测的是一个数字。但很多值得建模的问题都是二元的:客户会不会流失,病人能不能康复,邮件会不会被点击。用直线去拟合 0/1 的结果会立刻出问题:这条直线会若无其事地预测出 −0.3 或 1.4 这样的概率,这毫无意义。

逻辑回归解决这个问题的方法,是通过对数优势(logit)变换来为结果的概率建模:log(p / (1 − p)) = intercept + slope × x。对数优势的取值覆盖整条数轴,所以线性方程在这个尺度上很自然;再映射回去时,每个预测值都会沿着熟悉的 S 形曲线被压缩到 (0, 1) 之间。这个技巧的代价是:系数处在对数优势尺度上,而解读逻辑回归的全部功夫,就在于把它们转换回人能理解的东西。

拟合:glm() 与 family = binomial

glm()(广义线性模型)是 lm() 的大哥;family = binomial 选择的就是逻辑回归。在 mtcars 中,am 记录了变速箱类型(1 = 手动,0 = 自动)。省油的车是否更倾向于手动挡?

在阅读输出之前有两点要注意。第一,family = binomial 不能省略:省略它,glm() 会悄悄地拟合普通最小二乘模型。第二,结果变量必须是二元的:0/1、逻辑值,或者只有两个水平的因子(R 建模的是第二个水平出现的概率)。

现在逐段看 summary:

  • Coefficients:mpg 的 Estimate 约为 0.31,而且它是对数优势尺度上的斜率:mpg 每增加 1,“是手动挡”的对数优势就增加 0.31。正数表示“提高概率”,负数表示“降低概率”;除了正负号之外,没有人的直觉能在这个尺度上起作用,这正是下一节存在的原因。
  • z value 和 Pr(>|z|):和回归中 t 检验的逻辑相同(Estimate ÷ Std. Error,然后针对“它会不会是零?”给出 p 值),只是使用了正态近似,所以是 z 而不是 t。这里 p ≈ 0.011:mpg 与变速箱类型之间的关联不太可能是噪声。
  • Null deviance 与 Residual deviance:偏差(deviance)是 glm 世界里衡量拟合差劲程度的指标(越小越好)。Null deviance(31 个自由度下为 43.2)对应只有截距的模型;residual deviance(30 个自由度下为 29.7)对应你的模型。花掉一个预测变量的自由度,换来约 13.6 的下降,这就相当于 glm 版的“R 方提高了”。
  • AIC:在拟合优度和复杂度之间取得平衡的模型比较分数,越低越好。单独看没有意义,在同一份数据上比较候选模型时才有用。

从对数优势到优势比:exp(coef())

取指数会把系数从加法性质的对数优势尺度,转到乘法性质的优势尺度上:

exp(0.307) ≈ 1.36,下面是值得记住的规范句式:“mpg 每增加 1,手动变速箱的优势大约乘以 1.36。” 优势比大于 1 提高优势,小于 1 降低优势,正好等于 1 表示没有效应。这就是为什么对于优势比,置信区间的判断标准是“区间是否排除了 1?”(而不是零;零是对数优势尺度上的分界线)。

注意措辞:优势不是概率。优势 = p / (1 − p),所以概率 0.75 对应优势 3。把优势乘以 1.36,并不等于把概率乘以 1.36;当结果很常见时,两者差距很大。对于罕见的结果,优势比为 2 的表现接近“风险大约翻倍”;对于发生率在 50% 的结果,则绝对不是这样。除非结果很罕见,否则永远不要用风险比的措辞(“可能性是 1.36 倍”)来报告优势比。

预测概率:type = "response" 陷阱

现实中最常见的逻辑回归 bug:

第一个调用返回的是默认的 type = "link":对数优势尺度上的预测值,包括负值在内。第二个调用返回的才是真正的概率。如果你得到的“概率”出现了负数或大于 1 的值,原因就在这里。运行这段代码:15 mpg 的车几乎不可能是手动挡,30 mpg 的车很可能是手动挡,S 形曲线在中间弯过。

分类:设定阈值与混淆表

选定一个截断值(默认通常选 0.5),概率就变成了预测类别;而实事求是的成绩单,是一张预测值对实际值的表:

对角线上的格子是判断正确的;另外两个格子是两种不同的错误(把自动挡预测为手动挡,以及反过来)。只看整体准确率可能会严重美化模型:如果 95% 的客户不会流失,那么“预测没有人流失”能得到 95% 的分数,却一个流失的客户也抓不到。所以一定要同时看两种错误。0.5 只是惯例,不是定律:当两种错误的代价不同时,要相应地调整阈值。

还有一个必须坦白的地方:这张表是在拟合模型所用的同一份数据上给模型打分,这会美化结果。真正的评估要留出模型从未见过的数据。

多个预测变量

和 lm() 完全一样:用 + 添加项,每种解释都要加上“保持其他变量不变”这个限定:

现在,每个取指数后的系数都表示在其他预测变量相同的车之间,该预测变量每增加一个单位时优势的乘数。这套方法可以扩展,线性回归中的那些注意事项也一样适用:相关的预测变量会彼此重新分配系数。

注意事项

  • 完全分离。 如果某个预测变量能完美地划分结果(mpg 高于某个值的车全是手动挡,低于它的全是自动挡),最大似然估计的系数就会趋于无穷大。R 会发出警告:glm.fit: fitted probabilities numerically 0 or 1 occurred,并报告巨大的系数和荒谬的标准误。不要把这些数字拿去用;简化模型、收集更多数据,或者使用惩罚方法(brglm2 或 logistf 包)。
  • 足够的事件数。 真正的约束是较少出现的那种结果的数量,而不是总行数。一条老的经验法则要求每个预测变量对应大约 10 至 15 个事件;这里 32 辆车的示例是为了讲解操作方法,不能作为可发表研究的样本量模板。
  • 当结果很常见时,优势比不等于风险比:前面已经讲过,再重复一遍,因为就算你没注意到,审稿人也会发现。

要点回顾

  • 二元结果 → glm(y ~ x, data = df, family = binomial);千万不要忘了 family。
  • 原始系数是对数优势;exp(coef(fit)) 给出优势比,其区间的零效应值是 1。
  • 规范句式:“x 每增加一个单位,结果的优势乘以 exp(b)。”
  • 要得到概率,用 predict(..., type = "response");默认返回的是对数优势,这是头号困惑来源。
  • 用阈值做分类,用混淆表做评判;只看准确率可能会骗人。
  • 留意分离警告,数一数事件数,不要把优势比包装成风险比。

下一篇:你目前见过的每一个区间背后的机制,用 t.test()、confint() 和 prop.test() 计算置信区间。

常见问题

R 语言怎么做逻辑回归?

用 glm() 加上 family = binomial:fit <- glm(am ~ mpg, data = mtcars, family = binomial),然后 summary(fit)。结果变量必须是二元的:0/1、TRUE/FALSE,或者只有两个水平的因子。忘记写 family = binomial 的话,R 会悄悄改为拟合普通的线性回归。

R 语言中 glm 的系数怎么解读?

原始系数处在对数优势(log-odds)尺度上,没有人会用这个尺度思考。对它们取指数,即 exp(coef(fit)),就得到优势比:某个预测变量的值为 1.36,表示它每增加一个单位,结果发生的优势大约乘以 1.36。大于 1 提高优势,小于 1 降低优势,正好等于 1 表示没有效应。

R 语言中怎么从 glm 得到预测概率?

使用 predict(fit, newdata, type = "response")。这是头号陷阱:默认的 type = "link" 返回的是对数优势而不是概率,所以如果你得到的“概率”是负数或大于 1,那就是忘了写 type = "response"。

优势(odds)和概率有什么区别?

概率是成功次数除以全部试验次数;优势是成功次数除以失败次数。概率 0.75 对应的优势是 3(每失败一次就成功三次)。逻辑回归的优势比作用于优势而不是概率;当结果很常见时,优势比可能比对应的风险比大得多,所以不要把一个当作另一个来呈现。

Coddy 编程语言插图

和 Coddy 一起学编程

开始