简介:这份资源面向具备一定统计基础、希望用R语言落地机器学习算法的数据分析人员与学生,围绕监督学习与非监督学习两条主线,覆盖线性回归、逻辑回归、支持向量机、决策树、随机森林、梯度提升机、朴素贝叶斯、神经网络以及K-means聚类、关联规则等常见算法的实现思路。压缩包共20个文件,以8个R脚本和6个CSV数据集为主,另含许可证等说明文件,整体约16KB,体量轻便,便于直接运行与二次修改。目录按回归、决策树回归、多项式回归、简单与多元线性回归、SVR及数据预处理等模块组织,配套数据文件可支撑从数据清洗、特征选择到模型训练与评估的完整流程演练。已有244人学习下载,适合作为课程实验、项目练手或算法对照复现的参考素材,帮助读者在R环境中快速理解各算法的调用方式与适用场景。1. 一份把 R 语言机器学习算法拆到能跑的资源,到底长什么样很多人对 R 语言的印象还停留在统计检验和画图,觉得它跟「机器学习工程」沾不上边。但如果你手头正好有一份按算法逐个实现的 R 语言机器学习资源,情况就完全不一样了——它把分类、回归、聚类、降维这些常见任务,用 R 的原生语法和经典包一条条写出来,而不是调一个黑盒函数就完事。这份资源适合两类人:一类是想从零理解算法内部计算过程的开发者,另一类是已经会用 Python 跑模型、但需要把逻辑迁移到 R 环境做统计分析的人。它不追求工程化部署,重点在于「每个算法都能在本地 R 会话里跑通、看到中间结果、改参数看变化」。接下来我按实际拆包的顺序,把这份资源的结构、跑法和改造点讲清楚。2. 技术选型与目录结构:为什么用 R 而不是调包2.1 核心依赖与运行环境这份资源的技术栈非常克制,没有引入 tidymodels 或 caret 这类高层封装框架,而是直接用 R 基础语法配合几个经典包。常见做法是:数据处理用base R的data.frame和apply家族,矩阵运算用matrix原生支持,算法实现尽量手写核心循环,只在必要处调用stats包里的lm、glm、kmeans、prcomp等函数作为对照。运行环境上,R 版本建议 4.0 以上,因为部分矩阵运算和字符串处理在旧版本上有兼容问题。依赖包清单大致如下:包名用途是否必须stats线性模型、广义线性模型、聚类、主成分是(R 自带)MASS判别分析、稳健回归是classKNN 分类与回归是tree决策树构建与剪枝是randomForest随机森林实现是e1071支持向量机、朴素贝叶斯是nnet单层神经网络、多项逻辑回归是cluster轮廓系数、聚类评估是安装命令很直接,在 R 控制台里执行:# 一次性安装所有依赖包 install.packages(c("MASS", "class", "tree", "randomForest", "e1071", "nnet", "cluster"))这里有个细节:stats是 R 启动时自动加载的基础包,不需要单独安装,但如果你在脚本里显式调用library(stats)也不会报错。我一般会在脚本开头统一写library()调用,方便一眼看清依赖。2.2 目录组织与文件命名资源解压后,目录结构大致是这样的:R-ML-Algorithms/ ├── data/ # 数据集目录 │ ├── iris.csv │ ├── mtcars.csv │ └── simulated_reg.csv ├── R/ # 算法实现脚本 │ ├── 01_linear_regression.R │ ├── 02_logistic_regression.R │ ├── 03_knn.R │ ├── 04_decision_tree.R │ ├── 05_random_forest.R │ ├── 06_svm.R │ ├── 07_naive_bayes.R │ ├── 08_kmeans.R │ ├── 09_pca.R │ └── 10_neural_network.R ├── utils/ # 辅助函数 │ ├── plot_utils.R │ └── eval_utils.R └── README.md每个算法脚本都是独立可运行的,不依赖其他脚本的输出。utils/里放的是画图函数和评估指标计算,比如混淆矩阵、RMSE、轮廓系数,这些函数被各个算法脚本用source()引入。这种组织方式的好处是:你想看哪个算法,直接打开对应文件从头跑到尾就行,不用管其他文件的状态。2.3 数据流的统一约定所有脚本遵循同一套数据流约定:先读 CSV,再做训练测试划分,然后建模、预测、评估。划分比例默认 7:3,随机种子固定为 42,保证结果可复现。常见做法是写一个train_test_split函数放在utils/eval_utils.R里:# 简单的训练测试划分函数 train_test_split <- function(data, ratio = 0.7, seed = 42) { set.seed(seed) # 固定随机种子 n <- nrow(data) # 样本总数 idx <- sample(seq_len(n), size = floor(ratio * n)) # 随机抽取训练索引 list( train = data[idx, ], # 训练集 test = data[-idx, ] # 测试集 ) }参数说明:data是输入数据框,ratio是训练集占比,seed保证每次划分结果一致。返回一个列表,用$train和$test取对应数据。这个函数虽然简单,但统一了所有脚本的划分逻辑,避免每个文件里重复写sample()。3. 核心算法实现拆解:从线性回归到神经网络3.1 线性回归的手写梯度下降资源里线性回归给了两种实现:一种直接调lm(),另一种手写梯度下降。手写版本更能看清算法本质。核心代码如下:# 手写梯度下降求解线性回归 gradient_descent_lr <- function(X, y, lr = 0.01, epochs = 1000) { X <- cbind(1, X) # 添加截距列 m <- nrow(X) # 样本数 n <- ncol(X) # 特征数(含截距) theta <- rep(0, n) # 参数初始化 loss_history <- numeric(epochs) # 记录每轮损失 for (i in seq_len(epochs)) { pred <- X %*% theta # 预测值 error <- pred - y # 残差 grad <- (1 / m) * t(X) %*% error # 梯度计算 theta <- theta - lr * grad # 参数更新 loss_history[i] <- (1 / (2 * m)) * sum(error^2) # MSE损失 } list(theta = theta, loss = loss_history) }逻辑说明:X是特征矩阵,y是目标向量。第一步给X加一列全 1,对应截距项。梯度公式是(1/m) * X^T * (X*theta - y),这是均方误差对参数的导数。lr控制步长,太大会震荡,太小收敛慢,资源里默认 0.01,对标准化后的数据比较稳。epochs是迭代轮数,1000 轮对这个小数据集足够。返回的loss_history可以用来画收敛曲线,判断是否还需要增加轮数。参数调优建议:如果损失曲线下降太慢,先把lr调到 0.1 试试;如果震荡,降到 0.001。特征记得先标准化,否则不同量纲的列会导致梯度方向被大数值特征主导。3.2 KNN 的距离计算与 k 值选择KNN 部分没有直接调class::knn(),而是先手写了一个距离矩阵计算,再调包做对照。手写版本的核心是欧氏距离:# 计算欧氏距离矩阵 euclidean_dist <- function(A, B) { # A: m x d 矩阵,B: n x d 矩阵 # 返回 m x n 距离矩阵 A_sq <- rowSums(A^2) # A 每行平方和 B_sq <- rowSums(B^2) # B 每行平方和 cross <- A %*% t(B) # 交叉项 dist_sq <- outer(A_sq, B_sq, "+") - 2 * cross sqrt(pmax(dist_sq, 0)) # 防止负数开方 }这里用了矩阵运算加速,比双重循环快很多。pmax(dist_sq, 0)是防止浮点误差导致微小负数。得到距离矩阵后,对每个测试样本取最近的 k 个训练样本,投票决定类别。k 值选择上,资源里用了一个简单的循环,从 1 到 20 逐个试,看测试集准确率:# 遍历 k 值找最优 k_values <- 1:20 acc <- sapply(k_values, function(k) { pred <- knn(train = train_x, test = test_x, cl = train_y, k = k) mean(pred == test_y) }) best_k <- k_values[which.max(acc)]注意:knn()函数要求输入是矩阵或数据框,且不能有因子列。如果数据里有分类特征,需要先做哑变量编码。另外 k 一般取奇数,避免二分类投票平局。3.3 决策树的分裂准则与剪枝决策树部分用了tree包,但资源里把分裂过程用信息增益重新讲了一遍。核心分裂逻辑是遍历每个特征和每个切分点,计算分裂后的信息增益:# 计算信息熵 entropy <- function(y) { p <- table(y) / length(y) # 各类别比例 -sum(p * log2(p + 1e-10)) # 加小量防log(0) } # 计算按某特征某阈值分裂后的信息增益 info_gain <- function(x, y, threshold) { left <- y[x <= threshold] # 左子集标签 right <- y[x > threshold] # 右子集标签 if (length(left) == 0 || length(right) == 0) return(0) p_left <- length(left) / length(y) # 左子集权重 p_right <- 1 - p_left # 右子集权重 parent_entropy <- entropy(y) # 父节点熵 child_entropy <- p_left * entropy(left) + p_right * entropy(right) parent_entropy - child_entropy # 信息增益 }逻辑说明:entropy()计算标签分布的混乱程度,值越大越不纯。info_gain()遍历一个特征的所有可能切分点,选增益最大的。实际建树时对每个特征都算一遍,取全局最优。1e-10是防止log2(0)报错。剪枝部分用了tree::prune.tree(),通过交叉验证选择复杂度参数cp。资源里给了plotcp()的画图代码,能直观看到不同cp下的交叉验证误差。常见坑是cp设太小导致过拟合,设太大又欠拟合,一般选误差曲线拐点处的值。3.4 随机森林的袋外误差与变量重要性随机森林直接调randomForest包,但资源里解释了ntree和mtry两个关键参数。ntree是树的数量,默认 500,一般够用;mtry是每次分裂随机抽取的特征数,分类任务默认sqrt(p),回归默认p/3。# 随机森林建模与变量重要性 rf_model <- randomForest(Species ~ ., data = train, ntree = 500, mtry = 2, importance = TRUE) print(rf_model$confusion) # 混淆矩阵 varImpPlot(rf_model) # 变量重要性图importance = TRUE才会计算变量重要性。varImpPlot()画出两个指标:MeanDecreaseAccuracy 和 MeanDecreaseGini,前者更可靠。袋外误差(OOB error)在print(rf_model)里直接显示,可以当作测试误差的估计,不用单独划验证集。注意:如果特征里有缺失值,randomForest默认用近邻填补,但效果不一定好,建议先处理缺失。另外mtry可以写个循环调优,但资源里没展开,留作改造点。3.5 支持向量机与核函数选择SVM 部分用e1071::svm(),重点讲了核函数的选择。线性核适合线性可分数据,径向基核(RBF)适合非线性。资源里用 iris 数据集对比了两种核的效果:# 线性核与RBF核对比 svm_linear <- svm(Species ~ ., data = train, kernel = "linear") svm_rbf <- svm(Species ~ ., data = train, kernel = "radial", gamma = 0.1, cost = 1) pred_linear <- predict(svm_linear, test) pred_rbf <- predict(svm_rbf, test) cat("线性核准确率:", mean(pred_linear == test$Species), "\n") cat("RBF核准确率:", mean(pred_rbf == test$Species), "\n")gamma控制 RBF 核的宽度,越大越容易过拟合;cost控制惩罚力度,越大越严格。资源里建议用tune.svm()做网格搜索,但为了保持脚本简洁,只给了手动调参的示例。3.6 K-means 与 PCA 的无监督流程K-means 部分手写了迭代过程,核心是分配和更新两步循环:# 手写K-means my_kmeans <- function(X, k, max_iter = 100) { centroids <- X[sample(nrow(X), k), ] # 随机初始化中心 for (iter in seq_len(max_iter)) { dists <- as.matrix(dist(rbind(X, centroids)))[1:nrow(X), (nrow(X)+1):(nrow(X)+k)] labels <- apply(dists, 1, which.min) # 分配簇 new_centroids <- t(sapply(seq_len(k), function(i) { colMeans(X[labels == i, , drop = FALSE]) # 更新中心 })) if (all(new_centroids == centroids)) break # 收敛判断 centroids <- new_centroids } list(labels = labels, centroids = centroids) }PCA 部分用prcomp(),重点讲了标准化和主成分个数选择。资源里用碎石图(scree plot)判断保留几个主成分,一般选累计方差贡献率到 85% 以上。4. 避坑与常见问题:跑 R 机器学习脚本时容易翻车的几个点4.1 因子类型导致建模报错现象:调randomForest()或svm()时报错「分类变量不能有超过 53 个水平」或「因子水平不匹配」。原因:R 默认把字符串列读成因子,如果某列取值太多,或者训练集和测试集的因子水平不一致,模型函数就会拒绝。解决:读数据时加stringsAsFactors = FALSE,建模前手动把目标列转因子,并确保训练测试集用同一套水平:data$Species <- factor(data$Species, levels = c("setosa", "versicolor", "virginica"))4.2 特征量纲不统一导致距离计算失效现象:KNN 或 K-means 结果很差,准确率接近随机猜。原因:不同特征数值范围差异大,比如一个特征 0-1,另一个 0-10000,距离被大数值特征主导。解决:建模前对所有数值特征做标准化:data_scaled <- scale(data[, -target_col])注意scale()返回矩阵,如果后面要用数据框,记得as.data.frame()。4.3 随机种子没固定导致结果不可复现现象:同一份代码跑两次,准确率不一样。原因:sample()、randomForest()、kmeans()内部都有随机过程,没设种子每次结果都变。解决:在脚本开头统一set.seed(42),并在每次随机操作前再设一次。资源里所有脚本都在第一行设了种子,但如果你自己加代码,记得补上。4.4 训练测试划分后索引错位现象:预测结果长度和测试集对不上,或者评估指标算出来是 NA。原因:用sample()划分后,训练集和测试集的行名还是原始行名,某些函数会按行名对齐,导致错位。解决:划分后重置行名:train <- train[sample(nrow(train)), ] rownames(train) <- NULL rownames(test) <- NULL4.5 包版本冲突导致函数行为不一致现象:同样的svm()调用,在别人机器上能跑,在你这里报参数错误。原因:e1071不同版本对gamma默认值处理不同,旧版本默认1/ncol,新版本可能变了。解决:显式写出所有关键参数,不依赖默认值。另外用sessionInfo()记录包版本,方便排查。5. 二次开发与改造:把这份资源变成你自己的算法试验台这份资源最大的价值不是「跑通」,而是「改起来方便」。每个算法脚本都是独立的,你可以拿其中一个当模板,替换数据集、调整参数、甚至换算法实现。我一般会做三件事。第一,把utils/eval_utils.R里的评估函数扩展一下。资源里只有准确率和 RMSE,实际项目里还需要 F1、AUC、调整兰德指数。加一个evaluate_classification()函数,输入真实标签和预测标签,输出混淆矩阵和几个常用指标:evaluate_classification <- function(actual, predicted) { cm <- table(Predicted = predicted, Actual = actual) accuracy <- sum(diag(cm)) / sum(cm) precision <- diag(cm) / rowSums(cm) recall <- diag(cm) / colSums(cm) f1 <- 2 * precision * recall / (precision + recall) list(confusion = cm, accuracy = accuracy, precision = precision, recall = recall, f1 = f1) }第二,把参数调优从手动改成网格搜索。资源里 KNN 的 k 值选择是写死的循环,你可以抽成一个通用函数,输入参数列表和评估函数,返回最优组合。这样换算法时只改参数范围,不用重写循环。第三,加一个run_all.R脚本,用source()依次跑所有算法,把结果汇总到一个数据框里对比。这样你改完某个算法,跑一遍就能看到它在所有算法里的排名变化。改造时注意一个点:资源里的数据集都是小样本,如果你换成大样本,手写梯度下降和 KNN 距离矩阵会非常慢。这时候要么改用optim()做优化,要么直接用包里的高效实现。我自己的习惯是:先用小数据把手写版本跑通,理解算法行为,再切到包函数处理真实数据。从那以后我每次改算法参数,都强制先在小数据上验证逻辑,再上大数据跑性能,省得等半天才发现公式写错了。希望帮到你。本文还有配套的精品资源,点击获取
简介:这份资源面向具备一定统计基础、希望用R语言落地机器学习算法的数据分析人员与学生,围绕监督学习与非监督学习两条主线,覆盖线性回归、逻辑回归、支持向量机、决策树、随机森林、梯度提升机、朴素贝叶斯、神经网络以及K-means聚类、关联规则等常见算法的实现思路。压缩包共20个文件,以8个R脚本和6个CSV数据集为主,另含许可证等说明文件,整体约16KB,体量轻便,便于直接运行与二次修改。目录按回归、决策树回归、多项式回归、简单与多元线性回归、SVR及数据预处理等模块组织,配套数据文件可支撑从数据清洗、特征选择到模型训练与评估的完整流程演练。已有244人学习下载,适合作为课程实验、项目练手或算法对照复现的参考素材,帮助读者在R环境中快速理解各算法的调用方式与适用场景。1. 一份把 R 语言机器学习算法拆到能跑的资源,到底长什么样很多人对 R 语言的印象还停留在统计检验和画图,觉得它跟「机器学习工程」沾不上边。但如果你手头正好有一份按算法逐个实现的 R 语言机器学习资源,情况就完全不一样了——它把分类、回归、聚类、降维这些常见任务,用 R 的原生语法和经典包一条条写出来,而不是调一个黑盒函数就完事。这份资源适合两类人:一类是想从零理解算法内部计算过程的开发者,另一类是已经会用 Python 跑模型、但需要把逻辑迁移到 R 环境做统计分析的人。它不追求工程化部署,重点在于「每个算法都能在本地 R 会话里跑通、看到中间结果、改参数看变化」。接下来我按实际拆包的顺序,把这份资源的结构、跑法和改造点讲清楚。2. 技术选型与目录结构:为什么用 R 而不是调包2.1 核心依赖与运行环境这份资源的技术栈非常克制,没有引入 tidymodels 或 caret 这类高层封装框架,而是直接用 R 基础语法配合几个经典包。常见做法是:数据处理用base R的data.frame和apply家族,矩阵运算用matrix原生支持,算法实现尽量手写核心循环,只在必要处调用stats包里的lm、glm、kmeans、prcomp等函数作为对照。运行环境上,R 版本建议 4.0 以上,因为部分矩阵运算和字符串处理在旧版本上有兼容问题。依赖包清单大致如下:包名用途是否必须stats线性模型、广义线性模型、聚类、主成分是(R 自带)MASS判别分析、稳健回归是classKNN 分类与回归是tree决策树构建与剪枝是randomForest随机森林实现是e1071支持向量机、朴素贝叶斯是nnet单层神经网络、多项逻辑回归是cluster轮廓系数、聚类评估是安装命令很直接,在 R 控制台里执行:# 一次性安装所有依赖包 install.packages(c("MASS", "class", "tree", "randomForest", "e1071", "nnet", "cluster"))这里有个细节:stats是 R 启动时自动加载的基础包,不需要单独安装,但如果你在脚本里显式调用library(stats)也不会报错。我一般会在脚本开头统一写library()调用,方便一眼看清依赖。2.2 目录组织与文件命名资源解压后,目录结构大致是这样的:R-ML-Algorithms/ ├── data/ # 数据集目录 │ ├── iris.csv │ ├── mtcars.csv │ └── simulated_reg.csv ├── R/ # 算法实现脚本 │ ├── 01_linear_regression.R │ ├── 02_logistic_regression.R │ ├── 03_knn.R │ ├── 04_decision_tree.R │ ├── 05_random_forest.R │ ├── 06_svm.R │ ├── 07_naive_bayes.R │ ├── 08_kmeans.R │ ├── 09_pca.R │ └── 10_neural_network.R ├── utils/ # 辅助函数 │ ├── plot_utils.R │ └── eval_utils.R └── README.md每个算法脚本都是独立可运行的,不依赖其他脚本的输出。utils/里放的是画图函数和评估指标计算,比如混淆矩阵、RMSE、轮廓系数,这些函数被各个算法脚本用source()引入。这种组织方式的好处是:你想看哪个算法,直接打开对应文件从头跑到尾就行,不用管其他文件的状态。2.3 数据流的统一约定所有脚本遵循同一套数据流约定:先读 CSV,再做训练测试划分,然后建模、预测、评估。划分比例默认 7:3,随机种子固定为 42,保证结果可复现。常见做法是写一个train_test_split函数放在utils/eval_utils.R里:# 简单的训练测试划分函数 train_test_split <- function(data, ratio = 0.7, seed = 42) { set.seed(seed) # 固定随机种子 n <- nrow(data) # 样本总数 idx <- sample(seq_len(n), size = floor(ratio * n)) # 随机抽取训练索引 list( train = data[idx, ], # 训练集 test = data[-idx, ] # 测试集 ) }参数说明:data是输入数据框,ratio是训练集占比,seed保证每次划分结果一致。返回一个列表,用$train和$test取对应数据。这个函数虽然简单,但统一了所有脚本的划分逻辑,避免每个文件里重复写sample()。3. 核心算法实现拆解:从线性回归到神经网络3.1 线性回归的手写梯度下降资源里线性回归给了两种实现:一种直接调lm(),另一种手写梯度下降。手写版本更能看清算法本质。核心代码如下:# 手写梯度下降求解线性回归 gradient_descent_lr <- function(X, y, lr = 0.01, epochs = 1000) { X <- cbind(1, X) # 添加截距列 m <- nrow(X) # 样本数 n <- ncol(X) # 特征数(含截距) theta <- rep(0, n) # 参数初始化 loss_history <- numeric(epochs) # 记录每轮损失 for (i in seq_len(epochs)) { pred <- X %*% theta # 预测值 error <- pred - y # 残差 grad <- (1 / m) * t(X) %*% error # 梯度计算 theta <- theta - lr * grad # 参数更新 loss_history[i] <- (1 / (2 * m)) * sum(error^2) # MSE损失 } list(theta = theta, loss = loss_history) }逻辑说明:X是特征矩阵,y是目标向量。第一步给X加一列全 1,对应截距项。梯度公式是(1/m) * X^T * (X*theta - y),这是均方误差对参数的导数。lr控制步长,太大会震荡,太小收敛慢,资源里默认 0.01,对标准化后的数据比较稳。epochs是迭代轮数,1000 轮对这个小数据集足够。返回的loss_history可以用来画收敛曲线,判断是否还需要增加轮数。参数调优建议:如果损失曲线下降太慢,先把lr调到 0.1 试试;如果震荡,降到 0.001。特征记得先标准化,否则不同量纲的列会导致梯度方向被大数值特征主导。3.2 KNN 的距离计算与 k 值选择KNN 部分没有直接调class::knn(),而是先手写了一个距离矩阵计算,再调包做对照。手写版本的核心是欧氏距离:# 计算欧氏距离矩阵 euclidean_dist <- function(A, B) { # A: m x d 矩阵,B: n x d 矩阵 # 返回 m x n 距离矩阵 A_sq <- rowSums(A^2) # A 每行平方和 B_sq <- rowSums(B^2) # B 每行平方和 cross <- A %*% t(B) # 交叉项 dist_sq <- outer(A_sq, B_sq, "+") - 2 * cross sqrt(pmax(dist_sq, 0)) # 防止负数开方 }这里用了矩阵运算加速,比双重循环快很多。pmax(dist_sq, 0)是防止浮点误差导致微小负数。得到距离矩阵后,对每个测试样本取最近的 k 个训练样本,投票决定类别。k 值选择上,资源里用了一个简单的循环,从 1 到 20 逐个试,看测试集准确率:# 遍历 k 值找最优 k_values <- 1:20 acc <- sapply(k_values, function(k) { pred <- knn(train = train_x, test = test_x, cl = train_y, k = k) mean(pred == test_y) }) best_k <- k_values[which.max(acc)]注意:knn()函数要求输入是矩阵或数据框,且不能有因子列。如果数据里有分类特征,需要先做哑变量编码。另外 k 一般取奇数,避免二分类投票平局。3.3 决策树的分裂准则与剪枝决策树部分用了tree包,但资源里把分裂过程用信息增益重新讲了一遍。核心分裂逻辑是遍历每个特征和每个切分点,计算分裂后的信息增益:# 计算信息熵 entropy <- function(y) { p <- table(y) / length(y) # 各类别比例 -sum(p * log2(p + 1e-10)) # 加小量防log(0) } # 计算按某特征某阈值分裂后的信息增益 info_gain <- function(x, y, threshold) { left <- y[x <= threshold] # 左子集标签 right <- y[x > threshold] # 右子集标签 if (length(left) == 0 || length(right) == 0) return(0) p_left <- length(left) / length(y) # 左子集权重 p_right <- 1 - p_left # 右子集权重 parent_entropy <- entropy(y) # 父节点熵 child_entropy <- p_left * entropy(left) + p_right * entropy(right) parent_entropy - child_entropy # 信息增益 }逻辑说明:entropy()计算标签分布的混乱程度,值越大越不纯。info_gain()遍历一个特征的所有可能切分点,选增益最大的。实际建树时对每个特征都算一遍,取全局最优。1e-10是防止log2(0)报错。剪枝部分用了tree::prune.tree(),通过交叉验证选择复杂度参数cp。资源里给了plotcp()的画图代码,能直观看到不同cp下的交叉验证误差。常见坑是cp设太小导致过拟合,设太大又欠拟合,一般选误差曲线拐点处的值。3.4 随机森林的袋外误差与变量重要性随机森林直接调randomForest包,但资源里解释了ntree和mtry两个关键参数。ntree是树的数量,默认 500,一般够用;mtry是每次分裂随机抽取的特征数,分类任务默认sqrt(p),回归默认p/3。# 随机森林建模与变量重要性 rf_model <- randomForest(Species ~ ., data = train, ntree = 500, mtry = 2, importance = TRUE) print(rf_model$confusion) # 混淆矩阵 varImpPlot(rf_model) # 变量重要性图importance = TRUE才会计算变量重要性。varImpPlot()画出两个指标:MeanDecreaseAccuracy 和 MeanDecreaseGini,前者更可靠。袋外误差(OOB error)在print(rf_model)里直接显示,可以当作测试误差的估计,不用单独划验证集。注意:如果特征里有缺失值,randomForest默认用近邻填补,但效果不一定好,建议先处理缺失。另外mtry可以写个循环调优,但资源里没展开,留作改造点。3.5 支持向量机与核函数选择SVM 部分用e1071::svm(),重点讲了核函数的选择。线性核适合线性可分数据,径向基核(RBF)适合非线性。资源里用 iris 数据集对比了两种核的效果:# 线性核与RBF核对比 svm_linear <- svm(Species ~ ., data = train, kernel = "linear") svm_rbf <- svm(Species ~ ., data = train, kernel = "radial", gamma = 0.1, cost = 1) pred_linear <- predict(svm_linear, test) pred_rbf <- predict(svm_rbf, test) cat("线性核准确率:", mean(pred_linear == test$Species), "\n") cat("RBF核准确率:", mean(pred_rbf == test$Species), "\n")gamma控制 RBF 核的宽度,越大越容易过拟合;cost控制惩罚力度,越大越严格。资源里建议用tune.svm()做网格搜索,但为了保持脚本简洁,只给了手动调参的示例。3.6 K-means 与 PCA 的无监督流程K-means 部分手写了迭代过程,核心是分配和更新两步循环:# 手写K-means my_kmeans <- function(X, k, max_iter = 100) { centroids <- X[sample(nrow(X), k), ] # 随机初始化中心 for (iter in seq_len(max_iter)) { dists <- as.matrix(dist(rbind(X, centroids)))[1:nrow(X), (nrow(X)+1):(nrow(X)+k)] labels <- apply(dists, 1, which.min) # 分配簇 new_centroids <- t(sapply(seq_len(k), function(i) { colMeans(X[labels == i, , drop = FALSE]) # 更新中心 })) if (all(new_centroids == centroids)) break # 收敛判断 centroids <- new_centroids } list(labels = labels, centroids = centroids) }PCA 部分用prcomp(),重点讲了标准化和主成分个数选择。资源里用碎石图(scree plot)判断保留几个主成分,一般选累计方差贡献率到 85% 以上。4. 避坑与常见问题:跑 R 机器学习脚本时容易翻车的几个点4.1 因子类型导致建模报错现象:调randomForest()或svm()时报错「分类变量不能有超过 53 个水平」或「因子水平不匹配」。原因:R 默认把字符串列读成因子,如果某列取值太多,或者训练集和测试集的因子水平不一致,模型函数就会拒绝。解决:读数据时加stringsAsFactors = FALSE,建模前手动把目标列转因子,并确保训练测试集用同一套水平:data$Species <- factor(data$Species, levels = c("setosa", "versicolor", "virginica"))4.2 特征量纲不统一导致距离计算失效现象:KNN 或 K-means 结果很差,准确率接近随机猜。原因:不同特征数值范围差异大,比如一个特征 0-1,另一个 0-10000,距离被大数值特征主导。解决:建模前对所有数值特征做标准化:data_scaled <- scale(data[, -target_col])注意scale()返回矩阵,如果后面要用数据框,记得as.data.frame()。4.3 随机种子没固定导致结果不可复现现象:同一份代码跑两次,准确率不一样。原因:sample()、randomForest()、kmeans()内部都有随机过程,没设种子每次结果都变。解决:在脚本开头统一set.seed(42),并在每次随机操作前再设一次。资源里所有脚本都在第一行设了种子,但如果你自己加代码,记得补上。4.4 训练测试划分后索引错位现象:预测结果长度和测试集对不上,或者评估指标算出来是 NA。原因:用sample()划分后,训练集和测试集的行名还是原始行名,某些函数会按行名对齐,导致错位。解决:划分后重置行名:train <- train[sample(nrow(train)), ] rownames(train) <- NULL rownames(test) <- NULL4.5 包版本冲突导致函数行为不一致现象:同样的svm()调用,在别人机器上能跑,在你这里报参数错误。原因:e1071不同版本对gamma默认值处理不同,旧版本默认1/ncol,新版本可能变了。解决:显式写出所有关键参数,不依赖默认值。另外用sessionInfo()记录包版本,方便排查。5. 二次开发与改造:把这份资源变成你自己的算法试验台这份资源最大的价值不是「跑通」,而是「改起来方便」。每个算法脚本都是独立的,你可以拿其中一个当模板,替换数据集、调整参数、甚至换算法实现。我一般会做三件事。第一,把utils/eval_utils.R里的评估函数扩展一下。资源里只有准确率和 RMSE,实际项目里还需要 F1、AUC、调整兰德指数。加一个evaluate_classification()函数,输入真实标签和预测标签,输出混淆矩阵和几个常用指标:evaluate_classification <- function(actual, predicted) { cm <- table(Predicted = predicted, Actual = actual) accuracy <- sum(diag(cm)) / sum(cm) precision <- diag(cm) / rowSums(cm) recall <- diag(cm) / colSums(cm) f1 <- 2 * precision * recall / (precision + recall) list(confusion = cm, accuracy = accuracy, precision = precision, recall = recall, f1 = f1) }第二,把参数调优从手动改成网格搜索。资源里 KNN 的 k 值选择是写死的循环,你可以抽成一个通用函数,输入参数列表和评估函数,返回最优组合。这样换算法时只改参数范围,不用重写循环。第三,加一个run_all.R脚本,用source()依次跑所有算法,把结果汇总到一个数据框里对比。这样你改完某个算法,跑一遍就能看到它在所有算法里的排名变化。改造时注意一个点:资源里的数据集都是小样本,如果你换成大样本,手写梯度下降和 KNN 距离矩阵会非常慢。这时候要么改用optim()做优化,要么直接用包里的高效实现。我自己的习惯是:先用小数据把手写版本跑通,理解算法行为,再切到包函数处理真实数据。从那以后我每次改算法参数,都强制先在小数据上验证逻辑,再上大数据跑性能,省得等半天才发现公式写错了。希望帮到你。本文还有配套的精品资源,点击获取