代码之家  ›  专栏  ›  技术社区  ›  Interfector

在Spark ML中,为什么要将StringIndexer安装在一个包含数百万disctinc值的列上,从而产生OOM错误?

  •  4
  • Interfector  · 技术社区  · 7 年前

    我想用斯帕克的 StringIndexer 列上具有约15.000.000个唯一字符串值的功能转换器。不管我投入了多少资源,Spark总是会因为某种内存不足的异常而死于非命。

    from pyspark.ml.feature import StringIndexer
    
    data = spark.read.parquet("s3://example/data-raw").select("user", "count")
    
    user_indexer = StringIndexer(inputCol="user", outputCol="user_idx")
    
    indexer_model = user_indexer.fit(data) # This never finishes
    
    indexer_model \
        .transform(data) \
        .write.parquet("s3://example/data-indexed")
    

    #
    # There is insufficient memory for the Java Runtime Environment to continue.
    # Native memory allocation (mmap) failed to map 268435456 bytes for committing reserved memory.
    # Possible reasons:
    #   The system is out of physical RAM or swap space
    #   In 32 bit mode, the process size limit was hit
    # Possible solutions:
    #   Reduce memory load on the system
    #   Increase physical memory or swap space
    #   Check if swap backing store is full
    #   Use 64 bit Java on a 64 bit OS
    #   Decrease Java heap size (-Xmx/-Xms)
    #   Decrease number of Java threads
    #   Decrease Java thread stack sizes (-Xss)
    #   Set larger code cache with -XX:ReservedCodeCacheSize=
    # This output file may be truncated or incomplete.
    #
    #  Out of Memory Error (os_linux.cpp:2657)
    

    现在,如果我尝试手动索引这些值并将它们存储在一个数据框中,所有的工作都像charm一样,都在亚马逊上 c3.2xlarge 工人。

    from pyspark.sql.functions import row_number
    from pyspark.sql.window import Window
    
    data = spark.read.parquet("s3://example/data-raw").select("user", "count")
    
    uid_map = data \
        .select("user") \
        .distinct() \
        .select("user", row_number().over(Window.orderBy("user")).alias("user_idx"))
    
    data.join(uid_map, "user", "inner").write.parquet("s3://example/data-indexed")
    

    1 回复  |  直到 7 年前
        1
  •  3
  •   Interfector    7 年前

    你之所以会犯OOM错误是因为在窗帘后面,斯帕克 StringIndexer 电话 countByValue

    有了15M个不同的值,您实际上在驱动程序上创建了一个巨大的映射,它将耗尽内存。。。一个简单的解决方法是增加驱动程序的内存。如果你使用spark submit,你可以使用 --driver-memory 16g . 您也可以使用 spark.driver.memory

    最后,让我谈谈你的解决办法。在你的窗口中,你实际上把你所有的15个类别放在一个分区上,从而放在一个执行器上。如果这个数字增加,它就不会扩大。此外,使用非分区窗口通常是一个坏主意,因为它可以防止并行计算(除了将所有内容放在同一个分区上,这可能会导致OOM错误)。我会计算你的 uid_map

    # if you don't need consecutive indices
    uid_map = data\
        .select("user")\
        .distinct()\
        .withColumn("user_idx", monotonically_increasing_id())
    
    # if you do, you need to use RDDs
    uid_rdd = data\
        .select("user")\
        .distinct()\
        .rdd.map(lambda x : x["user"])\
        .zipWithIndex()
    uid_map = spark.createDataFrame(uid_rdd, ["user", "user_idx"])
    
    推荐文章