-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathKNN_tidymodels.Rmd
More file actions
195 lines (127 loc) · 5.77 KB
/
KNN_tidymodels.Rmd
File metadata and controls
195 lines (127 loc) · 5.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
---
title: "KNN_tidymoodels"
author: "liuc"
date: '2022-05-26'
output: pdf_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
## KNN
> https://mp.weixin.qq.com/s/Qqal3Afl8AoY_yxT8V8VOw
依旧使用隔壁prostat的数据集,可以看出大部分`tidymodels`的操作逻辑和`scikit-learn`一样具有模式化,这也是其的主要目的,虽然具体模型在parameter等方面不同,不过大部分还是一致的,建模、模型评估、在测试集的评估参数等总是需要的。
k-近邻算法(k-Nearest Neighbour algorithm)的工作原理:给定一个已知标签类别的训练数据集, 输入没有标签的新数据后,在训练数据集中找到与新数据最邻近的 k 个实例,如果这k 个实例的多数属于某个类别, 那么新数据就属于这个类别。即由那些离新数据最近的 k 个实例来投票决定新数据归为哪一类。
1. 计算已知类别数据集中的点与当前点之间的距离;
2. 按照距离递增次序排序;
3. 选取与当前点距离最小的 k 个点;
4. 确定前 k 个点所在类别的出现频率;
5. 返回前 k 个点出现频率最高的类别作为当前点的预测类别。
从其的定义中似乎可以看到模型中包含训练数据集?还有两个需要注意的点,最近邻是如何定义计算的?K值如何确定?如果K值等于训练集中所有点,那么模型就极端的简化为只有一种结果了,不论输入什么数据,其的分类都是训练集中点最多的那个分类。定义中所说的最近邻又是如何度量的呢?几种常见的距离度量的方法:欧式距离、Minkowski距离、曼哈顿距离。
kNN算法没有显式的训练过程,而是在训练阶段把样本保存起来,待接受到测试样本后在进行处理。这类方法称为懒惰学习(lazy learning)以区别于通常的急切学习(eager learning)。
kd树的结构.
*knn的超参数:*neighbors(K的数目),weight_func(), dist_power(在kknn包中似乎只提供了Minkowski distance)。
K值一般取值在3-10之间,一种常见的做法是设置K为训练集中case数目的平方根(不过K值不能选的太大)。K是否有必要设置成一个奇数呢?
*preprocessing steps before fitting an KNN: *
*knn中数据的输入和处理:*特征归一化的必要性。归一化的目的在于让每一个变量具有同等的重要性,不过还是赋予权重更合理(需要注意的是,为了不让测试集数据影响到模型,先划分训练集和测试集,再进行归一化)。
KNN算法对于高维数据而言,计算距离的时间复杂度会很高,可以合理的选择降维方法。KNN算法对于离群点敏感,需要进行异常值检测和处理。对于样本不均衡的情况也较为敏感。
#### 1. 加载需要的包
```{r}
library(tidyverse)
library(tidymodels)
library(usemodels)
```
#### 2. 整理输入数据并考虑好变量所要进行的操作
```{r}
# 参考SVM数据集
usemodels::use_kknn(formula = class ~ ., data = prostat_train)
```
#### 3. CV+GS 得到一系列表现的模型
一个72个样本、9300个变量的数据集,竟然跑了如此之久!Don't run it on your Mac!!!在服务器10个进程大概40分钟。
```{r}
kknn_recipe <-
recipe(formula = class ~ ., data = prostat_train) %>%
step_zv(all_predictors()) %>%
step_normalize(all_numeric_predictors())
kknn_spec <-
nearest_neighbor(neighbors = tune(), weight_func = tune()) %>%
set_mode("classification") %>%
set_engine("kknn")
kknn_workflow <-
workflow() %>%
add_recipe(kknn_recipe) %>%
add_model(kknn_spec)
doParallel::registerDoParallel()
prostat_folds <- vfold_cv(prostat_train, strata = class)
metrics = metric_set(accuracy, roc_auc, sens, spec)
```
```{r, eval=FALSE}
# 已经运行结束,直接load
set.seed(42)
kknn_tune <-
tune_grid(kknn_workflow,
resamples = prostat_folds,
grid = 20 # 生成20个网格搜索的结果
)
kknn_tune
```
在tune完多个模型参数后,统一的查看其的metrics以进一步确定所需要的模型参数。
```{r}
kknn_tune <- readRDS('./datasets/kknn_tune.rds')
kknn_tune %>%
collect_metrics()
```
*choose the best model*
依据roc-auc选择表现最佳的模型
```{r}
show_best(kknn_tune)
best_auc <- select_best(kknn_tune, "roc_auc")
final_mod <- finalize_model(
kknn_spec,
best_auc
)
final_mod
```
然后用得到的最佳参数重新拟合模型.
```{r}
final_wf <- workflow() %>% add_model(final_mod) %>%
add_formula(class ~ .)
# 此处的fit是否需要呢, final_res$.workflow[[1]]应该就是在训练集上的最后的模型
final_fit <- final_wf %>% fit(prostat_train)
```
```{r}
# last_fit在训练集拟合,在测试集verify performance
final_res <- final_wf %>%
last_fit(split = df_split)
final_res %>%
collect_metrics()
```
模型评价参数:
包括混淆矩阵和灵敏度、特异性等指标,不仅在测试集数据上需要,在训练集数据上也需要进行展示
```{r}
# confusion matrix
final_res %>%
collect_predictions() %>%
conf_mat(class, .pred_class)
# auc-roc
final_res %>%
collect_predictions() %>%
roc_curve(class, .pred_0)
final_res %>%
collect_predictions() %>%
roc_auc(class, .pred_0)
```
验证是否一致, final_fit & final_res$.workflow[[1]]是否是同一个对象。
初步验证是一样的。
```{r}
cancer_pred <- predict(final_fit, new_data = prostat_test) %>%
bind_cols(predict(final_fit, prostat_test, type = "prob")) %>%
bind_cols(prostat_test %>% select(class))
cancer_pred2 <- predict(final_res$.workflow[[1]], new_data = prostat_test) %>%
bind_cols(predict(final_res$.workflow[[1]], prostat_test, type = "prob")) %>%
bind_cols(prostat_test %>% select(class))
cancer_pred
cancer_pred2
```
保存模型:
```{r}
```