You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在R中正确使用KNN的predict函数?预测结果异常求助

你的KNN模型问题排查与修复指南

我来帮你拆解并解决遇到的两个核心问题:混淆矩阵仅第一类有预测结果,以及predict函数报错。咱们一步步来:

一、混淆矩阵全0行的核心原因(所有样本被预测为Art)

你看到的混淆矩阵异常,本质是文本数据没有转换成KNN能处理的数值特征,再加上k值设置不合理导致的。具体来说:

1. 最致命的遗漏:文本特征工程

你导入了tm库但完全没用到!KNN是基于数值特征距离计算的模型,不能直接处理原始文本(你的Terms列)。必须先把文本转换成文档词频矩阵(DTM),这是第一步就漏掉的关键步骤。

2. 其他辅助原因

  • k值过大:你设了k=70,如果训练样本总数不多,大k会让模型偏向训练集中的多数类(看起来Art类样本最多)
  • 没有特征缩放:KNN对特征尺度极度敏感,词频数值差异大的话,大词频特征会主导距离计算
  • 类别不平衡:如果训练集里Art类占比远高于其他类,模型会自然倾向于预测Art

修复步骤

先补全文本预处理和特征转换:

# 构建文本语料库
corpus <- VCorpus(VectorSource(PathFile$Terms))
# 标准化文本:转小写、去标点、去数字、去停用词、词干提取
corpus <- tm_map(corpus, content_transformer(tolower))
corpus <- tm_map(corpus, removePunctuation)
corpus <- tm_map(corpus, removeNumbers)
corpus <- tm_map(corpus, removeWords, stopwords("english")) # 是法语的话改成stopwords("french")
corpus <- tm_map(corpus, stemDocument)
# 转换成可用于建模的数值矩阵
dtm <- DocumentTermMatrix(corpus)
dtm_df <- as.data.frame(as.matrix(dtm))
dtm_df$Category <- PathFile$Category # 把类别列加回来

然后处理特征缩放和k值选择:

# 设置随机种子保证结果可复现
set.seed(123)
# 拆分训练测试集(用处理后的dtm_df,不是原始PathFile)
train <- sample(nrow(dtm_df), ceiling(nrow(dtm_df)*0.7))
test <- (1:nrow(dtm_df))[-train]
cl <- dtm_df$Category

# 特征缩放(KNN必须做!)
modeldata <- dtm_df[, !colnames(dtm_df) %in% "Category"]
train_scaled <- scale(modeldata[train, ])
test_scaled <- scale(modeldata[test, ], 
                     center = attr(train_scaled, "scaled:center"),
                     scale = attr(train_scaled, "scaled:scale"))

# 用交叉验证选最优k值(比手动设k靠谱多了)
ctrl <- trainControl(method = "cv", number = 10)
knn_model <- train(Category ~ ., data = dtm_df, method = "knn",
                   trControl = ctrl, preProcess = c("scale", "center"))
cat("最优k值:", knn_model$bestTune$k, "\n")

重新训练后,混淆矩阵应该会正常显示各类别的预测结果了。

二、predict(knn.pred, PathFilename)报错的原因

你搞错了predict函数的用法!knn.pred是class::knn()返回的预测结果向量,不是一个可重复使用的模型对象。predict需要传入训练好的模型,而不是之前的预测结果。

正确预测新数据的方式

如果要用新数据PathFilename做预测,必须保证新数据的预处理和训练集完全一致,然后用训练好的模型来预测:

# 对新数据做和训练集一模一样的文本预处理
new_corpus <- VCorpus(VectorSource(PathFilename$Terms))
new_corpus <- tm_map(new_corpus, content_transformer(tolower))
new_corpus <- tm_map(new_corpus, removePunctuation)
new_corpus <- tm_map(new_corpus, removeNumbers)
new_corpus <- tm_map(new_corpus, removeWords, stopwords("english"))
new_corpus <- tm_map(new_corpus, stemDocument)

# 构建和训练集特征完全对齐的DTM(必须用训练集的词库)
new_dtm <- DocumentTermMatrix(new_corpus, control = list(dictionary = Terms(dtm)))
new_dtm_df <- as.data.frame(as.matrix(new_dtm))

# 用caret训练的模型直接预测
new_pred <- predict(knn_model, newdata = new_dtm_df)
print(new_pred)

三、完整修正后的代码

把上面的步骤整合起来,完整代码如下:

# 加载所需库
library(tm)
library(class)
library(SnowballC)
library(gmodels)
library(caret)
library(e1071)
library(SparseM)

# 读取数据
PathFile <- read.csv(file.choose(), sep = ";", header = TRUE)
PathFilename <- read.csv(file.choose(), sep = ";", header = TRUE)

# ---------------------- 文本特征工程(核心步骤) ----------------------
corpus <- VCorpus(VectorSource(PathFile$Terms))
# 文本标准化
corpus <- tm_map(corpus, content_transformer(tolower))
corpus <- tm_map(corpus, removePunctuation)
corpus <- tm_map(corpus, removeNumbers)
corpus <- tm_map(corpus, removeWords, stopwords("english")) # 按需调整语言
corpus <- tm_map(corpus, stemDocument)
# 转换为建模用数据框
dtm <- DocumentTermMatrix(corpus)
dtm_df <- as.data.frame(as.matrix(dtm))
dtm_df$Category <- PathFile$Category

# ---------------------- 拆分训练测试集 ----------------------
set.seed(123)
train <- sample(nrow(dtm_df), ceiling(nrow(dtm_df)*0.7))
test <- (1:nrow(dtm_df))[-train]
cl <- dtm_df$Category

# ---------------------- 训练最优KNN模型 ----------------------
ctrl <- trainControl(method = "cv", number = 10)
knn_model <- train(Category ~ ., data = dtm_df, method = "knn",
                   trControl = ctrl, preProcess = c("scale", "center"))
cat("最优k值:", knn_model$bestTune$k, "\n")

# 预测测试集并评估
knn.pred <- predict(knn_model, newdata = dtm_df[test, ])
conf.mat <- table("Predictions" = knn.pred, Actual = cl[test])
print(conf.mat)
ct <- CrossTable(x = cl[test], y = knn.pred, prop.chisq = FALSE)
accuracy <- sum(diag(conf.mat))/length(test)*100
cat("模型准确率:", accuracy, "%\n")

# ---------------------- 预测新数据 ----------------------
new_corpus <- VCorpus(VectorSource(PathFilename$Terms))
new_corpus <- tm_map(new_corpus, content_transformer(tolower))
new_corpus <- tm_map(new_corpus, removePunctuation)
new_corpus <- tm_map(new_corpus, removeNumbers)
new_corpus <- tm_map(new_corpus, removeWords, stopwords("english"))
new_corpus <- tm_map(new_corpus, stemDocument)

new_dtm <- DocumentTermMatrix(new_corpus, control = list(dictionary = Terms(dtm)))
new_dtm_df <- as.data.frame(as.matrix(new_dtm))
new_pred <- predict(knn_model, newdata = new_dtm_df)
print(new_pred)

# 保存预测结果
setwd("C:/Users/Public/Desktop/")
df.pred <- cbind(knn.pred, dtm_df[test, !colnames(dtm_df) %in% "Category"])
write.table(df.pred, file="output.csv", sep=";")

四、额外提醒

  • 一定要保证新数据的预处理和训练集完全一致,不然特征不匹配会报错或预测不准
  • 用set.seed()固定随机种子,这样你的实验结果是可复现的
  • 交叉验证选k值比手动瞎试靠谱太多,能有效提升模型准确率

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:12:44