机器学习C10笔记: kNN推荐系统

来源:互联网 发布:kofi2012知乎 编辑:程序博客网 时间:2024/06/11 18:46

KNN简介

来自百度百科
邻近算法,或者说K最近邻(kNN,k-NearestNeighbor)分类算法是数据挖掘分类技术中最简单的方法之一。所谓K最近邻,就是k个最近的邻居的意思,说的是每个样本都可以用它最接近的k个邻居来代表。
kNN算法的核心思想是如果一个样本在特征空间中的k个最相邻的样本中的大多数属于某一个类别,则该样本也属于这个类别,并具有这个类别上样本的特性。该方法在确定分类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。 kNN方法在类别决策时,只与极少量的相邻样本有关。由于kNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,kNN方法较其他方法更为适合。

优点

  1. 简单,易于理解,易于实现,无需估计参数,无需训练;
  2. 适合对稀有事件进行分类;
  3. 特别适合于多分类问题(multi-modal,对象具有多个类别标签), kNN比SVM的表现要好。

缺点

  1. 该算法在分类时有个主要的不足是,当样本不平衡时,如一个类的样本容量很大,而其他类样本容量很小时,有可能导致当输入一个新样本时,该样本的K个邻居中大容量类的样本占多数。 该算法只计算“最近的”邻居样本,某一类的样本数量很大,那么或者这类样本并不接近目标样本,或者这类样本很靠近目标样本。无论怎样,数量并不能影响运行结果。
  2. 该方法的另一个不足之处是计算量较大,因为对每一个待分类的文本都要计算它到全体已知样本的距离,才能求得它的K个最近邻点。
    可理解性差,无法给出像决策树那样的规则。

算例

读取数据

# First code snippetdf <- read.csv(file.path('data', 'example_data.csv'))head(df)# X Y Label#1 2.373546 5.398106 0#2 3.183643 4.387974 0#3 2.164371 5.341120 0#4 4.595281 3.870637 0#5 3.329508 6.433024 0#6 2.179532 6.980400 0

计算每个样本间的欧氏距离

# Second code snippetdistance.matrix <- function(df){  distance <- matrix(rep(NA, nrow(df) ^ 2), nrow = nrow(df))  for (i in 1:nrow(df))  {    for (j in 1:nrow(df))    {      distance[i, j] <- sqrt((df[i, 'X'] - df[j, 'X']) ^ 2 + (df[i, 'Y'] - df[j, 'Y']) ^ 2)    }  }  return(distance)}

按照距离排序

# Third code snippetk.nearest.neighbors <- function(i, distance, k = 5){  return(order(distance[i, ])[2:(k + 1)])#返回与i最近的5个样本距离}

knn

# Fourth code snippetknn <- function(df, k = 5){  distance <- distance.matrix(df)  predictions <- rep(NA, nrow(df))  for (i in 1:nrow(df))  {    indices <- k.nearest.neighbors(i, distance, k = k)    predictions[i] <- ifelse(mean(df[indices, 'Label']) > 0.5, 1, 0)#按照label数多少分类  }  return(predictions)}

分类效果比较

# Fifth code snippetdf <- transform(df, kNNPredictions = knn(df))sum(with(df, Label != kNNPredictions))#[1] 7nrow(df)#[1] 100

用glm构建线性判别模型

library('class')df <- read.csv(file.path('data', 'example_data.csv'))n <- nrow(df)set.seed(1)indices <- sort(sample(1:n, n * (1 / 2)))training.x <- df[indices, 1:2]test.x <- df[-indices, 1:2]training.y <- df[indices, 3]test.y <- df[-indices, 3]# There's a bug here!predicted.y <- knn(training.x, test.x, training.y, k = 5)sum(predicted.y != test.y)#[1] 7length(test.y)#[1] 50# Seventh code snippetlogit.model <- glm(Label ~ X + Y, data = df[indices, ])predictions <- as.numeric(predict(logit.model, newdata = df[-indices, ]) > 0)sum(predictions != test.y)

误差16大于knn的7

sum(predictions != test.y)[1] 16

构建简单的推荐系统

利用kNN进行推荐的方法分为:
1. 基于物品(item-based)的方法: 为目标用户推荐那些与他已经喜欢的物品相似的物品, 这种方法为基于物品(item-based)方法.
2. 基于用户(user-based)的方法: 先利用kNN算法找到与目标用户品味比较相同的用户,然后根据这些品味相近的用户的喜好来为目标用户进行推荐.
两种方法适用于不同的场景, 当用户比物品多时,使用基于物品的推荐方法会节省许多的计算时间与存储空间. 反之,使用另外一种方法.  

若想要构建大型的推荐系统需要将kNN,矩阵分解和其他分类器结合起来. 即集成方法(ensemble method)
可参考:矩阵分解在推荐系统中的应用

接下来的例子是,r程序包推荐.有50个用户安装程序包的信息,利用此信息进行推荐.

dataclean

# Eighth code snippetinstallations <- read.csv(file.path('data', 'installations.csv'))head(installations)# Package User Installed#1 abind 1 1#2 AcceptanceSampling 1 0#3 ACCLMA 1 0#4 accuracy 1 1#5 acepack 1 0#6 aCGH.Spline 1 0# Ninth code snippetlibrary('reshape')# 利用cast对矩阵reshape, user为行package为列user.package.matrix <- cast(installations, User ~ Package, value = 'Installed')user.package.matrix[, 1]# 第一列是user# [1] 1 3 4 5 6 7 8 9 11 13 14 15 16 19 21 23 25 26 27 28 29 30 31 33 34#[26] 35 36 37 40 41 42 43 44 45 46 47 48 49 50 51 54 55 56 57 58 59 60 61 62 63#[51] 64 65user.package.matrix[, 2]#其他列是不同的包是否安装# [1] 1 1 0 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 0 0 1 1 1 1 1 1 1 0 1 0 1 0 1 1 1 1 1 1#[39] 1 1 1 1 1 1 1 1 0 1 1 1 1 1#将user列编程行名并去掉此列row.names(user.package.matrix) <- user.package.matrix[, 1]user.package.matrix <- user.package.matrix[, -1]

reshape后的数据:

> head(user.package.matrix[,1:5])  User abind AcceptanceSampling ACCLMA accuracy1    1     1                  0      0        12    3     1                  1      0        13    4     0                  1      1        14    5     1                  1      1        05    6     1                  1      1        06    7     1                  1      1        0

用列之间的相似度衡量包之间的相似度cor

similarities <- cor(user.package.matrix)nrow(similarities)#[1] 2487ncol(similarities)#[1] 2487similarities[1, 1]#[1] 1similarities[1, 2]#[1] -0.04822428

我们得到了不同包之间的相似度,但是我们需要把相似度转换成距离,利用以下公式转换:

distances <- -log((similarities / 2) + 0.5)```

knn推荐

找到与i包最相似的前25个包

# Twelfth code snippetk.nearest.neighbors <- function(i, distances, k = 25){  return(order(distances[i, ])[2:(k + 1)])}

计算一个用户安装一个包的概率

# Thirteenth code snippetinstallation.probability <- function(user, package, user.package.matrix, distances, k = 25){  neighbors <- k.nearest.neighbors(package, distances, k = k)  return(mean(sapply(neighbors, function (neighbor) {user.package.matrix[user, neighbor]})))#计算一个用户安装一个包的概率}
installation.probability(1, 1, user.package.matrix, distances)#[1] 0.76

有0.76的概率安装包1.

按照概率最大值为用户推荐包

most.probable.packages <- function(user, user.package.matrix, distances, k = 25){  return(order(sapply(1:ncol(user.package.matrix),               function (package)               {                 installation.probability(user,                                          package,                                          user.package.matrix,                                          distances,                                          k = k)               }),         decreasing = TRUE))}user <- 1listing <- most.probable.packages(user, user.package.matrix, distances)colnames(user.package.matrix)[listing[1:10]]
> colnames(user.package.matrix)[listing[1:10]] [1] "adegenet"            "AIGIS"               "ConvergenceConcepts" [4] "corcounts"           "DBI"                 "DSpat"               [7] "ecodist"             "eiPack"              "envelope"           [10] "fBasics"  

概率最大的是adegenet包。

0 0
原创粉丝点击