使用 sklearn 和 Spark 时不同的剪影分数

Different silhouette scores when using sklearn and Spark

当我在使用 Spark 和 sklearn 时使用相同的数据和相同的预测值计算剪影分数时,我得到了不同的结果。

这是用于 Spark 的代码:

>>> prediction.show()
+---+---+---------+----------+
|  a|  b| features|prediction|
+---+---+---------+----------+
|  1|  1|[1.0,1.0]|         1|
|  2|  2|[2.0,2.0]|         1|
|  3|  3|[3.0,3.0]|         0|
|  4|  4|[4.0,4.0]|         0|
+---+---+---------+----------+

>>> from pyspark.ml.evaluation import ClusteringEvaluator
>>> evaluator = ClusteringEvaluator()
>>> silhouette = evaluator.evaluate(prediction)
>>> silhouette
0.7230769230769223

这是用于 sklearn 的代码:

>>> from sklearn.cluster import KMeans
>>> from sklearn import metrics
>>> x=[[1,1],[2,2],[3,3],[4,4]]
>>> prediction = KMeans(n_clusters=2,max_iter=1000,random_state=123).fit_predict(x)
>>> prediction
array([1, 1, 0, 0], dtype=int32)
>>> silhouette = metrics.silhouette_score(x, prediction)
>>> silhouette
0.46666666666666673

如上所示,尽管输入相同,但分数却大不相同。这是为什么?

主要区别在于使用了不同的距离度量

Spark 使用平方欧几里德作为距离度量,而 sklearn 默认使用普通欧几里得距离。

在 Spark 中选择这种距离度量的原因是为了实现更高效的并行计算。可以预先计算部分方程,将计算复杂度从 O(N^2^*D) 降低到 O(C*D*N/W),其中 N 是点的数量,D 它们的维度,O(C*D*N/W) 其中 W 是工人的数量, C 集群的数量(假设相当低)。 轮廓分数的 Spark 数学推导和实现记录在 github (here) 上。


证明:

我们可以分析问题中的示例,并使用欧几里德距离和平方欧几里德距离手动计算轮廓分数。

我们有带点 (1,1)(2,2) 的聚类 1 和位于 (1.5,1.5) 的聚类中心,以及带 (3,3)(4,4) 和聚类的聚类 2中心位于 (3.5,3.5).

最终的剪影分数是所有样本剪影分数的平均值。由于问题中的四个点完全镜像并且只有两个集群,因此计算其中一个集群的分数就足够了(这里我选择了集群 1)。

下面a是平均簇内距离(到同一簇中所有点的平均距离)和b平均簇间距离(到最近的所有点的平均距离该点不属于的簇)。分数计算为 (b-a) / max(b,a).

欧氏距离:

  • (1,1):

    • a = sqrt((2-1)^2 + (2-1)^2) = sqrt(2)
    • b = (sqrt((3-1)^2 + (3-1)^2) + sqrt((4-1)^2 + (4-1)^2)) ) / 2 = (sqrt(8) + sqrt(18)) / 2 = 3.5355
    • 分数 = (3.5355 - sqrt(2)) / 3.5355 = 0.6
  • (2,2):

    • a = sqrt((2-1)^2 + (2-1)^2) = sqrt(2)
    • b = (sqrt((3-2)^2 + (3-2)^2) + sqrt((4-2)^2 + (4-2)^2)) ) / 2 = (sqrt(2) + sqrt(8)) / 2 = 2.1213
    • 分数 = (2.1213 - sqrt(2)) / 2.1213 = 0.33333

剪影分数 = (0.6 + 0.33333) / 2 = 0.4666667

平方欧氏距离:

  • (1,1):

    • a = (2-1)^2 + (2-1)^2 = 2
    • b = ((3-1)^2 + (3-1)^2 + (4-1)^2 + (4-1)^2) / 2 = (8 + 18) / 2 = 13
    • 分数 = (13 - 2) / 13 = 0.84615
  • (2,2):

    • a = (2-1)^2 + (2-1)^2 = 2
    • b = ((3-2)^2 + (3-2)^2 + (4-2)^2 + (4-2)^2) / 2 = (2 + 8) / 2 = 5
    • 点数 = (5 - 2) / 5 = 0.6

剪影分数 = (0.84615 + 0.6) / 2 = 0.723075