【问题标题】:Spark ML Pipeline with RandomForest takes too long on 20MB dataset带有 RandomForest 的 Spark ML Pipeline 在 20MB 数据集上花费的时间太长
【发布时间】:2017-12-05 14:18:17
【问题描述】:

我正在使用 Spark ML 运行一些 ML 实验,在一个 20MB (Poker dataset) 的小数据集和一个带有参数网格的随机森林上,需要 1 小时 30 分钟才能完成。与 scikit-learn 类似,所需的时间要少得多。

在环境方面,我使用 2 个从属设备,每个 15GB 内存,24 个内核进行测试。我认为它不应该花那么长时间,我想知道问题是否出在我的代码中,因为我对 Spark 还很陌生。

这里是:

df = pd.read_csv(http://archive.ics.uci.edu/ml/machine-learning-databases/poker/poker-hand-testing.data)
dataframe = sqlContext.createDataFrame(df)

train, test = dataframe.randomSplit([0.7, 0.3])

columnTypes = dataframe.dtypes

for ct in columnTypes:
    if ct[1] == 'string' and ct[0] != 'label':
        categoricalCols += [ct[0]]
    elif ct[0] != 'label':
        numericCols += [ct[0]]

stages = []

for categoricalCol in categoricalCols:

    stringIndexer = StringIndexer(inputCol=categoricalCol, outputCol=categoricalCol+"Index")

stages += [stringIndexer]

assemblerInputs = map(lambda c: c + "Index", categoricalCols) + numericCols

assembler = VectorAssembler(inputCols=assemblerInputs, outputCol="features")

stages += [assembler]

labelIndexer = StringIndexer(inputCol='label', outputCol='indexedLabel', handleInvalid='skip')

stages += [labelIndexer]

estimator = RandomForestClassifier(labelCol="indexedLabel", featuresCol="features")

stages += [estimator]

parameters = {"maxDepth" : [3, 5, 10, 15], "maxBins" : [6, 12, 24, 32], "numTrees" : [3, 5, 10]}

paramGrid = ParamGridBuilder()
for key, value in parameters.iteritems():
    paramGrid.addGrid(estimator.getParam(key), value)
estimatorParamMaps = (paramGrid.build())

pipeline = Pipeline(stages=stages)

crossValidator = CrossValidator(estimator=pipeline, estimatorParamMaps=estimatorParamMaps, evaluator=MulticlassClassificationEvaluator(labelCol='indexedLabel', predictionCol='prediction', metricName='f1'), numFolds=3)

pipelineModel = crossValidator.fit(train)

predictions = pipelineModel.transform(test)

evaluator = pipeline.getEvaluator().evaluate(predictions)

在此先感谢,非常感谢任何 cmets/建议 :)

【问题讨论】:

  • 交叉验证是一项繁重而漫长的任务,因为它与您的 3 个超参数乘以折叠次数乘以训练每个模型所花费的时间的组合成正比。您可能希望先缓存每个示例的数据,但它仍然不会为您赢得太多时间。我相信火花对于如此大量的数据来说是一种过度杀伤力。您可能想改用 scikit learn 并使用 github.com/databricks/spark-sklearn 进行分布式本地模型训练
  • 嗨@eliasah 谢谢你的评论。事实上,我正在使用 spark-sklearn 做到这一点,并取得了不错的成绩。但是,我只是想比较 sklearn 和 spark 之间的执行时间,但这些数字对我来说似乎很奇怪,因为一个需要几秒钟,另一个需要几个小时
  • 因为spark会在假设数据是分布式且大的假设下,分别依次学习每个模型。
  • 当您读取优化的文件格式(如镶木地板)时,您可以从性能上受益。也可以调整 spark 本身,但在这里讨论太广泛了。
  • 是的。这是可能的。

标签: apache-spark pyspark apache-spark-mllib apache-spark-ml


【解决方案1】:

以下内容可能无法完全解决您的问题,但应该为您提供一些开始的指导。

您面临的第一个问题是数据量与资源不成比例

这意味着由于您正在并行化本地集合(pandas 数据帧),Spark 将使用默认的并行配置。这很可能导致48 分区少于0.5mb 每个分区。 (Spark 不擅长处理小文件和小分区)

第二个问题与 Spark 中树模型使用的昂贵的优化/近似技术有关。

Spark 树模型使用一些技巧来优化存储连续变量。对于小数据,仅获得精确的拆分要便宜得多。 在这种情况下,它主要使用近似分位数。

通常,在单机框架场景中,例如scikit,树模型使用连续特征的唯一特征值作为最佳拟合计算的分割候选。而在 Apache Spark 中,树模型使用每个特征的分位数作为分割候选。

还要补充一点,您也不应该忘记交叉验证是一项繁重而漫长的任务,因为它与您的 3 个超参数乘以折叠次数乘以训练每个模型所花费的时间的组合成正比(GridSearch方法)。您可能希望缓存每个示例的数据作为开始,但它仍然不会为您赢得太多时间。我相信火花对于如此大量的数据来说是一种过度杀伤力。您可能想改用 scikit learn 并使用 spark-sklearn 进行分布式本地模型训练。

Spark 将根据数据分布且大的假设分别并按顺序学习每个模型。

您当然可以使用基于列数据的文件格式(例如 parquet 和调整 spark 本身等)来优化性能。这里讨论的范围太广了。

您可以在以下博文中阅读有关使用 spark-mllib 进行树模型可扩展性的更多信息:

【讨论】:

  • parquet 等基于列的文件格式如何提高性能?或者 Parquet 如何具体提供帮助。我将 parquet 与 Scala Spark API 一起使用,但唯一的优点是我可以保存数据类型、使用更少的内存占用以及对数据进行分区。我没有想过 Parquet 对迭代算法有何帮助。只觉得对IO有用,但我知道我错了
  • @Angelito 使用 parquet 的底层数据序列化将缩短分区的洗牌时间,因为它“精简”了数据结构
猜你喜欢
  • 2017-11-17
  • 2017-05-20
  • 2023-04-04
  • 2019-10-23
  • 1970-01-01
  • 2018-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多