我有大数据记录,其格式设置为以下示例:
// +---+------+------+
// |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条记录。
我正尝试使用cid
将StringIndexer
转换为整数,如下所示:
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集群。
是否可以进一步改善性能?任何帮助将不胜感激。
答案 0 :(得分:3)
如果列的基数很高,这是预期的行为。作为训练过程的一部分,StringIndexer
收集所有标签,并创建标签-索引映射(使用Spark的o.a.s.util.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"))
答案 1 :(得分:0)
假设:
cid
用作功能(在StringIndexer + OneHotEncoderEstimator之后)首先有几个问题:
cid
列中有多少个不同的值?在不了解更多信息的情况下,我的第一个猜测是您现在不必担心内存,而是先检查并行度。您只有2个R4 2XLarge
实例,它们将为您提供:
我个人会尝试:
R4 2XLarge
实例与具有更多CPU的其他实例交换不幸的是,在当前的EMR产品中,只能通过花钱解决问题来实现:
最后,repartition(50)
有什么需要? 可能只会引入更多延迟...