我正在try 理解使用tqdm的进度条到底是如何工作的.我有一些代码,如下所示:

import torch
import torchvision
print(f"torch version: {torch.__version__}")
print(f"torchvision version: {torchvision.__version__}")

load_data()
manual_transforms = transforms.Compose([])
train_dataloader, test_dataloader, class_names = data_setup.create_dataloaders()

# them within the main function I have placed the train function that exists in the `engine.py` file
def main():

      results = engine.train(model=model,
        train_dataloader=train_dataloader,
        test_dataloader=test_dataloader,
        optimizer=optimizer,
        loss_fn=loss_fn,
        epochs=5,
        device=device)

并且engine.train()函数包括以下代码for epoch in tqdm(range(epochs)):.然后,对每一批进行训练,以可视化训练的进度.每次 for each 步骤运行tqdm时,它还会打印以下语句:

print(f"torch version: {torch.__version__}")
print(f"torchvision version: {torchvision.__version__}")

最后,我的问题是为什么会发生这种情况.main函数如何访问这些全局语句,以及如何避免在每个循环中打印所有内容?

推荐答案

您所注意到的实际上与tqdm无关,而是与PyTorch的内部工作(特别是DataLoadernum_workers属性)和Python的底层multiprocessing框架有关.下面是一个最小的工作示例,它应该重现您的问题:

import multiprocessing
import torch
from torch.utils.data import DataLoader, Dataset
from tqdm import tqdm
print("torch version:", torch.__version__)

class DummyData(Dataset):
    def __len__(self): return 256
    def __getitem__(self, i): return i

def main():
    for batch in tqdm(DataLoader(DummyData(), batch_size=16, num_workers=4)):
        pass  # Do something
    
if __name__ == "__main__":
    try:
        multiprocessing.set_start_method("spawn")
    except:
        pass
    finally:
        main()

如果您运行这段代码,您应该看到您的PyTorch版本号被打印了4次,弄乱了您的tqdm进度条.这个数字与num_workers相同并不是巧合(你可以通过更改这个数字很容易地进行判断).

发生的情况如下:

  • 如果num_workers为0,则为工作器启动子进程.>
  • 在Windows和macOS上,默认情况下,这些子进程使用"spawn"方法启动(在Linux上,可以强制执行该方法来重现观察结果,我已经在set_start_method()中完成了这一点).
  • "spawn"方法将 for each 子进程启动一次主脚本,执行所有没有被if __name__ == "__main__":块保护的行.这包括你在 playbook 上的print()个电话.

记录了here个这种行为,以及可能的缓解措施.我想,对你有效的方法是:

将你的大部分主脚本代码包装在if __name__ == '__main__':块内,以确保它不会再次运行

所以要么

  1. print()个电话移到你的if __name__ == '__main__':区块的开头,
  2. print()个调用移到main()函数的开头,或者
  3. 删除这print()个呼叫.

或者,但这可能不是你想要的,你可以设置num_workers=0,这将完全禁用multiprocessing的底层使用(但这样你也会失go 并行化的好处).请注意,您可能还应该将其他函数调用(如load_data())移动到if __name__ == '__main__':块或main()函数中,以避免多次意外执行.

Python相关问答推荐

替换字符串中的多个重叠子字符串

滚动和,句号来自Pandas列

Django mysql图标不适用于小 case

如何删除索引过go 的lexsort深度可能会影响性能?' &>

大小为M的第N位_计数(或人口计数)的公式

用NumPy优化a[i] = a[i-1]*b[i] + c[i]的迭代计算

两个pandas的平均值按元素的结果串接元素.为什么?

Python键入协议默认值

当独立的网络调用不应该互相阻塞时,'

根据列值添加时区

Python全局变量递归得到不同的结果

使用Python异步地持久跟踪用户输入

判断Python操作:如何从字面上得到所有decorator ?

使用类型提示进行类型转换

在电影中向西北方向对齐""

有了Gekko,可以创建子模型或将模型合并在一起吗?

如何将一个文件的多列导入到Python中的同一数组中?

如何将列表从a迭代到z-以抓取数据并将其转换为DataFrame?

两个名称相同但值不同的 Select 都会产生相同的值(discord.py)

如何通过函数的强式路径动态导入函数?