[英]java.lang.OutOfMemoryError: Java heap space AND org.apache.spark.shuffle.MetadataFetchFailedException: Missing an output location for shuffle 4
[英]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
我已經看到並嘗試了許多解決方案,但沒有任何結果。 他們之中:
總是同樣的錯誤! 怎么可能吹走所有這些記憶? 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.