[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"similar-jia-zhuang--pytorch-multi-gpu-training":3,"tool-jia-zhuang--pytorch-multi-gpu-training":61},[4,18,26,36,44,53],{"id":5,"name":6,"github_repo":7,"description_zh":8,"stars":9,"difficulty_score":10,"last_commit_at":11,"category_tags":12,"status":17},4358,"openclaw","openclaw\u002Fopenclaw","OpenClaw 是一款专为个人打造的本地化 AI 助手，旨在让你在自己的设备上拥有完全可控的智能伙伴。它打破了传统 AI 助手局限于特定网页或应用的束缚，能够直接接入你日常使用的各类通讯渠道，包括微信、WhatsApp、Telegram、Discord、iMessage 等数十种平台。无论你在哪个聊天软件中发送消息，OpenClaw 都能即时响应，甚至支持在 macOS、iOS 和 Android 设备上进行语音交互，并提供实时的画布渲染功能供你操控。\n\n这款工具主要解决了用户对数据隐私、响应速度以及“始终在线”体验的需求。通过将 AI 部署在本地，用户无需依赖云端服务即可享受快速、私密的智能辅助，真正实现了“你的数据，你做主”。其独特的技术亮点在于强大的网关架构，将控制平面与核心助手分离，确保跨平台通信的流畅性与扩展性。\n\nOpenClaw 非常适合希望构建个性化工作流的技术爱好者、开发者，以及注重隐私保护且不愿被单一生态绑定的普通用户。只要具备基础的终端操作能力（支持 macOS、Linux 及 Windows WSL2），即可通过简单的命令行引导完成部署。如果你渴望拥有一个懂你",349277,3,"2026-04-06T06:32:30",[13,14,15,16],"Agent","开发框架","图像","数据工具","ready",{"id":19,"name":20,"github_repo":21,"description_zh":22,"stars":23,"difficulty_score":10,"last_commit_at":24,"category_tags":25,"status":17},3808,"stable-diffusion-webui","AUTOMATIC1111\u002Fstable-diffusion-webui","stable-diffusion-webui 是一个基于 Gradio 构建的网页版操作界面，旨在让用户能够轻松地在本地运行和使用强大的 Stable Diffusion 图像生成模型。它解决了原始模型依赖命令行、操作门槛高且功能分散的痛点，将复杂的 AI 绘图流程整合进一个直观易用的图形化平台。\n\n无论是希望快速上手的普通创作者、需要精细控制画面细节的设计师，还是想要深入探索模型潜力的开发者与研究人员，都能从中获益。其核心亮点在于极高的功能丰富度：不仅支持文生图、图生图、局部重绘（Inpainting）和外绘（Outpainting）等基础模式，还独创了注意力机制调整、提示词矩阵、负向提示词以及“高清修复”等高级功能。此外，它内置了 GFPGAN 和 CodeFormer 等人脸修复工具，支持多种神经网络放大算法，并允许用户通过插件系统无限扩展能力。即使是显存有限的设备，stable-diffusion-webui 也提供了相应的优化选项，让高质量的 AI 艺术创作变得触手可及。",162132,"2026-04-05T11:01:52",[14,15,13],{"id":27,"name":28,"github_repo":29,"description_zh":30,"stars":31,"difficulty_score":32,"last_commit_at":33,"category_tags":34,"status":17},1381,"everything-claude-code","affaan-m\u002Feverything-claude-code","everything-claude-code 是一套专为 AI 编程助手（如 Claude Code、Codex、Cursor 等）打造的高性能优化系统。它不仅仅是一组配置文件，而是一个经过长期实战打磨的完整框架，旨在解决 AI 代理在实际开发中面临的效率低下、记忆丢失、安全隐患及缺乏持续学习能力等核心痛点。\n\n通过引入技能模块化、直觉增强、记忆持久化机制以及内置的安全扫描功能，everything-claude-code 能显著提升 AI 在复杂任务中的表现，帮助开发者构建更稳定、更智能的生产级 AI 代理。其独特的“研究优先”开发理念和针对 Token 消耗的优化策略，使得模型响应更快、成本更低，同时有效防御潜在的攻击向量。\n\n这套工具特别适合软件开发者、AI 研究人员以及希望深度定制 AI 工作流的技术团队使用。无论您是在构建大型代码库，还是需要 AI 协助进行安全审计与自动化测试，everything-claude-code 都能提供强大的底层支持。作为一个曾荣获 Anthropic 黑客大奖的开源项目，它融合了多语言支持与丰富的实战钩子（hooks），让 AI 真正成长为懂上",149489,2,"2026-04-10T11:32:46",[14,13,35],"语言模型",{"id":37,"name":38,"github_repo":39,"description_zh":40,"stars":41,"difficulty_score":32,"last_commit_at":42,"category_tags":43,"status":17},2271,"ComfyUI","Comfy-Org\u002FComfyUI","ComfyUI 是一款功能强大且高度模块化的视觉 AI 引擎，专为设计和执行复杂的 Stable Diffusion 图像生成流程而打造。它摒弃了传统的代码编写模式，采用直观的节点式流程图界面，让用户通过连接不同的功能模块即可构建个性化的生成管线。\n\n这一设计巧妙解决了高级 AI 绘图工作流配置复杂、灵活性不足的痛点。用户无需具备编程背景，也能自由组合模型、调整参数并实时预览效果，轻松实现从基础文生图到多步骤高清修复等各类复杂任务。ComfyUI 拥有极佳的兼容性，不仅支持 Windows、macOS 和 Linux 全平台，还广泛适配 NVIDIA、AMD、Intel 及苹果 Silicon 等多种硬件架构，并率先支持 SDXL、Flux、SD3 等前沿模型。\n\n无论是希望深入探索算法潜力的研究人员和开发者，还是追求极致创作自由度的设计师与资深 AI 绘画爱好者，ComfyUI 都能提供强大的支持。其独特的模块化架构允许社区不断扩展新功能，使其成为当前最灵活、生态最丰富的开源扩散模型工具之一，帮助用户将创意高效转化为现实。",108322,"2026-04-10T11:39:34",[14,15,13],{"id":45,"name":46,"github_repo":47,"description_zh":48,"stars":49,"difficulty_score":32,"last_commit_at":50,"category_tags":51,"status":17},6121,"gemini-cli","google-gemini\u002Fgemini-cli","gemini-cli 是一款由谷歌推出的开源 AI 命令行工具，它将强大的 Gemini 大模型能力直接集成到用户的终端环境中。对于习惯在命令行工作的开发者而言，它提供了一条从输入提示词到获取模型响应的最短路径，无需切换窗口即可享受智能辅助。\n\n这款工具主要解决了开发过程中频繁上下文切换的痛点，让用户能在熟悉的终端界面内直接完成代码理解、生成、调试以及自动化运维任务。无论是查询大型代码库、根据草图生成应用，还是执行复杂的 Git 操作，gemini-cli 都能通过自然语言指令高效处理。\n\n它特别适合广大软件工程师、DevOps 人员及技术研究人员使用。其核心亮点包括支持高达 100 万 token 的超长上下文窗口，具备出色的逻辑推理能力；内置 Google 搜索、文件操作及 Shell 命令执行等实用工具；更独特的是，它支持 MCP（模型上下文协议），允许用户灵活扩展自定义集成，连接如图像生成等外部能力。此外，个人谷歌账号即可享受免费的额度支持，且项目基于 Apache 2.0 协议完全开源，是提升终端工作效率的理想助手。",100752,"2026-04-10T01:20:03",[52,13,15,14],"插件",{"id":54,"name":55,"github_repo":56,"description_zh":57,"stars":58,"difficulty_score":32,"last_commit_at":59,"category_tags":60,"status":17},4721,"markitdown","microsoft\u002Fmarkitdown","MarkItDown 是一款由微软 AutoGen 团队打造的轻量级 Python 工具，专为将各类文件高效转换为 Markdown 格式而设计。它支持 PDF、Word、Excel、PPT、图片（含 OCR）、音频（含语音转录）、HTML 乃至 YouTube 链接等多种格式的解析，能够精准提取文档中的标题、列表、表格和链接等关键结构信息。\n\n在人工智能应用日益普及的今天，大语言模型（LLM）虽擅长处理文本，却难以直接读取复杂的二进制办公文档。MarkItDown 恰好解决了这一痛点，它将非结构化或半结构化的文件转化为模型“原生理解”且 Token 效率极高的 Markdown 格式，成为连接本地文件与 AI 分析 pipeline 的理想桥梁。此外，它还提供了 MCP（模型上下文协议）服务器，可无缝集成到 Claude Desktop 等 LLM 应用中。\n\n这款工具特别适合开发者、数据科学家及 AI 研究人员使用，尤其是那些需要构建文档检索增强生成（RAG）系统、进行批量文本分析或希望让 AI 助手直接“阅读”本地文件的用户。虽然生成的内容也具备一定可读性，但其核心优势在于为机器",93400,"2026-04-06T19:52:38",[52,14],{"id":62,"github_repo":63,"name":64,"description_en":65,"description_zh":66,"ai_summary_zh":66,"readme_en":67,"readme_zh":68,"quickstart_zh":69,"use_case_zh":70,"hero_image_url":71,"owner_login":72,"owner_name":73,"owner_avatar_url":74,"owner_bio":75,"owner_company":76,"owner_location":77,"owner_email":78,"owner_twitter":79,"owner_website":80,"owner_url":81,"languages":82,"stars":87,"forks":88,"last_commit_at":89,"license":79,"difficulty_score":10,"env_os":90,"env_gpu":91,"env_ram":90,"env_deps":92,"category_tags":98,"github_topics":99,"view_count":32,"oss_zip_url":79,"oss_zip_packed_at":79,"status":17,"created_at":102,"updated_at":103,"faqs":104,"releases":119},6218,"jia-zhuang\u002Fpytorch-multi-gpu-training","pytorch-multi-gpu-training","整理 pytorch 单机多 GPU 训练方法与原理","pytorch-multi-gpu-training 是一个专为 PyTorch 开发者整理的单机多 GPU 训练指南与代码示例库。它旨在帮助用户在提升模型训练速度的同时，深入理解背后的实现原理，避免仅停留在“调包”层面。\n\n该资源重点解决了多卡训练中常见的负载不均衡与效率瓶颈问题。它详细对比了两种主流方案：一是基于 `nn.DataParallel` 的简易封装法，适合快速上手但存在主卡负载过高的问题；二是基于 `DistributedDataParallel` 的多进程分布式训练法，虽然配置稍复杂，但能显著降低通信开销，即使在单节点上也具备更高的训练效率。\n\n内容特色在于通过 MNIST 手写数字识别的完整案例，循序渐进地讲解了数据切分、进程通信、随机种子设定及 `local_rank` 机制等核心技术细节。无论是希望优化实验效率的算法研究人员，还是想要夯实底层基础的深度学习工程师，都能从中获得实用的代码参考与清晰的理论指引，轻松掌握从单卡到多卡训练的进阶技能。","# PyTorch 单机多GPU 训练方法与原理整理\n\n这里整理一些PyTorch单机多核训练的方法和简单原理，目的是既能在写代码时知道怎么用，又能从原理上知道大致是怎么回事儿。如果只是炼丹，有时候确实没时间和精力深挖太多实现原理，但又希望能理解简单逻辑。\n\nPyTorch单机多核训练方案有两种：一种是利用`nn.DataParallel`实现，实现简单，不涉及多进程；另一种是用`torch.nn.parallel.DistributedDataParallel`和`torch.utils.data.distributed.DistributedSampler`结合多进程实现。第二种方式效率更高，但是实现起来稍难，第二种方式同时支持多节点分布式实现。方案二的效率要比方案一高，即使是在单运算节点上。\n\n为方便理解，这里用一个简单的CNN模型训练MNIST手写数据集，相关代码：\n\n- [model.py](.\u002Fmodel.py)：定义一个简单的CNN网络\n- [data.py](.\u002Fdata.py)：MNIST训练集和数据集准备\n- [single_gpu_train.py](.\u002Fsingle_gpu_train.py)：单GPU训练代码\n\n### 方案一\n\n核心在于使用`nn.DataParallel`将模型wrap一下，代码其他地方不需要做任何更改:\n\n```python\nmodel = nn.DataParallel(model)\n```\n\n为方便说明，我们假设模型输入为(32, input_dim)，这里的 32 表示batch_size，模型输出为(32, output_dim)，使用 4 个GPU训练。`nn.DataParallel`起到的作用是将这 32 个样本拆成 4 份，发送给 4 个GPU 分别做 forward，然后生成 4 个大小为(8, output_dim)的输出，然后再将这 4 个输出都收集到`cuda:0`上并合并成(32, output_dim)。\n\n可以看出，`nn.DataParallel`没有改变模型的输入输出，因此其他部分的代码不需要做任何更改，非常方便。但弊端是，后续的loss计算只会在`cuda:0`上进行，没法并行，因此会导致负载不均衡的问题。\n\n如果把`loss`放在模型里计算的话，则可以缓解上述负载不均衡的问题，示意代码如下：\n\n```python\n\nclass Net:\n    def __init__(self,...):\n        # code\n    \n    def forward(self, inputs, labels=None)\n        # outputs = fct(inputs)\n        # loss_fct = ...\n        if labels is not None:\n            loss = loss_fct(outputs, labels)  # 在训练模型时直接将labels传入模型，在forward过程中计算loss\n            return loss\n        else:\n            return outputs\n```\n\n按照我们上面提到的模型并行逻辑，在每个GPU上会计算出一个loss，这些loss会被收集到`cuda:0`上并合并成长度为 4 的张量。这个时候在做backward的之前，必须对将这个loss张量合并成一个标量，一般直接取mean就可以。这在Pytorch官方文档[nn.DataParallel函数]()中有提到：\n\n> When `module` returns a scalar (i.e., 0-dimensional tensor) in forward(), this wrapper will return a vector of length equal to number of devices used in data parallelism, containing the result from each device.\n\n这部分的例子可以参考：[data_parallel_train.py](.\u002Fdata_parallel.py)\n\n### 方案二\n\n方案二被成为分布式数据并行(distributed data parallel)，是通过多进程实现的，相比与方案一要复杂很多。可以从以下几个方面理解：\n\n1. 从一开始就会启动多个进程(进程数等于GPU数)，每个进程独享一个GPU，每个进程都会独立地执行代码。这意味着每个进程都独立地初始化模型、训练，当然，在每次迭代过程中会通过进程间通信共享梯度，整合梯度，然后独立地更新参数。\n\n2. 每个进程都会初始化一份训练数据集，当然它们会使用数据集中的不同记录做训练，这相当于同样的模型喂进去不同的数据做训练，也就是所谓的数据并行。这是通过`torch.utils.data.distributed.DistributedSampler`函数实现的，不过逻辑上也不难想到，只要做一下数据partition，不同进程拿到不同的parition就可以了，官方有一个简单的demo，感兴趣的可以看一下代码实现：[Distributed Training](https:\u002F\u002Fpytorch.org\u002Ftutorials\u002Fintermediate\u002Fdist_tuto.html#distributed-training)\n\n3. 进程通过`local_rank`变量来标识自己，`local_rank`为0的为master，其他是slave。这个变量是`torch.distributed`包帮我们创建的，使用方法如下：\n\n    ```python\n    import argparse  # 必须引入 argparse 包\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\"--local_rank\", type=int, default=-1)\n    args = parser.parse_args()\n    ```\n\n    必须以如下方式运行代码：\n\n    ```bash\n    python -m torch.distributed.launch --nproc_per_node=2 --nnodes=1 train.py\n    ```\n\n    这样的话，`torch.distributed.launch`就以命令行参数的方式将`args.local_rank`变量注入到每个进程中，每个进程得到的变量值都不相同。比如使用 4 个GPU的话，则 4 个进程获得的`args.local_rank`值分别为0、1、2、3。\n\n    上述命令行参数`nproc_per_node`表示每个节点需要创建多少个进程(使用几个GPU就创建几个)；`nnodes`表示使用几个节点，因为我们是做单机多核训练，所以设为1。\n\n4. 因为每个进程都会初始化一份模型，为保证模型初始化过程中生成的随机权重相同，需要设置随机种子。方法如下：\n\n    ```python\n    def set_seed(seed):\n        random.seed(seed)\n        np.random.seed(seed)\n        torch.manual_seed(seed)\n        torch.cuda.manual_seed_all(seed)\n    ```\n\n\n使用方法通过如下示意代码展示：\n\n```python\nfrom torch.utils.data.distributed import DistributedSampler  # 负责分布式dataloader创建，也就是实现上面提到的partition。\n\n# 负责创建 args.local_rank 变量，并接受 torch.distributed.launch 注入的值\nparser = argparse.ArgumentParser()\nparser.add_argument(\"--local_rank\", type=int, default=-1)\nargs = parser.parse_args()\n\n# 每个进程根据自己的local_rank设置应该使用的GPU\ntorch.cuda.set_device(args.local_rank)\ndevice = torch.device('cuda', args.local_rank)\n\n# 初始化分布式环境，主要用来帮助进程间通信\ntorch.distributed.init_process_group(backend='nccl')\n\n# 固定随机种子\nseed = 42\nrandom.seed(seed)\nnp.random.seed(seed)\ntorch.manual_seed(seed)\ntorch.cuda.manual_seed_all(seed)\n\n# 初始化模型\nmodel = Net()\nmodel.to(device)\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.SGD(model.parameters(), lr=0.1)\n\n# 只 master 进程做 logging，否则输出会很乱\nif args.local_rank == 0:\n    tb_writer = SummaryWriter(comment='ddp-training')\n\n# 分布式数据集\ntrain_sampler = DistributedSampler(train_dataset)\ntrain_loader = torch.utils.data.DataLoader(train_dataset, sampler=train_sampler, batch_size=batch_size)  # 注意这里的batch_size是每个GPU上的batch_size\n\n# 分布式模型\nmodel = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.local_rank], output_device=args.local_rank, find_unused_parameters=True)\n```\n\n详细代码参考：[ddp_train.py](.\u002Fddp_train.py)\n\n\n### ddp有用的技巧\n\n官方推荐使用方案二(ddp)，所以这里收集ddp使用过程中的一些技巧。\n\n#### torch.distributed.barrier\n\n在读[huggingface\u002Ftransformers](https:\u002F\u002Fgithub.com\u002Fhuggingface\u002Ftransformers)中的源码，比如`examples\u002Frun_ner.py`会看到一下代码：\n\n```python\n    # Load pretrained model and tokenizer\n    if args.local_rank not in [-1, 0]:\n        torch.distributed.barrier()  # Make sure only the first process in distributed training will download model & vocab\n\n    args.model_type = args.model_type.lower()\n    config_class, model_class, tokenizer_class = MODEL_CLASSES[args.model_type]\n    config = config_class.from_pretrained(args.config_name if args.config_name else args.model_name_or_path,\n                                          num_labels=num_labels,\n                                          cache_dir=args.cache_dir if args.cache_dir else None)\n    tokenizer = tokenizer_class.from_pretrained(args.tokenizer_name if args.tokenizer_name else args.model_name_or_path,\n                                                do_lower_case=args.do_lower_case,\n                                                cache_dir=args.cache_dir if args.cache_dir else None)\n    model = model_class.from_pretrained(args.model_name_or_path,\n                                        from_tf=bool(\".ckpt\" in args.model_name_or_path),\n                                        config=config,\n                                        cache_dir=args.cache_dir if args.cache_dir else None)\n\n    if args.local_rank == 0:\n        torch.distributed.barrier()  # Make sure only the first process in distributed training will download model & vocab\n```\n\n上述代码要实现预训练模型的下载和读入内存，如果4个进程都分别下载一遍显然是不合理的，那如何才能实现只让一个进程下载呢？这个时候就可以使用`barrier`函数。当slave进程(local_rank!=0)运行到第一个`if`时就被barrier住了，只能等着，但master进程可以往下运行完成模型的下载和读入内存，\b但在第二个`if`语句时遇到barrier，那会不会被barrier住呢？答案是不会，因为master进程和slave进程集合在一起了(barrier)，barrier会被解除，这样大家都往下执行。当然这时大家执行的进度不同，master进程已经执行过模型读入，所以从第二个`if`往下执行，而slave进程尚未执行模型读入，只会从第一个`if`往下执行。\n\n可以看到`barrier`类似一个路障，进程会被拦住，直到所有进程都集合齐了才放行。适合这样的场景：只一个进程下载，其他进程可以使用下载好的文件；只一个进程预处理数据，其他进程使用预处理且cache好的数据等。\n\n#### 模型保存\n\n模型的保存与加载，与单GPU的方式有所不同。这里通通将参数以cpu的方式save进存储, 因为如果是保存的GPU上参数，pth文件中会记录参数属于的GPU号，则加载时会加载到相应的GPU上，这样就会导致如果你GPU数目不够时会在加载模型时报错，像下面这样：\n>RuntimeError: Attempting to deserialize object on CUDA device 1 but torch.cuda.device_count() is 1. Please use torch.load with map_location to map your storages to an existing device.\n\n模型保存都是一致的，不过时刻记住方案二中你有多个进程在同时跑，所以会保存多个模型到存储上，如果使用共享存储就要注意文件名的问题，当然一般只在rank0进程上保存参数即可，因为所有进程的模型参数是同步的。\n\n```python\ntorch.save(model.module.cpu().state_dict(), \"model.pth\")\n```\n\n模型的加载：\n\n```python\nparam=torch.load(\"model.pth\")\n```\n\n以下是[huggingface\u002Ftransformers]()代码中用到的模型保存代码\n\n```python\nif torch.distributed.get_rank() == 0:\n    model_to_save = model.module if hasattr(model, \"module\") else model  # Take care of distributed\u002Fparallel training\n    model_to_save.save_pretrained(args.output_dir)\n    tokenizer.save_pretrained(args.output_dir)\n```\n\n#### 同一台机器上跑多个 ddp task\n\n假设想在一台有4核GPU的电脑上跑两个ddp task，每个task使用两个核，很可能会需要如下错误：\n\n```\nRuntimeError: Address already in use\nRuntimeError: NCCL error in: \u002Fopt\u002Fconda\u002Fconda-bld\u002Fpytorch_1544081127912\u002Fwork\u002Ftorch\u002Flib\u002Fc10d\u002FProcessGroupNCCL.cpp:260, unhandled system error\n```\n\n原因是两个ddp task通讯地址冲突，这时候需要显示地设置每个task的地址\n\n> specifying a different master_addr and master_port in torch.distributed.launch\n\n```bash\n# 第一个task\nexport CUDA_VISIBLE_DEVICES=\"0,1\" \npython -m torch.distributed.launch --nproc_per_node=2 --master_addr=127.0.0.1 --master_port=29501 train.py\n\n# 第二个task\nexport CUDA_VISIBLE_DEVICES=\"2,3\" \npython -m torch.distributed.launch --nproc_per_node=2 --master_addr=127.0.0.2 --master_port=29502 train.py\n```\n\n\n### 参考\n\n[Pytorch 多GPU训练-单运算节点-All you need](https:\u002F\u002Fwww.cnblogs.com\u002Fwalter-xh\u002Fp\u002F11586507.html)\n\n[WRITING DISTRIBUTED APPLICATIONS WITH PYTORCH](https:\u002F\u002Fpytorch.org\u002Ftutorials\u002Fintermediate\u002Fdist_tuto.html)\n\n[pytorch 多GPU训练总结（DataParallel的使用）](https:\u002F\u002Fblog.csdn.net\u002Fweixin_40087578\u002Farticle\u002Fdetails\u002F87186613)\n","# PyTorch 单机多GPU 训练方法与原理整理\n\n这里整理一些PyTorch单机多核训练的方法和简单原理，目的是既能在写代码时知道怎么用，又能从原理上知道大致是怎么回事儿。如果只是炼丹，有时候确实没时间和精力深挖太多实现原理，但又希望能理解简单逻辑。\n\nPyTorch单机多核训练方案有两种：一种是利用`nn.DataParallel`实现，实现简单，不涉及多进程；另一种是用`torch.nn.parallel.DistributedDataParallel`和`torch.utils.data.distributed.DistributedSampler`结合多进程实现。第二种方式效率更高，但是实现起来稍难，第二种方式同时支持多节点分布式实现。方案二的效率要比方案一高，即使是在单运算节点上。\n\n为方便理解，这里用一个简单的CNN模型训练MNIST手写数据集，相关代码：\n\n- [model.py](.\u002Fmodel.py)：定义一个简单的CNN网络\n- [data.py](.\u002Fdata.py)：MNIST训练集和数据集准备\n- [single_gpu_train.py](.\u002Fsingle_gpu_train.py)：单GPU训练代码\n\n### 方案一\n\n核心在于使用`nn.DataParallel`将模型wrap一下，代码其他地方不需要做任何更改:\n\n```python\nmodel = nn.DataParallel(model)\n```\n\n为方便说明，我们假设模型输入为(32, input_dim)，这里的 32 表示batch_size，模型输出为(32, output_dim)，使用 4 个GPU训练。`nn.DataParallel`起到的作用是将这 32 个样本拆成 4 份，发送给 4 个GPU 分别做 forward，然后生成 4 个大小为(8, output_dim)的输出，然后再将这 4 个输出都收集到`cuda:0`上并合并成(32, output_dim)。\n\n可以看出，`nn.DataParallel`没有改变模型的输入输出，因此其他部分的代码不需要做任何更改，非常方便。但弊端是，后续的loss计算只会在`cuda:0`上进行，没法并行，因此会导致负载不均衡的问题。\n\n如果把`loss`放在模型里计算的话，则可以缓解上述负载不均衡的问题，示意代码如下：\n\n```python\n\nclass Net:\n    def __init__(self,...):\n        # code\n    \n    def forward(self, inputs, labels=None)\n        # outputs = fct(inputs)\n        # loss_fct = ...\n        if labels is not None:\n            loss = loss_fct(outputs, labels)  # 在训练模型时直接将labels传入模型，在forward过程中计算loss\n            return loss\n        else:\n            return outputs\n```\n\n按照我们上面提到的模型并行逻辑，在每个GPU上会计算出一个loss，这些loss会被收集到`cuda:0`上并合并成长度为 4 的张量。这个时候在做backward的之前，必须对将这个loss张量合并成一个标量，一般直接取mean就可以。这在Pytorch官方文档[nn.DataParallel函数]()中有提到：\n\n> When `module` returns a scalar (i.e., 0-dimensional tensor) in forward(), this wrapper will return a vector of length equal to number of devices used in data parallelism, containing the result from each device.\n\n这部分的例子可以参考：[data_parallel_train.py](.\u002Fdata_parallel.py)\n\n### 方案二\n\n方案二被成为分布式数据并行(distributed data parallel)，是通过多进程实现的，相比与方案一要复杂很多。可以从以下几个方面理解：\n\n1. 从一开始就会启动多个进程(进程数等于GPU数)，每个进程独享一个GPU，每个进程都会独立地执行代码。这意味着每个进程都独立地初始化模型、训练，当然，在每次迭代过程中会通过进程间通信共享梯度，整合梯度，然后独立地更新参数。\n\n2. 每个进程都会初始化一份训练数据集，当然它们会使用数据集中的不同记录做训练，这相当于同样的模型喂进去不同的数据做训练，也就是所谓的数据并行。这是通过`torch.utils.data.distributed.DistributedSampler`函数实现的，不过逻辑上也不难想到，只要做一下数据partition，不同进程拿到不同的parition就可以了，官方有一个简单的demo，感兴趣的可以看一下代码实现：[Distributed Training](https:\u002F\u002Fpytorch.org\u002Ftutorials\u002Fintermediate\u002Fdist_tuto.html#distributed-training)\n\n3. 进程通过`local_rank`变量来标识自己，`local_rank`为0的为master，其他是slave。这个变量是`torch.distributed`包帮我们创建的，使用方法如下：\n\n    ```python\n    import argparse  # 必须引入 argparse 包\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\"--local_rank\", type=int, default=-1)\n    args = parser.parse_args()\n    ```\n\n    必须以如下方式运行代码：\n\n    ```bash\n    python -m torch.distributed.launch --nproc_per_node=2 --nnodes=1 train.py\n    ```\n\n    这样的话，`torch.distributed.launch`就以命令行参数的方式将`args.local_rank`变量注入到每个进程中，每个进程得到的变量值都不相同。比如使用 4 个GPU的话，则 4 个进程获得的`args.local_rank`值分别为0、1、2、3。\n\n    上述命令行参数`nproc_per_node`表示每个节点需要创建多少个进程(使用几个GPU就创建几个)；`nnodes`表示使用几个节点，因为我们是做单机多核训练，所以设为1。\n\n4. 因为每个进程都会初始化一份模型，为保证模型初始化过程中生成的随机权重相同，需要设置随机种子。方法如下：\n\n    ```python\n    def set_seed(seed):\n        random.seed(seed)\n        np.random.seed(seed)\n        torch.manual_seed(seed)\n        torch.cuda.manual_seed_all(seed)\n    ```\n\n\n使用方法通过如下示意代码展示：\n\n```python\nfrom torch.utils.data.distributed import DistributedSampler  # 负责分布式dataloader创建，也就是实现上面提到的partition。\n\n# 负责创建 args.local_rank 变量，并接受 torch.distributed.launch 注入的值\nparser = argparse.ArgumentParser()\nparser.add_argument(\"--local_rank\", type=int, default=-1)\nargs = parser.parse_args()\n\n# 每个进程根据自己的local_rank设置应该使用的GPU\ntorch.cuda.set_device(args.local_rank)\ndevice = torch.device('cuda', args.local_rank)\n\n# 初始化分布式环境，主要用来帮助进程间通信\ntorch.distributed.init_process_group(backend='nccl')\n\n# 固定随机种子\nseed = 42\nrandom.seed(seed)\nnp.random.seed(seed)\ntorch.manual_seed(seed)\ntorch.cuda.manual_seed_all(seed)\n\n# 初始化模型\nmodel = Net()\nmodel.to(device)\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.SGD(model.parameters(), lr=0.1)\n\n# 只 master 进程做 logging，否则输出会很乱\nif args.local_rank == 0:\n    tb_writer = SummaryWriter(comment='ddp-training')\n\n# 分布式数据集\ntrain_sampler = DistributedSampler(train_dataset)\ntrain_loader = torch.utils.data.DataLoader(train_dataset, sampler=train_sampler, batch_size=batch_size)  # 注意这里的batch_size是每个GPU上的batch_size\n\n# 分布式模型\nmodel = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.local_rank], output_device=args.local_rank, find_unused_parameters=True)\n```\n\n详细代码参考：[ddp_train.py](.\u002Fddp_train.py)\n\n### DDP 有用技巧\n\n官方推荐使用方案二（DDP），因此这里收集了一些在使用 DDP 过程中的技巧。\n\n#### `torch.distributed.barrier`\n\n在阅读 [huggingface\u002Ftransformers](https:\u002F\u002Fgithub.com\u002Fhuggingface\u002Ftransformers) 的源码时，比如 `examples\u002Frun_ner.py` 中会看到如下代码：\n\n```python\n    # 加载预训练模型和分词器\n    if args.local_rank not in [-1, 0]:\n        torch.distributed.barrier()  # 确保只有分布式训练中的第一个进程下载模型和词汇表\n    args.model_type = args.model_type.lower()\n    config_class, model_class, tokenizer_class = MODEL_CLASSES[args.model_type]\n    config = config_class.from_pretrained(args.config_name if args.config_name else args.model_name_or_path,\n                                          num_labels=num_labels,\n                                          cache_dir=args.cache_dir if args.cache_dir else None)\n    tokenizer = tokenizer_class.from_pretrained(args.tokenizer_name if args.tokenizer_name else args.model_name_or_path,\n                                                do_lower_case=args.do_lower_case,\n                                                cache_dir=args.cache_dir if args.cache_dir else None)\n    model = model_class.from_pretrained(args.model_name_or_path,\n                                        from_tf=bool(\".ckpt\" in args.model_name_or_path),\n                                        config=config,\n                                        cache_dir=args.cache_dir if args.cache_dir else None)\n\n    if args.local_rank == 0:\n        torch.distributed.barrier()  # 确保只有分布式训练中的第一个进程下载模型和词汇表\n```\n\n上述代码的目的是加载预训练模型并将其读入内存。如果让四个进程各自都下载一次显然是不合理的，那么如何才能实现只让一个进程下载呢？这时就可以使用 `barrier` 函数。当从属进程（`local_rank != 0`）运行到第一个 `if` 语句时，会被 `barrier` 阻塞，只能等待；而主进程可以继续往下执行，完成模型的下载和读入内存的操作。然而，在第二个 `if` 语句处再次遇到 `barrier` 时，主进程是否会被阻塞呢？答案是不会，因为主进程和从属进程已经集合在一起了（即 `barrier` 被解除），于是所有进程都可以继续向下执行。当然，此时各进程的执行进度不同：主进程已经完成了模型的读取，所以会从第二个 `if` 继续执行；而从属进程尚未进行模型读取，只会从第一个 `if` 开始执行。\n\n由此可见，`barrier` 类似于一个路障，会将进程拦住，直到所有进程都集合齐后才会放行。这种机制适用于以下场景：只有一个进程负责下载数据，其他进程可以直接使用已下载好的文件；或者只有一个进程负责数据预处理，其他进程则使用预处理并缓存好的数据等。\n\n#### 模型保存\n\n模型的保存与加载方式与单 GPU 情况有所不同。在这里需要将参数以 CPU 方式保存到存储中，因为如果直接保存 GPU 上的参数，`.pth` 文件中会记录这些参数所属的 GPU 编号，导致在加载模型时会尝试将参数加载到相应的 GPU 上，从而引发错误。例如：\n\n> RuntimeError: Attempting to deserialize object on CUDA device 1 but torch.cuda.device_count() is 1. Please use torch.load with map_location to map your storages to an existing device.\n\n模型保存的方式是一致的，但需要注意的是，在方案二中存在多个进程同时运行，因此可能会有多个模型被保存到存储中。如果使用共享存储，则需注意文件名冲突的问题。通常情况下，只需在 rank 0 进程上保存模型参数即可，因为所有进程的模型参数是同步的。\n\n```python\ntorch.save(model.module.cpu().state_dict(), \"model.pth\")\n```\n\n模型的加载：\n\n```python\nparam = torch.load(\"model.pth\")\n```\n\n以下是 [huggingface\u002Ftransformers](https:\u002F\u002Fgithub.com\u002Fhuggingface\u002Ftransformers) 代码中使用的模型保存代码：\n\n```python\nif torch.distributed.get_rank() == 0:\n    model_to_save = model.module if hasattr(model, \"module\") else model  # 处理分布式\u002F并行训练\n    model_to_save.save_pretrained(args.output_dir)\n    tokenizer.save_pretrained(args.output_dir)\n```\n\n#### 同一台机器上运行多个 DDP 任务\n\n假设希望在一台拥有 4 个 GPU 的计算机上运行两个 DDP 任务，每个任务使用两个 GPU，很可能会遇到如下错误：\n\n```\nRuntimeError: Address already in use\nRuntimeError: NCCL error in: \u002Fopt\u002Fconda\u002Fconda-bld\u002Fpytorch_1544081127912\u002Fwork\u002Ftorch\u002Flib\u002Fc10d\u002FProcessGroupNCCL.cpp:260, unhandled system error\n```\n\n出现该错误的原因是两个 DDP 任务的通信地址发生了冲突。此时需要显式地为每个任务设置不同的通信地址。\n\n> 在 `torch.distributed.launch` 中指定不同的 `master_addr` 和 `master_port`\n\n```bash\n# 第一个任务\nexport CUDA_VISIBLE_DEVICES=\"0,1\"\npython -m torch.distributed.launch --nproc_per_node=2 --master_addr=127.0.0.1 --master_port=29501 train.py\n\n# 第二个任务\nexport CUDA_VISIBLE_DEVICES=\"2,3\"\npython -m torch.distributed.launch --nproc_per_node=2 --master_addr=127.0.0.2 --master_port=29502 train.py\n```\n\n### 参考资料\n\n[PyTorch 多 GPU 训练——单计算节点——你需要的一切](https:\u002F\u002Fwww.cnblogs.com\u002Fwalter-xh\u002Fp\u002F11586507.html)\n\n[使用 PyTorch 编写分布式应用程序](https:\u002F\u002Fpytorch.org\u002Ftutorials\u002Fintermediate\u002Fdist_tuto.html)\n\n[PyTorch 多 GPU 训练总结（DataParallel 的使用）](https:\u002F\u002Fblog.csdn.net\u002Fweixin_40087578\u002Farticle\u002Fdetails\u002F87186613)","# PyTorch 单机多 GPU 训练快速上手指南\n\n本指南基于 `pytorch-multi-gpu-training` 项目，帮助开发者快速掌握 PyTorch 在单机环境下的多 GPU 训练方案。主要介绍两种核心方法：简单易用的 `DataParallel` (DP) 和高效推荐的 `DistributedDataParallel` (DDP)。\n\n## 环境准备\n\n### 系统要求\n- **操作系统**: Linux (推荐 Ubuntu 18.04+) 或 Windows (需配置相应 CUDA 环境)\n- **GPU**: 2 块或以上 NVIDIA GPU，已安装兼容的 NVIDIA 驱动\n- **CUDA**: 已安装与 PyTorch 版本匹配的 CUDA Toolkit\n\n### 前置依赖\n- Python 3.6+\n- PyTorch (建议 1.4+)\n- torchvision\n- tensorboard (可选，用于日志可视化)\n\n**国内加速安装推荐**：\n使用清华源或阿里源可显著提升下载速度。\n\n```bash\n# 使用清华源安装 PyTorch (示例：CUDA 11.3)\npip install torch torchvision torchaudio --extra-index-url https:\u002F\u002Fdownload.pytorch.org\u002Fwhl\u002Fcu113 -i https:\u002F\u002Fpypi.tuna.tsinghua.edu.cn\u002Fsimple\n```\n\n## 安装步骤\n\n本项目主要为代码示例集合，无需额外安装工具包。只需确保上述依赖已就绪，并克隆或下载项目代码即可。\n\n```bash\n# 假设项目代码已在本地\ncd pytorch-multi-gpu-training\n# 确认目录包含 model.py, data.py, single_gpu_train.py 等文件\nls\n```\n\n## 基本使用\n\nPyTorch 单机多 GPU 训练主要有两种方案，请根据需求选择。\n\n### 方案一：DataParallel (DP)\n**特点**：实现极简，无需多进程，适合快速验证或小规模实验。但在单节点上效率略低于 DDP，且存在主卡负载不均问题。\n\n**核心代码**：\n只需在模型定义后包裹一层 `nn.DataParallel`。\n\n```python\nimport torch.nn as nn\nfrom model import Net # 假设这是你定义的模型\n\n# 初始化模型\nmodel = Net()\n\n# 【关键步骤】包裹 DataParallel\n# 自动将模型复制到所有可见 GPU，并将输入数据拆分\nmodel = nn.DataParallel(model)\n\n# 后续训练代码 (loss 计算、backward、optimizer step) 无需更改\n# 注意：Loss 计算默认在主卡 (cuda:0) 进行\n```\n\n**运行方式**：\n直接像单 GPU 一样运行脚本，PyTorch 会自动识别所有可用 GPU。\n\n```bash\npython single_gpu_train.py\n# 或者如果你修改了代码使用了 DP\npython data_parallel_train.py\n```\n\n---\n\n### 方案二：DistributedDataParallel (DDP) **[官方推荐]**\n**特点**：基于多进程实现，每个进程独占一个 GPU，通信效率高，支持多节点扩展。是生产环境和大规模训练的首选。\n\n**核心步骤**：\n\n1.  **代码修改**：\n    需要在代码中加入进程初始化、参数解析、Sampler 设置及模型包裹逻辑。\n\n    ```python\n    import argparse\n    import torch\n    import torch.distributed as dist\n    from torch.nn.parallel import DistributedDataParallel as DDP\n    from torch.utils.data.distributed import DistributedSampler\n    from model import Net\n    from data import train_dataset\n\n    # 1. 解析 local_rank 参数 (由启动命令注入)\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\"--local_rank\", type=int, default=-1)\n    args = parser.parse_args()\n\n    # 2. 设置当前进程使用的 GPU\n    torch.cuda.set_device(args.local_rank)\n    device = torch.device('cuda', args.local_rank)\n\n    # 3. 初始化分布式环境 (后端推荐 nccl)\n    dist.init_process_group(backend='nccl')\n\n    # 4. 固定随机种子 (保证多进程模型初始化一致)\n    seed = 42\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed_all(seed)\n\n    # 5. 准备数据 (使用 DistributedSampler 切分数据)\n    # 注意：DataLoader 中不要设置 shuffle=True，由 Sampler 控制\n    train_sampler = DistributedSampler(train_dataset)\n    train_loader = torch.utils.data.DataLoader(\n        train_dataset, \n        sampler=train_sampler, \n        batch_size=64  # 这里的 batch_size 是单个 GPU 上的数量\n    )\n\n    # 6. 初始化模型并移动到设备\n    model = Net().to(device)\n\n    # 7. 【关键步骤】包裹 DistributedDataParallel\n    model = DDP(model, device_ids=[args.local_rank], output_device=args.local_rank)\n\n    # 8. 定义优化器和损失函数\n    criterion = torch.nn.CrossEntropyLoss()\n    optimizer = torch.optim.SGD(model.parameters(), lr=0.1)\n\n    # 9. 训练循环 (仅在主进程打印日志，避免输出混乱)\n    if args.local_rank == 0:\n        print(\"Start training...\")\n    \n    for epoch in range(10):\n        train_sampler.set_epoch(epoch) # 每个 epoch 打乱数据顺序\n        for inputs, labels in train_loader:\n            inputs, labels = inputs.to(device), labels.to(device)\n            \n            optimizer.zero_grad()\n            outputs = model(inputs)\n            loss = criterion(outputs, labels)\n            loss.backward()\n            optimizer.step()\n            \n            if args.local_rank == 0 and step % 100 == 0:\n                print(f\"Epoch {epoch}, Loss: {loss.item()}\")\n    ```\n\n2.  **运行方式**：\n    必须使用 `torch.distributed.launch` 模块启动脚本，指定 GPU 数量。\n\n    ```bash\n    # 使用 4 个 GPU 进行训练\n    python -m torch.distributed.launch --nproc_per_node=4 ddp_train.py\n    ```\n\n    *注：若需在同一台机器运行多个 DDP 任务，需通过 `--master_port` 区分端口，并配合 `CUDA_VISIBLE_DEVICES` 指定显卡，详见原文技巧部分。*\n\n### 模型保存特别提示 (针对 DDP)\n在 DDP 模式下保存模型时，务必保存 `model.module` 的状态字典，以避免加载时出现 GPU 索引错误。\n\n```python\n# 仅在主进程保存\nif args.local_rank == 0:\n    # 正确做法：访问 .module 属性获取原始模型\n    torch.save(model.module.cpu().state_dict(), \"model_ddp.pth\")\n```","某计算机视觉团队正在训练一个高分辨率医学影像分割模型，面对海量数据，单卡训练耗时过长严重阻碍了算法迭代。\n\n### 没有 pytorch-multi-gpu-training 时\n- **训练周期漫长**：仅依赖单张 GPU 串行处理数据，完成一轮完整训练需数天时间，无法快速验证新想法。\n- **资源利用率低**：实验室服务器虽配备多张高端显卡，但因缺乏多进程调度机制，其余显卡处于闲置状态。\n- **负载严重不均**：若强行使用简单的 `DataParallel` 包装，所有损失计算与梯度更新都堆积在主卡（cuda:0）上，导致主卡显存爆满而其他卡“吃不饱”。\n- **代码改造困难**：开发者不清楚如何手动切分数据集或同步多进程梯度，担心引入复杂的分布式逻辑会破坏现有代码结构。\n\n### 使用 pytorch-multi-gpu-training 后\n- **训练效率倍增**：通过 `DistributedDataParallel` 方案启动多进程，每张显卡独立承担前向传播与反向传播，训练速度提升近 4 倍。\n- **硬件满负荷运转**：利用 `DistributedSampler` 自动将数据集均匀切分，确保所有 GPU 同时处理不同批次数据，彻底消除资源浪费。\n- **负载均衡优化**：每个进程独立计算 Loss 并同步梯度，避免了主卡成为性能瓶颈，显存占用在各卡间分布均匀。\n- **原理清晰易落地**：参考整理好的原理说明与示例代码，团队迅速理解了 `local_rank` 注入与随机种子设置逻辑，低成本完成了代码迁移。\n\npytorch-multi-gpu-training 将复杂的分布式并行原理转化为清晰的实战指南，帮助开发者以最小成本解锁多卡算力，大幅缩短模型研发周期。","https:\u002F\u002Foss.gittoolsai.com\u002Fimages\u002Fjia-zhuang_pytorch-multi-gpu-training_812ca5e9.png","jia-zhuang","Adam","https:\u002F\u002Foss.gittoolsai.com\u002Favatars\u002Fjia-zhuang_66e7fd6d.png","数据挖掘 | NLP | Kaggle Master","阿里巴巴","Hangzhou","jiazzzz@qq.com",null,"https:\u002F\u002Fwww.kaggle.com\u002Fjiazhuang","https:\u002F\u002Fgithub.com\u002Fjia-zhuang",[83],{"name":84,"color":85,"percentage":86},"Python","#3572A5",100,859,91,"2026-04-08T12:34:25","未说明","需要 NVIDIA GPU（支持多卡），需安装支持 NCCL 后端的 CUDA 环境，具体显存大小和 CUDA 版本未说明",{"notes":93,"python":90,"dependencies":94},"该工具主要提供 PyTorch 单机多 GPU 训练的代码示例和原理讲解（包含 DataParallel 和 DistributedDataParallel 两种方案）。运行分布式训练方案（DDP）时，必须使用 `torch.distributed.launch` 启动多进程，且依赖 NCCL 后端进行通信。若在同一台机器运行多个 DDP 任务，需手动指定不同的 master_addr 和 master_port 以避免端口冲突。模型保存时建议在 rank0 进程操作并使用 CPU 映射存储，以避免设备编号导致的加载错误。",[95,96,97],"torch","numpy","argparse",[14],[100,101],"pytorch","multi-gpu-training","2026-03-27T02:49:30.150509","2026-04-10T20:49:30.662096",[105,110,114],{"id":106,"question_zh":107,"answer_zh":108,"source_url":109},28140,"Accelerate 单机多卡训练时，每个进程是否会独立加载一份数据集导致内存占用倍增？","是的，内存占用会加倍，因为进程间没有共享数据集。对于特别大的数据集可能会导致内存溢出（爆内存）。解决方案是切换使用 PyTorch 的 IterableDataset，它原生支持多进程并行且效率较高，可以避免此问题。如果使用 np.memmap 则可能需要手动进行数据切分，较为麻烦。","https:\u002F\u002Fgithub.com\u002Fjia-zhuang\u002Fpytorch-multi-gpu-training\u002Fissues\u002F6",{"id":111,"question_zh":112,"answer_zh":113,"source_url":109},28141,"PyTorch 的 IterableDataset 和内存映射（np.memmap）哪种方式在多线程训练中效率更高？","PyTorch 的 IterableDataset 原生支持多进程并行，效率不低，通常不会成为训练过程中的瓶颈，推荐优先使用。相比之下，使用 np.memmap 可能需要开发者自己处理数据切分逻辑，实现起来更复杂且容易出错。",{"id":115,"question_zh":116,"answer_zh":117,"source_url":118},28142,"ddp_train.py 代码中是否遗漏了固定随机种子的操作？","是的，这是代码中的疏忽，需要补上。为了保证实验可复现性，应在代码中加入以下固定随机种子的操作：\n# 固定随机种子\nseed = 42\nrandom.seed(seed)\nnp.random.seed(seed)\ntorch.manual_seed(seed)\ntorch.cuda.manual_seed_all(seed)","https:\u002F\u002Fgithub.com\u002Fjia-zhuang\u002Fpytorch-multi-gpu-training\u002Fissues\u002F1",[]]