You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何用R内置函数实现无监督K近邻查找相似文章

如何用R的class包中监督式的knn函数实现无监督的文章相似性检索?

问题描述

现有一个数据集:

  • 行数=1000,每行对应一篇文章
  • 列数=502:第1列是文章标题,第2列是文章文本,第3至502列是语料库唯一单词的出现次数

需求是:给定某篇文章的标题,用K近邻算法找出10篇内容最相似的文章。希望使用R的现有函数,已知class包的knn()是监督学习用的,需要说明怎么把它用到这个无监督场景里。


回答

嘿,这个问题挺巧妙的——把监督式的knn()用到无监督的相似性检索上,其实只需要加个"伪标签"的小技巧就行!下面一步步给你拆解实现过程:

1. 拆分特征与元数据

首先我们要把用于计算相似性的词频特征和标题、文本这类元数据分开,方便后续操作。假设你的数据集叫article_data

# 提取词频特征矩阵(第3到502列)
feature_matrix <- article_data[, 3:502]
# 保留标题和文本,后续用来输出相似文章的信息
meta_data <- article_data[, 1:2]

2. 构造伪监督标签

knn()必须要有训练集的类别标签,那我们就给目标文章单独设一个独特标签,其他所有文章设成通用标签。这样knn()就会帮我们找出和目标标签最"像"的样本——也就是内容最相似的文章。
先定位到目标文章的行索引(假设目标标题是target_title):

# 找到目标文章在原数据中的行号
target_idx <- which(meta_data$标题 == target_title)
# 创建标签向量:默认都是"others",把目标文章标成"target"
labels <- rep("others", nrow(article_data))
labels[target_idx] <- "target"

3. 用knn查找最近邻

这里要注意参数设置:我们把除了目标文章之外的所有样本当训练集,把目标文章当测试集,k设为11(因为会把目标自己算进去,之后要排除掉,刚好剩下10个相似文章)。

library(class)
# 训练集:去掉目标文章的特征和标签
train_features <- feature_matrix[-target_idx, ]
train_labels <- labels[-target_idx]
# 测试集:目标文章的特征(加drop=FALSE保证是矩阵格式)
test_feature <- feature_matrix[target_idx, , drop = FALSE]
# 运行knn,顺便返回距离信息
nn_result <- knn(train = train_features, 
                 test = test_feature, 
                 cl = train_labels, 
                 k = 11, 
                 prob = TRUE)

4. 提取并整理相似文章

knn()返回的结果里,attr(nn_result, "nn.index")会给出训练集中最近邻的索引,我们需要把这些索引映射回原数据集,然后提取对应的标题和文本:

# 获取训练集中最近邻的索引
nn_train_indices <- attr(nn_result, "nn.index")[1, ]
# 映射回原数据集的行号(训练集去掉了目标文章,所以要对应回去)
nn_original_indices <- (1:nrow(article_data))[-target_idx][nn_train_indices]
# 提取前10个相似文章(排除目标自己)
similar_articles <- meta_data[nn_original_indices, ]
# 加上距离信息并排序(距离越小越相似)
similar_articles$distance <- attr(nn_result, "nn.dist")[1, ]
similar_articles <- similar_articles[order(similar_articles$distance), ]

关键逻辑说明

为什么这个方法能行?因为knn()的核心是计算样本间的距离,然后选出距离最近的k个样本。我们给目标文章单独设标签,本质上是让knn()完成"找和测试样本(目标文章)距离最近的训练样本"这个任务——这完全就是我们要的无监督相似性检索!另外,如果你觉得欧氏距离不适合文本,还可以通过调整距离计算方式(比如用余弦距离,需要先对特征矩阵做归一化处理)来优化结果。

内容的提问来源于stack exchange,提问作者Aargo

火山引擎 最新活动