如何将大型python模型应用于pyspark-dataframe?

时间:2019-05-15 15:51:32

标签: python apache-spark machine-learning pyspark pyspark-sql

我有:

  • 包含某些数据(功能)的大型数据框(镶木地板格式,100.000.000行,4.5TB大小)
  • 几个大型ML模型(每个模型需要5-15GB的RAM)
  • 火花群集(AWS EMR),典型的节点配置是8 CPU,32 RAM,可以根据需要进行更改。

我想使用PySpark来应用它们,但是我总是会遇到一些有线错误,例如:

  • OOM
  • 随机超时(节点不返回任何结果)->节点被YARN管理器杀死

我通常使用类似

的代码
def apply_model(partition):
    model = load(...)  # load model only when apply this function to avoid serialization issue
    for row in partition:
        yield model.infer(row)

def apply_model(partition):
    model = load(...)  # load model only when apply this function to 
    yield from model.infer(partition)

并使用

应用

df.select(...).rdd.mapPartitions(apply_model)

由于序列化的原因,我无法broadcast进行建模。

问题-如何将基于python / any-non-jvm的大型模型应用于触发数据帧并避免触发异常?

4 个答案:

答案 0 :(得分:3)

以下是一些其他建议,可以帮助您改善工作绩效:

  • 我要做的第一个更改是减小分区大小。如果我现在正确理解,您输入的数据为4.5TB。这意味着,如果您有1000个分区,那么最终将在每个执行器上为每个分区发送4,5GB!此大小被认为是large,我将尝试将分区大小保持在250-500MB之间。在您的情况下,这大概意味着约10000个(4.5TB / 500MB)分区。

  • 通过添加更多执行程序来提高并行度。这将增加数据locality的级别,从而减少执行时间。理想情况下,每个执行器应有5个核,每个群集节点应有两个执行器(如果可能)。每个执行程序的最大内核数不应大于5,因为这会导致I / O瓶颈(当/如果使用磁盘存储,则为)。

  • 至于记忆,我认为来自@rluta的建议已经足够了。通常,执行程序的内存值太大会对Java GC时间产生负面影响,因此,spark.executor.memory的理想值是10GB。

答案 1 :(得分:2)

当您在具有mapPartitions的分区上应用python函数时,Spark将在伴随的python进程中为每个基于JVM的执行程序评估它。

通常,python进程使用的内存很小,并且恰好在EMR上使用的YARN memoryOverhead设置之内。在您的特定情况下,此假设不成立,因为python进程必须将大型模型保存在内存中,因此您需要调整配置。

如果每个执行器主机坚持8 CPU / 32G RAM,则可以尝试以下操作作为基本配置:

spark.executor.cores=6
spark.executor.memory=8G
spark.executor.pyspark.memory=20G

请注意,设置spark.executor.pyspark.memory将对python进程的内存使用量施加硬限制(默认情况下是无限制的),因此您可能需要尝试找到适合您的进程的约束。

另一种配置是保持pyspark进程内存不受限制,但保留足够的YARN memoryOverhead来容纳它,例如:

spark.executor.cores=6
spark.executor.memory=8G
spark.executor.memoryOverhead=22G

答案 2 :(得分:0)

您的分区是否适合单个执行器的内存? 您可以尝试增加分区的数量,看看情况是否有所改善:

df.select(...).repartition(1000).rdd.mapPartitions(apply_model)

通过查看Spark UI的指标来证明这是一种改进,例如:

  • 输入大小/记录
  • 随机写入大小/记录
  • GC时间

比较Median75th percentileMax的值以查看您的数据是否不倾斜。

答案 3 :(得分:0)

如果我正确理解了您的问题,则说明您已经构建了模型,需要将其应用于4.5TB的数据。如果是这样,那么您可以像流一样处理它。您将文件划分为可管理的大小,然后提供目录以供Spark Stream读取和处理。

5/23: 根据我对mapPartition的理解,您将完全读取数据,然后将其拆分,这就是为什么内存溢出的原因。如在流中一样,您将创建一小批输入数据,然后一次处理该小批数据。参考:https://spark.apache.org/docs/2.2.0/structured-streaming-programming-guide.html#basic-concepts

如果您可以将大数据文件分解为一个较小的文件,然后如下面的链接所示,则可以一次处理一个文件,也可以一次处理一堆,直到大数据被完全使用。参考: https://spark.apache.org/docs/2.2.0/structured-streaming-programming-guide.html#input-sources