DEBUG.WIKI
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →

KNeighborsClassifier 的 brute 与 p=1 组合拒绝字符串标签

KNeighborsClassifier 的 brute 分类概率优化需要整数类索引,旧路径误传字符串时应更新修复版本或明确编码类别。

KNeighborsClassifier 已经拟合成功,预测时却报 invalid literal for int() ... 'bar'。若类别就是正常的字符串,而配置为 algorithm="brute", p=1,先检查分类概率的优化路径,不要把合法类别名改成能被 int() 解析的文本。

scikit-learn 1.8.0 的这项故障把 self.classes_ 中原始类别值交给底层 ArgKminClassMode。该接口需要的是类索引,训练样本的类别成员关系已经编码成整数,但唯一类别列表仍然是字符串,于是底层转换失败。

固定距离与算法复验

下面保留字符串标签,显式采用 Manhattan 距离对应的 p=1,避免 auto 选择另一条算法路径:

import numpy as np
import sklearn
from sklearn.neighbors import KNeighborsClassifier

rng = np.random.default_rng(0)
X = rng.normal(size=(5, 3))
y = np.array(["foo", "bar", "foo", "bar", "foo"])
query = rng.normal(size=(2, 3))
model = KNeighborsClassifier(algorithm="brute", p=1, n_neighbors=3).fit(X, y)
prediction = model.predict(query)
probability = model.predict_proba(query)
print(sklearn.__version__, model.classes_, prediction)
assert probability.shape == (len(query), len(model.classes_))
np.testing.assert_allclose(probability.sum(axis=1), 1.0)
assert set(prediction).issubset(set(y))

scikit-learn 1.9.0 包含修复,传给底层的是 0 到 len(classes_)-1 的整数索引,而不是类别名。这项修复也涉及 RadiusNeighborsClassifier 的对应分类概率路径。

更新后应覆盖 predict 和 predict_proba,并检查概率列仍对应 model.classes_。只运行 fit,不会执行原来失败的优化步骤;把 algorithm 改成 auto 后恰好成功,也不能证明固定 brute 路径已经恢复。

需要保留旧版时,显式管理类别编码

受版本约束时,可先把字符串类别编码为连续整数,模型输出后再恢复:

from sklearn.preprocessing import LabelEncoder

encoder = LabelEncoder().fit(y)
model = KNeighborsClassifier(algorithm="brute", p=1, n_neighbors=3)
model.fit(X, encoder.transform(y))
prediction = encoder.inverse_transform(model.predict(query))
probability_labels = encoder.inverse_transform(model.classes_)
print(prediction, probability_labels)

编码器需要与模型一起保存。类别排序应来自同一个编码器,不能在预测服务中另外排序后解释概率列。不要仅为避开错误把 p 改成 2,那会从 Manhattan 改为 Euclidean 距离,邻居选择和业务结果都可能改变。

本题处理的是底层分类概率优化收到错误类别表示,不是样本数小于 n_neighbors、距离矩阵形状不符或类别本身缺失。不同算法、metric、权重与输入类型可能选择不同路径;验收应保留原组合,再覆盖项目实际使用的概率输出与标签还原。