簡體   English   中英

Spark Java PCA:用於 shuffle 的 Java 堆空間和缺少輸出位置

[英]Spark Java PCA: Java Heap Space and Missing output location for shuffle

我嘗試在具有4.827 行和 40.107 列的數據幀上執行 PCA,但我出現了 Java 堆空間錯誤並且缺少 shuffle 的輸出位置(根據執行程序上的 sdterr 文件)。 該錯誤發生在 PCA 的“treeAggregate at RowMatrix.scala:122”階段。

集群

它是一個具有 16 個工作節點的獨立集群,每個節點有 1 個執行器,具有 4 個內核和 21.504 mb 內存。 主節點有 15g 內存,我用“Java -jar -Xmx15g myapp.jar”給出。 此外,“spark.sql.shuffle.partitions”為 192,“spark.driver.maxResultSize”為 6g。

簡化代碼

df1.persist (From the Storage Tab in spark UI it says it is 3Gb)
df2=df1.groupby(col1).pivot(col2).mean(col3) (This is a df with 4.827 columns and 40.107 rows)
df2.collectFirstColumnAsList
df3=df1.groupby(col2).pivot(col1).mean(col3) (This is a df with 40.107 columns and 4.827 rows)

-----it hangs here for around 1.5 hours creating metadata for upcoming dataframe-----

df4 = (..Imputer or na.fill on df3..)
df5 = (..VectorAssembler on df4..)
(..PCA on df5 with error Missing output location for shuffle..)
df1.unpersist

我已經看到並嘗試了許多解決方案,但沒有任何結果。 他們之中:

  1. 將 df5 或 df4 重新分區為 16、64、192、256、1000、4000(盡管數據看起來沒有傾斜)
  2. 將 spark.sql.shuffle.partitions 更改為 16、64、192、256、1000、4000
  3. 每個執行程序使用 1 個和 2 個內核,以便為每個任務提供更多內存。
  4. 有 2 個 2 核或 4 核的 executor。
  5. 將“spark.memory.fraction”更改為 0.8,將“spark.memory.storageFraction”更改為 0.4。

總是同樣的錯誤! 怎么可能吹走所有這些記憶? df 實際上可能不適合內存嗎? 如果您需要任何其他信息或打印屏幕,請告訴我。

編輯 1

我將集群更改為 2 個 spark 工作線程,每個執行程序帶有 spark.sql.shuffle.partitions=48。 每個執行器有 115g 和 8 個內核。 下面是我加載文件(2.2Gb)的代碼,將每一行轉換為密集向量並提供 PCA。

文件中的每一行都具有以下格式(4.568 行,每行 40.107 個雙精度值):

 "[x1,x2,x3,...]"

和代碼:

Dataset<Row> df1 = sp.read().format("com.databricks.spark.csv").option("header", "true").load("/home/ubuntu/yolo.csv");
StructType schema2 = new StructType(new StructField[] {
                        new StructField("intensity",new VectorUDT(),false,Metadata.empty())
            });
Dataset<Row> df = df1.map((Row originalrow) -> {
                    String yoho =originalrow.get(0).toString();
                    int sizeyoho=yoho.length();
                    String yohi = yoho.substring(1, sizeyoho-1);
                    String[] yi = yohi.split(",");
                    int s = yi.length;
                    double[] tmplist= new double[s];
                    for(int i=0;i<s;i++){
                        tmplist[i]=Double.parseDouble(yi[i]);
                    }
                    
                    Row newrow = RowFactory.create(Vectors.dense(tmplist));
                    return newrow;
            }, RowEncoder.apply(schema2));
PCAModel pcaexp = new PCA()
                    .setInputCol("intensity")
                    .setOutputCol("pcaFeatures")
                    .setK(2)
                    .fit(df);

我在 2 個工人之一的 stderr 上得到的確切錯誤是:

ERROR Executor: Exception in task 1.0 in stage 6.0 (TID 43)
java.lang.OutOfMemoryError
at java.io.ByteArrayOutputStream.hugeCapacity(ByteArrayOutputStream.java:123)
at java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:117)
at java.io.ByteArrayOutputStream.ensureCapacity(ByteArrayOutputStream.java:93)
at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:153)
at org.apache.spark.util.ByteBufferOutputStream.write(ByteBufferOutputStream.scala:41)
at java.io.ObjectOutputStream$BlockDataOutputStream.drain(ObjectOutputStream.java:1877)
at java.io.ObjectOutputStream$BlockDataOutputStream.setBlockDataMode(ObjectOutputStream.java:1786)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1189)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at org.apache.spark.serializer.JavaSerializationStream.writeObject(JavaSerializer.scala:43)
at org.apache.spark.serializer.JavaSerializerInstance.serialize(JavaSerializer.scala:100)
at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:456)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)

這是 SparkUI 的 Stages 選項卡:

階段表

這是失敗的階段(TreeAggregate at RowMatrix.scala:122):

樹聚合

編輯 2

安慰

火花階段

編輯 3

我讀取了整個文件,但每行只取 10 個值並創建密集向量。 我仍然遇到同樣的錯誤! 我有一個擁有 235g Ram 和 3 個工人(每個執行器有 4 個內核的執行器)和每個執行器 64g Ram 的主控器。 這怎么會發生? (不要忘記文件的總大小只有 2.3Gb!)

Dataset<Row> df1 = sp.read().format("com.databricks.spark.csv").option("header", "true").load("/home/ubuntu/yolo.csv");

StructType schema2 = new StructType(new StructField[] {
                        new StructField("intensity",new VectorUDT(),false,Metadata.empty())
            });
Dataset<Row> df = df1.map((Row originalrow) -> {
                    String yoho =originalrow.get(0).toString();
                    int sizeyoho=yoho.length();
                    String yohi = yoho.substring(1, sizeyoho-1);
                    String[] yi = yohi.split(",");//this string array has all 40.107 values
                    int s = yi.length;
                    double[] tmplist= new double[s];
                    for(int i=0;i<10;i++){//I narrow it down to take only the first 10 values of each row
                        tmplist[i]=Double.parseDouble(yi[i]);
                    }
                    Row newrow = RowFactory.create(Vectors.dense(tmplist));
                    return newrow;
            }, RowEncoder.apply(schema2));
      
PCAModel pcaexp = new PCA()
                    .setInputCol("intensity")
                    .setOutputCol("pcaFeatures")
                    .setK(2)
                    .fit(df);

當您的 Spark 應用程序執行大型 shuffle 階段時,會發生“缺少 shuffle 的輸出位置” ,它會嘗試在執行程序之間重新分配大量數據,並且您的集群網絡存在一些問題。

Spark 說你在某個階段沒有記憶。 您正在執行需要不同階段的轉換,並且它們也消耗內存。 此外,您首先要持久化數據幀,並且您應該檢查存儲級別,因為您有可能在內存中持久化。

您正在鏈接多個 Spark 范圍的轉換:執行第一個樞軸階段,例如,Spark 創建一個階段並執行 shuffle 以對您的列進行分組,也許您有數據傾斜,並且有些執行程序比其他執行程序消耗更多內存,也許錯誤可能發生在其中之一。

除了數據幀轉換之外,PCA 估計器將數據幀轉換為 RDD,從而增加了計算協方差矩陣的內存,並且它適用於非分布式NxN 元素的 Breeze 矩陣的密集表示。 例如,SVD 是用 Breeze 制作的。 這給其中一位執行者帶來了很大壓力。

也許您可以將結果數據幀保存在 HDFS(或其他)中,並執行 PCA 另一個 Spark 應用程序。

主要問題。 您擁有的是,在 de SVD 之前,算法需要計算 Grammian 矩陣,並且它使用來自 RDD 的 treeAggregate。 這將創建一個非常大的 Double 矩陣,該矩陣將發送給驅動程序,並且由於您的驅動程序沒有足夠的內存而出現錯誤。 您需要大幅增加驅動程序內存。 你有網絡錯誤,如果一個執行器失去連接,作業就會崩潰,它不會嘗試重新執行。

就個人而言,我會嘗試直接在驅動程序中的 Breeze(或 Smile)中執行 PCA,我的意思是,收集 RDD 字段,因為數據集比協方差矩陣小得多,並且使用 Float 表示手動執行。

僅使用 Breeze 計算 PCA 的代碼,既不使用 Spark 也不使用 TreeAgregation:

import breeze.linalg._
import breeze.linalg.svd._

object PCACode {
  
  def mean(v: Vector[Double]): Double = v.valuesIterator.sum / v.size

  def zeroMean(m: DenseMatrix[Double]): DenseMatrix[Double] = {
    val copy = m.copy
    for (c <- 0 until m.cols) {
      val col = copy(::, c)
      val colMean = mean(col)
      col -= colMean
    }
    copy
  }

  def pca(data: DenseMatrix[Double], components: Int): DenseMatrix[Double] = {
    val d = zeroMean(data)
    val SVD(_, _, v) = svd(d.t)
    val model = v(0 until components, ::)
    val filter = model.t * model
    filter * d
  }
  
  def main(args: Array[String]) : Unit = {
    val df : DataFrame = ???

    /** Collect the data and do the processing. Convert string to double, etc **/
    val data: Array[mutable.WrappedArray[Double]] =
      df.rdd.map(row => (row.getAs[mutable.WrappedArray[Double]](0))).collect()

    /** Once you have the Array, create the matrix and do the PCA **/
    val matrix = DenseMatrix(data.toSeq:_*)
    val pcaRes = pca(matrix, 2)

    println("result pca \n" + pcaRes)
  }
}

此代碼將在驅動程序中進行 PCA,檢查內存。 如果它崩潰了,它可以用 Float 精度來完成。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM