> library(pacman)
> p_load(data.table, ggplot2, mlr3, mlr3learners, mlr3tuning, paradox)
1、数据准备
> data("german", package = "rchallenge")
> DataExplorer::profile_missing(german)
## feature num_missing pct_missing
## 1 status 0 0
## 2 duration 0 0
## 3 credit_history 0 0
## 4 purpose 0 0
## 5 amount 0 0
## 6 savings 0 0
## 7 employment_duration 0 0
## 8 installment_rate 0 0
## 9 personal_status_sex 0 0
## 10 other_debtors 0 0
## 11 present_residence 0 0
## 12 property 0 0
## 13 age 0 0
## 14 other_installment_plans 0 0
## 15 housing 0 0
## 16 number_credits 0 0
## 17 job 0 0
## 18 people_liable 0 0
## 19 telephone 0 0
## 20 foreign_worker 0 0
## 21 credit_risk 0 0
2、创建任务
> task <- TaskClassif$new("German_Credit", german, target = "credit_risk")
>
> # 多核并行
> future::plan("multicore")
3、交叉验证
使用十折交叉验证评估所有的超参数配置。
> set.seed(123)
> cv10 <- rsmp("cv", folds = 10L)
>
> cv10$instantiate(task = task)
> head(cv10$instance)
## row_id fold
## 1: 19 1
## 2: 30 1
## 3: 35 1
## 4: 41 1
## 5: 50 1
## 6: 56 1
4、参数调优
paradox包定义超参数的搜索空间,mlr3tuning包用于对超参数调优。
4.1 创建学习器
> lrn.knn <- lrn("classif.kknn", predict_type = "prob")
>
> # 查看可以设置哪些参数
> lrn.knn$param_set
## <ParamSet>
## id class lower upper levels default value
## 1: k ParamInt 1 Inf 7
## 2: distance ParamDbl 0 Inf 2
## 3: kernel ParamFct NA NA rectangular,triangular,epanechnikov,biweight,triweight,cos,... optimal
## 4: scale ParamLgl NA NA TRUE,FALSE TRUE
## 5: ykernel ParamUty NA NA
> # 查看levels值(可以设置为哪些值)
> lrn.knn$param_set$levels
## $k
## NULL
##
## $distance
## NULL
##
## $kernel
## [1] "rectangular" "triangular" "epanechnikov" "biweight" "triweight" "cos" "inv"
## [8] "gaussian" "rank" "optimal"
##
## $scale
## [1] TRUE FALSE
##
## $ykernel
## NULL
> # 查看默认值
> lrn.knn$param_set$default
## $k
## [1] 7
##
## $distance
## [1] 2
##
## $kernel
## [1] "optimal"
##
## $scale
## [1] TRUE
##
## $ykernel
## NULL
kernel默认为"optimal",更改为“rectangular”。
> lrn.knn$param_set$values$kernel <- "rectangular"
4.2 创建搜索网格
首先调优k值,即最近邻数量。默认值为7,我们设置从3到20。
然后是距离函数,默认是L2,我们设置为L1或者L2。
> search.space <- ParamSet$new(list(
+ ParamInt$new("k", lower = 3, upper = 20),
+ ParamInt$new("distance", lower = 1, upper = 2)
+ ))
>
> search.grid <- TuningInstanceSingleCrit$new(
+ task = task,
+ learner = lrn.knn,
+ resampling = cv10,
+ measure = msr("classif.ce"),
+ search_space = search.space,
+ terminator = trm("none")
+ )
4.3 网格搜索
> set.seed(123)
> german.grid <- tnr("grid_search", resolution = 18, batch_size = 36)
>
> german.grid$optimize(search.grid)
## k distance learner_param_vals x_domain classif.ce
## 1: 7 1 <list[3]> <list[2]> 0.248
> search.grid$result
## k distance learner_param_vals x_domain classif.ce
## 1: 7 1 <list[3]> <list[2]> 0.248
找到的最优参数是k=7,distance=1时,错误率为24.8%。
画图查看不同的K值分类错误率走势:
> ggplot(search.grid$archive$data(),
+ aes(k, classif.ce, col = as.factor(distance))) +
+ geom_line() +
+ geom_point(size = 3) +
+ theme_bw() +
+ theme(legend.position = "top")

5、调优更多的参数
k在3与4之间和k在49与50之间的表现差距很大,因为4比3大33%而50比49只大2%,为了减小这种差距,所以对k进行指数变换,然后四舍五入转换为整型。
scale表示是否对特征做标准化处理。
> large.search <- ParamSet$new(list(
+ ParamDbl$new(id = "k", lower = log(3), upper = log(50)),
+ ParamDbl$new(id = "distance", lower = 1, upper = 3),
+ ParamFct$new(id = "kernel",
+ levels = c("rectangular", "gaussian", "rank", "optimal")),
+ ParamLgl$new(id = "scale")
+ ))
>
> large.search$trafo <- function(x, param_set) {
+ x$k = round(exp(x$k))
+ return(x)
+ }
网格搜索往往需要花费很多的时间,假如有3个不同的k值、distance值、kernel值和2个不同的scale值需要进行3 * 3 * 3 * 2 = 54次运算。所以我们设置不同的评估方式,即随机搜索方式,并设置在36次评估后算法停止。
> random.search <- tnr(.key = "random_search")
>
> search.random <- TuningInstanceSingleCrit$new(
+ task = task,
+ learner = lrn.knn,
+ resampling = cv10,
+ measure = msr("classif.ce"),
+ search_space = large.search,
+ terminator = trm("evals", n_evals = 36)
+ )
>
> # 运行
> random.search$optimize(search.random)
## k distance kernel scale learner_param_vals x_domain classif.ce
## 1: 2.18996 1.492898 rectangular TRUE <list[4]> <list[4]> 0.254
> # 查看结果
> search.random$result
## k distance kernel scale learner_param_vals x_domain classif.ce
## 1: 2.18996 1.492898 rectangular TRUE <list[4]> <list[4]> 0.254
> str(search.random$result)
## Classes 'data.table' and 'data.frame': 1 obs. of 7 variables:
## $ k : num 2.19
## $ distance : num 1.49
## $ kernel : chr "rectangular"
## $ scale : logi TRUE
## $ learner_param_vals:List of 1
## ..$ :List of 4
## .. ..$ kernel : chr "rectangular"
## .. ..$ k : num 9
## .. ..$ distance: num 1.49
## .. ..$ scale : logi TRUE
## $ x_domain :List of 1
## ..$ :List of 4
## .. ..$ k : num 9
## .. ..$ distance: num 1.49
## .. ..$ kernel : chr "rectangular"
## .. ..$ scale : logi TRUE
## $ classif.ce : num 0.254
## - attr(*, ".internal.selfref")=<externalptr>
画图查看结果。
查看是否标准化的表现:
> ggplot(search.random$archive$data(unnest = "x_domain"),
+ aes(x_domain_k, classif.ce, col = x_domain_scale)) +
+ geom_point(size = 3) +
+ geom_line() +
+ theme_bw() +
+ theme(legend.position = "top")

结果表明,标准化对性能表现具有显而易见的影响,特征标准化后的错误率明显明显低于未标准化的结果。
再查看不同kernel的影响:
> ggplot(search.random$archive$data(unnest = "x_domain"),
+ aes(x_domain_k, classif.ce, col = x_domain_kernel)) +
+ geom_point(size = 3) +
+ geom_line() +
+ theme_bw() +
+ theme(legend.position = "top")

图形表明不同的kernel对性能表现结果的影响没有明显的区别。
6、结果对比
> search.grid$result_y
## classif.ce
## 0.248
> search.random$result_y
## classif.ce
## 0.254
简单的网格搜索最优参数和大规模随机搜索最优参数最终模型的性能表现几乎无异,说明存在过度调优的现象。解决这个问题的方法是使用Nested Resampling(嵌套采样)。
7、自动调优
mlr3tuning包提供自动调优参数方法:
> search.grid.auto <- AutoTuner$new(
+ learner = lrn.knn,
+ resampling = rsmp("cv", folds = 5),
+ measure = msr("classif.ce"),
+ search_space = search.space,
+ terminator = trm("none"),
+ tuner = tnr("grid_search", resolution = 18)
+ )
自动调优功能的使用与标准的学习器一样,也可以将参数调优和模型训练结合起来一起实现。
> rr <- resample(task = task,
+ learner = search.grid.auto,
+ resampling = cv10,
+ store_models = T)
> # 外部十折交叉验证的结果
> rr$aggregate()
## classif.ce
## 0.269
这是通过网格搜索找到的具有最佳超参数的knn模型性能。
可以使用以下命令访问训练的AutoTuner对象五折交叉验证的结果:
> as.data.table(rr)$learner[[1]]$tuning_result
## k distance learner_param_vals x_domain classif.ce
## 1: 7 1 <list[3]> <list[2]> 0.2422222
网友评论