MS-SWIFT 数据缓存机制详解
2026-01-10
ms-swift
00

目录

MS-SWIFT 数据缓存机制详解
1. 总体缓存架构
2. 数据预处理缓存 (最常用)
2.1 缓存位置
2.2 缓存机制
2.3 控制缓存行为
通过配置参数控制:
通过环境变量控制缓存目录:
2.4 缓存工作流程
3. Packing 数据缓存 (Megatron专用)
3.1 什么是Packing?
3.2 Packing 缓存位置
3.3 Packing 缓存文件结构
3.4 Packing 数据生成流程
3.5 Packing 缓存特性
4. 媒体资源缓存
4.1 缓存位置
4.2 支持的媒体类型
4.3 媒体路径处理
5. 你的训练配置的缓存行为
5.1 当前配置分析
5.2 实际缓存目录
5.3 首次训练流程
5.4 后续训练流程
6. 缓存管理建议
6.1 清理缓存
6.2 何时需要清理缓存?
6.3 保留缓存的优势
7. 与你的maskhistorysample功能的关系
7.1 数据处理流程
7.2 日志示例
8. 常见问题
Q1: 缓存占用空间太大怎么办?
Q2: 如何强制重新处理数据?
Q3: 多个实验共享缓存安全吗?
Q4: Packing缓存何时生效?
Q5: 缓存会自动更新吗?
9. 总结
关键点
最佳实践

MS-SWIFT 数据缓存机制详解

1. 总体缓存架构

MS-SWIFT 的数据缓存分为三个层次:

  1. 数据预处理缓存 (HuggingFace Datasets Arrow格式)
  2. Packing数据缓存 (Megatron模式专用)
  3. 媒体资源缓存 (图片/视频等多模态数据)

2. 数据预处理缓存 (最常用)

2.1 缓存位置

数据预处理后的缓存使用 HuggingFace Datasets 的Arrow格式缓存。

默认缓存目录:

python
展开代码
# 通过 modelscope 的 get_cache_dir() 获取 from modelscope.hub.utils.utils import get_cache_dir cache_dir = get_cache_dir() # 默认: ~/.cache/modelscope/ dataset_cache = os.path.join(cache_dir, 'datasets', 'map_cache')

实际路径:

展开代码
~/.cache/modelscope/datasets/map_cache/

或者在你的环境中可能是:

展开代码
/mnt/jfs6/g-xiedong/.cache/modelscope/datasets/map_cache/

2.2 缓存机制

缓存文件名基于数据集的 fingerprint (指纹)生成:

python
展开代码
# 代码位置: swift/llm/dataset/preprocessor/core.py if isinstance(dataset, HfDataset) and not dataset.cache_files: map_kwargs['cache_file_name'] = os.path.join( get_cache_dir(), 'datasets', 'map_cache', f'{dataset._fingerprint}.arrow' # 使用数据集指纹作为文件名 )

缓存文件示例:

展开代码
~/.cache/modelscope/datasets/map_cache/ ├── abc123def456.arrow # 预处理后的数据集1 ├── xyz789uvw012.arrow # 预处理后的数据集2 └── ...

2.3 控制缓存行为

通过配置参数控制:

在你的 qwen3_vl_8b_train_swanlab.yaml 中:

yaml
展开代码
load_from_cache_file: true # 启用缓存 (默认) # load_from_cache_file: false # 禁用缓存,每次重新处理

通过环境变量控制缓存目录:

bash
展开代码
# 在 train_swanlab.sh 中设置 export MODELSCOPE_CACHE=/mnt/jfs6/g-xiedong/.cache/modelscope

2.4 缓存工作流程

  1. 首次运行:

    • 读取原始数据 (jsonl/json)
    • 应用预处理 (MessagesPreprocessor)
    • 如果启用 mask_history_sample,进行样本拆分
    • 将处理后的数据写入 .arrow 缓存文件
    • 训练使用缓存数据
  2. 后续运行:

    • 检查缓存文件是否存在
    • 如果存在且 load_from_cache_file=true,直接加载缓存
    • 跳过预处理步骤,直接训练
  3. 缓存失效条件:

    • 原始数据文件改变
    • 预处理代码改变
    • dataset_info.json 配置改变
    • 手动删除缓存文件

3. Packing 数据缓存 (Megatron专用)

3.1 什么是Packing?

在你的配置中:

yaml
展开代码
packing: true # 启用packing

Packing 将多个短样本打包到一个序列中,提高训练效率,减少padding浪费。

3.2 Packing 缓存位置

默认目录:

python
展开代码
# 代码位置: swift/llm/dataset/indexed_dataset.py cache_dir = os.getenv('PACKING_CACHE') or os.path.join(get_cache_dir(), 'tmp') cache_dir = os.path.join(cache_dir, dataset_name)

实际路径:

展开代码
~/.cache/modelscope/tmp/<dataset_name>/

或通过环境变量自定义:

bash
展开代码
export PACKING_CACHE=/mnt/jfs6/g-xiedong/.cache/packing

3.3 Packing 缓存文件结构

展开代码
~/.cache/modelscope/tmp/<dataset_name>/ ├── data.idx # 索引文件 (pickle格式) ├── data-00000.bin # 数据分片1 (pickle序列化) ├── data-00001.bin # 数据分片2 ├── data-00002.bin # 数据分片3 └── ...

索引文件内容 (data.idx):

python
展开代码
{ 'idx': [0, 1024, 2048, ...], # 每个样本的字节偏移量 'length': [512, 1024, 768, ...], # 每个样本的长度 'n_shard': 3, # 分片数量 'shard_offset': [0, 10GB, 20GB] # 每个分片的起始偏移 }

数据文件特点:

  • 使用 mmap (内存映射)读取,内存友好
  • 每个分片最大 10GB (CHUNK_SIZE = 1e10)
  • 数据用 pickle 序列化存储
  • 支持随机访问

3.4 Packing 数据生成流程

python
展开代码
# 1. 读取预处理后的数据集 train_dataset = load_dataset(...) # 2. 启用packing后,创建 PackingDataset if args.packing: from swift.llm.dataset.utils import PackingDataset train_dataset = PackingDataset( template=template, dataset=train_dataset, packing_length=max_length, packing_num_proc=num_proc ) # 3. Packing过程: # - 按长度排序样本 # - 贪心算法打包到packing_length # - 多进程加速 # 4. 写入IndexedDataset缓存 builder = IndexedDatasetBuilder(dataset_name) builder.add_items(packed_samples) builder.finalize()

3.5 Packing 缓存特性

优点:

  • ✅ 高效随机访问 (mmap)
  • ✅ 支持超大数据集 (分片存储)
  • ✅ 内存占用小
  • ✅ 多进程安全

缓存复用条件:

  • 数据集名称相同
  • packing_length 相同
  • 原始数据未改变

4. 媒体资源缓存

4.1 缓存位置

python
展开代码
cache_dir = os.path.join(get_cache_dir(), 'media_resources') # 实际: ~/.cache/modelscope/media_resources/

4.2 支持的媒体类型

  • 图片: images 字段
  • 视频: videos 字段
  • 音频: audios 字段

4.3 媒体路径处理

在你的 dataset_info.json 中:

json
展开代码
{ "mmdu_val": { "file_name": "data.jsonl", "media_dir": "/mnt/jfs6/g-xiedong/mmdu_val/images" } }

处理逻辑:

python
展开代码
# 如果jsonl中是相对路径 {"images": ["img1.jpg", "img2.jpg"]} # 自动拼接为绝对路径 images = [ "/mnt/jfs6/g-xiedong/mmdu_val/images/img1.jpg", "/mnt/jfs6/g-xiedong/mmdu_val/images/img2.jpg" ]

注意: 媒体文件本身不缓存,只有URL下载的资源会缓存。


5. 你的训练配置的缓存行为

5.1 当前配置分析

yaml
展开代码
# qwen3_vl_8b_train_swanlab.yaml load_from_cache_file: true # ✅ 启用数据预处理缓存 packing: true # ✅ 启用packing缓存 dataset_num_proc: 8 # 8进程并行处理

5.2 实际缓存目录

bash
展开代码
# 预处理缓存 /mnt/jfs6/g-xiedong/.cache/modelscope/datasets/map_cache/ └── <fingerprint>.arrow # 约几百MB到几GB # Packing缓存 (如果使用Megatron packing) /mnt/jfs6/g-xiedong/.cache/modelscope/tmp/mmdu_val/ ├── data.idx └── data-00000.bin # 媒体文件 (直接访问,不缓存) /mnt/jfs6/g-xiedong/mmdu_val/images/ └── *.jpg

5.3 首次训练流程

展开代码
1. 读取 /mnt/jfs6/g-xiedong/mmdu_val/dataset_info.json 2. 加载 mmdu_val 数据集 3. 检查缓存: ~/.cache/modelscope/datasets/map_cache/<hash>.arrow - 不存在 → 执行预处理 - 解析jsonl - 应用MessagesPreprocessor - 如果有mask_history_sample,拆分样本 - 拼接media_dir路径 - 写入.arrow缓存 4. 如果启用packing: - 检查 ~/.cache/modelscope/tmp/mmdu_val/data.idx - 不存在 → 执行packing - 计算样本长度 - 贪心打包 - 写入IndexedDataset 5. 训练使用缓存数据

5.4 后续训练流程

展开代码
1. 检查预处理缓存 → 存在,直接加载 2. 检查packing缓存 → 存在,直接加载 3. 立即开始训练 (跳过所有预处理)

6. 缓存管理建议

6.1 清理缓存

bash
展开代码
# 清理所有数据缓存 rm -rf ~/.cache/modelscope/datasets/map_cache/* # 清理packing缓存 rm -rf ~/.cache/modelscope/tmp/* # 或设置环境变量后清理 rm -rf /mnt/jfs6/g-xiedong/.cache/modelscope/datasets/map_cache/*

6.2 何时需要清理缓存?

  • ✅ 修改了 dataset_info.json 配置
  • ✅ 修改了原始数据文件
  • ✅ 修改了 mask_history_sample/max_human_steps 参数
  • ✅ 升级了 ms-swift 版本
  • ✅ 调试时想看最新的预处理结果

6.3 保留缓存的优势

  • ⚡ 大幅加速训练启动 (跳过预处理)
  • 💾 多次实验共享缓存
  • 🔄 修改训练超参数(lr, batch_size等)不影响缓存复用

7. 与你的mask_history_sample功能的关系

7.1 数据处理流程

展开代码
原始数据 (jsonl) ↓ 预处理器 (MessagesPreprocessor) ↓ [mask_history_sample=true, max_human_steps=2] 样本拆分 (1个样本 → N个样本) ↓ 写入Arrow缓存 ↓ [load_from_cache_file=true] 从缓存加载 (已拆分的N个样本) ↓ [packing=true] Packing (打包成更少的序列) ↓ 写入IndexedDataset缓存 ↓ 训练

7.2 日志示例

根据我添加的日志,你会看到:

展开代码
[数据集注册] 数据集 mmdu_val 启用了mask_history_sample功能 [数据集注册] - mask_history_sample: True [数据集注册] - max_human_steps: 2 [数据集拆分] 预处理前数据集大小: 1000 个样本 [数据集拆分] 第一个样本拆分详情: [数据集拆分] 原始对话轮数: 5 轮 [数据集拆分] 将拆分为: 5 个训练样本 [数据集拆分] 拆分样本 #1: 2条消息 [数据集拆分] 拆分样本 #2: 4条消息 [数据集拆分] 拆分样本 #3: 4条消息 [数据集拆分] 预处理后数据集大小: 5000 个样本 [数据集拆分] 样本数量变化: 1000 -> 5000 (扩增5.00x) # 写入缓存: ~/.cache/modelscope/datasets/map_cache/abc123.arrow # 包含5000个已拆分的样本 # 下次训练直接从缓存加载5000个样本

8. 常见问题

Q1: 缓存占用空间太大怎么办?

bash
展开代码
# 查看缓存大小 du -sh ~/.cache/modelscope/datasets/map_cache/ du -sh ~/.cache/modelscope/tmp/ # 清理旧缓存 find ~/.cache/modelscope/datasets/map_cache/ -mtime +7 -delete

Q2: 如何强制重新处理数据?

方法1: 修改配置

yaml
展开代码
load_from_cache_file: false

方法2: 删除缓存

bash
展开代码
rm -rf ~/.cache/modelscope/datasets/map_cache/*

Q3: 多个实验共享缓存安全吗?

✅ 安全! 缓存基于数据指纹(fingerprint),不同配置会生成不同缓存文件。

Q4: Packing缓存何时生效?

仅在启用 packing: true 且使用 Megatron 训练时生效。

Q5: 缓存会自动更新吗?

❌ 不会。数据改变后需要手动清理缓存或设置 load_from_cache_file: false


9. 总结

关键点

  1. 数据预处理缓存~/.cache/modelscope/datasets/map_cache/

    • Arrow格式,快速加载
    • 基于fingerprint自动管理
  2. Packing缓存~/.cache/modelscope/tmp/<dataset_name>/

    • mmap高效访问
    • 仅Megatron+packing模式使用
  3. 媒体文件 直接访问,不缓存

    • 通过media_dir配置路径
  4. mask_history_sample拆分 在预处理阶段完成

    • 拆分结果写入Arrow缓存
    • 后续训练直接使用缓存的拆分结果

最佳实践

  • ✅ 生产训练: load_from_cache_file: true (加速启动)
  • ✅ 调试数据: load_from_cache_file: false (实时看效果)
  • ✅ 定期清理旧缓存
  • ✅ 大数据集使用packing提高效率

生成时间: 2026-01-10
ms-swift 版本: 最新
文档作者: Claude (根据代码分析生成)

如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:Dong

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC。本作品采用《知识共享署名-非商业性使用 4.0 国际许可协议》进行许可。您可以在非商业用途下自由转载和修改,但必须注明出处并提供原作者链接。 许可协议。转载请注明出处!