Julia 中的并行梯度计算

2024-03-12

不久前我被说服放弃我舒适的 matlab 编程并开始使用 Julia 编程。我已经在神经网络方面工作了很长时间,我认为现在有了 Julia,我可以通过并行计算梯度来更快地完成工作。

不需要一次性对整个数据集计算梯度;相反,我们可以拆分计算。例如,通过将数据集分成几部分,我们可以计算每个部分的部分梯度。然后通过将部分梯度相加来计算总梯度。

虽然原理很简单,但当我与 Julia 并行时,性能会下降,即一个进程比两个进程更快!我显然做错了什么......我已经咨询了论坛中提出的其他问题,但我仍然无法拼凑出答案。我认为我的问题在于有很多不必要的数据正在移动,但我无法正确修复它。

为了避免发布混乱的神经网络代码,我在下面发布了一个更简单的示例,该示例在线性回归设置中复制了我的问题。

下面的代码块为线性回归问题创建一些数据。代码解释了常量,但是X是包含数据输入的矩阵。我们随机创建一个权重向量w当乘以X创建一些目标Y.

######################################
## CREATE LINEAR REGRESSION PROBLEM ##
######################################

# This code implements a simple linear regression problem

MAXITER = 100   # number of iterations for simple gradient descent
N = 10000       # number of data items
D = 50          # dimension of data items
X = randn(N, D) # create random matrix of data, data items appear row-wise
Wtrue = randn(D,1) # create arbitrary weight matrix to generate targets
Y = X*Wtrue     # generate targets

下面的下一个代码块定义了用于测量回归的适合度(即负对数似然)和权重向量的梯度的函数w:

####################################
##       DEFINE FUNCTIONS         ##
####################################

@everywhere  begin

  #-------------------------------------------------------------------
  function negative_loglikelihood(Y,X,W)
  #-------------------------------------------------------------------

    # number of data items
    N  = size(X,1)
    # accumulate here log-likelihood
    ll = 0
    for nn=1:N
      ll = ll - 0.5*sum((Y[nn,:] - X[nn,:]*W).^2)
    end

    return ll
  end


  #-------------------------------------------------------------------
  function negative_loglikelihood_grad(Y,X,W, first_index,last_index)
  #-------------------------------------------------------------------

    # number of data items
    N  = size(X,1)
    # accumulate here gradient contributions by each data item
    grad = zeros(similar(W))
    for nn=first_index:last_index
      grad = grad +  X[nn,:]' * (Y[nn,:] - X[nn,:]*W)
    end

    return grad
  end


end

请注意,上述函数是故意未矢量化的!我选择不进行矢量化,因为最终代码(神经网络情况)也不会接受任何矢量化(让我们不要深入了解这方面的更多细节)。

最后,下面的代码块显示了一个非常简单的梯度下降,试图恢复参数权重向量w从给定的数据Y and X:

####################################
##     SOLVE LINEAR REGRESSION    ##
####################################


# start from random initial solution
W = randn(D,1)

# learning rate, set here to some arbitrary small constant
eta = 0.000001

# the following for-loop implements simple gradient descent
for iter=1:MAXITER

  # get gradient
  ref_array = Array(RemoteRef, nworkers())

  # let each worker process part of matrix X
  for index=1:length(workers())

    # first index of subset of X that worker should work on
    first_index       = (index-1)*int(ceil(N/nworkers())) + 1
    # last index of subset of X that worker should work on
    last_index        = min((index)*(int(ceil(N/nworkers()))), N)

    ref_array[index] = @spawn negative_loglikelihood_grad(Y,X,W, first_index,last_index)
  end

  # gather the gradients calculated on parts of matrix X
  grad = zeros(similar(W))
  for index=1:length(workers())
    grad = grad + fetch(ref_array[index])
  end

  # now that we have the gradient we can update parameters W
  W = W + eta*grad;

  # report progress, monitor optimisation
  @printf("Iter %d neg_loglikel=%.4f\n",iter, negative_loglikelihood(Y,X,W))
end

正如希望可见的那样,我尝试在这里以最简单的方式并行计算梯度。我的策略是在尽可能多的可用工作人员的部分中打破梯度的计算。每个工作人员只需要对矩阵 X 的一部分进行工作,该部分由第一个索引 and 最后索引。因此,每个工人都应该与X[first_index:last_index,:]。例如,对于 4 个工人,N = 10000,工作应划分如下:

  • 工人 1 => 第一个索引 = 1,最后一个索引 = 2500
  • 工人 2 => 第一个索引 = 2501,最后一个索引 = 5000
  • 工人 3 => 第一个索引 = 5001,最后一个索引 = 7500
  • 工人 4 => 第一个索引 = 7501,最后一个索引 = 10000

不幸的是,如果我只有一名工作人员,则整个代码的运行速度会更快。如果通过添加更多工人addprocs(),代码运行速度较慢。人们可以通过创建更多数据项来加剧这一问题,例如使用N=20000。 随着数据项的增加,性能下降更加明显。 在我的特定计算环境中N=20000和一个核心,代码运行时间约为 9 秒。和N=200004 核则需要约 18 秒!

受到这个论坛中的问题和答案的启发,我尝试了很多不同的方法,但不幸的是没有成功。我意识到并行化很幼稚,数据移动一定是问题所在,但我不知道如何正确地做到这一点。似乎关于这个问题的文档也有点稀缺(Ivo Balbaert 的好书也是如此)。

我非常感谢您的帮助,因为我已经被困在这个问题上很长一段时间了,我的工作真的需要它。对于任何想要运行代码的人,为了省去复制粘贴的麻烦,您可以获得代码here https://www.dropbox.com/s/e22apyk5t3ucymz/simpleCode_1st_parallel_attempt.jl?dl=0.

感谢您花时间阅读这个很长的问题!帮助我将其变成一个模型答案,任何刚接触 Julia 的人都可以参考!


我想说,GD 并不是使用任何建议的方法进行并行化的良好候选者:要么SharedArray or DistributedArray,或者自己实现数据块的分布。

问题不在于Julia,而在于GD算法。 考虑代码:

主要流程:

for iter = 1:iterations #iterations: "the more the better"
    δ = _gradient_descent_shared(X, y, θ) 
    θ = θ -  α * (δ/N)   
end

问题出在上面的for循环中,这是必须的。不管多好_gradient_descent_shared也就是说,迭代总数扼杀了并行化的崇高概念。

阅读问题和上述建议后,我开始使用SharedArray。请注意,我不是 SharedArrays 领域的专家。

主要流程部分(简单实现,无需正则化):

run_gradient_descent(X::SharedArray, y::SharedArray, θ::SharedArray, α, iterations) = begin
  N = length(y)

  for iter = 1:iterations 
    δ = _gradient_descent_shared(X, y, θ) 
    θ = θ -  α * (δ/N)   
  end     
  θ
end

_gradient_descent_shared(X::SharedArray, y::SharedArray, θ::SharedArray, op=(+)) = begin            
    if size(X,1) <= length(procs(X))
        return _gradient_descent_serial(X, y, θ)
    else
        rrefs = map(p -> (@spawnat p _gradient_descent_serial(X, y, θ)), procs(X))
        return mapreduce(r -> fetch(r), op, rrefs)
    end 
end

所有工人通用的代码:

#= Returns the range of indices of a chunk for every worker on which it can work. 
The function splits data examples (N rows into chunks), 
not the parts of the particular example (features dimensionality remains intact).=# 
@everywhere function _worker_range(S::SharedArray)
    idx = indexpids(S)
    if idx == 0
        return 1:size(S,1), 1:size(S,2)
    end
    nchunks = length(procs(S))
    splits = [round(Int, s) for s in linspace(0,size(S,1),nchunks+1)]
    splits[idx]+1:splits[idx+1], 1:size(S,2)
end

#Computations on the chunk of the all data.
@everywhere _gradient_descent_serial(X::SharedArray, y::SharedArray, θ::SharedArray)  = begin
    prange = _worker_range(X)

    pX = sdata(X[prange[1], prange[2]])
    py = sdata(y[prange[1],:])

    tempδ = pX' * (pX * sdata(θ) .- py)         
end

数据加载和训练。让我假设我们有:

  • X::Array 中大小为 (N,D) 的特征,其中 N - 示例数量,特征的 D 维
  • y::Array 中大小为 (N,1) 的标签

主要代码可能如下所示:

X=[ones(size(X,1)) X] #adding the artificial coordinate 
N, D = size(X)
MAXITER = 500
α = 0.01

initialθ = SharedArray(Float64, (D,1))
sX = convert(SharedArray, X)
sy = convert(SharedArray, y)  
X = nothing
y = nothing
gc() 

finalθ = run_gradient_descent(sX, sy, initialθ, α, MAXITER);

实施此操作并运行(在我的 Intel Clore i7 的 8 核上)后,我在训练多类(19 类)训练数据上比串行 GD(1 核)获得了非常轻微的加速(串行 GD 为 715 秒/665 秒为共享GD)。

如果我的实现是正确的(请检查一下 - 我指望它),那么 GD 算法的并行化就不值得了。当然,在 1 核上使用随机 GD 可能会获得更好的加速。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Julia 中的并行梯度计算 的相关文章

  • Julia 中过时的软件包列表

    有没有办法列出 Julia 中所有过时的软件包 相当于pip3 list outdated在Python中 我做了几次搜索 1 https docs julialang org en v1 stdlib Pkg 2 https pkgdoc
  • 用顶点之间的渐变填充 matplotlib 多边形

    我正在使用 matplotlib 的 Poly3DCollection 绘制多边形 三角形 的集合 三角形位于具有与其关联的颜色的顶点之间 我目前正在用通过平均三个顶点的颜色确定的纯色填充每个三角形 绘制三角形以形成 3D 表面网格 I w
  • 如何在 Julia 中创建一个数组?

    在许多机器学习用例中 您需要创建一个充满 1 且具有特定维度的数组 在Python中 我会使用np ones 2 1 Julia 中的模拟版本是什么 朱莉娅有一个内置的ones可以使用如下函数 julia gt ones 1 2 1 2 M
  • 线程与并行处理

    Microsoft NET 4 0 为其框架引入了新的 并行增强功能 我想知道使用标准 System Threading 函数与新的并行增强功能创建应用程序之间有什么区别 并行扩展和常规线程之间最重要的区别可能是控制流 一个线程 使用创建n
  • 如何在R中制作渐变颜色填充时间序列图

    How to 填充区域 sp 线下方和上方渐变色 这个例子是在 Inkscape 中绘制的 但我需要垂直渐变 不是水平的 间隔从zero to positive 来自white to red 间隔从zero to negative 来自wh
  • OpenMP 动态调度与引导调度

    我正在研究 OpenMP 的调度 特别是不同的类型 我了解每种类型的一般行为 但澄清一下何时进行选择会很有帮助dynamic and guided调度 英特尔的文档 https software intel com en us articl
  • 用以前的非缺失值填充“缺失”值的有效方法是什么?

    我有一个向量 using Missings v allowmissing rand 100 v rand 100 lt 0 1 missing 最好的填充方式是什么v与最后一个非缺失值 现在 for i val in enumerate v
  • 垂直和水平平行度

    最近在并行领域工作 我了解到有两个术语 垂直并行 和 水平并行 有人说openmp 共享内存并行 是垂直并行 而mpi 分布式内存并行 是水平并行 为什么这些术语这么称呼 我不明白原因 这么称呼它们只是术语吗 这些术语似乎没有被广泛使用 也
  • 如何给DArray的元素设置值?

    我正在探索 Julia 的并行计算并尝试了以下方法 a dzeros 5 a 1 5 但刚刚收到此错误 setindex not defined for DArray Float64 1 Array Float64 1 嗯 我以为手册上说s
  • ElasticSearch 多滚动 Java API

    我想从索引中获取所有数据 由于项目数量对于内存来说太大 我使用滚动 很好的功能 client prepareSearch index setTypes myType setSearchType SearchType SCAN setScro
  • python 线程是如何工作的?

    我想知道 python 线程是并发运行还是并行运行 例如 如果我有两个任务并在两个线程中运行它们 它们是同时运行还是计划同时运行 我知道GIL并且线程仅使用一个 CPU 核心 这是一个复杂的问题 需要大量解释 我将坚持使用 CPython
  • Android 创建类似 iphone 的渐变

    我需要在我的 Android 应用程序中创建类似黑色 iphone 的渐变 请查看下图中顶部的黑色渐变 怎么做 谢谢 也许是这样的
  • 单机Octave并行计算——包和示例

    我想在单台机器 而不是集群 上并行化 Octave 中的 for 循环 前段时间我问了一个关于Octave并行版本的问题Octave并行计算 https stackoverflow com questions 7047840 paralle
  • cuda中内核的并行执行

    可以说我有三个全局数组 它们已使用 cudaMemcpy 复制到 GPU 中 但 c 中的这些全局数组尚未使用 cudaHostAlloc 分配 以便分配页面锁定的内存 而不是简单的全局分配 int a 100 b 100 c 100 cu
  • Java 中的并行编程

    我们如何在Java中进行并行编程 有什么特殊的框架吗 我们怎样才能让这些东西发挥作用 我会告诉你们我需要什么 认为我开发了一个网络爬虫 它从互联网上爬取了大量数据 一个爬行系统无法使事情正常工作 因此我需要更多的系统并行工作 如果是这种情况
  • 为什么 Julia 中的“where”语法对换行符敏感?

    在 Stack Overflow 上的另一个问题中 答案包括以下函数 julia gt function nzcols b SubArray T 2 P Tuple UnitRange Int64 UnitRange Int64 where
  • 如何在Python中使用多处理来加速循环执行

    我有两个清单 列表 A 包含 500 个单词 列表 B 包含 10000 个单词 我正在尝试为列表 A 找到与 B 相关的相似单词 我正在使用 Spacy 的相似函数 我面临的问题是计算需要很长时间 我是多处理使用的新手 因此请求帮助 如何
  • C# 的快速线程安全随机数生成器

    我需要在多个正在运行的线程中快速生成随机浮点数 我尝试过使用System Random 但它对于我的需求来说太慢了 并且它在多个线程中返回相同的数字 当我在单线程中运行应用程序时 它工作正常 此外 我需要确保生成的数字在 0 到 100 之
  • 在 Julia 中解压缩元组数组

    假设我有一个元组数组 arr 1 2 3 4 5 6 使用 python 我可以做zip arr 1 3 5 2 4 6 朱莉娅中与此等效的是什么 作为 splatting 的替代方案 因为这非常慢 您可以执行以下操作 unzip a ma
  • 使用 MPI 的 Allreduce 对 Python 对象求和

    我正在使用使用 Python 中的字典和计数器构建的稀疏张量数组操作 我想让并行使用这个数组操作成为可能 最重要的是 我最终在每个节点上都有计数器 我想使用 MPI Allreduce 或另一个不错的解决方案 将其添加在一起 例如 使用计数

随机推荐

  • 逻辑或在doctrine2 getRepository->findBy()

    如何像doctrine2中那样编写查询 SELECT from table where field value1 or field value2 我发现了类似的东西 em gt getRepository myentitity gt fin
  • 从 Selenium WebDriver 中的 WebElement 获取 CSS 选择器字符串 - Java

    我有一个 WebElement 我只是想提取 CSS 选择器字符串 这是我调试代码时变量的值 ChromeDriver MAC 上的 chrome 345345345n5435345b34 gt css 选择器 div class 警报警报
  • UICollectionView 自动调整大小和动态行数

    我正在尝试做这样的事情 Basically I am using a UICollectionView and the cells 3 diferent xib 到目前为止 它有效 我想做的事情是 Set a autoheight 如果旋转
  • 使用c#泛型继承,而类类型是继承的

    在 C 中可能有这样的事情吗 假设我有这个 public class T U 我想要这个 public class A
  • scipy linregress 函数错误的标准错误返回?

    我遇到了一个奇怪的情况 scipy stats linregress 似乎返回了不正确的标准错误 from scipy import stats x 5 05 6 75 3 21 2 66 y 1 65 26 5 5 93 7 96 gra
  • mergeMap 和 mergeMapTo 有什么区别?

    在 rxjs5 文档中 它提到 为了减少多态性并从运算符中获得更好的性能 一些运算符已被拆分为多个运算符 它的实际含义是什么以及如何使用 mergeMapTo 运算符 来自docs http reactivex io rxjs class
  • 如何更改 Rust 字符串中特定索引处的字符?

    我正在尝试更改字符串中特定索引处的单个字符 但我不知道如何更改 例如 我如何将 hello world 中的第四个字符更改为 x 以便它成为 helxo world 最简单的方法是使用replace range https doc rust
  • 让模型监听嵌套模型和集合的最佳模式?

    使用 Backbone js 让模型监听所有嵌套模型和集合的最佳模式是什么 我应该将嵌套模型 集合放入属性中吗 我应该手动创建亲子关系并触发事件吗 与 Backbone js 的大多数事情一样 您不会得到 正确 的答案 但我可以分享我是如何
  • 在一定规则下动态创建数组

    我需要创建具有遵循这些模式的某些值 属性的数组 不幸的是 我的数学知识不允许我找到规律 以下是应在 从下到上 n 1 2 和 3 处输出的数组示例 计算每条边上的红色框 所有红色和绿色方块都应该分配一些值 而所有白色方块都需要未定义 或空
  • 参数里面的冒号是什么意思? [复制]

    这个问题在这里已经有答案了 words pron dict str 上的冒号是什么意思 我在 python 2 7 上遇到语法错误 是Python 3吗 我该如何使用它 class TextToSpeech CHUNK 1024 def i
  • DDD 中哪一层应该包含查询

    我有一个简单的 DDD 服务 带有文章聚合根 我使用 MediatR 和 CQRS 来分离命令和查询 在 DDD 域中不应依赖于应用程序和基础设施层 我有一个存储库 IArticleRepository 用于从文章数据库中组合一些数据 我有
  • 显示字符串的可能组合

    我试图获取一个字符串并显示它的可能组合 在 PHP 中 但同时按每个单词的顺序说出 例如 你好吗 将返回 一个数组 How are you How are are you how you are 我现在的代码显示了所有组合 但我希望它保持它
  • OnLoad方法和Load事件之间的区别?

    有什么区别OnLoad方法和Load事件 我正在开发 WinForm 控件 我应该注册到Load事件或覆盖OnLoad方法 每一种的优点和缺点是什么 我会去覆盖OnLoad 这样您就可以节省 CPU 周期来调用事件处理程序 如果您从控件继承
  • 单元测试 MVC 控制器

    我的 ASP NET MVC 应用程序中的控制器根据几个相当简单的规则预先填充我的视图显示的表单数据 在我的单元测试中涵盖这似乎是一件好事 但我能看到验证表单中是否放置了正确数据的唯一方法是以一种不自然的方式从控制器中提取逻辑 有人可以提出
  • 如何在Sphinx中展开侧边栏目录树上的所有小节?

    我想知道是否有一种方法可以扩展包含在标题下的所有小节index rst file 举个例子 它是这样的 Section 1 Section 2 Section 3 这就是我希望的样子 Section 1 Subsection 1 1 Sub
  • 如何手动渲染表单字段并设置其初始值?

    我正在尝试手动渲染表单的字段 以便我的设计师同事可以操作 HTML 中的输入元素 而不是在 Python 源代码中苦苦挣扎 IE 而不是像这样声明表单字段 form first name 其实我确实
  • 静态发布和 HTTPS

    遵循这个问题 大型网站上 Silverstripe 的静态发布 https stackoverflow com questions 46313840 static publishing in silverstripe on large si
  • 生成偶数随机数

    我需要一个代码来仅生成随机偶数 2 100网上有生成随机数的教程 但它们有奇数和偶数 请理解我只需要生成偶数 1 生成数字1 50 2 将所有数字乘以2 所有数字乘以 2 都是偶数
  • Selenium 使用相对 XPath 定位器显式等待

    将 Selenium WebDriver 与 Python 3 4 结合使用 我正在编写一个抓取工具 并使用相对于某些非根祖先元素的 XPath 来定位元素 如下所示 ancestor element driver find element
  • Julia 中的并行梯度计算

    不久前我被说服放弃我舒适的 matlab 编程并开始使用 Julia 编程 我已经在神经网络方面工作了很长时间 我认为现在有了 Julia 我可以通过并行计算梯度来更快地完成工作 不需要一次性对整个数据集计算梯度 相反 我们可以拆分计算 例