KNN分类混淆矩阵右列全0、F1为0的原因排查及解决咨询
问题根源与解决方案
你的问题根本不是“独热编码后转回分类变量”,而是错误地对目标变量class做了独热编码,导致模型变成了极度不平衡的二分类任务,最终模型只预测占比高的那一类,出现混淆矩阵右侧全0、F1为0的情况。
具体问题分析
原汽车评估数据集的class列包含4个类别(unacc、acc、good、vgood),你用pd.get_dummies把它和特征列一起做了独热编码+drop_first=True,相当于把class拆成了3个二值列。之后你取y = data.iloc[:,6],这个列只是原class某一个子类别的二值标记,且该类别样本极少,模型自然只会预测占比高的0类,导致所有预测结果都是0——F1分数为0是因为没有正确预测1类样本,而0.74的准确率只是0类样本的占比。
修正步骤
- 只对特征列做独热编码:保留
class列作为原始分类目标,绝对不能对它做独热编码。 - 可选:用
LabelEncoder把class的字符串标签转成数值型(sklearn分类器也支持直接用字符串作为目标,但数值型更直观)。 - 重新划分数据集、训练模型。
修正后的代码
import pandas as pd import numpy as np from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler, LabelEncoder from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import confusion_matrix, f1_score, accuracy_score # 读取数据并命名列 data = pd.read_csv('car.data', sep=",") data.columns = ['buying', 'maint','doors', 'persons', 'lug_boots', 'safety','class'] # 仅对特征列做独热编码,保留class列作为目标变量 data_encoded = pd.get_dummies(data, columns=['buying', 'maint','doors', 'persons', 'lug_boots', 'safety'], drop_first=True, dtype=int) # 把class的字符串标签转成数值(可选,也可以直接用字符串训练) le = LabelEncoder() data_encoded['class'] = le.fit_transform(data_encoded['class']) # 拆分特征与目标变量 x = data_encoded.drop('class', axis=1) y = data_encoded['class'] # 划分训练集和测试集 x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=0, test_size=0.3) # 特征标准化 sc_x = StandardScaler() x_train = sc_x.fit_transform(x_train) x_test = sc_x.transform(x_test) # 训练KNN模型 classifier = KNeighborsClassifier(n_neighbors=17, p=2, metric='euclidean') classifier.fit(x_train, y_train) # 预测与评估 y_pred = classifier.predict(x_test) print("混淆矩阵:\n", confusion_matrix(y_test, y_pred)) print("F1分数(多分类加权):", f1_score(y_test, y_pred, average='weighted')) print("准确率:", accuracy_score(y_test, y_pred))
额外说明
- 如果想把预测的数值结果转回原始类别字符串,用
le.inverse_transform(y_pred)即可。 - 多分类任务计算F1时必须指定
average参数(比如weighted、macro),否则默认按二分类计算会报错或结果异常。
内容的提问来源于stack exchange,提问作者fjq
相关产品推荐
相关产品推荐

