DeepSpeed 保存 Qwen3 共享张量报错?用 TaoToken 让 Codex 对照改 utils.py
2026/9/21 0:31:23 网站建设 项目流程

1. DeepSpeed 保存 Qwen3 共享张量报错场景

DeepSpeed 保存 Qwen3 共享张量报错,通常发生在 DeepSpeed-Chat 的 SFT 阶段:你微调完 Qwen3-0.6B,训练日志看着正常,走到最后保存那一步,safetensors 突然提示某些张量共享内存。这个问题的核心不是训练没跑通,而是 Qwen3 这类模型开启tie_word_embeddings后,lm_head.weightmodel.embed_tokens.weight指向同一块存储。原版save_hf_format()直接拿state_dictsave_file,safetensors 检测到共享data_ptr就会拒绝,或者即使写出去也可能在加载时出现重复内存和值不一致。

本文按排障视角走:先用 TaoToken 给 Codex 配好 Key 和 Base URL,让 Codex 对照utils.pymain.py定位并补上克隆共享张量的逻辑,最后重新跑保存,验证能加载 311 个张量。TaoToken 在这里只负责给 Codex 这个 AI 编程工具提供 Key 和 Base URL,不参与 DeepSpeed 训练或张量保存本身。你需要改的还是本地仓库里的保存函数和调用位置。

1.1 报错长什么样

常见报错类似下面这种:

RuntimeError: Some tensors share memory, this will lead to duplicate memory on disk and potential differences when loading them. Please clone the tensors before saving.

在 DeepSpeed-Chat 的step1_supervised_finetuning里,保存入口通常还是save_hf_format(model, tokenizer, args)。它内部会走model.state_dict(),然后调用safetensors.torch.save_file。Qwen3-0.6B 的config.json里有"tie_word_embeddings": true,所以输入嵌入和输出 LM Head 共享权重。safetensors 出于安全考虑,不允许直接保存两个共享同一块内存的张量,因为反序列化时它们会变成两份独立内存,后续如果一方被修改,加载结果就可能和训练时不一致。

1.2 为什么只发生在 Qwen3 这类模型

不是所有模型都会触发这个报错。像 Qwen3 这种小模型,为了减少参数量、节省显存,会把lm_head.weightmodel.embed_tokens.weight绑定在一起。训练时这没问题,DeepSpeed 也能正常更新。但保存时,原逻辑没有处理共享关系,于是 safetensors 检查data_ptr时发现两个 key 指向同一地址,直接抛错。

修复思路不是关掉tie_word_embeddings,也不是改训练超参,而是改保存逻辑:在保存前遍历state_dict,检测哪些张量共享data_ptr,对后出现的共享张量执行clone(),再交给 safetensors 保存。这样保存出来的文件里,两个 key 各有一份独立权重,加载时不会报共享内存错误。

1.3 排障目标:改 utils.py 和 main.py

这条排障路线要盯住两个文件。第一个是dschat/utils/utils.py,里面通常有原版save_hf_format(),你要新增save_hf_format_safetensors()。第二个是training/step1_supervised_finetuning/main.py,里面会调用原保存函数,你要把调用替换成新函数。做完之后重新跑保存,日志里出现克隆共享张量的提示,并且最后能加载 311 个张量,就说明链路通了。

2. 用 TaoToken 给 Codex 配好排障通道

2.1 TaoToken 在这个流程里的边界

TaoToken 只负责给 Codex 提供 Key 和 Base URL,不参与 DeepSpeed 训练、不参与张量保存、也不碰你的模型权重。Codex 的作用是读报错、读utils.pymain.py的相关片段,帮你生成修改建议和补丁。你仍然在本地终端执行训练和保存命令。这个边界要先分清,否则容易把排障问题误判成平台问题。官网入口在这里:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=

2.2 创建 Key 与填写 Base URL

打开官网注册后,进入控制台创建 API Key。然后把 Codex 的 Base URL 填成https://taotoken.net/api,注意不要加/v1,也不要加 UTM 参数。很多配置失败不是 Key 错,而是 Base URL 多写了后缀。API 地址本身是 https://taotoken.net/api ,Codex 侧只认这个根地址。Key 建议放在环境变量里,不要硬编码到脚本。

2.3 Codex 侧环境变量与配置片段

如果你的 Codex 版本走环境变量,可以这样设置:

export TAOTOKEN_API_KEY="你的TaoTokenKey" export OPENAI_API_KEY="$TAOTOKEN_API_KEY" export OPENAI_BASE_URL="https://taotoken.net/api"

如果你的 Codex 使用config.toml,可以参考下面这种写法,不同版本字段可能略有差异,以本地版本为准:

model = "gpt-5-codex" model_provider = "taotoken" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" env_key = "TAOTOKEN_API_KEY"

配好之后,先在终端里跑一个最简单的 Codex 请求,确认它能正常返回。不要等训练跑完才验证通道,否则后面分不清是 Codex 没配好,还是保存函数没改对。

3. 让 Codex 对照 utils.py 与 main.py 的可复制做法

3.1 先给 Codex 的上下文与提问模板

把报错全文、dschat/utils/utils.py里原版save_hf_format函数、training/step1_supervised_finetuning/main.py里调用保存的位置一起丢给 Codex。只给报错不够,它可能改到别的保存函数。我试过只把报错丢给 Codex,结果它改的是另一个工具函数,main.py里的调用没动,重新跑还是原报错。可以用下面这个提问模板:

我在 DeepSpeed-Chat SFT 阶段保存 Qwen3-0.6B 时遇到 safetensors 共享内存报错。 报错信息:RuntimeError: Some tensors share memory... 相关文件: 1. dschat/utils/utils.py 中的 save_hf_format 函数 2. training/step1_supervised_finetuning/main.py 中调用 save_hf_format 的位置 请帮我: - 定位 state_dict 中所有共享 data_ptr 的张量; - 新增 save_hf_format_safetensors(),对共享张量执行 clone; - 替换 main.py 中的保存调用; - 给出保存后加载验证张量数量的代码。 不要修改训练超参和 DeepSpeed 配置。

3.2 新增 save_hf_format_safetensors() 的对照修改

Codex 给出的函数要满足几个点:解包 DeepSpeed 的module,遍历state_dict,检测共享data_ptr,克隆共享张量,保存model.safetensors,同时保存 tokenizer,最后用load_file验证。下面是一个可参照的版本:

import os import torch from safetensors.torch import save_file, load_file def save_hf_format_safetensors(model, tokenizer, args, sub_folder=""): model_to_save = model.module if hasattr(model, "module") else model output_dir = os.path.join(args.output_dir, sub_folder) os.makedirs(output_dir, exist_ok=True) state_dict = model_to_save.state_dict() new_state_dict = {} seen_ptrs = {} for key, tensor in state_dict.items(): if not isinstance(tensor, torch.Tensor): continue origin_ptr = tensor.data_ptr() if origin_ptr in seen_ptrs: print(f"检测到共享内存张量 {key} 与 {seen_ptrs[origin_ptr]} 共享内存,正在克隆") tensor = tensor.clone() new_state_dict[key] = tensor.detach().cpu().contiguous() seen_ptrs.setdefault(origin_ptr, key) output_file = os.path.join(output_dir, "model.safetensors") save_file(new_state_dict, output_file, metadata={"format": "pt"}) tokenizer.save_pretrained(output_dir) print(f"模型权重已保存至: {output_file}") loaded = load_file(output_file) print(f"验证通过,成功加载 {len(loaded)} 个张量") return output_file

关键点是origin_ptr要在转 CPU 之前取,否则每个张量经过.cpu()后可能变成新的地址,你就检测不到原本的共享关系。另外,clone()要作用在即将保存的张量上,克隆后不要再被原state_dict覆盖回去。

3.3 替换 main.py 的保存调用

改完utils.py后,还要去main.py里替换调用。原代码可能是:

save_hf_format(model, tokenizer, args)

改成:

from dschat.utils.utils import save_hf_format_safetensors save_hf_format_safetensors(model, tokenizer, args)

如果main.py里已经有from dschat.utils.utils import ...,就把新函数加进导入列表,不要重复导入。替换完成后,最好让 Codex 再 grep 一遍save_hf_format(,确认没有漏掉其他调用点。DeepSpeed-Chat 有些分支会在不同阶段保存,你只替换 SFT 阶段的入口就行。

3.4 保存后验证 311 个张量

保存成功后,用独立脚本加载model.safetensors,确认 key 数量和张量内容。Qwen3-0.6B 在这种保存逻辑下应该能加载 311 个张量。可以这样验证:

from safetensors.torch import load_file sd = load_file("./output/model.safetensors") print("tensor count:", len(sd)) print("has lm_head:", "lm_head.weight" in sd) print("has embed:", "model.embed_tokens.weight" in sd) print("same ptr:", sd["lm_head.weight"].data_ptr() == sd["model.embed_tokens.weight"].data_ptr())

预期结果是张量数量为 311,两个 key 都存在,并且same ptrFalse。如果数量少很多,通常是你保存时过滤了非张量参数,或者保存的是 DeepSpeed 的优化器状态而不是模型权重。

4. 验证请求与成功结果

4.1 重新跑保存请求

改完文件后,回到训练脚本目录,重新跑 Qwen3-0.6B 的 SFT 启动脚本。命令类似:

cd DeepSpeedExamples/applications/DeepSpeed-Chat/training/step1_supervised_finetuning bash training_scripts/qwen/run_qwen3-0.6b.sh

如果你已经训练完,只想验证保存逻辑,也可以让 Codex 帮你写一个独立加载模型并调用save_hf_format_safetensors()的小脚本。但更贴近原场景的做法还是重新跑训练脚本,让它在训练结束触发保存。跑之前先确认main.py里的调用已经替换,utils.py里新函数没有语法错误。

4.2 加载 safetensors 验证

保存完成后,直接到输出目录看文件:

ls -lh ./output/model.safetensors python -c "from safetensors.torch import load_file; sd=load_file('./output/model.safetensors'); print(len(sd))"

如果打印出 311,说明 safetensors 文件结构完整。再检查config.jsontokenizer.json等文件是否也写在同一个输出目录。新函数里除了保存权重,还要调用tokenizer.save_pretrained(output_dir),否则后续加载模型时可能找不到 tokenizer 配置。

4.3 日志关键行

成功时日志里会出现类似内容:

检测到共享内存张量 lm_head.weight 与 model.embed_tokens.weight 共享内存,正在克隆 模型权重已保存至: ./output/model.safetensors 验证通过,成功加载 311 个张量

看到这三行,基本可以确定共享张量问题已经解决。如果只看到保存路径,没有克隆提示,说明lm_head.weightmodel.embed_tokens.weight的共享检测没有命中。回头检查origin_ptr是不是在.cpu()之后才取,或者检查state_dict里是否已经因为其他包装变成了不同 storage。

5. 本篇 DeepSpeed/Qwen3 保存报错常见错排查

5.1 仍然报共享内存

如果重新跑还是提示共享内存,先在保存前打印所有张量的data_ptr,看看除了lm_head.weightmodel.embed_tokens.weight之外,是否还有别的 key 共享。不要硬编码只克隆这两个 key,应该让循环自动检测。另一个常见原因是克隆后又被原state_dict覆盖,比如先写了new_state_dict[key] = tensor,后面又写了new_state_dict[key] = state_dict[key],这样克隆等于没做。

5.2 加载张量数量不对

311 是 Qwen3-0.6B 在当前保存逻辑下的预期数量。如果数量明显不对,先看new_state_dict是否把非张量项也塞进去了,safetensors只接受 tensor。再看是否保存了 DeepSpeed 的module.前缀或_orig_mod.前缀,导致加载时 key 名和模型结构不匹配。可以用表格对照一下常见现象:

现象可能原因处理
数量少于 311过滤了部分 tensor打印 state_dict 的 key 对比
数量多于 311保存了优化器或调度器状态只保存模型 state_dict
加载缺 key保存时加了前缀去掉module.等包装前缀
加载多 key保存了 DeepSpeed 额外状态不要合并 optimizer state

5.3 main.py 没有真正替换

有时utils.py已经加了新函数,但main.py里还是调用旧函数。最直接的检查方式是:

grep -n "save_hf_format" training/step1_supervised_finetuning/main.py

如果输出里还有save_hf_format(model, tokenizer, args),就把它替换成save_hf_format_safetensors(model, tokenizer, args)。如果导入报错,检查PYTHONPATH是否包含 DeepSpeed-Chat 根目录。

5.4 DeepSpeed 包装与 device 问题

DeepSpeed 包装后模型可能在model.module里,也可能带_orig_mod。新函数里用hasattr(model, "module")解包是一种常见做法,但如果你的 DeepSpeed 版本包装层数更多,需要让 Codex 根据实际对象结构补一层。保存前把张量转 CPU 可以避免显存翻倍,但要在检测共享之后再转,顺序不要反。

5.5 环境配置小坑

CUDA_HOME没设置、PYTHONPATH没加、safetensors没安装,都会让保存阶段报不同错误。基础命令可以先跑:

pip install safetensors export PYTHONPATH=/path/to/DeepSpeedExamples/applications/DeepSpeed-Chat:$PYTHONPATH export CUDA_HOME=/usr/local/cuda-12.4 export PATH=$CUDA_HOME/bin:$PATH export LD_LIBRARY_PATH=$CUDA_HOME/lib64:$LD_LIBRARY_PATH

如果第二次训练加载了旧缓存,也可以清理临时数据目录后再跑。保存报错和缓存问题有时会混在一起,先把保存函数改对,再看数据侧。

6. 语义一致 CTA:排障后继续用 TaoToken 接 Codex

这条排障线的关键就是让 Codex 同时看到报错、utils.pymain.py,而不是只改其中一个文件。共享张量检测、克隆逻辑、保存调用替换、311 个张量验证,四步都做完,DeepSpeed 保存 Qwen3 的 safetensors 报错基本就能收住。后续如果你还要让 Codex 帮你继续看环境配置、训练日志或模型评测脚本,可以先把 TaoToken 的 Key 和 Base URL 保持好:Base URL 仍然是https://taotoken.net/api,不要加/v1或 UTM。

需要创建或管理 Key,可以走 API Keys 页面:https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=deepspeed_qwen3_shared_tensor&utm_campaign=rewrite

Codex 的接入方式和参数说明,可以对照接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=deepspeed_qwen3_shared_tensor&utm_campaign=rewrite

配好之后,把save_hf_format_safetensors()main.py的改动再让 Codex 复查一遍,重点看data_ptr检测顺序、clone 是否生效、保存后加载数量是否为 311。跑通保存后,再去接模型评测或长期编码任务,链路会顺很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询