我有一个非常宽的数据框,带有标签列。我想独立地为每个列运行逻辑回归。我正试图找到最有效的方法来并行运行它。
+----------+--------+--------+--------+-----+------------+
| features | label1 | label2 | label3 | ... | label30000 |
+----------+--------+--------+--------+-----+------------+
我最初的想法是使用ThreadPoolExecutor
,获取每列的结果,然后加入:
extract_prob = udf(lambda x: float(x[1]), FloatType())
def lr_for_column(argm):
col_name = argm[0]
test_res = argm[1]
lr = LogisticRegression(featuresCol="features", labelCol=col_name, regParam=0.1)
lrModel = lr.fit(tfidf)
res = lrModel.transform(test_tfidf)
test_res = test_res.join(res.select('id', 'probability'), on="id")
test_res = test_res.withColumn(col_name, extract_prob('probability')).drop("probability")
return test_res.select('id', col_name)
with futures.ThreadPoolExecutor(max_workers=100) as executor:
future_results = [executor.submit(lr_for_column, [colname, test_res]) for colname in list_of_label_columns]
futures.wait(future_results)
for future in future_results:
test_res = test_res.join(future.result(), on="id")
但这种方法效率不高。有更快的方法吗?
答案 0 :(得分:1)
考虑到使用ThreadPoolExecutor
- having 32 cores in total和200个分区无法获得的可用资源,您只能同时处理大约16%的数据,并且此分数只能变为更糟糕的是,如果数据增长。
如果你想训练30000个模型并使用默认的迭代次数(100,可能在实践中可能很低),你的Spark程序将提交大约3 000 000个作业(每次迭代创建一个单独的作业),每个只有一小部分可以同时处理 - 除非你添加更多资源,否则这对改进没有多大希望。
尽管你可以尝试一些事情:
如果您决定减少尺寸,请考虑采样以进一步减少训练数据的大小,从而减少分区数量并提高整体吞吐量。
如果您的数据存在强烈的线性趋势,即使样本较小也应该可见,而不会严重损失精确度。
考虑使用不需要多个作业的变体替换昂贵的pyspark.ml
算法,例如使用spark-sklearn
中的某些工具组合(您可以通过拟合{{创建集合模型) 1}}每个分区上的模型)。
超额订阅核心。例如,如果您有4个物理核心/节点,则允许8或16来计算IO等待时间。