如何在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
相关产品推荐
相关产品推荐

