Spark LDA消耗太多内存

时间:2016-03-14 03:59:21

标签: apache-spark apache-spark-mllib lda

我正在尝试使用spark mllib lda来总结我的文档语料库。

我的问题设置如下所示。

  • 约100,000份文件
  • 约400,000个独特单词
  • 100 cluster

我有16台服务器(每台服务器有20个核心和128GB内存)。

当我用OnlineLDAOptimizer执行LDA时,它会给出内存不足错误,建议我增加spark.driver.maxResultSize之类的 11个任务(1302 MB)的序列化结果总大小大于spark.driver.maxResultSize

我将spark.driver.maxResultSize增加到120GB(以及spark.driver.memory增加到120GB)并重新运行LDA但不缺。 它仍然说 11个任务(120.1 GB)的序列化结果的总大小大于spark.driver.maxResultSize

我尝试了另外一个包含大约100,000个独特单词的数据集,并且有效。

那么,在使用Spark mllib LDA时,如何估计内存使用量?我在官方文档中找不到任何规范。

注意我使用稀疏向量来构造传递给RDD[(Long, Vector)]的docuemnt LDA.run(),但不知道spark lda是否可以在内部正确处理稀疏格式。

(编辑)我使用了Scala版本的LDA。不是Python版本。

这可能是一个相关的问题,但没有给出明确的答案。 Spark LDA woes - prediction and OOM questions

(编辑)的

这是我的代码片段(要点)。 https://gist.github.com/lucidfrontier45/11420721c0078c5b7415

def startJob(args: RunArgs)(implicit sc: SparkContext): Unit = {
    val src = sc.textFile(args.fname, minPartitions = args.n_partitions).map(_.split("\t"))
        .flatMap {
            // input file's format is (user_id, product_name, count)
            case Array(u, p, r, t) => Some((u.toInt, p.toInt, r.toDouble))
            case _ => None
        }.persist()

    // Map to convert user_id or product_name into unique sequencential id
    val userid_map = src.map(_._1).distinct().zipWithIndex().collect().toMap
    val productid_map = src.map(_._2).distinct().zipWithIndex().collect().toMap
    val inverse_userid_map = userid_map.map(_.swap)

    // broadcat to speedup RDD map operation
    val b_userid_map = sc.broadcast(userid_map)
    val b_productid_map = sc.broadcast(productid_map)
    val b_inverse_userid_map = sc.broadcast(inverse_userid_map)

    // run map
    val transformed_src = src.map { case (u, p, r) =>
        (b_userid_map.value(u), b_productid_map.value(p).toInt, r)
    }

    println("unique items = %d".format(b_productid_map.value.size))

    // prepare for LDA input RDD[(LONG, Vector)]
    val documents = transformed_src.map { case (u, p, r) => (u, (p, r)) }
        .groupByKey()
        .map { t => (t._1, Vectors.sparse(b_productid_map.value.size, t._2.toSeq)) }.persist()

    documents.count()
    src.unpersist()

    // run Online Variational LDA
    val ldamodel = new LDA()
        .setK(args.k)
        .setMaxIterations(args.n_iter)
        .setOptimizer("online")
        .run(documents)
        .asInstanceOf[LocalLDAModel]


    val result = ldamodel.topicDistributions(documents)
        .map { case (i, v) =>
            val u = b_inverse_userid_map.value(i)
            "%d,%s".format(u, v.toArray.mkString(","))
        }
    result.saveAsTextFile(args.out)
}

实际上,我使用LDA来减少交易数据的尺寸。我的数据格式为(u, p, r) 其中u是用户ID,p是产品名称,r是用户up互动的数字。在这种情况下,user对应于文档和产品。由于用户ID和产品名称是任意字符串,因此在提交给LDA之前,我将它们转换为唯一的顺序整数。

谢谢。

1 个答案:

答案 0 :(得分:0)

此问题有三种常见原因,它们可能独立起作用,也可能协同作用。

  1. 该作业使用collect之类的东西向驱动程序返回了大量数据。 las,一些SparkML代码可以做到这一点。如果您不能将此归咎于下面的(2)或(3),则可能是您的数据与OnlineLDAOptimizer实现交互的结果。

  2. 该作业涉及大量任务,每个任务作为Spark作业管理的一部分将结果返回给驱动程序(与collect之类的东西相反)。检查SparkUI中的任务数。另请参阅Exceeding `spark.driver.maxResultSize` without bringing any data to the driver是堆栈跟踪中的org.apache.spark.scheduler.TaskSetManager#canFetchMoreResults还是org.apache.spark.scheduler.TaskResultGetter#enqueueSuccessfulTask

  3. 估计错误:Spark大大高估了将要返回给驱动程序的数据大小,并抛出此错误,以防止群集的驱动程序发生OOM。请参见What is spark.driver.maxResultSize?,对此进行测试的一种方法是将spark.driver.maxResultSize设置为0(无限制),然后看看会发生什么。

希望这会有所帮助!