使用Python多进程时调用sklearn LinearRegression fit方法子进程挂起
我之前也碰到过一模一样的情况!这个问题的根源在于sklearn的LinearRegression底层依赖Numpy的BLAS/LAPACK多线程加速,而Python多进程环境下这种多线程会和子进程的启动机制冲突,导致死锁挂起,而决策树回归器本身是单线程运行,所以没这个麻烦。结合你的环境版本,给你几个靠谱的解决办法:
方法1:在子进程中强制限制Numpy线程数
在子进程的任务函数开头,先把Numpy的线程池大小设为1,避免多线程竞争:
import numpy as np import multiprocessing as mp from sklearn.linear_model import LinearRegression from sklearn.tree import DecisionTreeRegressor def exp(X,y): model = LinearRegression() model.fit(X,y) print('fitted') def job(X,y): # 强制Numpy只用1个线程,避免多进程+多线程的冲突 np.set_num_threads(1) model = LinearRegression() model.fit(X,y) print('fitted (child process)') if __name__ == '__main__': n_obs = 100 X = np.random.random((n_obs,1)) y = np.random.random((n_obs)) exp(X,y) # 把进程创建的代码放到if __name__ == '__main__'里面! p = mp.Process(target = job, args=(X,y)) p.start() p.join() print('done')
方法2:全局限制Numpy线程数(更省心)
如果你不想在每个子进程里都写一遍,也可以在主程序最开头就设置全局的Numpy线程数,这样所有子进程都会继承这个设置:
import numpy as np # 全局设置Numpy只用1个线程 np.set_num_threads(1) import multiprocessing as mp from sklearn.linear_model import LinearRegression # 后面的代码和修正后的版本一致...
额外注意:你的代码的一个小问题
原来的代码里把p = mp.Process(...放在了if __name__ == '__main__':外面,这在Windows系统下会触发重复创建进程的问题(因为Windows的multiprocessing是spawn模式,会重新导入整个模块),虽然这不一定是挂起的直接原因,但也是必须修正的隐患——把进程创建、启动的逻辑都放到if __name__ == '__main__':块里面就没问题了。
为什么LinearRegression会有这个问题?简单说:LinearRegression的fit方法会调用Numpy的矩阵运算,而默认的Numpy后端(比如OpenBLAS)会自动开启多个线程来加速;当子进程启动时,会继承父进程的线程池状态,加上Python的GIL(全局解释器锁)在多线程中的切换,就容易出现线程死锁,导致进程挂起不动。而DecisionTreeRegressor的拟合过程是纯单线程的,不会触发这个冲突。
内容的提问来源于stack exchange,提问作者Daahorst

