Python 3.x Pytork:为什么DDP中的日志记录失败?
我想在分布式数据并行管理的一个进程中使用日志记录。但是,日志记录不会在以下代码中打印任何内容(这些代码源自): 但是,当我取消注释第4行时,日志记录工作正常。我可以知道原因和如何修复错误吗?更新 让我们简要回顾一下Python 3.x Pytork:为什么DDP中的日志记录失败?,python-3.x,pytorch,python-multiprocessing,python-logging,Python 3.x,Pytorch,Python Multiprocessing,Python Logging,我想在分布式数据并行管理的一个进程中使用日志记录。但是,日志记录不会在以下代码中打印任何内容(这些代码源自): 但是,当我取消注释第4行时,日志记录工作正常。我可以知道原因和如何修复错误吗?更新 让我们简要回顾一下logging模块中的记录器是如何工作的 记录器以树形结构组织,即每个记录器都有一个唯一的父记录器。默认情况下,它将是root记录器,而root记录器没有父记录器 在记录器上调用Logger.info方法(为简单起见,此处忽略级别检查)时,记录器迭代其所有处理程序,并让它们处理当前记录
logging
模块中的记录器是如何工作的
记录器以树形结构组织,即每个记录器都有一个唯一的父记录器。默认情况下,它将是root
记录器,而root
记录器没有父记录器
在记录器上调用Logger.info
方法(为简单起见,此处忽略级别检查)时,记录器迭代其所有处理程序
,并让它们处理当前记录,例如,处理程序可以是可打印到标准输出的StreamHandler,或可打印到某个文件的FileHandler)。当前记录器的所有处理程序完成其作业后,记录将被提供给其父记录器,父记录器以相同的方式处理记录,即迭代父记录器的所有处理程序并让它们处理记录,最后将记录传递给“祖父母”。此过程将继续,直到到达当前记录器树的根,该树没有父级
检查以下实现或:
def调用处理程序(self,record):
c=自我
找到=0
而c:
对于c.Handler中的hdlr:
找到=找到+1
如果record.levelno>=hdlr.level:
hdlr.句柄(记录)
如果不是c,则传播:
c=无#中断
其他:
c=c父项
因此,在您的例子中,您没有为train
记录器指定任何处理程序。当您取消注释第6行时,即通过调用logging.basicConfig(level=logging.DEBUG)
,将为root
记录器创建一个StreamHandler
。虽然train
记录器没有任何处理程序,但它的父级(即root
记录器)有一个StreamHandler
,它打印您实际看到的任何内容,而train
记录器在这种情况下不打印任何内容。当注释中的第6行时,即使是一个StreamHandler
也不会为根处理程序创建,因此在这种情况下不会打印任何内容。因此,事实上,这个问题与DDP无关
顺便说一句,我不能首先重现您的问题的原因是因为我使用PyTorch 1.8,其中logging.info
将在执行dist.init\u process\u group
期间被调用,用于MPI以外的后端,后者隐式调用basicConfig
,为根记录器创建一个StreamHandler
,并按预期打印消息
======================================================================
一个可能的原因是:因为在执行dist.init\u process\u group
期间,它将调用\u store\u based\u barrier
,最终将调用logging.info
(请参阅源代码)。因此,如果在调用dist.init\u process\u group
之前调用logging.basicConfig
,它将提前初始化,这将使根日志记录器忽略所有级别的日志
代码中不是这种情况,因为logging.basicConfig
位于文件的顶部,将在dist.init\u process\u组之前的第一次执行。实际上,我能够运行您提供的代码,在填充缺少的导入(例如nn
和dist
)之后,日志正常工作。也许您试图减少代码以重现问题,但却在不知不觉中规避了真正的问题?你能再检查一下这是否解决了你的问题吗?除非我算错了,否则第六行是空的——我想你是指第四行?@Xtrem532谢谢你指出。现在更正。您好,由于stackoverflow的限制,其余导入语句已从第6行中删除。除了这些,其他什么都不缺。我在torch 1.7上尝试了上面的代码,但是日志记录不起作用。顺便说一句,我留下了一个类似的问题。我不熟悉日志记录和多进程。如果你能弄明白,我将不胜感激D@Tengerye查看我的更新,我想它现在也可以回答你的另一个问题。我可以从你的回答中理解第一个问题。但是,为什么在单个进程中创建或在单个进程外创建记录器时输出不同?(提供了示例)
#!/usr/bin/python
import os, logging
# logging.basicConfig(level=logging.DEBUG)
def setup(rank, world_size):
os.environ['MASTER_ADDR'] = 'localhost'
os.environ['MASTER_PORT'] = '12355'
# Initialize the process group.
dist.init_process_group('NCCL', rank=rank, world_size=world_size)
def cleanup():
dist.destroy_process_group()
class ToyModel(nn.Module):
def __init__(self):
super(ToyModel, self).__init__()
self.net1 = nn.Linear(10, 10)
self.relu = nn.ReLU()
self.net2 = nn.Linear(10, 5)
def forward(self, x):
return self.net2(self.relu(self.net1(x)))
def demo_basic(rank, world_size):
setup(rank, world_size)
if rank == 0:
logger = logging.getLogger('train')
logger.setLevel(logging.DEBUG)
logger.info(f'Running DPP on rank={rank}.')
# Create model and move it to GPU.
model = ToyModel().to(rank)
ddp_model = DDP(model, device_ids=[rank])
loss_fn = nn.MSELoss()
optimizer = optim.SGD(ddp_model.parameters(), lr=0.001) # optimizer takes DDP model.
optimizer.zero_grad()
inputs = torch.randn(20, 10) # .to(rank)
outputs = ddp_model(inputs)
labels = torch.randn(20, 5).to(rank)
loss_fn(outputs, labels).backward()
optimizer.step()
cleanup()
def run_demo(demo_func, world_size):
mp.spawn(
demo_func,
args=(world_size,),
nprocs=world_size,
join=True
)
def main():
run_demo(demo_basic, 4)
if __name__ == "__main__":
main()