在R语言中为核平滑回归寻找最优带宽:基于训练与测试集RMSE的实现方案
没问题!我帮你写一个能自动遍历带宽、计算训练/测试集RMSE并找出最优值的函数,完美适配你现有的核平滑回归代码~
首先,咱们得明确核心逻辑:遍历一系列带宽候选值,对每个带宽用ksmooth拟合训练数据,然后分别计算训练集和测试集的RMSE,最后找出能最小化测试集RMSE的带宽(毕竟我们要的是泛化能力好的模型)。
完整函数代码
# 定义带宽选择函数 find_optimal_bandwidth <- function(train_data, test_data, h_min = 0.1, h_max = 2, h_step = 0.1) { # 生成带宽候选序列 h_candidates <- seq(h_min, h_max, by = h_step) # 初始化存储RMSE的结果框 rmse_results <- data.frame( bandwidth = h_candidates, train_rmse = NA, test_rmse = NA ) # 遍历每个带宽 for (i in seq_along(h_candidates)) { h <- h_candidates[i] # 用当前带宽做核平滑回归 kernel_fit <- ksmooth(train_data$x, train_data$y, kernel = "normal", bandwidth = h) # 对训练集x值做预测(因为ksmooth返回的是排序后的x,需要用approx匹配原训练集的x) train_pred <- approx(kernel_fit$x, kernel_fit$y, xout = train_data$x)$y # 计算训练集RMSE rmse_results$train_rmse[i] <- sqrt(mean((train_data$y - train_pred)^2)) # 对测试集x值做预测 test_pred <- approx(kernel_fit$x, kernel_fit$y, xout = test_data$x)$y # 计算测试集RMSE rmse_results$test_rmse[i] <- sqrt(mean((test_data$y - test_pred)^2)) } # 找出测试集RMSE最小的带宽 optimal_h <- rmse_results$bandwidth[which.min(rmse_results$test_rmse)] # 返回结果:包含所有RMSE数据和最优带宽 return(list( rmse_table = rmse_results, optimal_bandwidth = optimal_h )) }
关键细节解释
approx函数的作用:ksmooth返回的是排序后的x和对应的拟合值,但你的训练/测试集x可能是无序的,而且测试集x可能不在训练集的x中。用approx可以根据核平滑的拟合结果,对任意x值做线性插值得到预测值,保证每个样本都能拿到对应的预测结果。- 带宽范围调整:函数里的
h_min、h_max、h_step是默认值,你可以根据自己的数据分布调整(比如如果你的x范围很小,就把h的范围调小)。 - 结果返回:函数会返回两个东西:一个是所有带宽对应的RMSE表格,另一个是最优带宽值,方便你后续分析和使用。
使用示例
假设咱们先模拟你说的二元数据(y均值为1/x加噪声),然后拆分训练测试集:
# 模拟数据 set.seed(123) # 设置随机种子保证结果可复现 x <- runif(200, 0.5, 5) # x在0.5到5之间均匀分布 y <- 1/x + rnorm(200, 0, 0.1) # y=1/x加正态噪声 # 拆分训练集和测试集(7:3) train_idx <- sample(1:length(x), size = 0.7*length(x)) train_points <- data.frame(x = x[train_idx], y = y[train_idx]) test_points <- data.frame(x = x[-train_idx], y = y[-train_idx]) # 调用函数找最优带宽 bandwidth_result <- find_optimal_bandwidth(train_points, test_points, h_min = 0.05, h_max = 1, h_step = 0.05) # 查看最优带宽 cat("最优带宽值:", bandwidth_result$optimal_bandwidth, "\n") # 可视化RMSE随带宽的变化 plot(bandwidth_result$rmse_table$bandwidth, bandwidth_result$rmse_table$test_rmse, type = "l", lwd = 2, col = "dodgerblue", xlab = "带宽h", ylab = "测试集RMSE", main = "RMSE随带宽变化趋势") points(bandwidth_result$optimal_bandwidth, bandwidth_result$rmse_table$test_rmse[which.min(bandwidth_result$rmse_table$test_rmse)], pch = 19, col = "red", cex = 1.5) text(bandwidth_result$optimal_bandwidth, bandwidth_result$rmse_table$test_rmse[which.min(bandwidth_result$rmse_table$test_rmse)], labels = "最优带宽", pos = 3, col = "red") # 用最优带宽拟合并画图 optimal_fit <- ksmooth(train_points$x, train_points$y, kernel = "normal", bandwidth = bandwidth_result$optimal_bandwidth) plot(y~x, train_points, cex = 0.5, col = "dodgerblue", main = "核平滑回归拟合结果(最优带宽)") lines(optimal_fit, lwd = 2, col = 2) points(y~x, test_points, cex = 0.5, col = "darkgreen") # 叠加测试集数据
小提示
- 如果你的测试集RMSE先降后升,说明存在过拟合(带宽太小)和欠拟合(带宽太大)的情况,最优值就在中间的最低点。
- 你也可以根据训练集RMSE来选带宽,但通常测试集RMSE更能反映模型的泛化能力。
内容的提问来源于stack exchange,提问作者s2798ww
相关产品推荐
相关产品推荐

