Welcome to R Square

逻辑斯蒂(logistic)回归

楚新元 / 2025-06-14


理解逻辑斯蒂回归

线性回归预测定量结果,而逻辑斯蒂回归预测定性结果,这样的结果可能是二值变量,也可能是多值变量,分析的任务是对一个观测属于结果变量的某个类别的概率做出预测,换句话说,算法的目的是对观测进行分类。有时候虚拟变量回归是可行的,但是必须满足不同类别之间的差别是相等的,是一个线性关系。另外,如果我们预测某个类别的概率,虚拟变量的预测值可能超出这个范围。

$$ P=P(Y=1|X) $$ $$ \overline{P}=P\left(Y=0|X\right) $$ $$ \ln\frac{P}{1-P}=\ln\frac{P(Y=1|X)}{1-P(Y=1|X)}=W\cdot X $$ $$ \Rightarrow P(Y=1|X)=\frac{1}{1+e^{-W\cdot X}} $$

P 为概率,P/(1-P) 为赔率,两个赔率之比为优势比。

从一个简单案例说起

为研究高压电线对牲畜的影响,R.Norell 研究小的电流对农场动物的影响。他在实验中,选择了 7 头牛,6 种电击强度(0、1、2、3、4、5mA),每头牛被电击 30 下,每种强度 5 下,按随机的次序进行,然后重复整个实验,每头牛总共被电击 60 下。对每次电击,响应变量——嘴巴运动,或者出现,或者未出现。下表中的数据给出每种电击强度 70 次试验中响应的总次数。

‌电流/mA‌‌试验次数‌‌响应次数‌‌响应的比例‌
07000.000
17090.129
270210.300
370470.671
470600.857
570630.900

用数据框形式输入数据,再构造矩阵,一列是成功(响应)的次数,另一列是失败(不响应)的次数,然后再作 logistic 回归。

norell = data.frame(
  x = 0:5, n = rep(70, 6), success = c(0, 9, 21, 47, 60, 63)
)
norell$Y = cbind(norell$success, norell$n - norell$success)
fit_logit = glm(Y ~ x, family = binomial, data = norell)
summary(fit_logit)
#> 
#> Call:
#> glm(formula = Y ~ x, family = binomial, data = norell)
#> 
#> Coefficients:
#>             Estimate Std. Error z value Pr(>|z|)    
#> (Intercept)  -3.3010     0.3238  -10.20   <2e-16 ***
#> x             1.2459     0.1119   11.13   <2e-16 ***
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> (Dispersion parameter for binomial family taken to be 1)
#> 
#>     Null deviance: 250.4866  on 5  degrees of freedom
#> Residual deviance:   9.3526  on 4  degrees of freedom
#> AIC: 34.093
#> 
#> Number of Fisher Scoring iterations: 4

回归模型为:$P = \frac{1}{1 + \exp(3.3010 - 1.2459X)}$

在得到回归模型后,可以作预测,例如,当电流强度为 3.5mA 时,有响应的牛的概率为:

p = predict(fit_logit, data.frame(x = 3.5), type = "response")
# pre = predict(fit_logit, data.frame(x = 3.5))
# p = 1 / (1 + exp(-pre))
print(p[[1]])
#> [1] 0.742642

可以作控制,例如,有 50% 的牛有响应,其电流强度为多少?当 P=0.5 时,$\ln\frac{P}{1-P}=0$,所以 $X = \frac{-\beta_0}{\beta_1}$

X = -coef(fit_logit)[[1]] / coef(fit_logit)[[2]]
print(X)
#> [1] 2.649439

当电流强度为 2.65mA 时,可以使 50% 的牛有响应。

最后,画出响应比例与电流强度拟合的 logistic 回归曲线

norell$y = norell$success / norell$n
plot(norell$x, norell$y, col = "red")

d = seq(0, 5, len = 100)
p = predict(fit_logit, data.frame(x = d), type = "response")
lines(d, p, col = "blue")

下面是一个逻辑斯蒂回归实战案例,进一步学习该模型的应用。

第一步数据准备

数据为威斯康星大学的威廉.沃尔博格博士 1990 年发布的威斯康星州乳腺癌数据集。数据包含 699 个患者的组织样本,保存在 11 个变量的数据框中。

# 对原始数据添加变量名称
data(biopsy, package = "MASS")
names(biopsy) = c(
  "ID", "clumpThickness", "sizeUniformity", "shapeUniformity",
  "maginalAdhesion", "singleEpithelialCellSize", "bareNuclei",
  "blandChromatin", "normalNucleoli", "mitosis", "class"
)

biopsy |> 
  subset(select = -ID) |>  # 去掉 ID 列,此列属于无关变量
  na.omit() -> data  # 缺失值占比很少,此处直接删除

第二步探索和理解数据

在分类问题中,我们可以通过作图来直观地理解数据。

library(tidyr)
library(ggplot2)
data |> 
  pivot_longer(
    -class,
    names_to = "variable",
    values_to = "value"
  ) |> 
  ggplot(aes(x = class, y = value)) + 
  geom_boxplot() + 
  facet_wrap(~ variable, ncol = 3)

从上面的箱线图我们很难确定哪个特征在分类算法中是重要的,但是可以清楚地看到,除了有丝分裂(mitosis)特征变量外,几乎所有的特征变量关于良性和恶性两类细胞的中位数是有差异的。

逻辑斯蒂回归中的共线性也会使估计发生偏离,因此有必要检查特征变量之间的相关性。

library(corrplot)
bc = cor(data[, 1:9])
corrplot.mixed(bc)

从相关系数可以看出,我们会遇到共线性问题,特笔试细胞大小均匀性和细胞形状的均匀性表现出非常明显的共线性。

第三步创建随机的训练数据集和测试数据集

# 确定样本比例和样本量
split_size = 0.7
sample_size = floor(nrow(data) * split_size)

# 随机抽样产生训练数据和测试数据
set.seed(123)
train_ind = sample(nrow(data), size = sample_size)
train = data[train_ind, ]
test = data[-train_ind, ]
# ind = sample(
#   2, nrow(data), 
#   replace = TRUE, 
#   prob = c(sample_size, 1 - sample_size)
# )
# train = data[ind == 1, ]
# test = data[ind == 2, ]

# 确定特征变量和目标变量
label_train = train[, "class"]
data_train = subset(train, select = -class)

label_test = test[, "class"]
data_test = subset(test, select = -class)

第四步模型构建与评价

fit_logit = glm(class ~ ., family = binomial, data = train)
summary(fit_logit)
#> 
#> Call:
#> glm(formula = class ~ ., family = binomial, data = train)
#> 
#> Coefficients:
#>                          Estimate Std. Error z value Pr(>|z|)    
#> (Intercept)              -9.50632    1.30102  -7.307 2.74e-13 ***
#> clumpThickness            0.36186    0.17133   2.112   0.0347 *  
#> sizeUniformity            0.21625    0.26067   0.830   0.4068    
#> shapeUniformity           0.21135    0.26471   0.798   0.4246    
#> maginalAdhesion           0.33846    0.15098   2.242   0.0250 *  
#> singleEpithelialCellSize  0.09755    0.22564   0.432   0.6655    
#> bareNuclei                0.46052    0.11574   3.979 6.92e-05 ***
#> blandChromatin            0.44995    0.24127   1.865   0.0622 .  
#> normalNucleoli            0.12504    0.13271   0.942   0.3461    
#> mitosis                   0.49338    0.33330   1.480   0.1388    
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> (Dispersion parameter for binomial family taken to be 1)
#> 
#>     Null deviance: 625.719  on 477  degrees of freedom
#> Residual deviance:  72.034  on 468  degrees of freedom
#> AIC: 92.034
#> 
#> Number of Fisher Scoring iterations: 8

模型 95% 置信区间检验

confint(fit_logit)
#> Waiting for profiling to be done...
#>                                  2.5 %     97.5 %
#> (Intercept)              -1.251128e+01 -7.3071792
#> clumpThickness            4.318107e-02  0.7242080
#> sizeUniformity           -2.548373e-01  0.7839527
#> shapeUniformity          -3.387017e-01  0.7159371
#> maginalAdhesion           4.653941e-02  0.6576959
#> singleEpithelialCellSize -3.399246e-01  0.5427241
#> bareNuclei                2.467177e-01  0.7071627
#> blandChromatin           -8.285753e-04  0.9512558
#> normalNucleoli           -1.341275e-01  0.3963560
#> mitosis                  -3.326754e-02  1.1529143

回归系数转化为优势比

exp(coef(fit_logit))
#>              (Intercept)           clumpThickness           sizeUniformity 
#>             7.438038e-05             1.436002e+00             1.241413e+00 
#>          shapeUniformity          maginalAdhesion singleEpithelialCellSize 
#>             1.235349e+00             1.402779e+00             1.102468e+00 
#>               bareNuclei           blandChromatin           normalNucleoli 
#>             1.584897e+00             1.568237e+00             1.133198e+00 
#>                  mitosis 
#>             1.637848e+00

如果系数大于1,就说明当特征的值增加时,优势比增加。反之,系数小于1,就说明当特征的值增加时,优势比减小。

通过变量筛选改进模型

fit_logit_new = step(fit_logit)
#> Start:  AIC=92.03
#> class ~ clumpThickness + sizeUniformity + shapeUniformity + maginalAdhesion + 
#>     singleEpithelialCellSize + bareNuclei + blandChromatin + 
#>     normalNucleoli + mitosis
#> 
#>                            Df Deviance     AIC
#> - singleEpithelialCellSize  1   72.222  90.222
#> - shapeUniformity           1   72.641  90.641
#> - sizeUniformity            1   72.784  90.784
#> - normalNucleoli            1   72.933  90.933
#> <none>                          72.034  92.034
#> - mitosis                   1   75.303  93.303
#> - blandChromatin            1   75.861  93.861
#> - clumpThickness            1   77.037  95.037
#> - maginalAdhesion           1   77.179  95.179
#> - bareNuclei                1   91.113 109.113
#> 
#> Step:  AIC=90.22
#> class ~ clumpThickness + sizeUniformity + shapeUniformity + maginalAdhesion + 
#>     bareNuclei + blandChromatin + normalNucleoli + mitosis
#> 
#>                   Df Deviance     AIC
#> - shapeUniformity  1   72.905  88.905
#> - sizeUniformity   1   73.203  89.203
#> - normalNucleoli   1   73.275  89.275
#> <none>                 72.222  90.222
#> - mitosis          1   75.509  91.509
#> - blandChromatin   1   76.415  92.415
#> - clumpThickness   1   77.063  93.063
#> - maginalAdhesion  1   78.586  94.586
#> - bareNuclei       1   93.771 109.771
#> 
#> Step:  AIC=88.91
#> class ~ clumpThickness + sizeUniformity + maginalAdhesion + bareNuclei + 
#>     blandChromatin + normalNucleoli + mitosis
#> 
#>                   Df Deviance     AIC
#> - normalNucleoli   1   74.383  88.383
#> <none>                 72.905  88.905
#> - mitosis          1   76.009  90.009
#> - blandChromatin   1   77.107  91.107
#> - sizeUniformity   1   77.713  91.713
#> - clumpThickness   1   78.638  92.638
#> - maginalAdhesion  1   79.720  93.720
#> - bareNuclei       1   98.072 112.072
#> 
#> Step:  AIC=88.38
#> class ~ clumpThickness + sizeUniformity + maginalAdhesion + bareNuclei + 
#>     blandChromatin + mitosis
#> 
#>                   Df Deviance     AIC
#> <none>                 74.383  88.383
#> - mitosis          1   77.102  89.102
#> - blandChromatin   1   80.092  92.092
#> - clumpThickness   1   81.012  93.012
#> - maginalAdhesion  1   82.090  94.090
#> - sizeUniformity   1   83.672  95.672
#> - bareNuclei       1  101.323 113.323
summary(fit_logit_new)
#> 
#> Call:
#> glm(formula = class ~ clumpThickness + sizeUniformity + maginalAdhesion + 
#>     bareNuclei + blandChromatin + mitosis, family = binomial, 
#>     data = train)
#> 
#> Coefficients:
#>                 Estimate Std. Error z value Pr(>|z|)    
#> (Intercept)      -9.5415     1.2780  -7.466 8.26e-14 ***
#> clumpThickness    0.4051     0.1684   2.406  0.01612 *  
#> sizeUniformity    0.4702     0.1768   2.660  0.00782 ** 
#> maginalAdhesion   0.3903     0.1456   2.680  0.00736 ** 
#> bareNuclei        0.4974     0.1112   4.472 7.73e-06 ***
#> blandChromatin    0.5318     0.2302   2.310  0.02088 *  
#> mitosis           0.4634     0.3366   1.377  0.16858    
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> (Dispersion parameter for binomial family taken to be 1)
#> 
#>     Null deviance: 625.719  on 477  degrees of freedom
#> Residual deviance:  74.383  on 471  degrees of freedom
#> AIC: 88.383
#> 
#> Number of Fisher Scoring iterations: 8

模型对测试集数据进行分类

library(gmodels)
prob_test = predict(
  fit_logit_new, 
  type = "response", 
  newdata = data_test
)
CrossTable(
  x = label_test,
  y = ifelse(prob_test >= 0.5, "malignant", "benign"),
  dnn = c("Actual", "Predicted"),
  prop.chisq = FALSE
)
#> 
#>  
#>    Cell Contents
#> |-------------------------|
#> |                       N |
#> |           N / Row Total |
#> |           N / Col Total |
#> |         N / Table Total |
#> |-------------------------|
#> 
#>  
#> Total Observations in Table:  205 
#> 
#>  
#>              | Predicted 
#>       Actual |    benign | malignant | Row Total | 
#> -------------|-----------|-----------|-----------|
#>       benign |       136 |         3 |       139 | 
#>              |     0.978 |     0.022 |     0.678 | 
#>              |     0.986 |     0.045 |           | 
#>              |     0.663 |     0.015 |           | 
#> -------------|-----------|-----------|-----------|
#>    malignant |         2 |        64 |        66 | 
#>              |     0.030 |     0.970 |     0.322 | 
#>              |     0.014 |     0.955 |           | 
#>              |     0.010 |     0.312 |           | 
#> -------------|-----------|-----------|-----------|
#> Column Total |       138 |        67 |       205 | 
#>              |     0.673 |     0.327 |           | 
#> -------------|-----------|-----------|-----------|
#> 
#> 

从预测结果看,预测的准确率是 (136 + 64) / 205 = 97.56%,效果令人满意。