R语言多分类Logistic回归变量筛选实战:最优子集与逐步回归
这次我们来看一个在R语言中构建多分类Logistic回归模型,并应用最优子集选择和逐步回归进行变量筛选的实战项目。对于数据分析师和机器学习实践者来说,面对包含多个预测变量的分类问题时,如何从众多特征中挑选出最相关、最简洁的子集来构建一个既准确又可解释的模型,是一个核心挑战。最优子集回归和逐步回归正是解决这一问题的经典统计方法。
本文将直接切入主题,带你快速掌握在R环境中实现多分类Logistic回归模型构建与变量筛选的全流程。核心内容包括:如何使用glmnet、bestglm等关键R包;如何评估和比较不同变量子集模型的性能;以及如何将筛选出的最优模型应用于实际预测。整个过程不涉及复杂的深度学习框架,对硬件(CPU/内存)要求友好,重点在于算法思想的理解与代码的复现。
如果你正在处理客户分群、疾病诊断、产品品类预测等多分类问题,并且希望提升模型的效率和解释性,那么这篇文章提供的思路和代码可以直接套用。下面,我们将从核心概念速览开始,逐步完成环境准备、数据模拟、模型构建、变量筛选以及最终的模型评估与部署。
1. 核心能力速览
在深入代码之前,我们先通过一个表格快速了解本技术方案的核心要点、资源需求和产出。
| 能力项 | 说明 |
|---|---|
| 项目类型 | 统计机器学习 / 传统建模 |
| 核心算法 | 多分类Logistic回归 (Multinomial Logistic Regression) |
| 关键方法 | 最优子集选择 (Best Subset Selection)、逐步回归 (Stepwise Regression) |
| 主要R包 | nnet,glmnet,bestglm,MASS,caret |
| 硬件门槛 | 极低。普通CPU即可,内存消耗取决于数据量(通常百兆级别足够)。 |
| 核心产出 | 1. 经过变量筛选的、更简洁的Logistic回归模型。 2. 模型性能评估报告(准确率、混淆矩阵等)。 3. 可用于新数据预测的R脚本或函数。 |
| 适合场景 | 1. 因变量为无序多分类(>2类)的预测问题。 2. 特征数量中等(如10-50个),需要做特征选择以简化模型、防止过拟合、增强解释性。 3. 学术研究、商业分析、风控模型等注重模型可解释性的领域。 |
| 不适合场景 | 1. 特征维度极高(如成千上万个),此时最优子集选择计算不可行,需用L1/L2正则化。 2. 数据非线性关系极强,可能需要树模型或神经网络。 |
2. 适用场景与使用边界
多分类Logistic回归结合变量筛选,是一套非常实用的“白盒”建模工具。
它最适合谁?
- 数据分析师/统计学家:需要向业务方清晰解释哪些变量影响了分类结果。
- 机器学习入门者:希望从经典的统计方法入手,理解模型选择与评估的基本原理。
- 从事金融风控、医疗诊断、市场研究的专业人员,这些领域通常要求模型具备良好的可解释性。
它能解决什么问题?
- 特征选择:从大量候选预测变量中,自动识别出对分类最重要的一个子集。
- 模型简化:避免包含冗余或不相关变量,使模型更简洁,预测更稳定(减少过拟合风险)。
- 性能优化:在保证甚至提升预测准确率的前提下,降低模型的复杂度。
- 洞察发现:通过观察最终入选的变量,可以分析出影响分类结果的关键因素。
使用边界与注意事项:
- 计算复杂度:最优子集选择需要评估 2^p 个模型(p为变量数),当p>40时计算量巨大,几乎不可行。此时应优先考虑逐步回归或正则化方法(如LASSO)。
- 数据假设:Logistic回归假设特征与log-odds之间存在线性关系。对于非线性关系,可能需要引入特征变换(如多项式项、交互项)。
- 多重共线性:高度相关的自变量会影响模型稳定性和系数解释。在筛选前,应进行相关性分析或VIF(方差膨胀因子)检查。
- 过拟合风险:即使在训练集上通过筛选得到了“最优”子集,也必须在独立的测试集上验证其泛化能力。务必进行严格的训练-测试集划分或交叉验证。
3. 环境准备与前置条件
我们的操作完全在R语言环境中进行。以下是需要准备的软件和环境。
3.1 基础环境
- 操作系统:Windows, macOS, 或 Linux (包括WSL) 均可。
- R语言:版本 >= 4.0.0。建议使用最新稳定版以获取更好的包兼容性。
- R集成开发环境(可选但推荐):RStudio 或 VS Code with R Extension。
3.2 必需R包安装
我们将使用以下几个核心包。请在R控制台或RStudio中执行以下安装命令。如果已安装,R会自动跳过。
# 安装CRAN上的核心包 install.packages(c( "nnet", # 用于拟合多分类Logistic回归 (multinom) "glmnet", # 用于拟合带正则化的Logistic回归,也可用于特征选择 "MASS", # 包含 stepAIC 函数,用于逐步回归 "bestglm", # 专门用于最优子集选择的包 "caret", # 强大的机器学习统一接口,用于数据分割、模型训练与评估 "tidyverse", # 数据清洗、整理和可视化套件 (包含dplyr, ggplot2等) "pROC", # 用于绘制ROC曲线(二分类时) "yardstick" # 另一种模型评估工具,与tidyverse生态整合好 )) # 如果bestglm安装失败,可以尝试从GitHub安装开发版 # install.packages("devtools") # devtools::install_github("ledell/bestglm")3.3 验证安装
安装完成后,运行以下代码加载包,确保没有报错。
library(nnet) library(glmnet) library(MASS) library(bestglm) library(caret) library(tidyverse)如果所有library()命令都成功执行,说明环境已就绪。
4. 数据准备与模拟
为了完整演示流程,我们首先模拟一个包含10个预测变量(X1-X10)和1个三分类因变量(y)的数据集。这样我们可以完全控制数据生成过程,便于理解。
# 设置随机种子保证结果可复现 set.seed(123) # 生成1000个样本,10个特征 n <- 1000 p <- 10 # 生成协变量矩阵X,部分变量有相关性 X <- matrix(rnorm(n * p), n, p) colnames(X) <- paste0("X", 1:p) # 人为制造一些相关性:X3与X6相关,X8与X10相关 X[, 3] <- X[, 3] + 0.7 * X[, 6] X[, 8] <- X[, 8] - 0.5 * X[, 10] # 定义真实的逻辑关系:只有X1, X4, X7, X9是真正影响分类的变量 true_beta <- matrix(0, nrow = p, ncol = 2) # 对于3分类,需要2组合的系数 true_beta[1, ] <- c(1.2, -0.5) # X1 对类别1 vs 3,类别2 vs 3的影响 true_beta[4, ] <- c(-0.8, 1.0) true_beta[7, ] <- c(0.5, 0.3) true_beta[9, ] <- c(0.0, 0.9) # X9 主要影响类别2 # 计算线性预测值 (log-odds) linear_predictor1 <- X %*% true_beta[, 1] # 类别1 vs 基准类(类别3) linear_predictor2 <- X %*% true_beta[, 2] # 类别2 vs 基准类(类别3) # 计算概率 prob1 <- exp(linear_predictor1) prob2 <- exp(linear_predictor2) prob3 <- rep(1, n) # 基准类概率为1 sum_probs <- prob1 + prob2 + prob3 prob_matrix <- cbind(prob1/sum_probs, prob2/sum_probs, prob3/sum_probs) # 根据概率矩阵生成多分类响应变量y y <- apply(prob_matrix, 1, function(p) sample(1:3, size=1, prob=p)) y <- factor(y, levels = 1:3, labels = c("Class_A", "Class_B", "Class_C")) # 创建最终的数据框 sim_data <- data.frame(y, X) head(sim_data) # 查看类别分布 table(sim_data$y)运行后,你将得到一个名为sim_data的数据框,包含1000行,11列(1个因子型y,10个数值型X1-X10)。这就是我们后续建模的“原料”。
5. 模型构建:基础多分类Logistic回归
在筛选变量之前,我们先建立一个使用全部变量的“全模型”(Full Model),作为后续比较的基准。
5.1 使用nnet::multinom拟合全模型
multinom函数是拟合多分类Logistic回归最常用的函数。
# 拟合全模型(使用所有10个预测变量) full_model <- multinom(y ~ ., data = sim_data, trace = FALSE) # trace=FALSE关闭迭代日志 # 查看模型摘要 summary(full_model) # 计算全模型在训练集上的准确率(作为基准) train_pred_full <- predict(full_model, newdata = sim_data) confusionMatrix(train_pred_full, sim_data$y)summary(full_model)会输出每个预测变量对于两个logit(Class_A vs Class_C, Class_B vs Class_C)的系数估计、标准误和z值。由于我们数据是模拟的,你可能会发现一些变量的系数不显著(p值大),这与我们预设的只有X1, X4, X7, X9是真实变量相符。
confusionMatrix来自caret包,它会给出一个详细的分类报告,包括准确率、Kappa值以及每个类别的敏感性、特异性等。记录下此时的准确率。
6. 变量筛选方法一:最优子集选择
最优子集选择(Best Subset Selection)旨在从p个预测变量中,找出在某种评价准则(如AIC, BIC, 调整R方)下“最优”的包含k个变量的模型(k=1,2,...,p)。
6.1 使用bestglm包进行筛选
bestglm包要求数据以矩阵形式输入,且因变量在最后一列。同时,它默认处理二分类逻辑回归。对于多分类,我们需要一些技巧,例如将其转化为多个“一对多”的二分类问题,或者使用其他准则。这里我们展示一种基于模型整体AIC的迂回方法:对每个可能的子集,用multinom拟合模型并计算AIC,然后选择AIC最小的模型。
由于计算2^10=1024个模型对于多分类multinom负担较重,我们演示一个简化版本:使用regsubsets函数(来自leaps包,但bestglm也类似)并指定评价指标。
更实用的方法是,我们可以利用glmnet的交叉验证路径来近似选择重要变量,或者针对多分类问题,使用caret包中的递归特征消除(RFE)。但为了紧扣“最优子集”主题,我们展示基于bestglm对其中一个二分类子问题的分析(例如,将问题简化为“Class_A vs 非Class_A”)。
# 为了演示,我们先创建一个二分类数据子集(Class_A vs Others) binary_data <- sim_data binary_data$y_binary <- ifelse(binary_data$y == "Class_A", 1, 0) binary_data$y <- NULL # 移除多分类y # 准备bestglm所需格式:Xy矩阵,最后一列是响应变量 Xy <- as.matrix(binary_data) # bestglm expects the response in the last column, which it already is (y_binary) # 使用BIC准则进行最优子集选择 # 注意:family=binomial(link='logit')指定为二项逻辑回归 best_model_bic <- bestglm(Xy, family = binomial, IC = "BIC", method = "exhaustive") summary(best_model_bic) # 查看BIC选择的最佳子集是哪些变量 best_model_bic$BestModel best_model_bic$Subsetsbestglm会输出一个表格,显示从0个变量到全部变量的每个子集大小对应的最佳模型及其BIC值。我们可以通过best_model_bic$BestModel查看最终被BIC准则选中的变量是哪些。通常,BIC倾向于选择更简洁的模型。
重要提醒:对于真正的多分类问题,最优子集选择计算量巨大,且bestglm不直接支持multinom。在实际工作中,更常见的做法是:
- 使用
glmnet的L1正则化(LASSO)进行特征选择,它能为多分类问题产生稀疏解。 - 使用
caret包的rfe(递归特征消除)函数,它可以与multinom等任何模型结合,通过交叉验证来评估不同特征子集的性能。
7. 变量筛选方法二:逐步回归
逐步回归(Stepwise Regression)是一种计算效率更高的变量选择方法,它通过逐步添加(前向)或删除(后向)变量来搜索“较优”模型,而非遍历所有子集。
7.1 使用MASS::stepAIC进行逐步选择
stepAIC函数基于AIC准则,可以进行双向(既考虑添加也考虑删除)的逐步搜索。它直接支持multinom对象。
# 基于之前拟合的full_model,使用stepAIC进行变量选择 # direction可以是 "both"(默认), "backward", "forward" step_model <- stepAIC(full_model, direction = "both", trace = FALSE) # 查看逐步回归筛选后的模型摘要 summary(step_model) # 比较全模型和逐步回归模型的AIC cat("Full Model AIC:", AIC(full_model), "\n") cat("Stepwise Model AIC:", AIC(step_model), "\n") # 查看逐步回归模型保留了哪些变量 step_model$callstepAIC会输出一个过程(如果trace=TRUE),显示每一步添加或删除变量时AIC的变化。最终模型通常比全模型具有更少的变量和更低的AIC值,意味着在拟合优度和复杂度之间取得了更好的平衡。
7.2 评估逐步回归模型性能
现在,让我们在训练集上评估一下筛选后模型的性能,并与全模型对比。
# 使用筛选后的模型进行预测 train_pred_step <- predict(step_model, newdata = sim_data) # 生成混淆矩阵和性能指标 cm_step <- confusionMatrix(train_pred_step, sim_data$y) print(cm_step) # 与全模型性能对比 (使用之前记录的准确率) # 假设全模型准确率存储在acc_full中 acc_full <- mean(train_pred_full == sim_data$y) acc_step <- mean(train_pred_step == sim_data$y) cat(sprintf("Full Model Training Accuracy: %.4f\n", acc_full)) cat(sprintf("Stepwise Model Training Accuracy: %.4f\n", acc_step))你会发现,step_model的准确率可能与full_model非常接近,甚至可能因为减少了过拟合而略高。关键在于,step_model使用的变量更少,模型更简洁,可解释性更强。
8. 更稳健的评估:训练-测试集划分与交叉验证
到目前为止,我们都在训练集上评估模型,这会导致对模型性能的乐观估计。为了获得对泛化能力更可靠的估计,必须使用未参与训练的数据。
8.1 划分训练集和测试集
我们使用caret包的createDataPartition函数进行分层抽样,保证训练集和测试集中各类别的比例与原数据集一致。
# 按7:3比例划分训练集和测试集 set.seed(456) # 确保划分可复现 train_index <- createDataPartition(sim_data$y, p = 0.7, list = FALSE) train_data <- sim_data[train_index, ] test_data <- sim_data[-train_index, ] cat("Training set size:", nrow(train_data), "\n") cat("Test set size:", nrow(test_data), "\n") table(train_data$y) table(test_data$y)8.2 在训练集上重新训练和筛选模型
现在,我们只在train_data上重复之前的建模和筛选步骤。
# 在训练集上拟合全模型 full_model_train <- multinom(y ~ ., data = train_data, trace = FALSE) # 在训练集上进行逐步回归筛选 step_model_train <- stepAIC(full_model_train, direction = "both", trace = FALSE) # 查看筛选后的模型变量 coef(step_model_train)8.3 在测试集上评估最终模型
这是检验模型泛化能力的“终考”。
# 使用在训练集上得到的step_model_train来预测测试集 test_pred_step <- predict(step_model_train, newdata = test_data) # 计算测试集上的性能 cm_test <- confusionMatrix(test_pred_step, test_data$y) print(cm_test) test_accuracy <- cm_test$overall['Accuracy'] cat(sprintf("Stepwise Model TEST Accuracy: %.4f\n", test_accuracy)) # 作为对比,也用训练集上的全模型预测测试集(可能过拟合) test_pred_full <- predict(full_model_train, newdata = test_data) cm_test_full <- confusionMatrix(test_pred_full, test_data$y) test_accuracy_full <- cm_test_full$overall['Accuracy'] cat(sprintf("Full Model TEST Accuracy: %.4f\n", test_accuracy_full))比较test_accuracy和test_accuracy_full。一个成功的变量筛选应该使得测试集准确率与全模型相当甚至更高,同时模型更简单。如果全模型在测试集上准确率显著下降,而筛选模型保持稳定,则说明筛选有效去除了噪声变量,减轻了过拟合。
9. 方法三:使用LASSO进行特征选择(拓展)
对于特征数量较多的情况,最优子集和逐步回归可能效率不足或效果不佳。此时,LASSO(Least Absolute Shrinkage and Selection Operator)是一种非常有效的替代方案。它通过在损失函数中加入L1正则化项,自动将某些特征的系数压缩至0,从而实现特征选择。
9.1 使用glmnet拟合多分类LASSO
glmnet包支持多分类逻辑回归的LASSO。
# 准备数据:将因子型y转换为数值型(glmnet要求) y_numeric <- as.numeric(train_data$y) - 1 # 变为0,1,2 x_matrix <- as.matrix(train_data[, -1]) # 去掉y列 # 拟合多分类LASSO回归模型 # family = "multinomial" 指定为多分类 # alpha = 1 表示LASSO (L1正则化);alpha=0为岭回归(Ridge),alpha在0-1之间为弹性网(Elastic Net) lasso_cv <- cv.glmnet(x = x_matrix, y = y_numeric, family = "multinomial", alpha = 1, type.measure = "class", # 用错分类率作为交叉验证衡量标准 nfolds = 10) # 10折交叉验证 # 绘制交叉验证误差随lambda变化的曲线 plot(lasso_cv) # 查看使得交叉验证误差最小的lambda值 (lambda.min) 和误差在一个标准差内的lambda值 (lambda.1se) # lambda.1se选择的模型更简洁(变量更少) cat("Lambda.min:", lasso_cv$lambda.min, "\n") cat("Lambda.1se:", lasso_cv$lambda.1se, "\n") # 提取在lambda.1se下的系数(这是一个稀疏矩阵列表,每个类别对应一个) coef_lasso <- coef(lasso_cv, s = "lambda.1se") print(coef_lasso) # 非零系数的变量即为被选中的特征9.2 基于LASSO筛选的变量重新建模
我们可以从LASSO结果中提取非零系数的变量,然后用multinom只对这些变量进行建模,得到一个更传统的、可解释的Logistic回归模型。
# 找出被LASSO选中的变量名(在至少一个类别的logit方程中系数非零) selected_vars <- unique(unlist(lapply(coef_lasso, function(x) rownames(x)[x@i + 1]))) # +1跳过截距项 selected_vars <- selected_vars[-1] # 移除截距项“(Intercept)” cat("Variables selected by LASSO (lambda.1se):\n") print(selected_vars) # 如果选中的变量不为空,则用这些变量构建新模型 if(length(selected_vars) > 0) { formula_lasso <- as.formula(paste("y ~", paste(selected_vars, collapse = " + "))) lasso_refit_model <- multinom(formula_lasso, data = train_data, trace = FALSE) # 在测试集上评估 test_pred_lasso <- predict(lasso_refit_model, newdata = test_data) cm_lasso <- confusionMatrix(test_pred_lasso, test_data$y) cat("\nLASSO-Refit Model Test Accuracy:", cm_lasso$overall['Accuracy'], "\n") } else { cat("LASSO selected no variables. Model may be too sparse.\n") }10. 模型比较与最终选择
现在,我们手头可能有多个候选模型:
full_model_train:使用全部变量的模型(基准)。step_model_train:通过逐步回归筛选变量的模型。lasso_refit_model:基于LASSO筛选变量后重新拟合的模型。
如何选择最终模型?需要综合考量:
- 测试集准确率/其他指标:首要标准是泛化性能。
- 模型简洁性(变量数):在性能相近时,选择变量更少的模型。
- AIC/BIC:在训练集上衡量模型拟合优度与复杂度的平衡。
- 业务可解释性:入选的变量是否在业务逻辑上说得通。
我们可以制作一个对比表格:
# 假设我们已经有了上述模型的测试集准确率 # acc_full_test, acc_step_test, acc_lasso_test model_comparison <- data.frame( Model = c("Full Model", "Stepwise AIC", "LASSO-Refit"), Num_Variables = c(10, length(coef(step_model_train)) - 1, length(selected_vars)), # 减去截距 AIC = c(AIC(full_model_train), AIC(step_model_train), AIC(lasso_refit_model)), Test_Accuracy = c(test_accuracy_full, test_accuracy, cm_lasso$overall['Accuracy']) ) print(model_comparison)根据这个表格,结合你的具体问题(是更看重预测精度还是模型简洁),做出最终选择。
11. 最终模型部署与预测
选定最终模型后(例如我们选择step_model_train),就可以将其用于对新数据的预测了。
11.1 保存模型
可以将模型对象保存为R数据文件,方便以后加载使用。
# 保存最终模型 final_model <- step_model_train saveRDS(final_model, file = "final_multinomial_logistic_model.rds") # 在另一个R会话中,可以这样加载 # loaded_model <- readRDS("final_multinomial_logistic_model.rds")11.2 预测新数据
假设我们有一个新的数据框new_data,其结构与train_data相同(包含相同的预测变量)。
# 模拟新数据(3个观测) new_data <- data.frame( X1 = rnorm(3), X2 = rnorm(3), X3 = rnorm(3), X4 = rnorm(3), X5 = rnorm(3), X6 = rnorm(3), X7 = rnorm(3), X8 = rnorm(3), X9 = rnorm(3), X10 = rnorm(3) ) # 确保列名和顺序与训练数据一致 # 使用最终模型进行预测 new_predictions <- predict(final_model, newdata = new_data, type = "class") # 预测类别 new_probabilities <- predict(final_model, newdata = new_data, type = "probs") # 预测各类别概率 print("Predicted Classes:") print(new_predictions) print("\nPredicted Probabilities:") print(new_probabilities)type = "class"给出最可能的类别标签,type = "probs"给出属于每个类别的概率,这在需要计算风险或排序时非常有用。
12. 常见问题与排查方法
在实际操作中,你可能会遇到以下问题:
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
multinom拟合时报错或警告“秩不足” | 预测变量中存在完全共线性或某个类别样本量太少。 | 检查数据:sum(complete.cases(data)),table(y),car::vif(full_model)。 | 1. 删除高度共线性的变量。 2. 对样本量极少的类别进行合并或重采样。 3. 增加正则化(使用 glmnet)。 |
stepAIC运行非常慢 | 变量数量(p)太多。 | 查看p的大小。最优子集计算复杂度为O(2^p)。 | 1. 对于p>15,优先使用逐步回归或LASSO。 2. 先使用领域知识或单变量分析进行初步筛选。 |
LASSO (cv.glmnet) 选择的变量数为0 | 正则化强度(lambda)太大,或信号太弱。 | 检查plot(lasso_cv),看CV误差曲线是否在末端上升。检查预测变量是否已标准化(glmnet默认会做)。 | 1. 使用lambda.min而非lambda.1se。2. 减小 alpha值(如0.9)尝试弹性网。3. 重新审视特征工程,可能现有特征与y关系不大。 |
| 测试集准确率远低于训练集 | 模型过拟合。 | 比较训练集和测试集准确率。检查模型是否过于复杂(变量太多)。 | 1. 加强变量筛选,选择更简洁的模型。 2. 增加训练数据量。 3. 使用交叉验证确定超参数,而非仅依赖训练集。 |
| 预测新数据时报错“变量未找到” | 新数据的变量名、类型或顺序与训练数据不一致。 | 使用names(new_data)和names(train_data)对比。使用str()对比数据类型。 | 1. 确保新数据框的列名与训练时完全一致。 2. 确保因子型变量的水平(levels)一致。可使用 caret::predictors(final_model)获取模型需要的变量名。 |
| 多分类模型预测概率之和不为1 | 通常不会发生,multinom和glmnet输出的概率矩阵行和均为1。 | 检查计算过程,可能是手动处理概率时出错。 | 直接使用predict(..., type="probs")获得概率矩阵,不要自行计算。 |
13. 最佳实践与使用建议
- 始于探索性数据分析(EDA):在建模前,务必进行EDA。了解每个变量的分布、与目标变量的关系、缺失值、异常值。这对后续特征工程和模型解释至关重要。
- 划分数据是金科玉律:永远在开始任何模型训练之前,就划分好训练集、验证集(如需调参)和测试集。测试集只在最终评估时使用一次,避免数据泄露。
- 变量筛选是迭代过程:不要指望一次
stepAIC或LASSO就能得到完美特征集。可以结合业务知识、统计检验(如卡方检验、ANOVA)和多种机器学习筛选方法(如随机森林重要性)进行多轮筛选。 - 重视可解释性:Logistic回归的优势在于可解释性。最终模型确定后,仔细检查系数符号和大小,确保其业务含义合理。可以计算优势比(Odds Ratio)来量化影响。
- 记录完整流程:使用R Markdown或Jupyter Notebook记录从数据加载、清洗、探索、建模、评估到预测的完整分析流程。这保证了研究的可复现性。
- 考虑集成方法:如果预测精度是唯一目标,可以尝试随机森林、梯度提升树(如XGBoost)等集成方法,它们通常能获得更高的准确率,但会牺牲一些可解释性。
- 合规与伦理:当模型应用于金融、医疗、招聘等敏感领域时,必须关注模型的公平性、无偏见性和合规性。定期审计模型,防止产生歧视性结果。
通过以上步骤,你不仅学会了如何在R中构建多分类Logistic回归模型,更掌握了通过最优子集选择、逐步回归和LASSO等方法来优化模型、提升其泛化能力和可解释性的核心技能。这套流程可以直接迁移到你的实际数据分析项目中,成为你解决分类问题的可靠工具箱。
