R 推荐系统构建指南(二)

原文:annas-archive.org/md5/5643d791b5330511d9d6cee88149f2fc

译者:飞龙

协议:CC BY-NC-SA 4.0

第五章. 案例研究 – 构建您自己的推荐引擎

前两章展示了如何使用 R 语言构建、测试和优化推荐系统。尽管这些章节充满了示例,但它们基于 R 包提供的数据集。数据使用 redyal 结构化,并已准备好处理。然而,在现实生活中,数据准备是一个重要、耗时且困难的步骤。

之前示例的另一个局限性是它们仅基于评分。在大多数情况下,还有其他数据源,如项目描述和用户资料。一个很好的解决方案是结合所有相关信息。

本章将展示一个实际示例,我们将从原始数据开始构建和优化推荐系统。本章将涵盖以下主题:

  • 准备数据以构建推荐引擎

  • 通过可视化技术探索数据

  • 选择和构建推荐模型

  • 通过设置其参数来优化推荐模型的性能

最后,我们将构建一个生成推荐的引擎。

准备数据

从原始数据开始,本节将向您展示如何为推荐模型准备输入。

数据描述

数据是关于一周内访问网站的微软用户。对于每个用户,数据显示了用户访问的区域。为了简化,从现在起我们将使用“项目”一词来指代网站区域。

有 5,000 个用户,它们由 10,001 到 15,000 之间的连续数字表示。项目由 1,000 到 1,297 之间的数字表示,即使它们小于 298。

数据集是一个未结构化的文本文件。每个记录包含 2 到 6 个字段。第一个字段是一个字母,定义了记录包含的内容。主要有三种类型的记录,如下所示:

  • 属性 (A):这是网站区域的描述

  • 案例 ©:这是每个用户的案例,包含其 ID

  • 投票 (V):这是案例的投票行

每个案例记录后面跟着一个或多个投票,每个用户只有一个案例。

我们的目标是为每个用户推荐他们尚未探索的网站区域。

导入数据

本节将向您展示如何导入数据。首先,让我们加载我们将要使用的包:

library("data.table")
library("ggplot2")
library("recommenderlab")
library("countrycode")

以下代码将在以下要点中解释:

  • data.table:这个包用于处理数据

  • ggplot2:这个包用于制作图表

  • recommenderlab:这个包用于构建推荐引擎

  • countrycode:这个包包含国家名称

然后,让我们将表格加载到内存中。如果文本文件已经在我们的工作目录中,只需定义其名称即可。否则,我们需要定义其完整路径:

file_in <- "anonymous-msweb.test.txt"

行包含不同数量的列,这意味着数据是无结构的。然而,最多有六列,因此我们可以使用 read.csv 将文件加载到表格中。包含少于六个字段的行将只有空值:

table_in <- read.csv(file_in, header = FALSE)
head(table_in)
V1V2V3V4V5V6
I4www.microsoft.comcreated by getlog.pl
T1VRoot00VRoot
N00
N11
T2Hide100Hide
N00

前两列包含用户 ID 及其购买记录。我们可以简单地删除其他列:

table_users <- table_in[, 1:2]

为了更轻松地处理数据,我们可以将其转换为数据表,使用以下命令:

table_users <- data.table(table_users)

列如下:

  • 分类: 这是一个指定列内容的字母。包含用户或项目 ID 的列分别属于 C 和 V 分类。

  • : 这是一个指定用户或项目 ID 的数字。

我们可以分配列名并选择包含用户或项目的行:

setnames(table_users, 1:2, c("category", "value"))
table_users <- table_users[category %in% c("C", "V")]
head(table_users)

分类
C10001
V1038
V1026
V1034
C10002
V1008

table_users 对象包含结构化数据,这是我们定义评分矩阵的起点。

定义评分矩阵

我们的目标是定义一个表格,其中每一行代表一个项目,每一列代表一次购买。对于每个用户,table_users 包含其 ID 和购买记录分别在不同的行中。在每个块或行中,第一列包含用户 ID,其他列包含项目 ID。

您可以使用以下步骤来定义一个评分矩阵:

  1. 标记案例。

  2. 定义一个长格式表格。

  3. 定义一个宽格式表格。

  4. 定义评分矩阵。

为了重塑表格,第一步是定义一个名为 chunk_user 的字段,为每个用户包含一个递增的数字。当用户行是块的第一行时,category == "C" 条件为真。使用 cumsum,每当有新用户的行时,我们就在索引 1 上递增:

table_users[, chunk_user := cumsum(category == "C")]
head(table_users)

分类chunk_user
C100011
V10381
V10261
V10341
C100022
V10082

下一步是定义一个表格,其中行对应购买。我们需要一个包含用户 ID 的列和一个包含项目 ID 的列。新表格称为 table_long,因为它处于 格式:

table_long <- table_users[, list(user = value[1], item = value[-1]), by = "chunk_user"]
head(table_long)

chunk_useruseritem
1100011038
1100011026
1100011034
2100021008
2100021056
2100021032

现在,我们可以定义一个表格,其中每一行代表一个用户,每一列代表一个项目。如果项目已被购买,则值为 1,否则为 0。我们可以使用 reshape 函数来构建表格。其输入如下:

  • data:这是long格式的表。

  • direction:这显示了我们是将数据从长格式转换为宽格式还是其他方式。

  • idvar:这是标识组的变量,在这种情况下,是用户。

  • timevar:这是标识同一组内记录的变量。在这种情况下,它是项目。

  • v.names:这是值的名称。在这种情况下,它是始终等于一的评分。缺失的用户-项目组合将是 NA 值。

在将列value设置为1后,我们可以使用reshape构建table_wide

table_long[, value := 1]
table_wide <- reshape(data = table_long,direction = "wide",idvar = "user",timevar = "item",v.names = "value")
head(table_wide[, 1:5, with = FALSE])

chunk_useruservalue.1038value.1026value.1034
110001111
210002NANANA
31000311NA
410004NANANA
510005111
610006NANA1

为了构建评分矩阵,我们需要只保留包含评分的列。此外,用户名将是矩阵的行名,因此我们需要将它们存储在vector_users向量中:

vector_users <- table_wide[, user]
table_wide[, user := NULL]
table_wide[, chunk_user := NULL]

为了使列名与项目名相等,我们需要从value前缀中获取。为此,我们可以使用substring函数:

setnames(x = table_wide,old = names(table_wide),new = substring(names(table_wide), 7))

我们需要在recommenderlab对象中存储评分矩阵。为此,我们首先需要将table_wide转换为矩阵。此外,我们还需要将行名设置为用户名:

matrix_wide <- as.matrix(table_wide)rownames(matrix_wide) <- vector_users
head(matrix_wide[, 1:6])

| user | 1038 | 1026 | 1034 | 1008 | 1056 | 1032 |
| — | — | — | — | — |
| 10001 | 1 | 1 | 1 | NA | NA | NA |
| 10002 | NA | NA | NA | 1 | 1 | 1 |
| 10003 | 1 | 1 | NA | NA | NA | NA |
| 10004 | NA | NA | NA | NA | NA | NA |
| 10005 | 1 | 1 | 1 | 1 | NA | NA |
| 10006 | NA | NA | 1 | NA | NA | NA |

最后一步是使用asmatrix_wide强制转换为二进制评分矩阵,如下所示:

matrix_wide[is.na(matrix_wide)] <- 0
ratings_matrix <- as(matrix_wide, "binaryRatingMatrix")
ratings_matrix
## 5000 x 236 rating matrix of class binaryRatingMatrix with 15191 ratings.

让我们使用image查看矩阵:

image(ratings_matrix[1:50, 1:50], main = "Binary rating matrix")

下图显示了二进制评分矩阵:

https://github.com/OpenDocCN/freelearn-ml-zh/raw/master/docs/bd-recsys-r/img/B03888_05_01.jpg

如预期的那样,矩阵是稀疏的。我们还可以可视化购买某个物品的用户数量的分布:

n_users <- colCounts(ratings_matrix)
qplot(n_users) + stat_bin(binwidth = 100) + ggtitle("Distribution of the number of users")

下图显示了用户数量的分布:

https://github.com/OpenDocCN/freelearn-ml-zh/raw/master/docs/bd-recsys-r/img/B03888_05_02.jpg

有一些异常值,即被许多用户购买的物品。让我们排除它们来可视化分布:

qplot(n_users[n_users < 100]) + stat_bin(binwidth = 10) + ggtitle("Distribution of the number of users")

下图显示了用户数量的分布:

https://github.com/OpenDocCN/freelearn-ml-zh/raw/master/docs/bd-recsys-r/img/B03888_05_03.jpg

有许多物品只被少数用户购买,我们不会推荐它们。由于它们增加了计算时间,我们可以通过定义最小购买数量来删除它们,例如,5

ratings_matrix <- ratings_matrix[, colCounts(ratings_matrix) >= 5]
ratings_matrix
## 5000 x 166 rating matrix of class 'binaryRatingMatrix' with 15043 ratings.

现在,我们有 166 个项目,与最初的 236 个相比。至于用户,我们希望向每个人推荐项目。然而,可能有一些用户只购买了我们已经删除的项目。让我们检查一下:

sum(rowCounts(ratings_matrix) == 0)
## _15_

有 15 个用户没有购买任何东西。这些购买应该被删除。此外,只购买了几件商品的用户的处理比较困难。因此,我们只保留至少购买过五件商品的用户:

ratings_matrix <- ratings_matrix[rowCounts(ratings_matrix) >= 5, ]
ratings_matrix
## 959 x 166 rating matrix of class 'binaryRatingMatrix' with 6816 ratings

提取项目属性

table_in原始数据中包含一些以A开头的记录,并显示有关项目的一些信息。为了提取这些记录,我们可以将table_in转换为数据表,并提取第一列包含A的行:

table_in <- data.table(table_in)
table_items <- table_in[V1 == "A"]
head(table_items)

V1V2V3V4V5
A12771NetShow for PowerPoint/stream
A12531MS Word Development/worddev
A11091TechNet (World Wide Web Edition)/technet
A10381SiteBuilder Network Membership/sbnmember
A12051Hardware Supprt/hardwaresupport
A10761NT Workstation Support/ntwkssupport

相关的列是:

  • V2: 项目 ID

  • V4: 项目描述

  • V5: 网页 URL

为了使表格更清晰,我们可以提取并重命名它们。此外,我们可以按项目 ID 对表格进行排序:

table_items <- table_items[, c(2, 4, 5), with = FALSE]
setnames(table_items, 1:3, c("id", "description", "url"))
table_items <- table_items[order(id)]
head(table_items)

iddescriptionurl
1000regwiz/regwiz
1001Support desktop/support
1002End user produced view/athome
1003Knowledge base/kb
1004Microsoft.com search/search
1005Norway/norge

我们需要识别一个或多个描述项目的特征。如果我们看一下表格,我们可以识别出两种网页类别:

  • 微软产品

  • 地理位置信息

我们可以识别包含地理位置信息的记录,并将剩余的视为产品。为此,我们可以开始定义字段category,目前,对于所有记录,该字段等于product

table_items[, category := "product"]

国家代码包为我们提供了包含大多数国家名称的countrycode_data对象。我们可以定义一个包含国家和地理位置名称的name_countries向量。然后,我们可以将所有描述在name_countries中的记录分类为region

name_countries <- c(countrycode_data$country.name, "Taiwan", "UK", "Russia", "Venezuela", "Slovenija", "Caribbean", "Netherlands (Holland)", "Europe", "Central America", "MS North Africa")
table_items[description %in% name_countries, category := "region"]

有其他记录包含单词region。我们可以通过使用grepl的正则表达式来识别它们:

table_items[grepl("Region", description), category := "region"]
head(table_items)

V2descriptionurlcategory
1000regwiz/regwizproduct
1001Support Desktop/supportproduct
1002End User Produced View/athomeproduct
1003Knowledge Base/kbproduct
1004Microsoft.com Search/searchproduct
1005Norway/norgeregion

让我们看一下结果,并找出每个类别中我们有多少个项目:

table_items[, list(n_items = .N), by = category]

categoryn_items
product248
region46

大约 80%的网页是产品,其余 20%是地区。

我们现在已准备好构建推荐模型。

构建模型

本节将向您展示如何使用商品描述和用户购买构建推荐模型。该模型结合了基于商品的协同过滤和一些关于商品的信息。我们将使用具有特征组合的单体混合系统包含商品描述。推荐器将在两个不同的阶段从两个数据源中学习。

按照第三章中描述的方法,第三章,推荐系统,让我们将数据分为训练集和测试集:

which_train <- sample(x = c(TRUE, FALSE),size = nrow(ratings_matrix),replace = TRUE,prob = c(0.8, 0.2))recc_data_train <- ratings_matrix[which_train, ]
recc_data_test <- ratings_matrix[!which_train, ]

现在,我们可以使用Recommender构建一个 IBCF 模型。由于评分矩阵是二进制的,我们将距离方法设置为Jaccard。更多详情,请参阅第三章中关于第三章,推荐系统二进制数据上的协同过滤部分。其余参数保持默认值:

recc_model <- Recommender(data = recc_data_train,method = "IBCF",parameter = list(method = "Jaccard"))

IBCF 的引擎基于一个关于商品的相似度矩阵。距离是从购买中计算出来的。相同用户购买的物品越多,它们就越相似。

我们可以从槽模型中的sim元素中提取矩阵。让我们看看它:

class(recc_model@model$sim)
## dgCMatrix
dim(recc_model@model$sim)
## _166_ and _166_

该矩阵属于dgCMatrix类,并且是方阵。我们可以使用image可视化它:

image(recc_model@model$sim)

下面的图像是前面代码的输出:

https://github.com/OpenDocCN/freelearn-ml-zh/raw/master/docs/bd-recsys-r/img/B03888_05_04.jpg

我们无法识别任何明显的模式,这是因为商品没有排序。让我们看看值的范围:

range(recc_model@model$sim)
## _0_ and _1_

所有距离都在 0 到 1 之间。

我们的目标是将距离矩阵与商品描述结合,通过以下步骤:

  1. 基于购买定义一个相似度矩阵。

  2. 基于商品描述定义一个相似度矩阵。

  3. 结合两个矩阵。

recc_model开始,我们可以定义购买相似度矩阵。我们所需做的只是将dgCMatrix对象转换为matrix

dist_ratings <- as(recc_model@model$sim, "matrix")

为了根据商品描述构建矩阵,我们可以使用dist函数。鉴于它仅基于类别列,距离将如下所示:

  • 1,如果两个商品属于同一类别

  • 0,如果两个商品属于不同类别

我们需要构建一个相似度矩阵,我们有一个距离矩阵。由于距离在 0 到 1 之间,我们可以直接使用1 - dist()。所有操作都在数据表中执行:

dist_category <- table_items[, 1 - dist(category == "product")]class(dist_category)
## dist

dist_category原始数据是一个dist对象,可以使用as()函数轻松转换为矩阵:

dist_category <- as(dist_category, "matrix")

让我们比较dist_categorydist_ratings的维度:

dim(dist_category)
## _294_ and _294_
dim(dist_ratings)
## _166_ and _166_

dist_category表有更多的行和列,原因是它包含所有商品,而dist_ratings只包含那些已经被购买的商品。

为了将dist_categorydist_ratings结合,我们需要有相同的项。此外,它们需要按相同的方式排序。我们可以使用以下步骤使用项目名称匹配它们:

  1. 确保两个矩阵的行和列名中都包含项目名称。

  2. dist_ratings中提取行和列名。

  3. 根据dist_ratings的名称子集和排序dist_category

dist_ratings表格已经包含了行和列名。我们需要从table_items开始将它们添加到dist_category中:

rownames(dist_category) <- table_items[, id]
colnames(dist_category) <- table_items[, id]

现在,我们只需要从dist_ratings中提取名称并子集dist_category

vector_items <- rownames(dist_ratings)
dist_category <- dist_category[vector_items, vector_items]

让我们检查两个矩阵是否匹配:

identical(dim(dist_category), dim(dist_ratings))
## TRUE
identical(rownames(dist_category), rownames(dist_ratings))
## TRUE
identical(colnames(dist_category), colnames(dist_ratings))
## TRUE

一切都是相同的,所以它们匹配。让我们看看dist_category

image(dist_category)

下面的图像是前面代码的输出:

https://github.com/OpenDocCN/freelearn-ml-zh/raw/master/docs/bd-recsys-r/img/B03888_05_05.jpg

矩阵只包含 0 和 1,它基于两个类别,因此有明显的模式。此外,我们可以注意到矩阵是对称的。

我们需要合并两个表,我们可以通过加权平均来实现。由于dist_category只考虑两种项目类别,因此最好不要给它太多的相关性。例如,我们可以将其权重设置为 25%:

weight_category <- 0.25
dist_tot <- dist_category * weight_category + dist_ratings * (1 - weight_category)

让我们看看使用imagedist_tot矩阵:

image(dist_tot)

下面的图像是前面代码的输出:

https://github.com/OpenDocCN/freelearn-ml-zh/raw/master/docs/bd-recsys-r/img/B03888_05_06.jpg

我们可以看到一些代表非常相似项目的白色点。此外,我们还可以在背景中看到dist_category的模式。

现在,我们可以在recc_model中包含新的矩阵。为此,将dist_tot转换为dgCMatrix并插入到recc_model中:

recc_model@model$sim <- as(dist_tot, "dgCMatrix")
recc_model@model$sim <- as(dist_tot, "dgCMatrix")

如第三章中所示,推荐系统,我们可以使用predict()来推荐项目:

n_recommended <- 10
recc_predicted <- predict(object = recc_model, newdata = recc_data_test, n = n_recommended)

recc_predicteditemLabels槽包含项目名称,即它们的代码:

head(recc_predicted@itemLabels)

1038, 1026, 1034, 1008, 1056 and 1032

为了显示项目描述,我们可以使用table_items。我们只需要确保项目按与itemLabels相同的方式排序。为此,我们将准备一个包含项目信息的数据框。我们还将确保它按项目标签的相同方式排序,以下步骤:

  1. 定义一个包含有序项目标签列的数据框。

    table_labels <- data.frame(id = recc_predicted@itemLabels)
    
    
  2. table_labelstable_items之间进行左连接。注意参数sort = FALSE不允许我们重新排序表格:

    table_labels <- merge(table_labels, table_items,
     by = "id", all.x = TRUE, all.y = FALSE,
     sort = FALSE)
    
    
  3. 将描述从因子转换为字符:

    descriptions <- as(table_labels$description, "character")
    
    

让我们看看table_labels

head(table_labels)
iddescriptionurlcategory
1038SiteBuilder Network Membership/sbnmemberproduct
1026Internet Site Construction for Developers/sitebuilderproduct
1034Internet Explorer/ieproduct
1008Free Downloads/msdownloadproduct
1056sports/sportsproduct
1032Games/gamesproduct

如预期的那样,表格包含了项目的描述。现在,我们能够提取推荐。例如,我们可以为第一个用户做这件事:

recc_user_1 <- recc_predicted@items[[1]]
items_user_1 <- descriptions[recc_user_1]
head(items_user_1)

Windows 操作系统系列、支持桌面、知识库、Microsoft.com 搜索、产品、Windows 95。

现在,我们可以定义一个包含对所有用户推荐的表格。每一列对应一个用户,每一行对应一个推荐的项目。将 n_recommended 设置为 10 后,表格应该有 10 行。为此,我们可以使用 sapply()。对于 recc_predicted@items 的每个元素,我们识别相关的项目描述。

然而,每个用户被推荐的项目数量是一个介于 1 到 10 之间的数字,对每个用户来说都不相同。为了定义一个有 10 行的结构化表格,我们需要每个用户有相同数量的元素。因此,我们将缺失的推荐替换为空字符串。我们可以通过使用 rep() 来复制空字符串来获得它:

recc_matrix <- sapply(recc_predicted@items, function(x){
 recommended <- descriptions[x]
 c(recommended, rep("", n_recommended - length(recommended)))
})
dim(recc_matrix)
## _10_ and _191_

让我们看看前三个用户的推荐:

head(recc_matrix[, 1:3])

Windows 操作系统系列产品开发者工作坊
支持桌面MS Word站点构建网络会员
知识库isapiisapi
Microsoft.com 搜索regwizMicrosoft.com 搜索
产品Windows 操作系统系列Windows 操作系统系列
Windows 95Microsoft.com 搜索网站构建者画廊

我们可以注意到,有些项目被推荐给了三个用户:产品和支持桌面。因此,我们怀疑有些项目更有可能被推荐。

就像我们在第三章中做的那样,推荐系统,我们可以探索输出。对于每个项目,我们可以计算它被推荐了多少次:

table_recomm_per_item <- table(recc_matrix)
recomm_per_item <- as(table_recomm_per_item, "numeric")

为了可视化结果,我们使用 cut()bin_recomm_per_item 进行操作:

bin_recomm_per_item <- cut(recomm_per_item,breaks = c(0, 10, 20, 100,max(recomm_per_item)))

使用 qplot,我们可以可视化 recomm_per_item 的分布:

qplot(bin_recomm_per_item) + ggtitle("Recommendations per item")

以下图像显示了每个项目的推荐:

https://github.com/OpenDocCN/freelearn-ml-zh/raw/master/docs/bd-recsys-r/img/B03888_05_07.jpg

大多数项目被推荐了 10 次或更少,而其中一些项目被推荐了超过 100 次。分布具有长尾。

我们还可以通过排序 recomm_per_item 来识别最受欢迎的项目:

recomm_per_item_sorted <- sort(table_recomm_per_item,decreasing = TRUE) recomm_per_item_top <- head(recomm_per_item_sorted, n = 4)
table_top <- data.frame(
 name = names(recomm_per_item_top), n_recomm = recomm_per_item_top)
table_top

名称n_recomm
Internet Explorer126
Windows 操作系统系列120
知识库118
产品115

在本节中,我们构建并探索了一个混合推荐模型。下一步是评估它并优化其参数。

评估和优化模型

本节将向您展示如何评估我们推荐系统的性能。从评估开始,我们可以尝试一些参数配置,并选择表现最好的一个。有关更多详细信息,请参阅第四章,评估推荐系统

以下是对模型进行评估和优化的步骤:

  • 构建一个给定参数配置评估模型的函数

  • 使用该函数测试不同的参数配置,并选择最佳配置

让我们详细地走一遍这些步骤。

构建一个用于评估模型的函数

本节将向您展示如何定义一个函数:

  1. 使用k-折设置交叉验证。

  2. 构建一个混合 IBCF。

  3. 向测试集中的用户推荐项目。

  4. 评估推荐。

我们函数的输入如下:

  • 数据:这是包含项目描述的评分矩阵表

  • k-折参数:这是折数,测试集中保留的项目数

  • 模型参数:这是最近邻的数量,基于描述的距离权重,推荐的项目数量

让我们定义函数参数。您可以在每个参数名称旁边的注释中找到每个参数的描述:

evaluateModel <- function (
 # data inputs
 ratings_matrix, # rating matrix
 table_items, # item description table
 # K-fold parameters
 n_fold = 10, # number of folds
 items_to_keep = 4, # number of items to keep in the test set
 # model parameters
 number_neighbors = 30, # number of nearest neighbors
 weight_description = 0.2, # weight to the item description-based distance
 items_to_recommend = 10 # number of items to recommend
){
 # build and evaluate the model
}

现在,我们可以一步一步地遍历函数体。对于更详细的解释,请参阅上一节和第四章,评估推荐系统

  1. 使用evaluationScheme()函数设置k-折。参数k和给定分别根据输入n_folditems_to_keep设置。set.seed(1)命令确保示例可重复,也就是说,如果重复执行,随机成分将是相同的:

    set.seed(1)
    eval_sets <- evaluationScheme(data = ratings_matrix,
     method = "cross-validation",
     k = n_fold,
     given = items_to_keep)
    
    
  2. 使用Recommender()构建一个定义距离函数为Jaccardk参数为number_neighbors输入的 IBCF:

    recc_model <- Recommender(data = getData(eval_sets, "train"),
     method = "IBCF",
     parameter = list(method = "Jaccard",
     k = number_neighbors))
    
    
  3. recc_model模型中提取基于评分的距离矩阵:

    dist_ratings <- as(recc_model@model$sim, "matrix")
    vector_items <- rownames(dist_ratings)
    
    
  4. table_items输入开始,定义基于描述的距离矩阵:

    dist_category <- table_items[, 1 - as.matrix(dist(category == "product"))]
    rownames(dist_category) <- table_items[, id]
    colnames(dist_category) <- table_items[, id]
    dist_category <- dist_category[vector_items, vector_items]
    
    
  5. 定义结合dist_ratingsdist_category的距离矩阵。组合是加权平均,权重由weight_description输入定义:

    dist_tot <- dist_category * weight_description +
     dist_ratings * (1 - weight_description)
    recc_model@model$sim <- as(dist_tot, "dgCMatrix")
    
    
  6. 预测具有已知购买的测试集用户。由于我们使用的是只有 0 和 1 评分的表格,我们可以指定使用type = "topNList"参数预测前n个推荐项。定义推荐项目数量的参数n来自items_to_recommend输入:

    eval_prediction <- predict(object = recc_model, newdata = getData(eval_sets, "known"), n = items_to_recommend, type = "topNList")
    
    
  7. 使用calcPredictionAccuracy()评估模型性能。指定byUser = FALSE,我们得到一个包含平均指标(如精确率和召回率)的表格:

    eval_accuracy <- calcPredictionAccuracy(
     x = eval_prediction,
     data = getData(eval_sets, "unknown"),
     byUser = FALSE,
     given = items_to_recommend)
    
    
  8. 函数输出是eval_accuracy表:

    return(eval_accuracy)
    
    
  9. 现在,我们可以测试我们的函数:

    model_evaluation <- evaluateModel(ratings_matrix = ratings_matrix, table_items = table_items)
    model_evaluation
    
    
    indexvalue
    TP2
    FP8
    FN1
    TN145
    precision19%
    recall64%
    TPR64%
    FPR5%

您可以在第四章中找到索引的详细描述,评估推荐系统

在本节中,我们定义了一个使用给定设置的函数来评估我们的模型。这个函数将帮助我们进行参数优化。

优化模型参数

从我们的 evaluateModel() 函数开始,我们可以优化模型参数。在本节中,我们将优化以下参数:

  • number_neighbors:这是 IBCF 的最近邻数量

  • weight_description:这是分配给基于描述的距离的权重

尽管我们可以优化其他参数,但为了简化,我们将它们保留为默认值。

我们的推荐模型结合了 IBCF 和项目描述。因此,首先优化 IBCF 是一个好习惯,即number_neighbors参数。

首先,我们必须决定我们想要测试哪些值。我们考虑 k,即最多为商品的一半,即大约80。另一方面,我们排除小于 4 的值,因为算法将过于不稳定。设置粒度为2,我们可以生成一个包含要测试的值的向量:

nn_to_test <- seq(4, 80, by = 2)

现在,我们可以根据 number_neighbors 来衡量性能。由于我们只优化 IBCF 部分,我们将 weight_description = 0。使用 lapply,我们可以构建一个包含每个 nn_to_test 值的性能的元素列表:

list_performance <- lapply(
 X = nn_to_test,
 FUN = function(nn){
 evaluateModel(ratings_matrix = ratings_matrix, table_items = table_items, number_neighbors = nn, weight_description = 0)
 })

让我们看看列表的第一个元素:

list_performance[[1]]

namevalue
TP1.663
FP8.337
FN1.683
TN144.3
精确度0.1663
召回率0.5935
TPR0.5935
FPR0.05449

第一个元素包含所有性能指标。为了评估我们的模型,我们可以使用精确度和召回率。参见第四章,评估推荐系统获取更多信息。

我们可以使用 sapply 提取精确度(或召回率)的向量:

sapply(list_performance, "[[", "precision")^t

0.1663, 0.1769, 0.1769, 0.175, 0.174, 0.1808, 0.176, 0.1779, 0.1788, 0.1788, 0.1808, 0.1817, 0.1817, 0.1837, 0.1846, 0.1837, 0.1827, 0.1817, 0.1827, 0.1827, 0.1817, 0.1808, 0.1817, 0.1808, 0.1808, 0.1827, 0.1827, 0.1837, 0.1827, 0.1808, 0.1798, 0.1798, 0.1798, 0.1798, 0.1798, 0.1798, 0.1788, 0.1788 and 0.1788

为了分析输出,我们可以定义一个表格,其列包括 nn_to_test、精确度和召回率:

table_performance <- data.table(
 nn = nn_to_test, precision = sapply(list_performance, "[[", "precision"), recall = sapply(list_performance, "[[", "recall")
)

此外,我们可以定义一个性能指数,我们将对其进行优化。性能指数可以是精确度和召回率之间的加权平均值。权重取决于用例,因此我们可以将其保留为 50%:

weight_precision <- 0.5
table_performance[
 performance := precision * weight_precision + recall * (1 - weight_precision)]
head(table_performance)

nn精确度召回率性能
40.16630.59350.3799
60.17690.6210.399
80.17690.59730.3871
100.1750.59430.3846
120.1740.59090.3825
140.18080.60460.3927

精确度是推荐商品中已购买的比例,召回率是已购买商品中被推荐的比例。

table_performance 表格包含所有评估指标。从它开始,我们可以构建图表,帮助我们确定最优的 nn

在构建图表之前,让我们定义一个 convertIntoPercent() 函数,我们将在 ggplot2 函数中使用它:

convertIntoPercent <- function(x){
 paste0(round(x * 100), "%")
}

我们已经准备好构建图表。第一个图表是基于 nn 的精确度。我们可以使用以下函数构建它:

  • qplot:这个函数用于构建散点图。

  • geom_smooth:这添加了一条平滑线。

  • scale_y_continuous:这改变了y轴的刻度。在我们的例子中,我们只想显示百分比。

以下命令包括前面的点:

 qplot(table_performance[, nn], table_performance[, precision]) + geom_smooth() + scale_y_continuous(labels = convertIntoPercent)

以下图像是前面代码的输出:

https://github.com/OpenDocCN/freelearn-ml-zh/raw/master/docs/bd-recsys-r/img/B03888_05_08.jpg

平滑的线条增长到全局最大值,大约在nn = 35,然后缓慢下降。这个指标表示成功推荐的百分比,因此当与广告相关的成本较高时很有用。

让我们看看召回率,使用相同的命令:

 qplot(table_performance[, nn], table_performance[, recall]) + geom_smooth() + scale_y_continuous(labels = convertIntoPercent)

以下图像是前面屏幕截图的输出:

https://github.com/OpenDocCN/freelearn-ml-zh/raw/master/docs/bd-recsys-r/img/B03888_05_09.jpg

最大召回率大约在nn = 40。这个指标表示我们推荐的购买百分比,因此如果我们想确保预测大多数购买很有用。

性能同时考虑了精确度和召回率。让我们看看它:

 qplot(table_performance[, nn], table_performance[, performance]) + geom_smooth() + scale_y_continuous(labels = convertIntoPercent)

https://github.com/OpenDocCN/freelearn-ml-zh/raw/master/docs/bd-recsys-r/img/B03888_05_10.jpg

最佳性能在 30 到 45 之间。我们可以使用which.max识别最佳的nn

row_best <- which.max(table_performance$performance)
number_neighbors_opt <- table_performance[row_best, nn]
number_neighbors_opt
## _34_

最佳值是34。我们优化了 IBCF 参数,下一步是确定项目描述组件的权重。首先,让我们定义要尝试的权重。可能的权重范围在 0 到 1 之间,我们只需要设置粒度,例如,0.05

wd_to_try <- seq(0, 1, by = 0.05)

使用lapply,我们可以根据权重测试推荐器:

list_performance <- lapply(
 X = wd_to_try, FUN = function(wd){
 evaluateModel(ratings_matrix = ratings_matrix, table_items = table_items, number_neighbors = number_neighbors_opt, weight_description = wd) })

就像我们之前做的那样,我们可以构建一个包含精确度、召回率和性能的表格:

table_performance <- data.table(
 wd = wd_to_try, precision = sapply(list_performance, "[[", "precision"), recall = sapply(list_performance, "[[", "recall")
)
table_performance[
 performance := precision * weight_precision + recall * (1 - weight_precision)]

现在,我们可以通过图表可视化基于权重的性能:

 qplot(table_performance[, wd], table_performance[, performance]) + geom_smooth() + scale_y_continuous(labels = convertIntoPercent)

以下图像是前面命令的输出:

https://github.com/OpenDocCN/freelearn-ml-zh/raw/master/docs/bd-recsys-r/img/B03888_05_11.jpg

性能在每个点上都是相同的,除了极端值。因此,平滑线没有用。

我们得到了考虑评分和描述的最佳性能。极端的0.00对应于纯 IBCF,它的表现略逊于混合模型。极端的1.00模型仅基于项目描述,这就是为什么它的表现如此糟糕。

性能变化不大的原因是项目描述仅基于二进制特征。如果我们添加其他特征,我们将看到更大的影响。

这一节向您展示了如何基于两个参数优化我们的推荐算法。下一步可以是基于剩余的 IBCF 参数优化并改进项目描述。

摘要

本章向您展示了如何在现实生活场景中应用这些技术。我们从原始的非结构化数据开始,构建了一个评分矩阵,这是协同过滤的输入。此外,我们还提取了项目描述,这提高了我们模型的表现。通过性能评估,我们优化了模型参数。如果适当细化,这种方法也可以应用于现实生活场景。

本书是一条路径,首先展示了机器学习的基础知识,然后是实际应用。阅读完本书后,您将能够应对现实生活中的挑战,确定最合适的推荐解决方案。感谢您一直跟随到这里。

如果您有任何疑问,请随时联系我们。

附录 A. 参考文献

想了解更多关于推荐系统和机器学习的知识,请参考以下参考资料:

  • Dietmar JannachMarkus ZankerAlexander FelfernigGerhard Friedrich合著的推荐系统:入门

  • Francesco RicciLior RokachBracha ShapiraPaul B. Kantor合著的推荐系统手册

  • Gareth JamesDaniela WittenTrevor HastieRobert Tibshirani合著的统计学习基础与应用:R 语言版

  • en.wikipedia.org/wiki/Precision_and_recall

  • 安德鲁·吴www.coursera.org/提供的在线课程机器学习

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值