【发布时间】:2020-10-28 17:54:47
【问题描述】:
(已根据 Julia 的回复在最后更新。TL;DR:这似乎是底层 kknn 包的问题,而不是 tidymodels)
我正在使用 tidymodels 做一些 k-最近邻回归模型。这是通过nearest_neighbor() 函数实现的。我想看看有没有对特征进行归一化的结果有什么区别。
现在set_engine("kknn") 在底层使用kknn::train.kknn() 函数,它有一个标准化参数scale = TRUE。我想将模型与scale = FALSE 与scale = TRUE 进行比较(实际上,我想在配方中这样做,但这是不可能的,我将在下面解释)。
但我似乎无法通过 tidymodels 可靠地设置 scale = FALSE。下面是一个代表我看到的内容。
问题这么久:我做错了什么还是这是一个错误?如果它是一个错误,它是否已知并且我可以在某处阅读它吗?如果有人能阐明这一点,我将不胜感激。
设置代表
这里我将使用mtcars:
library(tidymodels)
data("mtcars")
训练测试拆分是:
set.seed(1)
mtcars_split <- initial_split(mtcars, prop = 0.7)
这是我将使用的常见食谱:
mtcars_recipe <- recipe(mpg ~ disp + wt, data = mtcars)
这是模型 1(称为knn_FALSE),其中scale = FALSE:
knn_FALSE <- nearest_neighbor(neighbors = 5) %>%
set_mode("regression") %>%
set_engine("kknn", scale = FALSE)
这是模型 2(称为knn_TRUE),其中scale = TRUE:
knn_TRUE <- nearest_neighbor(neighbors = 5) %>%
set_mode("regression") %>%
set_engine("kknn", scale = TRUE)
我将这两个模型捆绑到两个工作流程中:
## Workflow with scale = FALSE
wf_FALSE <- workflow() %>%
add_model(knn_FALSE) %>%
add_recipe(mtcars_recipe)
## Worflow with scale = TRUE
wf_TRUE <- workflow() %>%
add_model(knn_TRUE) %>%
add_recipe(mtcars_recipe)
使用fit(),可以有scale = FALSE
在工作流中使用fit() 时,似乎可以有一个带有scale = TRUE 的版本和一个带有scale = FALSE 的版本。
例如,对于scale = TRUE,我得到:
wf_TRUE %>% fit(mtcars)
== Workflow [trained] ===============================================================================================
Preprocessor: Recipe
Model: nearest_neighbor()
-- Preprocessor -----------------------------------------------------------------------------------------------------
0 Recipe Steps
-- Model ------------------------------------------------------------------------------------------------------------
Call:
kknn::train.kknn(formula = ..y ~ ., data = data, ks = ~5, scale = ~TRUE)
Type of response variable: continuous
minimal mean absolute error: 2.09425
Minimal mean squared error: 7.219114
Best kernel: optimal
Best k: 5
而对于scale = FALSE 我有:
wf_FALSE %>% fit(mtcars)
== Workflow [trained] ===============================================================================================
Preprocessor: Recipe
Model: nearest_neighbor()
-- Preprocessor -----------------------------------------------------------------------------------------------------
0 Recipe Steps
-- Model ------------------------------------------------------------------------------------------------------------
Call:
kknn::train.kknn(formula = ..y ~ ., data = data, ks = ~5, scale = ~FALSE)
Type of response variable: continuous
minimal mean absolute error: 2.1665
Minimal mean squared error: 6.538769
Best kernel: optimal
Best k: 5
结果明显不同,这来自于scale参数的不同。
但情节变厚了。
与last_fit()没有区别
但是,当使用 last_fit() 时,scale = TRUE 和 scale = FALSE 的结果是相同的。
对于scale = TRUE:
wf_TRUE %>% last_fit(mtcars_split) %>% collect_metrics()
# A tibble: 2 x 3
.metric .estimator .estimate
<chr> <chr> <dbl>
1 rmse standard 3.16
2 rsq standard 0.663
而对于scale = FALSE:
wf_FALSE %>% last_fit(mtcars_split) %>% collect_metrics()
# A tibble: 2 x 3
.metric .estimator .estimate
<chr> <chr> <dbl>
1 rmse standard 3.16
2 rsq standard 0.663
这些显然 --- 出乎意料地 --- 相同。
使用tune_grid()调优也没有区别
如果我使用tune_grid() 和validation_split() 进行调优,scale = TRUE 和scale = FALSE 的结果也没有区别。
下面是代码:
## Tune grid
knn_grid <- tibble(neighbors = c(5, 15))
## Tune Model 1: kNN regresson with no scaling in train.kknn
knn_FALSE_tune <- nearest_neighbor(neighbors = tune()) %>%
set_mode("regression") %>%
set_engine("kknn", scale = FALSE)
## Model 2: kNN regresson with scaling in train.kknn
knn_TRUE_tune <- nearest_neighbor(neighbors = tune()) %>%
set_mode("regression") %>%
set_engine("kknn", scale = TRUE)
## Workflow with scale = FALSE
wf_FALSE_tune <- workflow() %>%
add_model(knn_FALSE_tune) %>%
add_recipe(mtcars_recipe)
## Worflow with scale = TRUE
wf_TRUE_tune <- workflow() %>%
add_model(knn_TRUE_tune) %>%
add_recipe(mtcars_recipe)
## Validation split
mtcars_val <- validation_split(mtcars)
## Tune results: Without scaling
wf_FALSE_tune %>%
tune_grid(resamples = mtcars_val,
grid = knn_grid) %>%
collect_metrics()
## Tune results: With scaling
wf_TRUE_tune %>%
tune_grid(resamples = mtcars_val,
grid = knn_grid) %>%
collect_metrics()
scale = FALSE时的结果:
> wf_FALSE_tune %>%
+ tune_grid(resamples = mtcars_val,
+ grid = knn_grid) %>%
+ collect_metrics()
# A tibble: 4 x 7
neighbors .metric .estimator mean n std_err .config
<dbl> <chr> <chr> <dbl> <int> <dbl> <chr>
1 5 rmse standard 1.64 1 NA Model1
2 5 rsq standard 0.920 1 NA Model1
3 15 rmse standard 2.55 1 NA Model2
4 15 rsq standard 0.956 1 NA Model2
scale = TRUE时的结果:
> wf_TRUE_tune %>%
+ tune_grid(resamples = mtcars_val,
+ grid = knn_grid) %>%
+ collect_metrics()
# A tibble: 4 x 7
neighbors .metric .estimator mean n std_err .config
<dbl> <chr> <chr> <dbl> <int> <dbl> <chr>
1 5 rmse standard 1.64 1 NA Model1
2 5 rsq standard 0.920 1 NA Model1
3 15 rmse standard 2.55 1 NA Model2
4 15 rsq standard 0.956 1 NA Model2
问题
是我误解(或遗漏了我自己的错误),还是 last_fit() 和 tune_grid() 函数不尊重我对 scale 的选择?
我是 tidymodels 的新手,所以我可能错过了一些东西。非常感谢您的回答。
我希望在配方中使用step_normalize() 自己进行规范化,但由于我无法在底层引擎中可靠地设置scale = FALSE,因此我无法对此进行试验。
朱莉娅回复后更新
正如 Julia 所示,来自train.kknn() 的预测为scale = FALSE 和scale = TRUE 提供相同的预测。所以这不是 tidymodels 问题。而是 kknn:::predict.train.kknn() 函数在预测时不尊重传递给 train.kknn() 的所有参数。
考虑以下使用kknn() 而不是train.kknn() 的输出:
kknn::kknn(formula = mpg ~ disp + wt, train = training(mtcars_split),
test = testing(mtcars_split), k = 5, scale = FALSE) %>%
predict(newdata = testing(mtcars_split))
## [1] 21.276 21.276 16.860 16.276 21.276 16.404 29.680 15.700 16.020
kknn::kknn(formula = mpg ~ disp + wt, train = training(mtcars_split),
test = testing(mtcars_split), k = 5, scale = TRUE) %>%
predict(newdata = testing(mtcars_split))
## [1] 21.032 21.784 16.668 16.052 21.264 16.404 26.340 16.076 15.620
它们应该是不同的。问题是kknn:::predict.train.kknn() 调用kknn(),但没有传递scale(和其他一些可选参数):
function (object, newdata, ...)
{
if (missing(newdata))
return(predict(object, ...))
res <- kknn(formula(terms(object)), object$data, newdata,
k = object$best.parameters$k, kernel = object$best.parameters$kernel,
distance = object$distance)
return(predict(res, ...))
}
<bytecode: 0x55e2304fba10>
<environment: namespace:kknn>
【问题讨论】:
-
我已经在 Github 上向 kknn 维护者报告了这个问题:github.com/KlausVigo/kknn/issues/22
标签: r tidymodels