Q:TypeError: function object is not iterable@
为了TypeError:
TypeError 由于语法错误(对 joblib.Parallel( delayed( ... ) ... ) 的格式错误调用错误地遵守了记录的调用语法构造函数。
示例 1:正确调用:
此调用遵循记录在案的语法规范,直至最后一个点:
>>> from joblib import Parallel, delayed
>>> parallel = Parallel( n_jobs = -1 )
>>> import numpy as np
>>> parallel( delayed( np.sqrt ) ( i**2 ) for i in range( 10 ) )
# ^ ^^^^^^^ ^^^^ ^^^^ |||
# | ||||||| |||| |||| vvv
#JOBS(-1):-+ ||||||| |||| |||| |||
#DELAYED:-----+++++++ |||| |||| |||
#FUN( par ):--------------++++ |||| |||
# ||| |||| |||
# +++-FUN(signature-"decl.")---++++ |||
# ^^^ |||
# ||| |||
# +++-<<<-<iterator>-<<<-<<<-<<<-<<<--+++
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
生成的结果确认调用完全合规且可解释。
示例 2:错误调用:
>>> from joblib import Parallel, delayed
>>> parallel = Parallel( n_jobs = -1 )
>>> import numpy as np
>>> parallel( delayed( np.sqrt( 10 ) ) ) #### THIS SLOC IS KNOWINGLY WRONG
# ^ ^^^^^^^ ^^^^(????) ???? ??? ####
# | ||||||| |||| |||| vvv ####
#JOBS(-1):-+ ||||||| |||| |||| ||| ####
#DELAYED:-----+++++++ |||| |||| ||| #### DELAYED( <float64> )
#FUN( par ):--------------++++ |||| ||| #### GOT NO CALLABLE FUN( par )
# ||| |||| ||| #### BUT A NUMBER
# +++-FUN(signature-"decl.")------++++ ||| #### FUN( signature )
# ^^^ ||| #### NOT PRESENT
# ||| ||| #### AND FEEDER
# +++-<<<-<iterator>-<<<-<<<-<<<-<<<-<<<-+++ #### <ITERATOR> MISSING
# ####
Traceback (most recent call last): #### FOR DETAILS, READ THE O/P
File "<stdin>", line 1, in <module> #### AND EXPLANATION BELOW
File ".../lib/python3.5/site-packages/joblib/parallel.py", line 947, in __call__
iterator = iter(iterable)
TypeError: 'function' object is not iterable
结果证实,O/P 使用的语法与记录在案的 joblib.Parallel( delayed(...) ... ) 不兼容
Q.E.D.
补救措施:
遵循joblib.Parallel( delayed( ... ) ... ) 记录的语法:
#entroids, _ = parallel( delayed( kmeans(weights1d, cluster)))
# ^^^^^^(..................)
# ||||||(..................)
#THIS-IS-NOT-A-CALLABLE-BUT-VALUE-++++++(..................)
#
centroids, _ = parallel( delayed( kmeans ) ( weights1d, cluster ) for ... )
# ^^^^^^ ^^^^^^^^^^^^^^^^^^ |||||||
# |||||| |||||||||||||||||| vvvvvvv
# CALLABLE FUN()------------------++++++ |||||||||||||||||| |||||||
# FUN( <signature> )----------------++++++++++++++++++ |||||||
# ^^^^^^^^^^^ |||||||
# ||||||||||| |||||||
# +++++++++++------------<<<--feeding-<iterator>----+++++++
最好的第一步:
是重新阅读joblib.Parallel的设计方式和使用模式的文档细节,以便更好地熟悉该工具:
joblib.Parallel( n_jobs = None, # how many jobs will get instantiated
backend = None, # a method, how these will get instantiated
verbose = 0,
timeout = None,
pre_dispatch = '2 * n_jobs',
batch_size = 'auto',
temp_folder = None,
max_nbytes = '1M',
mmap_mode = 'r',
prefer = None, # None | { ‘processes’, ‘threads’ }
require = None # None | ‘sharedmem’ ~CONSTRAINTS backend
)
接下来,人们可能会掌握一些琐碎的例子(并进行实验并将其扩展到自己的预期用例):
Parallel( n_jobs = 2 ) ( delayed( sqrt ) ( i ** 2 ) for i in range( 10 ) )
# ^ ^^^^^^^ ^^^^ ^^^^^^ |||
# | ||||||| |||| |||||| vvv
#JOBS:-----+ ||||||| |||| |||||| |||
#DELAYED:-----------------+++++++ |||| |||||| |||
#FUN( par ):-----------------------++++ |||||| |||
# ||| |||||| |||
# +++--FUN(-signature-"declaration"-)---++++++ |||
# ^^^ |||
# ||| |||
# +++-<<<-<iterator>-<<<-<<<-<<<-<<<-<<<-<<<-<<<-+++
Parallel( n_jobs = -1 ) (
delayed( myTupleConsumingFUN ) ( # aFun( aTuple = ( a, b, c, d ) )
aTupleOfParametersGeneratingFUN( i ) )
for i in range( 10 )
)
下一步:尝试了解使用n_jobs 实例化的成本和限制
joblib 的默认后端将在 isolated Python 进程中运行每个函数调用,因此 它们不能改变常见的主程序中定义的 Python 对象。
但如果并行函数确实需要依赖线程的共享内存语义,则应使用require='sharedmem' 明确说明
请记住,从性能的角度来看,依赖共享内存语义可能不是最佳的,因为对共享 Python 对象的并发访问将遭受锁争用。
使用基于线程的后端允许“共享”,但这意味着这样做的巨大成本 - 线程重新引入 GIL 步进,这将重新[SERIAL] - 以 GIL 锁步方式将代码执行流程重新转换为一个接一个接一个。对于产生比原始纯[SERIAL] 代码更差的性能的计算密集型处理(虽然此模式有助于延迟屏蔽用例,其中等待网络响应可能允许线程释放 GIL 锁并让其他线程继续工作)
有一些步骤可以实现,以便使基于流程的单独计算能够传达这种需求,但是,这需要一些附加成本。
计算密集型问题必须平衡对最终性能的需求(使用更多内核),但要记住只有一个隔离(拆分)的工作单元和最小的参数传输和结果返回的附加成本,所有与利用 joblib.Parallel 可用形式的 just-[CONCURRENT] 流程调度的错误设计意图相比,这很容易花费更多。
了解更多details on joblib.Parallel
更多details on add-on costs and atomicity-of-work对并行加速的影响