【问题标题】:Implementing KNN with different distance metrics using R使用 R 实现具有不同距离度量的 KNN
【发布时间】:2018-05-19 18:05:25
【问题描述】:

我正在处理一个数据集,以便比较不同距离指标的效果。我正在使用 KNN 算法。

R 中的 KNN 算法默认使用欧几里得距离。所以我自己写了一篇。我想找到最近邻居和目标之间正确的类标签匹配的数量。

我首先准备了数据。然后我调用了数据(wdbc_n),我选择了 K=1。我用欧几里得距离作为测试。

library(philentropy)
knn <- function(xmat, k,method){
  n <- nrow(xmat)
  if (n <= k) stop("k can not be more than n-1")
  neigh <- matrix(0, nrow = n, ncol = k)
  for(i in 1:n) {
    ddist<- distance(xmat, method)  
    neigh[i, ] <- order(ddist)[2:(k + 1)]
  }
  return(neigh)
}
wdbc_nn <-knn(wdbc_n ,1,method="euclidean")

希望得到与论文类似的结果(“关于高维空间中距离度量的令人惊讶的行为”)(https://bib.dbvis.de/uploadedFiles/155.pdf,第 431 页,表 3)。

我的问题是

我的代码是对还是错?

任何可以指导我的建议或参考将不胜感激。

编辑

我的数据 (breast-cancer-wisconsin)(wdbc) 维度是

569  32

在规范化和删除id和目标列之后,维度是

dim(wdbc_n)
569  30

训练和测试的划分由

给出
wdbc_train<-wdbc_n[1:469,]
wdbc_test<-wdbc_n[470:569,]

【问题讨论】:

  • 您引用的论文说欧几里得会提供您通常最差的结果,并且似乎确实是提供 46 种其他距离度量的 philentropy 包的重点。你的函数编译没有抱怨,我知道 k=1,method = "euclidean"。那么你的“xmat”是什么?
  • @Chris,我的xmat是'wdbc_n',就是我准备好的数据矩阵
  • 您的数据是什么,您的 wdbc_n (xmat) 的尺寸是多少?是不是那种你对结果可能看起来有直觉的数据,合理的。
  • @Chris,我已经编辑了我的问题。

标签: r knn nearest-neighbor


【解决方案1】:

我的代码是对还是错?

你的代码错了。

在我最近的 PC 上每次调用距离函数大约需要 3 秒,所以我只为 k=3 做了前 30 行,并注意到邻矩阵的每一行都是相同的。这是为什么?看看这一行:

ddist<- distance(xmat, method)  

每个循环在距离函数处输入 整个 xmat 矩阵,然后仅使用结果矩阵的第一行。这会计算训练集行之间的距离,并执行 n 次,丢弃除第一行之外的每一行。这不是你想做的。 knn算法应该计算测试集中每一行与训练集中每一行的距离。

我们看一下距离函数的文档:

距离(x,方法 =“欧几里得”,p = NULL,test.na = TRUE,单位 = "日志", est.prob = NULL)

x 数字 data.frame 或矩阵(存储概率向量)或 数字 data.frame 或矩阵存储计数(如果 est.prob 是 指定)。

(...)

如果 nrow(x) = 2 :单个距离值。如果 nrow(x) > 2 : 存储所有成对概率的距离值的距离矩阵 向量比较。

在您的特定情况下(knn 分类),您希望使用 2 行版本。

最后一件事:您使用了 order,它将返回 ddist 向量中 k 个最大距离的位置。我认为你想要的是距离本身,所以你需要使用 sort 而不是 order

根据您的代码和您的代码似乎基于的 Lantz (2013) 中的示例,这是一个完整的工作解决方案。我冒昧地添加了几行来制作一个独立的程序。

独立工作解决方案

library(philentropy)
normalize <- function(x) {
 return ((x - min(x)) / (max(x) - min(x)))
}

knn <- function(train, test, k, method){
  n.test <- nrow(test)
  n.train <- nrow(train)
  if (n.train + n.test <= k) stop("k can not be more than n-1")
  neigh <- matrix(0, nrow = n.test, ncol = k) 
  ddist <- NULL
  for(i in 1:n.test) {
    for(j in 1:n.train) {
      xmat <- rbind(test[i,], train[j,]) #we make a 2 row matrix combining the current test and train rows
      ddist[j] <- distance(as.data.frame(xmat), method, k)  #then we calculate the distance and append it to the ddist vector.
    }
    neigh[i, ] <- sort(ddist)[2:(k + 1)] 
  }
  return(neigh)
}

wbcd <- read.csv("https://resources.oreilly.com/examples/9781784393908/raw/ac9fe41596dd42fc3877cfa8ed410dd346c43548/Machine%20Learning%20with%20R,%20Second%20Edition_Code/Chapter%2003/wisc_bc_data.csv")
rownames(wbcd) <- wbcd$id
wbcd$id <- NULL
wbcd_n <- as.data.frame(lapply(wbcd[2:31], normalize))

wbcd_train<-wbcd_n[1:469,]
wbcd_test<-wbcd_n[470:549,]
wbcd_nn <-knn(wbcd_train, wbcd_test ,3, method="euclidean")

请注意,由于对 distance 函数的调用次数众多(100 次 469),此解决方案可能会很慢。但是,由于我们一次只将 2 行输入到距离函数中,因此执行时间是可控的。

现在可以了吗?

使用自定义 knn 函数的前两个测试行:

          [,1]      [,2]      [,3]
[1,] 0.3887346 0.4051762 0.4397497
[2,] 0.2518766 0.2758161 0.2790369

让我们与FNN包中的等效函数进行比较:

library(FNN)
alt.class <- get.knnx(wbcd_train, wbcd_test, k=3, algorithm = "brute")
alt.class$nn.dist

          [,1]      [,2]      [,3]
[1,] 0.3815984 0.3887346 0.4051762
[2,] 0.2392102 0.2518766 0.2758161

结论:还不算太寒酸。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-06
    • 1970-01-01
    • 2020-07-14
    • 2018-06-11
    相关资源
    最近更新 更多