【问题标题】:Nested for loops using multiprocessing使用多处理嵌套 for 循环
【发布时间】:2016-11-01 18:22:21
【问题描述】:

我有一个关于 python 中的多处理的快速问题。

我正在对三个参数进行相当大的网格搜索,计算大约需要 14 小时才能完成。我想通过使用多处理来缩短运行时间。

我的代码的一个非常简化的例子在这里:

import numpy as np
import pickle
import time

a_range = np.arange(14, 18, 0.2)
b_range = np.arange(1000, 5000, 200)
c_range = np.arange(12, 21, .5)

a_position = range(len(a_range))
b_position = range(len(b_range))
c_position = range(len(c_range))

data_grid = np.zeros([len(a_range), len(b_range), len(c_range)])
record_data = []

start_time = time.time()

for (a,apos) in zip(a_range, a_position):
    for (b, bpos) in zip(b_range, b_position):
        for (c, cpos) in zip(c_range, c_position):
            example = a+b+c  #The math in my model is much more complex and takes
            #about 7-8 seconds to process
            data_grid[apos, bpos, cpos] = example
            record_data.append([a, b, c, example])

with open('Test_File', 'wb') as f: 
    pickle.dump(record_data, f) 

np.save('example_values', data_grid) 

print 'Code ran for ', round(time.time()-start_time,2), ' seconds'

现在,我在多处理方面的经验绝对为零,所以我的第一次尝试是将 for 循环更改为一个函数,然后像这样调用多处理函数:

def run_model(a, b, c, apos, bpos, cpos):
    example=a+b+c  
    data_grid[apos, bpos, cpos]=example
    record_data.append([a, b, c, example])

from multiprocessing import Pool

if __name__=='__main__':
    pool=Pool(processes=4)
    pool.map(run_model, [a_range, b_range, c_range, a_position, b_positon, c_positon])
    pool.close()
    pool.join()

但是在 pool.map 调用中失败了。我知道这个函数只需要一个可迭代的参数,但我不知道如何解决这个问题。我也怀疑 data_grid 变量是否会被正确填充。我想要从这个函数得到的结果是保存了两个文件,一个是一个值数组,其索引对应于 a、b 和 c 值,最后一个是包含 a、b、c 值和结果值的列表列表(例如在上面的代码中)

感谢您的帮助!

-会

【问题讨论】:

  • 我认为numpy.meshgrid 会有所帮助,但我现在无法尝试。看看吧。
  • 只是关于您如何使用 Pool 和 map 的评论:我想您可能需要从您的 run_model 函数返回值(而不是将值附加到 record_data)并在变量中获取结果喜欢res = p.map(f, [1, 2, 3])map 方法也采用(至少在 python 3 中)一个 chunksize 参数来分块你的 iterable
  • (我还没有看到您的 data_grid 对象也是全局变量,但您可能需要查看文档的 synchronization primitives 或共享 ctypes 对象部分在进程之间共享变量)
  • 谢谢mgc。我会调查这一切。

标签: python multiprocessing grid-search


【解决方案1】:

这并不能解决您的多处理问题,但可能会使您的处理速度更快。

您使用嵌套循环构造 n-d 坐标然后对它们进行操作的模式可以使用```numpy.meshgrid```` 进行矢量化d。在不知道您的实际计算的情况下,无法测试此方法。

import numpy as np
a = np.array([0,1,2])
b = np.array([10,11,12])
c = np.array([20,21,22])

x, y, z = np.meshgrid(a,b,c)

>>> x
array([[[0, 0, 0],
        [1, 1, 1],
        [2, 2, 2]],

       [[0, 0, 0],
        [1, 1, 1],
        [2, 2, 2]],

       [[0, 0, 0],
        [1, 1, 1],
        [2, 2, 2]]])
>>> y
array([[[10, 10, 10],
        [10, 10, 10],
        [10, 10, 10]],

       [[11, 11, 11],
        [11, 11, 11],
        [11, 11, 11]],

       [[12, 12, 12],
        [12, 12, 12],
        [12, 12, 12]]])
>>> z
array([[[20, 21, 22],
        [20, 21, 22],
        [20, 21, 22]],

       [[20, 21, 22],
        [20, 21, 22],
        [20, 21, 22]],

       [[20, 21, 22],
        [20, 21, 22],
        [20, 21, 22]]])
>>> 



f = x + y + z

>>> f
array([[[30, 31, 32],
        [31, 32, 33],
        [32, 33, 34]],

       [[31, 32, 33],
        [32, 33, 34],
        [33, 34, 35]],

       [[32, 33, 34],
        [33, 34, 35],
        [34, 35, 36]]])
>>> 

还可以选择使用meshgrid 创建实际点,然后使用单个循环迭代这些点 - 使用这种方法会丢失空间信息,除非你能弄清楚如何重塑结果。我在 SO 回答 https://stackoverflow.com/a/18253506/2823755

中找到了这个
points = np.vstack([x,y,z]).reshape(3, -1).T

>>> points
array([[ 0, 10, 20],
       [ 0, 10, 21],
       [ 0, 10, 22],
       [ 1, 10, 20],
       [ 1, 10, 21],
       [ 1, 10, 22],
       [ 2, 10, 20],
       [ 2, 10, 21],
       [ 2, 10, 22],
       [ 0, 11, 20],
       [ 0, 11, 21],
       [ 0, 11, 22],
       [ 1, 11, 20],
       [ 1, 11, 21],
       [ 1, 11, 22],
       [ 2, 11, 20],
       [ 2, 11, 21],
       [ 2, 11, 22],
       [ 0, 12, 20],
       [ 0, 12, 21],
       [ 0, 12, 22],
       [ 1, 12, 20],
       [ 1, 12, 21],
       [ 1, 12, 22],
       [ 2, 12, 20],
       [ 2, 12, 21],
       [ 2, 12, 22]])
>>>

您可以创建一个函数并将其应用于points

def g(point):
    x, y, z = point
    return x + y + z

result = np.apply_along_axis(g, 1, points)

>>> result
array([30, 31, 32, 31, 32, 33, 32, 33, 34, 31, 32, 33, 32, 33, 34, 33, 34, 35, 32, 33, 34, 33, 34, 35, 34, 35, 36])
>>>

重塑这个例子很简单:

>>> result.reshape(3,3,3)
array([[[30, 31, 32],
        [31, 32, 33],
        [32, 33, 34]],

       [[31, 32, 33],
        [32, 33, 34],
        [33, 34, 35]],

       [[32, 33, 34],
        [33, 34, 35],
        [34, 35, 36]]])
>>> 

测试以确保它们都相同

>>> np.all(result.reshape(3,3,3) == f)
True
>>> 

对于更复杂的数学,只需迭代点:

result = []
for point in points:
    example = some_maths
    result.append(example)

result = np.array(result).reshape(shape_of_the_3d_data)

【讨论】:

  • 感谢您的建议。不幸的是,计算并不像 a+b+c 那样简单,现在要更改将是一个巨大的痛苦。我更愿意使用我拥有的框架,但如果可能的话,使用多处理来加速它。
  • @Will.Evo - 所以第二种方法虽然比第一种慢,但可能仍然比嵌套的 Python 循环快一点,当您进行多处理时,您仍然可以使用它。您不需要使用apply_along_axis,只需遍历points - 在一个小数据集上尝试它,看看它是否有帮助..
  • 我开始明白你在说什么......你的建议实际上让我开始运行多处理。我从模型返回了一个值列表,但我仍然需要验证它们的顺序。我还坚持让多处理来运行代码的某些部分而不是整个文件。不管怎样,我正在努力(尽管我知道这个网站上有人可以在两秒钟内完成哈哈)
  • @Will.Evo - 您能否将模型修改为除了 namedtuple 之类的东西,它有一个用于数据的字段和一个用于原始 位置 的字段?该模型对数据字段进行操作,并返回一个带有坐标的namedtuple,以便您可以重建它?
  • 其实我只是在模型的meshgrid方法中写了,并在短期内进行了测试。似乎工作正常,但直到今晚我运行更长的模型迭代时,我才知道节省了多少时间。感谢所有的帮助。我会让你知道情况如何。 PS:我已经放弃了我的多处理愿望。我正在实习,我的项目只剩下三周了,我必须专注于获得结果(即使这意味着模型运行时间很长)。
【解决方案2】:

根据用户 wwii 的建议,我通过使用 numpy 的 meshgrid 重写了上面的示例,并为单个循环摆脱了嵌套的 for 循环。这是工作代码的示例。

import numpy as np
import time

a_range = np.arange(14, 18, 1)
b_range = np.arange(1000, 2200, 200)
c_range = np.arange(12, 21, 1)

a_position = range(len(a_range))
b_position = range(len(b_range))
c_position = range(len(c_range))

mesha, meshb, meshc = np.meshgrid(a_range, b_range, c_range)
mesh_vals = np.vstack([mesha, meshb, meshc]).reshape(3, -1).T

mesha_pos, meshb_pos, meshc_pos = np.meshgrid(a_position, b_position, c_position)
mesh_positions = np.vstack([mesha_pos, meshb_pos, meshc_pos]).reshape(3,-1).T

data_grid = np.zeros([len(a_range), len(b_range), len(c_range)])
record_data = []

start_time = time.time()

for pol in range(len(mesh_positions)):
    example = mesh_vals[pol][0]+ mesh_vals[pol][1]+ mesh_vals[pol][2]
    data_grid[mesh_positions[pol][0], mesh_positions[pol][1], mesh_positions[pol][2]] = example
    record_data.append([mesh_vals[pol][0], mesh_vals[pol][1], mesh_vals[pol][2], example])

print 'Code ran for ', round(time.time()-start_time,2), ' seconds'

实际上,经过进一步调查,这导致运行时间显着增加。当对 a、b 和 c 进行大范围处理时,for 循环与此方法之间的差异为 20 秒。我不知道为什么,但我知道这个问题的构造应该使多处理更容易,因为只有一个 for 循环要处理。

【讨论】:

    猜你喜欢
    • 2011-05-19
    • 2016-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-07
    • 1970-01-01
    • 1970-01-01
    • 2021-05-25
    相关资源
    最近更新 更多