pairwise_distances 开两线程报维度不符,预计算范数没有一起切片
pairwise_distances 并行切分 Y 却没有同步切分预计算范数时会报维度错误,可更新修复版本并对照单线程结果。
pairwise_distances(..., metric="euclidean") 单线程返回正常,设置 n_jobs=2 后却报 Incompatible dimensions for Y。如果同时传了 Y_norm_squared,先比较它与每个线程收到的 Y,不必先更换距离公式或重算整个特征矩阵。
scikit-learn 1.8.0 的并行路径把 Y 分成多个行块,却把完整的 Y_norm_squared 传给每一个块。原数组长度可能完全正确,错误发生在分块之后:局部 Y 只有六行,对应范数仍然有十三项。
用不同大小的 X、Y 检查配对
下面显式计算每个 Y 样本的平方范数,比较相同输入的单线程与双线程结果:
import numpy as np
import sklearn
from sklearn.metrics import pairwise_distances
rng = np.random.RandomState(42)
X = rng.rand(13, 4)
Y = rng.rand(15, 4)
norms = (Y ** 2).sum(axis=1)
single = pairwise_distances(
X, Y, metric="euclidean", n_jobs=1, Y_norm_squared=norms,
)
parallel = pairwise_distances(
X, Y, metric="euclidean", n_jobs=2, Y_norm_squared=norms,
)
print(sklearn.__version__, parallel.shape)
np.testing.assert_allclose(parallel, single)
scikit-learn 1.9.0 已让预计算范数随 Y 使用同一块切片。更新后使用这类行数不能整除线程数的输入检查,比仅测试一个恰好均分的小矩阵更能覆盖分块边界。
Y_norm_squared 必须来源于当前 Y,并保持同一行顺序。若调用方改了 Y、筛掉了行,却复用了旧范数,升级也不能纠正这个输入错误。它表示平方范数,不是直接传入向量长度或任意缓存统计量。
旧版可取消预计算,或先保持单线程
如果暂时不能更新,可不传 Y_norm_squared,让欧氏距离函数对实际收到的行块计算所需范数;或者保持 n_jobs=1 使用完整数组:
without_precomputed = pairwise_distances(X, Y, metric="euclidean", n_jobs=2)
np.testing.assert_allclose(without_precomputed, single)
这两种方式可能改变性能,应在项目可接受的资源范围内评估。不要靠给完整范数随机截短来让某一个线程通过:不同块对应不同的 Y 行,截取前几项不等于取到了正确样本。
验收需要检查矩阵形状为 (len(X), len(Y)),数值与相同输入的单线程基准一致,并覆盖实际使用的 dtype 与自定义参数。浮点结果使用合适容差比较,不要求逐字节相同。
这项修复针对欧氏距离的预计算范数与线程分块同步,不承诺解决所有并行后端、内存限制或自定义 metric 的异常。如果不传范数也失败,或新的错误发生在任务创建阶段,继续按实际堆栈排查,不能仅凭 n_jobs > 1 就归入这个故障。


