[英]Spark StringIndexer.fit is very slow on large records
我有大型數據記錄格式如下:
// +---+------+------+
// |cid|itemId|bought|
// +---+------+------+
// |abc| 123| true|
// |abc| 345| true|
// |abc| 567| true|
// |def| 123| true|
// |def| 345| true|
// |def| 567| true|
// |def| 789| false|
// +---+------+------+
cid
和itemId
是字符串。
有965,964,223條記錄。
我試圖使用StringIndexer
將cid
轉換為整數,如下所示:
dataset.repartition(50)
val cidIndexer = new StringIndexer().setInputCol("cid").setOutputCol("cidIndex")
val cidIndexedMatrix = cidIndexer.fit(dataset).transform(dataset)
但是這些代碼行很慢(大約需要30分鍾)。 問題是它太大了,以至於我之后無法做任何事情。
我正在使用具有2個節點(61 GB內存)的R4 2XLarge集群的亞馬遜EMR集群。
我可以做進一步的性能提升嗎? 任何幫助都感激不盡。
如果列的基數很高,那么這是預期的行為。 作為培訓過程的一部分, StringIndexer
收集所有標簽,並創建標簽 - 索引映射(使用Spark的oasutil.collection.OpenHashMap
)。
在最壞的情況下,該過程需要O(N)存儲器,並且在計算和存儲器方面都是密集的。
如果列的基數很高,並且其內容將用作特征,則最好應用FeatureHasher
(Spark 2.3或更高版本)。
import org.apache.spark.ml.feature.FeatureHasher
val hasher = new FeatureHasher()
.setInputCols("cid")
.setOutputCols("cid_hash_vec")
hasher.transform(dataset)
它不保證唯一性並且不可逆,但它對於許多應用來說已經足夠好了,並且不需要裝配過程。
對於不會用作功能的列,您還可以使用hash
函數:
import org.apache.spark.sql.functions.hash
dataset.withColumn("cid_hash", hash($"cid"))
假如說:
cid
用作功能(在StringIndexer + OneHotEncoderEstimator之后) 先問幾個問題:
cid
列中有多少個不同的值? 在不知情的情況下,我的第一個猜測是你現在不應該擔心內存並首先檢查你的並行度。 您只有2個R4 2XLarge
實例可以為您提供:
就個人而言,我會嘗試:
R4 2XLarge
實例與具有更多CPU的其他實例交換 不幸的是,對於目前的EMR產品,這只能通過在問題上投入資金來實現:
最后,需要repartition(50)
? 這可能會引入進一步的延誤......
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.