我使用pyspark
中的Spark ML GBTClassifier在AWS EMR集群上的约400k行和~9k列的数据帧上训练二进制分类模型。我将此与我当前的解决方案进行比较,该解决方案在一个巨大的EC2上运行XGBoost,可以将整个数据帧放入内存中。
我希望我可以在Spark中更快地训练(并获得新的观察结果)因为它将是分布式/并行的。然而,当观察我的集群(通过神经节)时,我看到只有3-4个节点具有活动CPU,而其余节点只是坐在那里。事实上,从它的外观来看,它可能只使用一个节点进行实际训练。
我似乎无法在文档中找到有关节点限制或分区的任何内容或与此相关的任何内容。也许我只是误解了算法的实现,但我认为它的实现方式是可以并行化训练以利用Spark的EMR /集群方面。如果没有,这样做是否有任何优势,而不是仅仅在单个EC2的内存中进行?我想你不必将数据加载到内存中,但这并不是一个优势。
这是我的代码的一些样板。谢谢你的任何想法!
import pyspark
from pyspark.sql import SparkSession
from pyspark.sql.functions import udf
from pyspark.sql.types import DoubleType
from pyspark.ml.classification import GBTClassifier
from pyspark.ml.evaluation import BinaryClassificationEvaluator
# Start Spark context:
sc = pyspark.SparkContext()
sqlContext = SparkSession.builder.enableHiveSupport().getOrCreate()
# load data
df = sqlContext.sql('SELECT label, features FROM full_table WHERE train = 1')
df.cache()
print("training data loaded: {} rows".format(df.count()))
test_df = sqlContext.sql('SELECT label, features FROM full_table WHERE train = 0')
test_df.cache()
print("test data loaded: {} rows".format(test_df.count()))
#Create evaluator
evaluator = BinaryClassificationEvaluator()
evaluator.setRawPredictionCol('prob')
evaluator.setLabelCol('label')
# train model
gbt = GBTClassifier(maxIter=100,
maxDepth=3,
stepSize=0.1,
labelCol="label",
seed=42)
model = gbt.fit(df)
# get predictions
gbt_preds = model.transform(test_df)
gbt_preds.show(10)
# evaluate predictions
getprob=udf(lambda v:float(v[1]),DoubleType())
preds = gbt_preds.withColumn('prob', getprob('probability'))\
.drop('features', 'rawPrediction', 'probability', 'prediction')
preds.show(10)
auc = evaluator.evaluate(preds)
auc
旁注:我正在使用的表格已经过矢量化。该模型使用此代码运行,它运行缓慢(训练约10-15分钟)并且仅使用3-4个(或者可能只有一个)核心。
答案 0 :(得分:3)
感谢上面的澄清评论。
Spark的实现不一定比XGBoost快。事实上,我期待你所看到的。
最大的因素是XGBoost是专为Gradient Boosted Trees设计和编写的。另一方面,Spark更通用,并且很可能没有XGBoost所具有的那种优化。有关XGBoost和scikit-learn的分类器算法实现之间的区别,请参阅here。如果你想深入了解细节,你可以阅读论文,甚至是XGBoost和Spark实现背后的代码。
请记住,XGBoost也是并行/分布式的。它只在同一台机器上使用多个线程。当数据不适合单个机器时,Spark可帮助您运行算法。
我能想到的其他一些小问题是a)Spark确实有一个非平凡的启动时间。不同机器之间的通信也可以相加。 b)XGBoost是用C ++编写的,它通常适用于数值计算。
至于为什么Spark只使用3-4个核心,这取决于你的数据集大小是什么,它是如何在节点之间分配的,启动火花的执行器数量是多少,哪个阶段占用了大部分时间,内存配置等。您可以使用Spark UI来尝试找出正在发生的事情。很难说为什么在没有查看数据集的情况下会发生这种情况。
希望有所帮助。
编辑:我刚刚找到了一个很好的答案,比较了简单的Spark应用程序与独立的Java应用程序 - https://stackoverflow.com/a/49241051/5509005之间的执行时间。同样的原则同样适用于此,事实上更是如此,因为XGBoost经过了高度优化。