基于R语言的机器学习学习笔记
随机森林
使用R语言实现随机森林(randomFores)
## 本例使用的数据集来自R语言, 预测变量是连续值变量,
## 随机森林执行回归任务, 而不是其它博客常用的分类变量,执行分类任务
# 安装包
# install.package("randomForest")
library("randomForest")
# 加载数据
data(airquality) # 使用R语言自带数据(data()可以查看所有可用数据), airquality数据集为连续变量, iris数据集有分类变量
summary(airquality) # 查看数据统计信息; str(),查看数据结构; head()和tail(), 查看数据的首尾; dim() 查看行列号
# read.csv("C:/test.csv") # 也可读取自己的数据
# 处理数据
# 划分数据集为训练集(建模)和测试集(预测), 训练集和测试集的比例为7:3或者8:2. caret::createDataPartition函数也可分割数据集
D <- na.omit(airquality) # 去除NA值
nrow <- nrow(D) # 计算行数
set.seed(1) # 设置随机种子, 不然每次随机的结果都不一样, 无法复现结果
sample <- sample(1:nrow, round(nrow*0.7)) # 随机选择, 7:3分割数据集
D_trainSet <- D[sample, ] # 训练数据集
D_testSet <- D[-sample, ] # 验证数据集
# 构建随机森林模型
set.seed(2) # 设置种子, 用于复现
# RF预测Ozone(连续的数值变量). ntree设置构建树的数量, 默认为500. maxnodes设置每棵树的最大节点数
RF <- randomForest(Ozone ~ ., data=D_trainSet, importance=TRUE, na.action = na.omit)
print(RF) # 查看变量解释百分比
plot(RF) # 绘图, error随着trees变化的折线图, 预测变量为连续变量时, error指的是MSE
hist(treesize(RF)) # 每棵树的节点数, 直方图显示
# 变量重要性排序
imp <- importance(RF) # 没有设置type参数,两种方法计算的重要性的都会输出
varImpPlot(RF, main = "variable importance") # 图可视化重要性排序(Dotchart)
# write.csv(imp, file = 'D:/RF_importance.csv') # 保存为csv
# 使用构建的随机森林模型预测新数据集
predict <- predict(RF, D_testSet)
plot(D_testSet$Ozone, predictSet) # 简单绘制散点图
# 对比训练数据集和预测数据集的预测精度
pred_trainSet <- predict(RF, D_trainSet) # 使用训练数据预测变量
cat('R2_trainSet', summary(lm(D_trainSet$Ozone ~ pred_trainSet))$r.squared) # 计算模型精度
pred_testSet <- predict(RF, D_testSet) # 使用测试数据预测变量,并计算模型精度
cat('R2_testSet', summary(lm(D_testSet$Ozone ~ pred_testSet))$r.squared) # 计算模型精度
# 关于网上教程提到的混淆矩阵和ROC曲线应该是分类变量的分析工具, 连续变量用不了
# 下面代码不重要了----------------------------------------------------------------------------
# 统计模型的预测精度--------------------------------------------------
df <- data.frame( obs = D_testSet$Ozone, pred = pred_testSet ) |> na.omit() # 构建数据框, 并去除空值
summ <- summary(lm(df$obs ~ df$pred) )
N <- length(df$obs)
intercept <- round(summ$coefficients[1,1], 4)
slope <- round(summ$coefficients[2,1], 4)
R2 <- round(summ$r.squared, 3); R <- round(cor(df)[1,2], 3)
P <- formatC(summ$coefficients[2,4], digits = 4, format = "f")
residual <- df$obs - df$pred
MSE <- mean(residual^2)
MAE <- mean(abs(residual))
RMSE <- roune(sqrt(MSE), 2) # 均方根误差(残差平方和的均值的平方根),表示模型的误差大小
NRMSE <- round(RMSE/mean(df$obs)*100, 2) # 相对均方根误差,使用百分比的形式表示模型的误差大小
NRMAE <- round(MAE/mean(df$obs)*100, 2)
# 使用ggplot绘制散点图-------------------------------------
library(ggplot2)
min <- min(df, na.rm = T); max <- max(df, na.rm = T)
str <- paste0('RMSE = ', round(RMSE,3), '\n',
'NRMSE = ', round(NRMSE,2), ' %', '\n',
'R^2 = ', round(R2,3), '\n')
# 绘制散点图
F <- ggplot(df, aes(obs, pred)) + geom_point() +
geom_abline(intercept = 0, slope = 1, linetype ="longdash", colour = 'black', size=0.5) + # 绘制对角线
geom_smooth(se = T, method = 'lm', size = 0.5, colour = 'black',fill = 'gray') + # 绘制拟合曲线
lims(x=c(min, max), y=c(min, max)) +
labs(x = paste('Observation'), y = paste('Prediction') ) +
annotate('text', x = min, y = max, hjust = 'left', vjust = 'top',label = str, size = 4, family = "sans") + # 标注模型精度
theme_bw()
F
ggsave(paste0('D:/','point_RF','.tiff'), F, width = 10, height = 9, units = c("cm"), dpi = 600)
相关知识
注意事项:
(一) 模型中关于分类任务以及回归预测任务的区别:
随机森林模型,分类和回归预测的操作不同之处在于预测变量(因变量)的类型。
如果因变量是因子则执行分类任务;
如果因变量是连续性变量,则执行回归预测任务。
不同因变量类型对应的分析也不一样,很多教程没有说明, 就用分类变量开始演示了,混淆矩阵和ROC曲线是分类变量的分析工具, 连续变量的回归预测用不了的。
其实无论是决策树回归还是分类,思路都是一样的。
遍历所有的特征的特征值,来作为分割点,然后看哪一个分割点的效果最好。
效果好不好可以通过各种特征选择算法量化,主要看分割后的子集的纯度。
分类问题看的是信息增益(C3),信息增益比(C4.5)以及gini指数(CART)。随机森林采用的CART决策树就是基于基尼系数选择特征的。
回归问题就是看子节点的残差平方和之和。然后再在各个子节点中继续选择分割点,直到层次达到了指定阈值或者子节点只有一个样本。
(二) 随机森林的过拟合问题
随机森林容易产生过拟合,特别是在数据集相对小的时候。
当你的模型对于测试集合做出“太好”的预测的时候就应该怀疑一下了。
避免过拟合的一个方法是在模型中只使用有相关性的特征,比如使用之前提到的特征选择。
(三)随机森林的两个参数(候选特征数和决策树数量)
候选特征数K
K越大,单棵树的效果会提升,但树之间相关性也会增强(proximity参数可以输出)。
决策树数量M
M越大,模型效果会有提升(可以绘图查看),但计算量会变大。
(四) 关于袋外数据OOD
- OOD
首先回顾一下随机森林的装袋(Bagging)
由于采用放回随机抽样, 从总数据集(N个样本)随机抽取的N个样本组成的子集有一个特点.
约1/3的样本没被抽到, 也就是说约1/3的样本是重复的.
这样导致每次得到的子集都不一样(之间会有部分重复), 每个子集都可以用来构建一个树.
对于每个测试样本, 随机森林的每棵树的预测结果汇总在一起, 概率最大的就是随机森林的分类结果. (当然中间还有比较重要的其步骤, 特征装袋)
在上面描述中每个子集没有抽到的1/3, 就是袋外数据.
- OOD_score
每棵树的OOD袋外数据都可以被用来验证这棵树的分类精度, 袋外数据的预测结果相对于袋外数据可以计算OOD_score(对于回归分析是R平方,对于分类分析是).
所有树OOD_score的均值就是随机森林模型可解释性(决定系数).
- 通过OOD计算特征重要性
用oob样本在训练好的单棵决策树上运行,可以得到袋外数据误差 e1。
然后保持其他列不变,permutate(随机重排/洗牌)第 i 列特征的特征值或者加噪音在 oob中第 i 列特征的特征值上,得到袋外数据误差 e2(误差会增大)。
特征的重要性计算为 这个特征在所有树的(e2-e1)均值。值越高的特征越重要。
- R语言关于变量重要性的定义
R语言importance {randomForest}关于变量重要性度量(the variable importance measures)的定义。
以下是变量重要性测量的定义。
第一个衡量标准是通过permuting OOB数据计算的:
对于每一棵树,记录数据中袋外部分的预测误差(分类的误差率,回归的MSE)。
然后在对每个预测变量进行排列组合后进行同样的操作。
然后将两者之间的差异在所有树上取平均值,并以差异的标准差进行标准化。
如果某个变量的差异标准差等于0,则不进行划分(但在这种情况下,平均数几乎总是等于0)。
第二个衡量标准是对变量进行拆分后的节点杂质的总减少量,所有树的平均值。
对于分类,节点杂质是由基尼指数来衡量的。
对于回归来说,它是由残差平方和来衡量的。
参考链接:
R语言代码实现
- R语言实现随机森林
https://blog.csdn.net/water200101/article/details/124384878 - 随机森林实例(R语言实现)
https://blog.csdn.net/qq_51165184/article/details/123362161 - R语言之Random Forest随机森林
https://www.cnblogs.com/nxld/p/6374945.html
关于OOD
- ROC曲线
https://zhuanlan.zhihu.com/p/573964757 - 随机森林oob_score及oob判断特征重要性
https://blog.csdn.net/qq_36535820/article/details/119797794
随机森林与其他模型的区别
- R语言︱决策树族——随机森林算法
https://zhuanlan.zhihu.com/p/44545822
算法
- 决策树是怎么做回归的?
https://zhuanlan.zhihu.com/p/159600175 - 决策树 – 回归
https://blog.csdn.net/sakura_saku/article/details/116401456 - 将决策树用于回归
https://blog.csdn.net/SunJW_2017/article/details/125741763
-「西瓜书」阅读笔记——决策树
https://blog.csdn.net/SunJW_2017/article/details/122590515
多元线性回归
使用R语言实现多元线性回归
# 获取数据----
data(state.x77) # 使用R语言数据
D <- data.frame(state.x77) |> na.omit() # 转数据框格式, 并移除NA
str(D); head(D) # 查看数据
# 处理数据(分割数据集为训练和预测)----
set.seed(11) # 设置随机种子,用于复现
ind <- sample(nrow(D), nrow(D)*0.7) # 按照7:3分割数据集为训练和预测
trainSet <- D[, ind] # 训练集
testSet <- D[, -ind] # 预测集
# 构建模型----
model <- lm(Murder~., trainSet) # 使用数据框其他变量预测Murder变量
summary(model) # 查看模型拟合结果
# 多重共线性----
cor(D) # 相关性矩阵, 查看特征之间的相关性
model_step <- step(model) # 逐步回归,剔除存在共线性的变量
summary(model_step)
# 使用模型预测----
pred <- predict(model_step, newdata = testSet) # 使用测试集和逐步回归的多元线性模型预测Murder
df_pred <- data.frame(obs = testSet$Murder, pred = pred) #
summ <- summary(lm(obs ~ pred, df_pred))
# 统计模型的精度----
residual <- df_pred$obs - df_pred$pred
MSE <- mean(residual^2)
RMSE <- sqrt(MSE)
NRMSE <- RMSE/mean(df$obs)*100
R2 <- summ$r.squared
P <- summ$coefficients[2,4]
# 绘制散点图----
library(ggplot2); library(ggpubr)
min <- min(df, na.rm = T); max <- max(df, na.rm = T)
str <- paste0('RMSE = ', round(RMSE,3), '\n',
'NRMSE = ', round(NRMSE,2), '%', '\n',
'R^2 = ', round(R2,3), '\n')
F <- ggplot(df_pred, aes(obs, pred)) + geom_point() +
geom_abline(intercept = 0, slope = 1, linetype ="longdash", colour = 'black', size=0.5) + # 绘制对角线
geom_smooth(se = T, method = 'lm', size = 0.5, colour = 'black',fill = 'gray') + # 绘制拟合线
lims( x=c(min, max), y=c(min, max) ) +
labs(x = paste('Observation'), y = paste('Prediction') ) +
annotate('text', x = min, y = max, hjust = 'left', vjust = 'top',label = str, size = 4, family = "sans") + # 标注模型精度
theme_bw()
F
ggsave(paste0('D:/','point_MLR','.tiff'), F, width = 10, height = 9, units = c("cm"), dpi = 600)
参考链接:
# R语言 —— 多元线性回归
https://blog.csdn.net/m0_51339444/article/details/124590708
# R语言——多元线性回归
https://blog.csdn.net/weixin_41030360/article/details/80891738
XGboost(eXtreme Gradient Boosting)
使用R语言的xgboost包
# 安装并加载包 ----
#install.packages("xgboost", repos="http://dmlc.ml/drat/", type = "source")
library(xgboost)
library(ggplot2)
# 加载数据 ----
data(airquality)
str(airquality)
# 分割数据 -----
D <- na.omit(airquality) # 去除NA值
set.seed(1) # 设置随机种子, 不然每次随机的结果都不一样, 无法复现结果
sample <- sample(nrow(D), nrow(D)*0.7) # 随机选择, 7:3分割数据集
trainSet <- D[sample, ] # 训练数据集
testSet <- D[-sample, ] # 验证数据集
# 将数据处理成XGboost的xgb.DMatrix格式 ------
dtrain <- xgb.DMatrix(data = as.matrix(trainSet[,-1]), label = trainSet[, 1] )
dtest <- xgb.DMatrix(data = as.matrix(testSet[,-1]), label = testSet[, 1] )
watchlist <- list(train = dtrain, test = dtest)
# 构建模型 ----
params <- list(eta = 0.5, # 学习率, 默认是0.3
max_depth = 3 # 每棵树的最大树深, 默认是6
# objective 默认值是"reg:squarederror", 指定学习任务(回归:reg)和学习目标(squarederror)
# nthread 默认为最大可用线程数
)
xgb <- xgb.train(params = params, data = dtrain, nrounds = 10, watchlist = watchlist) # booster默认为"gbtree";eta默认0.3;max_dept
# 重要性排序
importance <- xgb.importance(xgb)
print(importance)
xgb.ggplot.importance(importance) # 重要性排序绘图
# 查看模型的单颗决策数
# xgb.plot.tree(model = xgb, trees = 1, plot_width = 500, plot_height = 500)
# xgb.plot.tree(model = xgb, trees = 1:3, plot_width = 500, plot_height = 500)
# 模型预测 -----
pred_test <- predict(xgb, newdata = dtest)
# 统计模型的精度RMSE -----
df <- data.frame(obs = testSet[,1], pred = pred)
residual <- (df$obs - df$pred); MSE <- mean(residual^2)
RMSE <- sqrt(MSE); NRMSE <- RMSE/mean(df$obs)*100
# 统计实测值和预测值线性模型的进度
summ <- summary(lm(obs ~ pred, df))
# intercept <- round(summ$coefficients[1,1],4); slope <- round(summ$coefficients[2,1],4)
# P <- formatC(summ$coefficients[2,4], digits = 4, format = "f" )
R2 <- summ$r.squared; R <- cor(df)[1,2]
# 绘制散点图 ----
library(ggplot2); library(ggpubr)
min <- min(df, na.rm = T); max <- max(df, na.rm = T)
str <- paste0('RMSE = ', round(RMSE,3), '\n',
'NRMSE = ', round(NRMSE,2), '%', '\n',
'R^2 = ', round(R2,3), '\n')
F <- ggplot(df_pred, aes(obs, pred)) + geom_point() +
geom_abline(intercept = 0, slope = 1, linetype ="longdash", colour = 'black', size=0.5) + # 绘制对角线
geom_smooth(se = T, method = 'lm', size = 0.5, colour = 'black',fill = 'gray') + # 绘制拟合线
lims( x=c(min, max), y=c(min, max) ) +
labs(x = paste('Observation'), y = paste('Prediction') ) +
annotate('text', x = min, y = max, hjust = 'left', vjust = 'top', label = str, size = 4) + # 标注模型精度
theme_bw()
F
ggsave(paste0('D:/','point_MLR','.tiff'), F, width = 10, height = 9, units = c("cm"), dpi = 500)
-
XGBoost(eXtreme Gradient Boosting)原理:
是在决策树的基础上产生迭代,它以 boosting 的方式结合了多个决策树。通常创建每棵新树是为了通过梯度提升来减少先前模型的误差,误差指的是实际值和预测值之间的差异。把误差作为协变量参与下一个模型的预测,反复执行这个过程,降低出错率,直到决策树指定阈值,模型已经被训练成功。 -
主要参数介绍:
-
调参注意事项:
当我们允许模型变得更复杂(例如更深)时,模型具有更好的拟合训练数据的能力,从而产生更少偏差的模型。
然而,如此复杂的模型需要更多的数据来拟合。
控制过拟合
当你观察到训练准确率很高,但测试准确率很低时,很可能你遇到了过拟合问题。
通常有两种方法可以控制 XGBoost 中的过度拟合:
第一种方式是直接控制模型复杂度。
这包括max_depth 和 min_child_weight 和 gamma。
第二种方法是添加随机性以使训练对噪声具有鲁棒性。
这包括 subsample 和 colsample_bytree。
您还可以减步长eta。这样做时请记住增加num_round。
更快的训练性能
有一个名为tree_method的参数,将其设置为historgpu_hist以加快计算速度。
参考链接:
# XGBoost(二):R语言实现
https://www.jianshu.com/p/38009bec6a55
# R语言机器学习-xgboost (知乎)
https://zhuanlan.zhihu.com/p/607919007
# 官方文档(这里很全面, 有时间建议浏览一下)
https://xgboost.readthedocs.io/en/stable/index.html
# 官方文档 (调参注意事项)
https://xgboost.readthedocs.io/en/stable/tutorials/param_tuning.html#