Spark MLLib的LassoWithSGD无法扩展?

时间:2015-11-08 00:34:10

标签: performance scala apache-spark apache-spark-mllib

我的代码类似于以下内容:

if (mAnswer.equals("no")) {
    System.out.println("Maybe next time");
    sayGoodbye();
}

我在我的虚拟服务器上使用20个核心在云中运行它。该文件是具有几百万行的“本地”(即不在HDFS中)文件。我在本地模式下运行,运行sbt(即我不使用集群,我不使用spark-submit)。

我希望这会越来越快,因为我将spark.master = local [*]设置从local [8]增加到local [40]。相反,无论我使用什么设置,它都需要相同的时间(但我从Spark UI中注意到,我的执行程序在任何给定时间的最大活动任务数等于预期数量,即本地约为8 [8],〜40为本地[40]等 - 所以似乎并行化有效。)

默认情况下,我的数据集RDD为4的分区数。我尝试将分区数量强制为20,但没有成功 - 事实上它使Lasso算法的速度降低了更多......

我对缩放过程的期望是否不正确?有人可以帮我解决这个问题吗?

1 个答案:

答案 0 :(得分:3)

  

我对缩放过程的期望是否不正确?

好吧,有点儿。我希望你不介意我用一点Python来证明我的观点。

  1. 让我们慷慨,并说几百万行实际上是一千万。凭借40 000 000个值(拦截+ 3个特征+每行标签),它可以提供大约380 MB的数据(Java Doubledouble-precision 64-bit IEEE 754 floating point)。让我们创建一些虚拟数据:

    import numpy as np
    
    n = 10 * 1000**2
    X = np.random.uniform(size=(n, 4))  # Features
    y = np.random.uniform(size=(n, 1))  # Labels
    theta = np.random.uniform(size=(4, 1))  # Estimated parameters
    
  2. 梯度下降的每一步(因为miniBatchFraction的默认LassoWithSGD是1.0,它实际上并不是随机的)忽略正则化需要像这样的操作。

    def step(X, y, theta):
        return ((X.dot(theta) - y) * X).sum(0)
    

    因此,让我们看看我们的数据在本地需要多长时间:

    %timeit -n 15 step(X, y, theta)
    ## 15 loops, best of 3: 743 ms per loop
    

    每步不到一秒,没有任何额外的优化。直觉上它非常快,并且不容易匹配。只是为了好玩,让我们看看为这样的数据获取封闭的表单解决方案需要多少

    %timeit -n 15 np.linalg.inv(X.transpose().dot(X)).dot(X.transpose()).dot(y)
    ## 15 loops, best of 3: 1.33 s per loop
    
  3. 现在让我们回到Spark。可以并行计算单个点的残差。因此,当您增加并行处理的分区数时,这是一个线性扩展的部分。

    问题是您必须在本地聚合数据,序列化,传输到驱动程序,反序列化并在本地减少以在每个步骤后获得最终结果。然后你有计算新的theta,序列化发回等等。

    所有这一切都可以通过正确使用迷你批次和一些进一步的优化来改善,但在一天结束时,您受到整个系统的延迟的限制。值得注意的是,当您增加工作人员的并行性时,您还会增加必须在驱动程序上顺序执行的工作量,反之亦然。 Amdahl's law会以某种方式咬你。

    以上所有内容都忽略了实际的实现。

    现在让我们进行另一个实验。首先是一些虚拟数据:

    nCores = 8  # Number of cores on local machine I use for tests
    rdd = sc.parallelize([], nCores)
    

    和bechmark:

    %timeit -n 40 rdd.mapPartitions(lambda x: x).count()
    ## 40 loops, best of 3: 82.3 ms per loop
    

    这意味着有8个内核,没有任何实际处理或网络流量,我们通过增加Spark中的并行性来达到我们无法做得更好的程度(假设并行部分的线性可扩展性,743ms / 8 =每个分区92.875ms)

  4. 总结一下:

    • 如果使用梯度下降的封闭形式解决方案可以在本地轻松处理数据,那只是浪费时间。如果要增加并行性/减少延迟,可以使用良好的线性代数库
    • Spark旨在处理大量数据,以减少延迟。如果您的数据适合几年前智能手机的内存,那么这是一个不是正确工具的好兆头
    • 如果计算便宜,那么固定成本就成了限制因素

    附注:

    • 每台机器相对较多的核心通常不是最佳选择,除非您可以将其与IO吞吐量相匹配