发布时间:2026/7/17 16:58:36
大规模训练的数据流水线:预处理、分片、预取的三层并行 大规模训练的数据流水线预处理、分片、预取的三层并行一、个性化深度引言训练一个 13B 参数的模型数据集 2TB。最初的 Pipeline 是单机处理读文件、Tokenize、Batching数据加载占了 GPU 训练时间的 40%。A100 显卡每秒钟等着 CPU 喂数据——计算资源浪费触目惊心。重写了 Data Pipeline预处理层在读写分离的存储集群上并行完成分片层按 rank 均匀分配数据预取层用多进程提前准备下一批。数据加载耗时从 40% 降到 3%。见证奇迹的时刻不是在 GPU 利用率从 55% 飙升到 92%。而是发现很多人花几个月调模型结构却从没认真审视过 Data Pipeline。在分布式训练中数据加载效率对总训练时间的贡献经常超过模型结构优化。二、个性化原理剖析大规模训练的数据流水线分为三层三层职责预处理层离线在训练启动前完成。Tokenization、数据清洗、格式标准化。目标是输出可直接加载的二进制数据文件。分片层训练启动时全局 Shuffle 保证跨 Epoch 的数据随机性。Rank 分配确保每个 GPU 拿到互不重复的数据。负载均衡让各 rank 处理的数据量一致。预取层运行时多进程并行读取和预处理。内存缓冲池提前准备 2-3 个 batch。Pinned memory 加速 CPU→GPU 的数据传输。三、个性化代码实践 大规模训练的三层 Data Pipeline 实现。 设计理念每一层的瓶颈都必须独立测量和优化。 import os import time import queue import threading import multiprocessing as mp from dataclasses import dataclass, field from pathlib import Path from typing import Iterator, List, Optional, Tuple import torch import numpy as np from torch.utils.data import Dataset, DataLoader, IterableDataset from torch.utils.data.distributed import DistributedSampler # ═══════════════════════════════════════════ # 第一层预处理 # ═══════════════════════════════════════════ class PreprocessingPipeline: 设计原因离线预处理将原始文本转为二进制 token 文件。 一次预处理多次训练复用——避免每次训练都重复 Tokenization。 def __init__( self, tokenizer, output_dir: Path, chunk_size: int 1024 * 1024, # 1M tokens per file ): self.tokenizer tokenizer self.output_dir output_dir self.chunk_size chunk_size self.output_dir.mkdir(parentsTrue, exist_okTrue) def process_files( self, input_files: List[Path], num_workers: int 8 ) - List[Path]: 设计原因多进程并行处理充分利用多核 CPU。 输出文件为 .bintoken ids .idx文件偏移索引。 # 设计原因将文件列表均匀分给各个 worker。 chunks np.array_split(input_files, num_workers) with mp.Pool(num_workers) as pool: results pool.map(self._process_chunk, chunks) # 设计原因汇总所有 worker 的输出文件路径。 output_files [] for file_list in results: output_files.extend(file_list) # 设计原因生成全局索引文件记录每个 .bin 的元信息。 self._write_global_index(output_files) return output_files def _process_chunk( self, files: List[Path] ) - List[Path]: 设计原因每个 worker 处理一个文件子集。 独立进程运行不共享状态避免 GIL 和锁竞争。 output_files [] chunk_tokens [] chunk_id 0 for file_path in files: # 设计原因流式读取大文件避免内存爆炸。 with open(file_path, r) as f: for line in f: line line.strip() if not line: continue # 设计原因Tokenization 后直接 append。 tokens self.tokenizer.encode(line) chunk_tokens.extend(tokens) # 设计原因攒够 chunk_size 就写入文件。 if len(chunk_tokens) self.chunk_size: output_file self._write_chunk( chunk_tokens, chunk_id ) output_files.append(output_file) chunk_tokens [] chunk_id 1 # 设计原因最后一个不满的 chunk 也写入。 if chunk_tokens: output_file self._write_chunk(chunk_tokens, chunk_id) output_files.append(output_file) return output_files def _write_chunk( self, tokens: List[int], chunk_id: int ) - Path: 设计原因写入 .bin 文件uint16 紧凑存储。 使用 uint16 而非 int32节省 50% 存储空间。 大多数 tokenizer 的 vocab_size 65536uint16 足够。 output_path self.output_dir / fchunk_{chunk_id:06d}.bin np.array(tokens, dtypenp.uint16).tofile(output_path) return output_path staticmethod def _write_global_index(files: List[Path]) - None: 设计原因全局索引记录每个 chunk 的元信息。 pass # ═══════════════════════════════════════════ # 第二层分片 第三层预取 # ═══════════════════════════════════════════ class ShardedDataset(IterableDataset): 设计原因IterableDataset 支持流式读取大文件。 每个 rank 取互不重叠的数据切片。 def __init__( self, data_dir: Path, rank: int, world_size: int, seq_length: int 2048, ): self.data_dir data_dir self.rank rank self.world_size world_size self.seq_length seq_length # 设计原因获取所有 .bin 文件按 rank 均匀分配。 self.bin_files sorted(self.data_dir.glob(*.bin)) # 设计原因只处理分配给当前 rank 的文件。 self.rank_files [ f for i, f in enumerate(self.bin_files) if i % world_size rank ] def __iter__(self) - Iterator[torch.Tensor]: 设计原因迭代器按 rank_files 循环读取数据。 每个 epoch 随机打乱文件顺序shuffle files, not tokens。 while True: # 设计原因每个 epoch 打乱文件顺序 # 全局 shuffle 保证不同 epoch 的数据随机性。 np.random.shuffle(self.rank_files) for file_path in self.rank_files: # 设计原因使用 np.memmap 做零拷贝读取 # 数据直接映射到内存不需要复制。 tokens np.memmap( file_path, dtypenp.uint16, moder ) # 设计原因滑动窗口生成训练样本。 for i in range( 0, len(tokens) - self.seq_length, self.seq_length ): yield torch.from_numpy( tokens[i:i self.seq_length].astype(np.int64) ) class PrefetchDataLoader: 设计原因预取层——在 GPU 计算当前 batch 时 后台线程已经在准备下几个 batch。 def __init__( self, dataset: IterableDataset, batch_size: int, prefetch_factor: int 3, num_workers: int 4, ): self.dataset dataset self.batch_size batch_size self.prefetch_factor prefetch_factor # 设计原因预取队列缓冲 prefetch_factor 个 batch。 self._prefetch_queue queue.Queue( maxsizeprefetch_factor ) # 设计原因后台线程持续从 dataset 取数据放入缓冲。 self._loader_thread: Optional[threading.Thread] None self._stop_flag threading.Event() def start(self) - None: 设计原因启动后台预取线程。 self._loader_thread threading.Thread( targetself._prefetch_worker, daemonTrue ) self._loader_thread.start() def stop(self) - None: 设计原因停止预取线程。 self._stop_flag.set() if self._loader_thread: self._loader_thread.join(timeout5) def _prefetch_worker(self) - None: 设计原因后台工作线程。 持续从 dataset 取数据、组成 batch、放入缓冲队列。 iterator iter(self.dataset) batch [] while not self._stop_flag.is_set(): try: sample next(iterator) batch.append(sample) if len(batch) self.batch_size: # 设计原因组成 batch 后堆叠为 tensor。 stacked torch.stack(batch) # 设计原因使用 pinned memory 加速 GPU 传输。 pinned stacked.pin_memory() # 设计原因put 到缓冲队列如果队列满了会阻塞。 self._prefetch_queue.put(pinned) batch [] except StopIteration: iterator iter(self.dataset) def next_batch(self) - torch.Tensor: 设计原因训练循环中调用从预取队列获取 batch。 如果队列不为空立即返回否则阻塞等待。 return self._prefetch_queue.get() def __len__(self) - int: 设计原因返回当前缓冲队列中的 batch 数量。 return self._prefetch_queue.qsize() def create_distributed_dataloader( data_dir: Path, rank: int, world_size: int, batch_size: int, seq_length: int 2048, num_workers: int 4, prefetch_factor: int 3, ) - PrefetchDataLoader: 设计原因工厂函数一键创建分布式 DataLoader。 封装了分片 预取的完整配置。 # 第二层分片 dataset ShardedDataset( data_dirdata_dir, rankrank, world_sizeworld_size, seq_lengthseq_length, ) # 第三层预取 loader PrefetchDataLoader( datasetdataset, batch_sizebatch_size, prefetch_factorprefetch_factor, num_workersnum_workers, ) loader.start() return loader四、个性化边界权衡1. 离线预处理 vs 在线处理离线预处理一次处理可反复复用适合固定数据集。在线处理灵活适应动态数据集但增加训练时的 CPU 开销。建议数据稳定时离线处理数据持续增长时混合方案增量在线处理 定期离线归档。2. 文件大小大文件 vs 小文件大文件1GB减少文件打开次数但 Shuffle 粒度粗糙。小文件100MBShuffle 灵活但产生大量文件句柄。推荐1-5GB 的 chunk 文件单文件约 100 万条样本。3. Prefetch 数量1 vs NPrefetch 1 个 batch 内存开销最小但 GPU 可能短暂空转。Prefetch 3-5 个 batch 消除空转但多占用batch_size × prefetchN的 CPU 内存。推荐GPU 内存充足时 prefetch 3显存紧张时 prefetch 1。4. Shuffle 策略全局随机 vs 文件内随机全局随机所有样本完全打乱最优但需要大量内存全量索引。文件间随机 文件内顺序读取内存开销小但随机性降低。推荐文件间随机 文件内顺序读取内存可控且随机性够用。5. 数据校验训练前 vs 运行时训练前全量校验保证数据质量但 2TB 数据集全量扫描耗时数小时。运行时抽样校验轻量但可能遗漏问题。建议训练前做语法和格式校验运行时做长度和分布监控。五、总结大规模训练的数据流水线通过预处理、分片、预取三层并行架构将数据加载开销从训练时间的 40% 压缩到 3%。预处理层将原始文本离线转为紧凑二进制格式分片层按 world_size 均匀分配数据预取层用多进程 缓冲队列消除 GPU 空转。工程实践中需要在文件大小、Prefetch 数量、Shuffle 策略上做权衡核心原则是每一层的吞吐量必须大于训练吞吐量任何一层的瓶颈都会拖慢整体。

相关新闻

2026/7/17 16:53:35

[实战] 2026年高效创建气泡图(Balloon Drawing)

在 2026 年的智能制造环境下,气泡图(balloon drawing)已成为连接工程设计与质量控制的核心数字化桥梁。无论是进行首件检验(FAI)还是生产件批准程序(PPAP),准确且高效地为工程图纸标…

2026/7/17 18:13:52

全志V851se芯片Linux 6.1 + Debian 12镜像构建指南

1. 项目背景与目标在嵌入式开发领域,全志V851se芯片因其出色的性价比和丰富的外设接口备受开发者青睐。TinyVision作为基于该芯片的典型开发板,其官方提供的系统镜像往往无法满足开发者对最新内核和定制化系统的需求。手动构建Linux 6.1 Debian 12镜像的…

2026/7/17 18:13:52

基于GH6108指纹识别与树莓派的智能棋盘:硬件集成与AI部署实战

1. 项目概述:当传统棋盘遇上AI与生物识别最近在捣鼓一个挺有意思的玩意儿:把一块普通的棋盘,改造成能“认人”的智能设备。听起来有点科幻?其实核心思路很简单,就是给棋盘加上一个“眼睛”和一个“大脑”。“眼睛”负责…

2026/7/17 18:13:52

SpringBoot-Scan路线图:构建下一代SpringBoot应用安全扫描平台

1. 项目概述:SpringBoot-Scan是什么,以及我们为什么要规划它的未来如果你是一名Java开发者,尤其是深度使用SpringBoot框架的工程师,那么“安全”这个词,在你日常的编码、测试和部署中,一定是个绕不开的坎。…

2026/7/17 18:13:52

CANN/asc-devkit HCCL v1 TilingData接口

v1版本TilingData(废弃) 【免费下载链接】asc-devkit 本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求…

2026/7/17 18:08:51

Deemix WebUI详细教程:打造云端音乐下载管理平台

Deemix WebUI详细教程:打造云端音乐下载管理平台 【免费下载链接】deemix 项目地址: https://gitcode.com/gh_mirrors/de/deemix 想要打造一个属于自己的云端音乐下载管理平台吗?Deemix WebUI就是你的终极解决方案!🚀 这款…

2026/7/17 5:59:06

3步解锁音乐自由:ncmdumpGUI终极NCM文件解密转换指南

3步解锁音乐自由:ncmdumpGUI终极NCM文件解密转换指南 【免费下载链接】ncmdumpGUI C#版本网易云音乐ncm文件格式转换,Windows图形界面版本 项目地址: https://gitcode.com/gh_mirrors/nc/ncmdumpGUI 你是否曾在网易云音乐下载了心爱的歌曲&#…

2026/7/17 1:21:45

CANoe 19 SP3 配置 GB/T 27930-2023 A类系统:3步搭建BMS仿真测试环境

CANoe 19 SP3 配置 GB/T 27930-2023 A类系统:3步搭建BMS仿真测试环境随着新能源汽车行业的快速发展,充电通信协议的标准化和测试验证变得尤为重要。GB/T 27930-2023作为中国智能充电协议的最新版本,对充电机与电动汽车之间的通信提出了更严格…

2026/7/17 7:39:19

3步搞定RTL8852BE驱动:从零开始配置Wi-Fi 6网卡

3步搞定RTL8852BE驱动:从零开始配置Wi-Fi 6网卡 【免费下载链接】rtl8852be Realtek Linux WLAN Driver for RTL8852BE 项目地址: https://gitcode.com/gh_mirrors/rt/rtl8852be 还在为Linux系统无法识别RTL8852BE Wi-Fi 6网卡而烦恼吗?&#x1f…

2026/7/17 0:04:23

BiSheng JDK-build性能调优:构建速度提升30%的优化策略

BiSheng JDK-build性能调优:构建速度提升30%的优化策略 【免费下载链接】bishengjdk-build BiSheng JDK build and test scripts - common across all releases/versions 项目地址: https://gitcode.com/openeuler/bishengjdk-build 前往项目官网免费下载&am…

2026/7/17 14:59:44

3个高效策略:快速掌握Axure中文界面配置

3个高效策略:快速掌握Axure中文界面配置 【免费下载链接】axure-cn Chinese language file for Axure RP. Axure RP 简体中文语言包。支持 Axure 11、10、9。不定期更新。 项目地址: https://gitcode.com/gh_mirrors/ax/axure-cn 还在为Axure RP的英文界面感…