使用 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
当我在使用 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 = 2b
= ((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 = 2b
= ((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