[英]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.