欧氏距离,Scipy、纯 Python 和 Java 之间的不同结果
Euclidean distance, different results between Scipy, pure Python, and Java
我正在研究欧几里德距离度量的不同实现,我注意到 Scipy、纯 Python 和 Java.[=23= 得到了不同的结果]
这是我使用 Scipy(= 选项 1)计算距离的方法:
distance = scipy.spatial.distance.euclidean(sample, training_vector)
这是我在论坛(选项 2)中找到的 Python 中的一个实现:
distance = math.sqrt(sum([(a - b) ** 2 for a, b in zip(training_vector, sample)]))
最后,这是我在 Java 中的实现(选项 3):
public double distance(int[] a, int[] b) {
assert a.length == b.length;
double squaredDistance = 0.0;
for(int i=0; i<a.length; i++){
squaredDistance += Math.pow(a[i] - b[i], 2.0);
}
return Math.sqrt(squaredDistance);
}
sample
和 training_vector
都是长度为 784 的一维数组,取自 MNIST 数据集。我用相同的 sample
和 training_vector
尝试了所有三种方法。问题是三种不同的方法导致了三个明显不同的距离(即选项 1 大约在 1936 年左右,选项 2 大约在 1914 年左右,选项 3 大约在 1382 年左右)。有趣的是,当我在选项 1 和选项 2 中对 sample
和 training_vector
使用相同的参数顺序时(即将参数翻转到选项 1),我对这两个选项得到相同的结果。但是距离度量应该是对称的,对吧...?
同样有趣的是:我将这些指标用于 MNIST 数据集的 k-NN 分类器。我的 Java 实现对 100 个测试样本和 2700 个训练样本产生了大约 94% 的准确率。但是,使用选项 1 的 Python 实现仅产生大约 75% 的准确度...
关于为什么我会得到这些不同的结果,您有什么想法吗?如果您有兴趣,我可以在线 post 两个数组的 CSV,并在此处 post link。
我正在使用 Java 8、Python 2.7 和 Scipy 1.0.0。
编辑:
将选项 2 更改为
distance = math.sqrt(sum([(float(a) - float(b)) ** 2 for a, b in zip(training_vector, sample)]))
这产生了以下影响:
- 它摆脱了 ubyte 溢出警告(我之前一定错过了这个警告...)
- 更改选项 1 和 2 的参数顺序不再产生影响。
- 选项 2(纯 Python)和选项 3(Java)的结果现在相等
所以,这只会留下以下问题:为什么使用 SciPy 时结果不同(即错误?)?
好的,我找到了解决方案:我使用 pandas 和 dtype=np.uint8
导入了训练和测试数据集。因此,sample
和 training_vector
都是类型为 uint8
的 numpy 数组。我将数据类型更改为 np.float32
,现在我的所有三个选项都给出了相同的结果。我也试过 np.uint32
,效果也很好。
我不太清楚为什么,但显然,SciPy 在使用 uint8
时不会给出 "expected" 结果。也许 SciPy 中有一些内部溢出?不太确定,但至少它现在有效。感谢所有帮助过的人!
我正在研究欧几里德距离度量的不同实现,我注意到 Scipy、纯 Python 和 Java.[=23= 得到了不同的结果]
这是我使用 Scipy(= 选项 1)计算距离的方法:
distance = scipy.spatial.distance.euclidean(sample, training_vector)
这是我在论坛(选项 2)中找到的 Python 中的一个实现:
distance = math.sqrt(sum([(a - b) ** 2 for a, b in zip(training_vector, sample)]))
最后,这是我在 Java 中的实现(选项 3):
public double distance(int[] a, int[] b) {
assert a.length == b.length;
double squaredDistance = 0.0;
for(int i=0; i<a.length; i++){
squaredDistance += Math.pow(a[i] - b[i], 2.0);
}
return Math.sqrt(squaredDistance);
}
sample
和 training_vector
都是长度为 784 的一维数组,取自 MNIST 数据集。我用相同的 sample
和 training_vector
尝试了所有三种方法。问题是三种不同的方法导致了三个明显不同的距离(即选项 1 大约在 1936 年左右,选项 2 大约在 1914 年左右,选项 3 大约在 1382 年左右)。有趣的是,当我在选项 1 和选项 2 中对 sample
和 training_vector
使用相同的参数顺序时(即将参数翻转到选项 1),我对这两个选项得到相同的结果。但是距离度量应该是对称的,对吧...?
同样有趣的是:我将这些指标用于 MNIST 数据集的 k-NN 分类器。我的 Java 实现对 100 个测试样本和 2700 个训练样本产生了大约 94% 的准确率。但是,使用选项 1 的 Python 实现仅产生大约 75% 的准确度...
关于为什么我会得到这些不同的结果,您有什么想法吗?如果您有兴趣,我可以在线 post 两个数组的 CSV,并在此处 post link。
我正在使用 Java 8、Python 2.7 和 Scipy 1.0.0。
编辑: 将选项 2 更改为
distance = math.sqrt(sum([(float(a) - float(b)) ** 2 for a, b in zip(training_vector, sample)]))
这产生了以下影响:
- 它摆脱了 ubyte 溢出警告(我之前一定错过了这个警告...)
- 更改选项 1 和 2 的参数顺序不再产生影响。
- 选项 2(纯 Python)和选项 3(Java)的结果现在相等
所以,这只会留下以下问题:为什么使用 SciPy 时结果不同(即错误?)?
好的,我找到了解决方案:我使用 pandas 和 dtype=np.uint8
导入了训练和测试数据集。因此,sample
和 training_vector
都是类型为 uint8
的 numpy 数组。我将数据类型更改为 np.float32
,现在我的所有三个选项都给出了相同的结果。我也试过 np.uint32
,效果也很好。
我不太清楚为什么,但显然,SciPy 在使用 uint8
时不会给出 "expected" 结果。也许 SciPy 中有一些内部溢出?不太确定,但至少它现在有效。感谢所有帮助过的人!