如何在 Jupyter Notebook 的引擎上正确导入模块以进行并行处理?

How to correctly import modules on engines in Jupyter Notebook for parallel processing?

我希望 运行 一个令人尴尬的并行函数,它使用带有 Python 的 Jupyter Notebook 创建绘图(并最终将它们保存到文件中)(编辑 - 我找到了一种更简单的方法正是这个here)。我正在尝试最简单的版本,但出现导入错误。

我应该在哪里以及为什么要导入相关模块?我想我到处导入它们只是为了确定,但我仍然有错误!

导入文件中的位置编号为 1-4

[1] 这条线真的有必要吗?为什么?

[2] 这条线真的有必要吗?为什么?

[3] 这条线真的有必要吗?为什么?

[4] 这条线真的有必要吗?为什么?

以下是我的文件: jupyter 笔记本文件:

import ipyparallel
clients = ipyparallel.Client()
print(clients.ids)
dview = clients[:]
with dview.sync_imports():
    import module #[1]
    import matplotlib #[2]
import module #[3]
dview.map_sync(module.pll, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

和一个python文件名module.py

import matplotlib #[4]
def pll(x):
    matplotlib.pyplot.plot(x, '.')

当我 运行 笔记本时,我得到以下输出

[0, 1, 2, 3, 4, 5]
importing module on engine(s)
importing matplotlib on engine(s)
[Engine Exception]
NameErrorTraceback (most recent call last)<string> in <module>()
(...)
NameError: name 'matplotlib' is not defined

简答

sync_imports 在使用模块函数时是不必要的。这应该足够了:

# notebook:
import ipyparallel as ipp
client = ipp.Client()
dview = client[:]

import module
dview.map_sync(module.pll, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

# module.py
from matplotlib import pyplot
def pll(x):
    pyplot.plot(x, '.')

一个警告:您几乎肯定希望设置 matplotlib 以在引擎上使用非默认后端。 您必须在导入 pyplot 之前执行此操作。 ipython 并行的两个逻辑选择是 agg 如果你只是保存到文件,或者 %matplotlib inline 如果你想在笔记本中交互地查看绘图。要使用聚合:

import matplotlib
dview.apply_sync(matplotlib.use, 'agg')

或设置内联绘图:

%px %matplotlib inline

长答案

回答您的项目符号问题:

  • [1] 不,除非你想 module 在全局中到处定义
  • [2] 不,除非你想 matplotlib 在全局中到处定义
  • [3] 是的,这是将 .pll 传递给 map
  • 所必需的
  • [4] 是的,因为 module 是不同于 __main__ 的命名空间,运行.
  • 是您所有笔记本代码所在的地方

在处理需要导入的内容和导入位置时,您需要考虑两种情况:

交互式定义的函数

当一个函数被交互定义时(也就是说,def foo()在你的笔记本中),在交互命名空间中执行名称查找,并且交互命名空间在引擎和客户端之间 可能会有所不同 。例如,您可以通过以下方式查看:

import numpy
%px numpy = 'whywouldyoudothis'

def return_numpy():
    return numpy # resolved locally *on engines*
dview.apply_sync(return_numpy)

其中 apply 将 return 一个 ['why..'] 字符串列表,而不是本地 numpy 导入。 Python 不知道名称是指模块还是其他任何东西;这完全取决于使用什么名称空间来查找名称。这就是为什么您会经常看到类似以下之一的交互式定义函数的原因:

import module
%px import module
def foo():
    return module.x

或者这个:

def foo():
    import module
    return module.x

这两种方法都可以确保 module in foo 映射到引擎上导入的模块:一种方法在任何地方执行交互式命名空间导入并依赖于全局命名空间查找。函数中的其他导入,所以不会错。

sync_imports() 是一种纯粹的 Python 方法,可以做与以下内容相同的事情:

import module
%px import module

它在这里和那里导入模块。如果使用sync_imports,也不需要在本地重复导入,因为已经进行了本地导入。

模块函数

如果函数是在一个模块中定义的,就像你的一样,它会在它的模块中找到全局变量,而不是在交互式命名空间中。所以你笔记本中的 import matplotlib 对调用 module.pll 时是否定义了 matplotlib 名称没有影响。同样,在模块中导入 matplotlib 不会使其在交互式命名空间中可用。

需要考虑的重要事项:当您向引擎发送模块函数时,它只会向函数发送 reference,而不是 content 函数或模块。因此,如果 from module import pll return 在引擎上与客户端有所不同,您将获得不同的行为。当在 ipython 中并行使用 local 模块同时主动更改该模块时,这可能会使人们感到困惑。在笔记本中重新加载该模块不会在引擎上重新加载该模块。它将发送相同的 module.pll 参考。因此,如果您正在积极致力于 module.py,您将需要在该模块更改时随处调用 reload(module)