背景:
最近在学习d2l的时候,运行for X, y in train_iter:的时候,总是报错。查看报错的内容:
RuntimeError: DataLoader worker (pid 41847) exited unexpectedly with exit code 1.
Details are lost due to multiprocessing. Rerunning with num_workers=0 may give better error trace.
看到这个提示,想到了在调用d2l.load_data_fashion_mnist(batch_size)的时候,内部会设置4个进程进行预读取数据,那么问题可能就出在这里。
问了gpt之后,给的提示是要在这个判断里面执行一系列的操作:if __name__ == “__main__”:。因为是学习阶段所以没有管是不是主进程,都是直接全局写。可能是导致了该原因。
此时就很困惑,在预读取数据创建多进程,为何会影响到另外这边的代码的执行。
通过查询资料之后,确实了解到了原来python有3种不同的创建子进程的方式,并且不同的平台,默认的创建方式还可能不一样。
如下是对各个不同进程的创建的说明
二、fork的启动方式
1、这是Unix/linux系统上默认的启动方式。当进程使用fork时,子进程会复制父进程的整个执行环境,跟咱们平时linux的C++执行的fork是一样的。子进程通常立即开始执行,在某些情况下这可能会导致状态冲突。
2、因为子进程会无条件地复制父进程的资源和状态,某些资源可能不应该被两个进程共享(例如,锁、文件描述符)。
三、spawn的启动方式
1、这是windows系统上默认的启动方式。启动方法会创建一个全新的Python解释器进程,并只将需要执行的函数及其参数序列化后传送给子进程。与`fork`不同,父进程的资源并没有被子进程复制。所有的准备工作都是在子进程中“从头开始”的,这就意味着子进程基本上是一个全新的实例,而不是父进程的一个复制品。
2、spawn的创建方式避免了由于资源共享带来的状态冲突和错误。但是创建进程的速度相对会比较慢,因为还要创建一个全新的解释进程
四、forkserver的启动方式
1、这是Unix和类Unix系统上可用。(读者没有跑成功过,所以这里只写理论)
2、`forkserver`是一个特别的启动方法,它对`fork`做了一些改良。当程序第一次初始化multiprocessing时,它会启动一个新的服务器进程。之后,每当用户代码想要创建一个新的进程时,它会请求forkserver去fork一个新的进程。服务器进程在干净的状态下运行,不包含程序可能改变的全局状态,从而避免了一些由`fork`引起的问题。
五、关于获取和设置当前是用什么方式启动进程
import multiprocessing current_method = multiprocessing.get_start_method() print('Current start method:', current_method) # 获取当前启动多进程的方法 multiprocessing.set_start_method('fork') # 设置当前的启动进程方法为fork,也可以是其他的方法,比如:spawn、forkserver
六、说明本文章的背景所说的,总是出现的异常
1、通过multiprocessing.get_start_method()方法得知,当前正在运行的创建进程的方法是spawn
2、因为spawn是重新执行一遍全局的代码,所以导致了上述的报错。(因为我把d2l.load_data_fashion_mnist(batch_size)写在全局当中,所以每个子进程都会重新再执行一遍)
3、当将启动进程方法设置为fork之后,那么会有对应的警告:UserWarning: Cannot set number of intraop threads after parallel work has started or after set_num_threads call when using native parallel backend (Triggered internally at /Users/runner/work/pytorch/pytorch/pytorch/aten/src/ATen/ParallelNative.cpp:230.)
即:试图在Python代码中使用PyTorch时更改并行工作已开始之后的内部操作线程数量。当你尝试设置线程数,而并行任务已经开始或者`set_num_threads`函数已经被调用时,就会触发这个警告。
所以编写python时候,要明确知道当前是怎样的创建进程的方式。并且最好不要在全局中做各种的计算。
备注:关于背景的报错内容,看网上有的人说该错误是因为OOM导致的,疑惑但是自己暂时不确定,先在这里做个记录。(有机会会用大内存测试一下对应的代码)(经过测试,依旧会出现对应的报错,所以虽然是类似的提示错误但是有不同的原因)