如何利用Pycharm进行分布式的Debug训练

PyCharm 调试 PyTorch 分布式程序:从 torchrun 启动改为直接调试 main.py

一、问题背景

在使用 PyCharm 调试 PyTorch 分布式训练程序时,常见的启动方式是:

bash 复制代码
python -m torch.distributed.run --nproc_per_node=1 main.py

或者:

bash 复制代码
torchrun --nproc_per_node=1 main.py

这种方式在命令行中通常可以正常运行,但在 PyCharm 的 Debug 模式下,经常会出现以下问题:

text 复制代码
Connected to pydev debugger

程序虽然进入了训练过程,但断点无法命中,表现为"明明点击了 Debug,程序却一直往下跑"。

例如日志中已经可以看到:

text 复制代码
world_size: 1
local_rank: 0
Epoch[1] Iteration[50/1156]

说明程序已经正常进入训练阶段,但 PyCharm 的断点没有生效。


二、原因分析

造成该问题的核心原因是:

PyCharm 调试器连接到的是 torch.distributed.runtorchrun 启动器进程,而真正执行训练代码的 main.py 可能是由启动器进一步拉起的子进程。

也就是说,当使用下面这种方式启动时:

bash 复制代码
python -m torch.distributed.run --nproc_per_node=1 main.py

PyCharm 首先调试的是:

python 复制代码
torch.distributed.run

然后该模块再启动真正的训练脚本:

python 复制代码
main.py

因此,PyCharm 不一定能够稳定接管真正执行训练代码的进程,导致断点无法命中。

另外,训练代码中的 DataLoader 如果设置了多个工作进程,例如:

python 复制代码
num_workers = 8

也会进一步增加调试复杂度。因为 DataLoader 会额外启动多个子进程,导致 PyCharm 更难准确进入断点。


三、推荐解决方案:调试时不要使用 torchrun,直接运行 main.py

如果当前只是为了 Debug,而不是必须验证多卡通信,那么最稳妥的方式是:

直接让 PyCharm 运行 main.py,并手动补充分布式所需的环境变量。

也就是说,调试阶段不要再使用:

bash 复制代码
python -m torch.distributed.run ...

也不要使用:

bash 复制代码
torchrun ...

而是让 PyCharm 直接执行:

bash 复制代码
python main.py

这样 PyCharm 调试器会直接接管 main.py,断点最容易命中。


四、PyCharm 推荐配置

1. Run 类型

在 PyCharm 的 Run/Debug Configuration 中选择:

text 复制代码
Script path

不要选择:

text 复制代码
Module name

因为此时我们不再通过:

bash 复制代码
python -m torch.distributed.run

启动,而是直接运行训练入口脚本。


2. Script path

填写远程服务器上的 main.py 路径,例如:

bash 复制代码
/home/XX/XX/main.py

不要填写:

bash 复制代码
torch.distributed.run

也不要填写:

bash 复制代码
/root/miniconda3/envs/xxx/bin/torchrun

调试阶段的核心目标是让 PyCharm 直接进入 main.py


3. Python Interpreter

解释器选择项目所使用的 Python 环境,例如:

bash 复制代码
/root/miniconda3/envs/xxx/bin/python

注意,解释器必须是 Python,而不是 torchrun。

错误写法:

bash 复制代码
/root/miniconda3/envs/xxx/bin/torchrun

正确写法:

bash 复制代码
/root/miniconda3/envs/rt/bin/python

4. Working directory

工作目录填写项目根目录,例如:

bash 复制代码
/home/xx/xx/xx/project

该目录应当与 main.py、配置文件、数据路径所在项目保持一致。

尤其要注意 Linux 区分大小写,例如下面两个目录可能是不同目录:如果路径不一致,可能导致代码运行的是服务器上的另一份文件,从而出现断点无法命中的问题。


5. Parameters

如果 main.py 不需要额外参数,可以先留空。

如果原本运行命令中有参数,例如:

bash 复制代码
main.py --config configs/xxx.yaml --output_dir output/debug

则在 Parameters 中填写:

bash 复制代码
--config configs/xxx.yaml --output_dir output/debug

注意,此时不要再写:

bash 复制代码
--nproc_per_node=1 main.py

因为 main.py 已经在 Script path 中指定了。


6. Environment variables

由于直接运行 main.py 不再经过 torchrun,所以需要手动补齐分布式环境变量。

单卡 Debug 推荐设置:

bash 复制代码
CUDA_VISIBLE_DEVICES=2;WORLD_SIZE=1;RANK=0;LOCAL_RANK=0;MASTER_ADDR=127.0.0.1;MASTER_PORT=29501;NCCL_DEBUG=INFO;NCCL_IB_DISABLE=1;PYTHONUNBUFFERED=1

其中:

text 复制代码
CUDA_VISIBLE_DEVICES=2

表示只使用第 2 张 GPU。

text 复制代码
WORLD_SIZE=1

表示当前只有 1 个训练进程。

text 复制代码
RANK=0

表示当前进程是第 0 个进程。

text 复制代码
LOCAL_RANK=0

表示当前进程在本机使用的 GPU 编号为 0。需要注意,这里的 LOCAL_RANK=0 对应的是 CUDA_VISIBLE_DEVICES 可见范围内的第 0 张卡。若设置 CUDA_VISIBLE_DEVICES=2,那么程序中的 cuda:0 实际对应物理 GPU 2。

text 复制代码
MASTER_ADDR=127.0.0.1
MASTER_PORT=29501

用于初始化分布式进程组。

text 复制代码
NCCL_IB_DISABLE=1

用于关闭 InfiniBand,单机服务器或容器环境中通常更稳。

text 复制代码
PYTHONUNBUFFERED=1

用于让日志实时输出,便于调试观察。


五、调试阶段建议关闭 DataLoader 多进程

如果配置文件中有:

python 复制代码
num_workers = 8

建议调试时临时改为:

python 复制代码
num_workers = 0

可以在配置文件中修改:

yaml 复制代码
num_workers: 0

也可以在 main.py 读取配置后临时加入:

python 复制代码
config["data"]["num_workers"] = 0

这样做的好处是:

  1. 避免 DataLoader 启动额外子进程;
  2. 断点更容易命中;
  3. 数据读取报错更容易定位;
  4. PyCharm 调试器不会被多个 worker 进程干扰。

调试完成后,再恢复为:

python 复制代码
num_workers = 8

六、建议在 main.py 中加入强制断点测试

为了确认 PyCharm 是否真正进入了 main.py,可以在 main.py 开头临时加入:

python 复制代码
print("===== enter main.py =====")
breakpoint()

如果程序能够在这里停住,说明 PyCharm 已经成功接管 main.py

如果 breakpoint() 能停,但 PyCharm 红点断点不能停,则一般说明:

text 复制代码
本地代码和远程执行代码不是同一份

此时需要检查:

text 复制代码
1. PyCharm 的 Deployment 路径映射
2. Working directory 是否正确
3. Script path 是否指向正确的 main.py
4. 本地代码是否已经同步到远程服务器
5. 远程项目目录名称是否存在大小写或字母混淆

七、如果仍然使用 torch.distributed.run,需要额外设置

如果后续确实需要用下面方式调试:

bash 复制代码
python -m torch.distributed.run --nproc_per_node=2 main.py

那么 PyCharm 中需要开启:

text 复制代码
Settings -> Build, Execution, Deployment -> Python Debugger

勾选:

text 复制代码
Attach to subprocess automatically while debugging

中文界面一般是:

text 复制代码
调试时自动附加到子进程

但即使开启该选项,torch.distributed.run 加 DataLoader 多进程仍然不如直接运行 main.py 稳定。因此,建议先用单进程方式调通核心代码,再恢复多卡运行。


八、推荐的调试流程

第一步:单卡直接调试 main.py

PyCharm 配置为:

text 复制代码
Script path:
/home/xxx/xxx/main.py

Working directory:
/home/xxx/xxx

Environment variables:
CUDA_VISIBLE_DEVICES=2;WORLD_SIZE=1;RANK=0;LOCAL_RANK=0;MASTER_ADDR=127.0.0.1;MASTER_PORT=29501;NCCL_DEBUG=INFO;NCCL_IB_DISABLE=1;PYTHONUNBUFFERED=1

同时将:

python 复制代码
num_workers = 0

main.py 开头加入:

python 复制代码
print("===== enter main.py =====")
breakpoint()

确认断点可以正常停住。


第二步:调试训练逻辑

在以下位置设置断点:

python 复制代码
run(config)

或者训练循环中:

python 复制代码
for i, batch in enumerate(train_loader):

也可以在 batch 解包前设置断点,用于查看 DataLoader 返回的数据结构。

例如:

python 复制代码
for i, batch in enumerate(train_loader):
    print("batch type:", type(batch))
    print("batch len:", len(batch))
    break

第三步:恢复 torchrun 或 torch.distributed.run 多卡运行

当单卡 Debug 没有问题后,再恢复分布式启动方式,例如:

bash 复制代码
python -m torch.distributed.run --standalone --nnodes=1 --nproc_per_node=2 main.py

或:

bash 复制代码
torchrun --standalone --nnodes=1 --nproc_per_node=2 main.py

对应环境变量可以设置为:

bash 复制代码
CUDA_VISIBLE_DEVICES=2,3;NCCL_DEBUG=INFO;NCCL_IB_DISABLE=1

如果只想继续单卡运行:

bash 复制代码
CUDA_VISIBLE_DEVICES=2

九、常见问题总结

1. 为什么 Debug 模式下程序一直跑?

因为 Debug 模式并不代表程序会自动暂停,只有遇到有效断点才会停。如果使用 torch.distributed.run,PyCharm 可能连接的是启动器进程,而不是实际执行 main.py 的训练进程。


2. 为什么程序能训练,但断点不生效?

常见原因包括:

text 复制代码
1. PyCharm 没有接管 main.py 子进程;
2. DataLoader 的 num_workers 大于 0;
3. 本地代码和远程代码不同步;
4. Script path 或 Working directory 指向了错误目录;
5. 断点所在代码没有被执行到;
6. 使用了 torchrun,导致真实训练逻辑在子进程中运行。

3. 为什么调试时建议不用 torchrun?

因为单卡 Debug 的目标是排查代码逻辑,不是测试多卡通信。直接运行 main.py 可以让 PyCharm 直接接管训练脚本,断点更稳定。


4. 直接运行 main.py 会不会缺少 WORLD_SIZE?

会,所以需要手动设置:

bash 复制代码
WORLD_SIZE=1;RANK=0;LOCAL_RANK=0

这样代码中读取分布式环境变量时就不会报错。


5. 为什么 CUDA_VISIBLE_DEVICES=2 时,日志里仍然是 cuda:0

因为 CUDA_VISIBLE_DEVICES=2 会让程序只看到一张卡,这张可见卡在程序内部编号为 cuda:0,但它实际对应的是物理 GPU 2。


十、最终结论

在 PyCharm 中调试 PyTorch 分布式程序时,推荐遵循以下原则:

text 复制代码
训练时可以使用 torchrun;
调试时优先直接运行 main.py。

最稳的 Debug 配置是:

text 复制代码
Script path:
/home/zmt/ai/qg/BiI-RRA/main.py

Interpreter:
/root/miniconda3/envs/rt/bin/python

Working directory:
/home/zmt/ai/qg/BiI-RRA

Environment variables:
CUDA_VISIBLE_DEVICES=2;WORLD_SIZE=1;RANK=0;LOCAL_RANK=0;MASTER_ADDR=127.0.0.1;MASTER_PORT=29501;NCCL_DEBUG=INFO;NCCL_IB_DISABLE=1;PYTHONUNBUFFERED=1

同时,将 DataLoader 的:

python 复制代码
num_workers = 8

临时改为:

python 复制代码
num_workers = 0

这样可以最大程度保证 PyCharm 断点能够正常命中,便于定位代码问题。等代码逻辑调试完成后,再恢复 torchruntorch.distributed.run 进行多卡训练。

相关推荐
Jumbo星2 小时前
新版vscode侧边资源管理器的文件搜索
ide·vscode·编辑器
睡不醒男孩0308232 小时前
第三篇:打破云厂商锁定:基于CLup构建私有化PolarDB分布式集群高可用方案
分布式·clup·中启乘数
ABAP-張旺3 小时前
ABAP:Visual Studio Code開發ABAP教程
ide·vscode·编辑器
前端不太难3 小时前
鸿蒙 App 分布式数据同步:架构设计 + Demo 实现
分布式·状态模式·harmonyos
水木流年追梦4 小时前
大模型入门-大模型优化方法13- MTP 多 token 输出、DCA 双块注意力
人工智能·分布式·算法·正则表达式·prompt
Francek Chen5 小时前
【大数据处理与分析】MapReduce:05 MapReduce的具体应用
大数据·hadoop·分布式·mapreduce
我是一颗柠檬6 小时前
【Java项目技术亮点】分布式锁实现与优化:从Redisson到ZooKeeper,彻底搞懂分布式锁的底层原理
java·redis·分布式·中间件·java-zookeeper
stevenzqzq6 小时前
vsCode AI插件
ide·人工智能·vscode
moonsims7 小时前
基于Lattice Mesh的AI 的分布式共识与动态任务分配架构的无人机群“去中心化无声协同”技术和极低带宽下的韧性通信技术
人工智能·分布式·架构