This is an automated email from the ASF dual-hosted git repository.

zhengruifeng pushed a commit to branch branch-4.x
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/branch-4.x by this push:
     new c121eb46fff8 [SPARK-58715][ML][CONNECT] Use explicit fields for 
clustering model size estimation
c121eb46fff8 is described below

commit c121eb46fff841886cb77a1e992f51f6d3ee6d3f
Author: Ruifeng Zheng <[email protected]>
AuthorDate: Wed Aug 12 08:13:02 2026 +0800

    [SPARK-58715][ML][CONNECT] Use explicit fields for clustering model size 
estimation
    
    ### What changes were proposed in this pull request?
    
    This PR updates the ML `KMeansModel`, `BisectingKMeansModel`, and 
`GaussianMixtureModel` size estimates to pass only their persisted MLlib data 
fields to `SizeEstimator`.
    
    For Gaussian mixture models, it estimates each Gaussian mean and covariance 
rather than the `MultivariateGaussian` wrapper. For bisecting K-means, it makes 
the MLlib model root available to Spark-internal code so the ML wrapper can 
list the fields directly.
    
    ### Why are the changes needed?
    
    `SizeEstimator` recursively follows object references. Estimating an entire 
parent MLlib model can include transient or lazily initialized derived state, 
such as distance structures, statistics, or logging fields, rather than only 
persisted model data. Explicit fields make the accounting predictable and avoid 
retaining those auxiliary object graphs during estimation.
    
    ### Does this PR introduce _any_ user-facing change?
    
    No.
    
    ### How was this patch tested?
    
    ```
    build/sbt -java-home /usr/lib/jvm/java-17-openjdk-amd64 'mllib / Compile / 
compile'
    ```
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    Generated-by: Codex (GPT-5)
    
    Closes #57932 from zhengruifeng/SPARK-58715-clustering-size-estimation.
    
    Authored-by: Ruifeng Zheng <[email protected]>
    Signed-off-by: Ruifeng Zheng <[email protected]>
    (cherry picked from commit afa9031aafbe50259ff56ff68d78c0bc8c7ac79c)
    Signed-off-by: Ruifeng Zheng <[email protected]>
---
 .../org/apache/spark/ml/clustering/BisectingKMeans.scala     | 12 +++++++-----
 .../org/apache/spark/ml/clustering/GaussianMixture.scala     |  5 +++--
 .../main/scala/org/apache/spark/ml/clustering/KMeans.scala   | 12 +++++++++---
 .../apache/spark/mllib/clustering/BisectingKMeansModel.scala |  2 +-
 4 files changed, 20 insertions(+), 11 deletions(-)

diff --git 
a/mllib/src/main/scala/org/apache/spark/ml/clustering/BisectingKMeans.scala 
b/mllib/src/main/scala/org/apache/spark/ml/clustering/BisectingKMeans.scala
index e63a3eaa6455..6b149102e253 100644
--- a/mllib/src/main/scala/org/apache/spark/ml/clustering/BisectingKMeans.scala
+++ b/mllib/src/main/scala/org/apache/spark/ml/clustering/BisectingKMeans.scala
@@ -182,11 +182,13 @@ class BisectingKMeansModel private[ml] (
   private[spark] override def estimatedSize: Long = {
     var size = estimateMatadataSize
     if (parentModel != null) {
-      // parentModel contains:
-      // - root: ClusteringTreeNode containing centers, costs, and children.
-      // - distanceMeasure: String and trainingCost: Double.
-      // - distanceMeasureInstance, derived from distanceMeasure.
-      size += SizeEstimator.estimate(parentModel)
+      // root: ClusteringTreeNode
+      // distanceMeasure: String
+      // trainingCost: Double
+      size += SizeEstimator.estimate((
+        parentModel.root,
+        parentModel.distanceMeasure,
+        parentModel.trainingCost))
     }
     size
   }
diff --git 
a/mllib/src/main/scala/org/apache/spark/ml/clustering/GaussianMixture.scala 
b/mllib/src/main/scala/org/apache/spark/ml/clustering/GaussianMixture.scala
index 272fce565a8f..817802746bbf 100644
--- a/mllib/src/main/scala/org/apache/spark/ml/clustering/GaussianMixture.scala
+++ b/mllib/src/main/scala/org/apache/spark/ml/clustering/GaussianMixture.scala
@@ -225,8 +225,9 @@ class GaussianMixtureModel private[ml] (
   private[spark] override def estimatedSize: Long = {
     var size = estimateMatadataSize
     // weights: Array[Double]
-    // gaussians: Array[MultivariateGaussian], each containing a mean Vector 
and covariance Matrix
-    size += SizeEstimator.estimate((weights, gaussians))
+    size += SizeEstimator.estimate(weights)
+    // gaussians: Array[MultivariateGaussian], each with mean: Vector and cov: 
Matrix
+    gaussians.foreach(gaussian => size += 
SizeEstimator.estimate((gaussian.mean, gaussian.cov)))
     size
   }
 
diff --git a/mllib/src/main/scala/org/apache/spark/ml/clustering/KMeans.scala 
b/mllib/src/main/scala/org/apache/spark/ml/clustering/KMeans.scala
index 243077374296..5d410f77c7eb 100644
--- a/mllib/src/main/scala/org/apache/spark/ml/clustering/KMeans.scala
+++ b/mllib/src/main/scala/org/apache/spark/ml/clustering/KMeans.scala
@@ -216,9 +216,15 @@ class KMeansModel private[ml] (
   private[spark] override def estimatedSize: Long = {
     var size = estimateMatadataSize
     if (parentModel != null) {
-      // parentModel contains clusterCenters, distanceMeasure, trainingCost, 
and numIter.
-      // It also has transient derived fields for distance calculation and 
statistics.
-      size += SizeEstimator.estimate(parentModel)
+      // clusterCenters: Array[Vector]
+      // distanceMeasure: String
+      // trainingCost: Double
+      // numIter: Int
+      size += SizeEstimator.estimate((
+        parentModel.clusterCenters,
+        parentModel.distanceMeasure,
+        parentModel.trainingCost,
+        parentModel.numIter))
     }
     size
   }
diff --git 
a/mllib/src/main/scala/org/apache/spark/mllib/clustering/BisectingKMeansModel.scala
 
b/mllib/src/main/scala/org/apache/spark/mllib/clustering/BisectingKMeansModel.scala
index 083c3e3e77a9..113445269ce7 100644
--- 
a/mllib/src/main/scala/org/apache/spark/mllib/clustering/BisectingKMeansModel.scala
+++ 
b/mllib/src/main/scala/org/apache/spark/mllib/clustering/BisectingKMeansModel.scala
@@ -41,7 +41,7 @@ import org.apache.spark.util.ArrayImplicits._
  */
 @Since("1.6.0")
 class BisectingKMeansModel private[clustering] (
-    private[clustering] val root: ClusteringTreeNode,
+    private[spark] val root: ClusteringTreeNode,
     @Since("2.4.0") val distanceMeasure: String,
     @Since("3.0.0") val trainingCost: Double
   ) extends Serializable with Saveable with Logging {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to