H3-World 为何在加载 LoRA 前,先检查导入的代码?

0 阅读15分钟

H3 -World 为何在加载 LoRA 前,先检查导入的代码?

补丁在磁盘,进程用的是哪份?

作者解释或未执行记录;不代表模型运行、加载或动作遵从已经通过。

拿到 H3-World 的 LoRA 文件后,很容易把注意力放在权重路径、显存和生成参数上。但官方推理脚本没有先打印“权重加载成功”:它先导入实际使用的 DiffSynth 模块,检查模块来自哪里、是否具有动作路由和逐区间文本打包的代码特征。权重检查在后面,代码身份检查在前面。

这不是额外的安装仪式。LoRA 是适配参数,执行这些参数的 pipeline、动作入口和注意力代码是另一组对象。文件名相同、参数能附着到模型,不能独自说明正在执行 H3-World 预期的动作路径。接入时需要回答的具体问题是:这次进程实际导入了什么代码,随后把哪份权重接到了什么对象上?

本文依据 2026 年 10 月 12 日重新获取的 H3-World 官方固定提交 f0c7be2acbbde8256b2473f6e7b088f58272acae,以及仓库指定的 DiffSynth 基础提交 300e3e4da76e881d5e6bd97d897810c18f6e4893。只读取源码并用标准库解析调用签名,没有安装依赖、导入供应商模块、加载权重或运行 GPU。下文的检查记录是作者设计的待执行产物,不是目标机器已经通过的兼容性报告。

平台生产前于2026年10月16日重新读取官方main、指定基础提交和本篇所引文件:H3-World仍为上述固定提交,八份相关源码与10月12日留存逐字节相同。本文因此保留原机制判断;这次更新只核公开源码时效,不把主分支未变当作目标机器文件也未变。

1. 补丁在磁盘上,不等于进程导入了它

训练入口先验对象,再进入train

官方固定源码忠实摘录/真实本地静态断言,非原网页或终端截图;2026-10-16重核。

官方 README 的设置流程把两件事明确放在一起:切到指定 DiffSynth 提交,再应用 code/diffsynth_h3_action.patch。目录名是 DiffSynth-Studio-h3-v2,但命名并不产生补丁内容。基础提交说明修改从哪一版开始,补丁说明动作接入增加了哪些代码;两者均需留存,不能只写一个“最新版 DiffSynth”。官方设置说明

更有信息量的证据在补丁末尾。作者新增了 examples/minimax_h3/model_training/train_v2.py,用途是先处理 Python 导入路径,再交给同目录下原来的 train.py。源码注释报告:其环境里 editable 安装注册的查找器指向旧 checkout,一次训练实际使用了旧的对称掩码,日志、损失和吞吐看起来仍正常。这里引用的是作者在代码中的故障说明;我们没有其现场日志,也没有复现这次训练,不能据此给出故障发生率或把所有错误导入都称为静默失败。

wrapper 的实际动作比“切换工作目录”多。它过滤类型名包含 Editable 的 sys.meta_path 查找器,处理相应的 sys.path_hooks,清空路径导入器缓存,把 v2 根目录置于 sys.path 前面,然后才导入模型与 pipeline 模块。随后检查实际导入对象,再通过 runpy.run_path 启动原训练文件。code/train.sh 也确实指向这个 wrapper,而不是只在仓库中放一份未使用的修复文件。固定补丁中的 wrapper

这条链的关键顺序是“处理查找路径 → 导入实际对象 → 验对象 → 进入训练”。如果把检查移到另一个进程,或只确认当前 shell 位于哪个目录,就不能代替这里的对象核对。一个进程准备进入的目录与它已经绑定到名字上的模块对象,不必然是同一份文件。就本文的目标而言,最有用的证据不是目录存在,而是这次导入对象的实际路径及其源码。

同时要保留一个容易被源码注释带偏的范围。Python 的导入机制先查 sys.modules 缓存,未命中才按顺序查询 sys.meta_path;默认的路径查找器也是这条链的一部分。自定义查找器能影响路径解析,但具体结果取决于注册顺序、缓存状态与返回的模块规范。作者环境中“设置路径仍导入旧 checkout”的报告,不能扩写成“所有 editable 安装都永远压过 sys.path”。Python 3.10 导入机制

因此,官方 wrapper 是一个针对其执行环境的入口实现,不是通用清理命令。它没有逐一解释你的环境里每个自定义查找器属于谁,也不能因为一行名称过滤就证明所有导入状态已恢复。本文不建议在已运行的应用进程中盲目删除查找器或模块缓存。更简单的判断是在将要运行模型的进程里取得实际对象身份:若来源不符,先停止这次模型执行,再按该环境的安装与导入路径定位原因。

2. 官方预检能证明什么,也可能在哪一步停下?

没看到FAILED,检查运行了吗?

官方固定源码忠实摘录/真实本地静态断言,非原网页或终端截图;2026-10-16重核。

推理入口 code/abot/infer.py 先设置 DIFFSYNTH_ROOT。默认值是项目下的 v2 目录,也允许通过 ABOT_DIFFSYNTH_ROOT 覆盖。脚本插入路径之后,导入 diffsynth.models.minimax_h3_dit 和 diffsynth.pipelines.minimax_h3_audio_video,在这些已经导入的模块对象上做检查。固定推理入口

下面这三项是同一段预检源码中的条件,不是我们得到的运行输出。

官方检查对象当前条件可以据此确认的有限范围
实际模型模块 _dit'-v2/' in _dit.file实际模块路径包含预期命名片段
_build_action_block_masks 源码包含 leak_out该函数源码出现定向路由特征字符串
pipeline 模块源码包含 action_text_spans_local模块源码出现逐区间动作 span 的特征字符串

路径检查与代码特征检查互补:目录取名 v2,但缺少函数或字段,不足以通过;文件包含某个动作字符串,但实际导入到了其他路径,也不满足这一段预期。检查对象来自本次 import,能拦住“磁盘上准备的是甲,进程拿到的是乙”这一类偏差。它们仍不是文件全文哈希,也不是完整补丁等价性证明:名字或字符串保留而实现被改写,不能靠这三项排除所有差异。

尤其要注意启动条件。推理源码将整段检查放在 if '-v2' in str(DIFFSYNTH_ROOT): 之下。默认目录命名会进入这段分支;若覆盖变量指向一个不含该片段的自定义目录,这一组检查就不会运行。这是静态读解能确认的分支条件,不能直接写成“换个目录名就成功绕过且正常生成”。后续导入、函数调用或模型运行仍可能报错。它提醒接入者:没有看到预检报错,先核是否进入预检,不要先认定代码身份已通过。

官方训练 wrapper 则在导入后直接构造检查项,不采用推理文件中的这层目录字符串条件。两份入口都检查类似特征,却有不同的启动结构。把它们都简称为“官方已有预检”,会丢掉到底由哪一个入口执行、在哪个条件下执行的问题。将官方脚本嵌入其他启动器时,也应记录实际进入的是原入口、修改后的入口,还是仅复用了它的一部分函数。

还有一处日志边界值得具体说明:三项检查在一个列表中构造,第二项直接访问 _dit._build_action_block_masks,再交给 inspect.getsource。如果导入对象根本没有这个函数,访问属性就可能在打印循环之前失败。若源码无法取到,也可能更早停下。不能预先要求所有身份错误都恰好留下三行 FAILED,然后因为没有这三行就把运行判断为正常。

对待执行记录来说,正确做法是分别留“是否进入检查分支”“实际对象路径”“取得了哪些源码特征”“停止位置或异常”。异常发生在检查项构造阶段,与三项布尔条件完成后明确拒绝继续,是两种不同的观察。本文没有在目标进程执行这段代码,不能替读者填写其中任一结果。

这也解释了为什么启动器的文件路径不能替代模型模块路径。infer.py 可以来自正确仓库,ABOT_DIFFSYNTH_ROOT 可以指向你希望使用的目录,但这些都是输入侧选择。_dit.__file__ 与实际 pipeline 对象来自哪里,才是导入后的观察。判断执行身份时,需要把选择与观察放在同一条记录里,才能看见是否发生了偏离。

3. LoRA 附着完成之后,动作接口还要对得上

推理脚本对权重也有独立检查。load_checkpoint_lora 只保留键名包含 .lora_A. 或 .lora_B. 的张量,若没有这类权重,或还存在其他张量,则拒绝这份输入。之后 pipe.load_lora(..., hotload=True) 将它们附着到 pipe.dit,脚本比较附着前后的 LoRA A 权重数量与 checkpoint 中的对数,不匹配则停止。这些都是源码已存在的条件,本文没有读取真实 checkpoint 或看到加载日志。权重与调用路径

这些检查能回答权重结构和附着数量的问题,却不能替代前面的代码身份检查。即使名称匹配、对数匹配,也不能由此推出实际掩码实现、动作 span 的传递或负向动作参考与训练时完全一致。相反,即使代码身份检查满足,错误的权重文件仍可能被后面的结构检查拒绝。它们不是两次重复验证,而是对不同对象提出要求。

权重不会增加函数的命名参数

官方固定源码忠实摘录/真实本地静态断言,非原网页或终端截图;2026-10-16重核。

另一组可直接核对的证据是调用接口。 当前 infer.py 在实际 pipe(...) 调用中传入 action_script 与 negative_action_script。指定 DiffSynth 基础提交里的 MiniMaxH3Pipeline.__call__ 尚无这两个参数,也没有用任意关键字参数承接它们。H3-World 补丁才给这条入口加入相应参数并继续传递。只把权重文件移过去,不会自动给未修改的 Python 函数增加命名参数。指定基础 pipeline

本文用标准库 AST(语法树)读取两个固定文件,确认调用端包含这两个关键字,基础实现的签名不接受它们。这个静态结果没有执行函数、装载模型或试验某台机器。若最终直接调用的就是该未修改实现,传参本身便存在不匹配,可能先在接口处失败。因此,把作者旧训练注释中的静默故障一概搬到所有未打补丁推理路径,反而会让读者漏掉更早、可见的错误。

已有的本地静态检查使用标准库语法树,提取固定基础类的完整参数名及任意关键字参数,再定位推理入口中的pipe调用,核两个动作关键字。检查只读保存的源码文本,不import模块、不安装DiffSynth,也不准备权重;历史断言退出码为0。它只说明这两份固定文本满足所列条件,不说明目标进程实际调用了其中任一对象。

读者可从本节给出的固定提交链接,分别核对基础类__call__的完整签名、是否有任意关键字参数,以及推理入口的两个实际调用关键字。若使用其他版本,先记录新SHA和版本,不把旧断言结果沿用为新文件的通过证据。进入目标机器后,还须在实际目标进程取得对象的签名与来源,并与固定文本分开记录;本地解析正确与线上导入错误可以同时成立。原静态脚本及四份输入继续保存在研究材料,本文不把包内文件路径当作公网下载入口。

代码、权重、输出各回答什么?

作者解释或未执行记录;不代表模型运行、加载或动作遵从已经通过。

在其他封装器里,情况还可能不同:调用器可以改参数、丢弃参数,或换成另一种 pipeline。这些仅是需要核对的接入条件,不是本文发现它们已经发生。一次兼容性检查不能停在“封装器接受了参数”,还应追到哪个实际对象消耗了动作输入。这里不继续展开所有移植框架;只保留本篇的单条身份记录,让接入者能够判定自己是否真的走到了预期的入口。

留一条本次进程记录:目前未执行

作者解释或未执行记录;不代表模型运行、加载或动作遵从已经通过。

下面是一条作者未执行的记录。它描述执行前应准备的依据和执行时应取得的观察,不是新的 H3-World API,也不是已经运行过的终端回执。

记录项预期依据这次进程的实际观察
基础与补丁指定 DiffSynth 基础 commit,加固定 H3-World patch实际文件版本与修改范围待核
启动入口原推理入口或训练 wrapper;改动需明确记录实际入口文件、进程与修改待填
路径选择默认 v2 或明确的 ABOT_DIFFSYNTH_ROOT传入值待填,不据值认定导入成功
模块身份预期模型与 pipeline 代码模型模块、pipeline 类的实际来源待核
预检覆盖推理分支条件、模块路径与两处特征是否进入、取得哪些结果、停止位置待填
动作接口本篇固定调用包含正向与负向动作脚本实际调用参数及接收对象签名待核
权重身份纯 LoRA 张量;附着前后对数相符文件身份、实际张量与附着结果待核
行为观察代码与权重身份成立后,再检查生成响应本文未运行,动作结果、兼容性、性能均未知

这条记录最先帮团队避免的是把不同性质的证据合并成一句“加载成功”。补丁应用在磁盘上,可以证明你准备了什么代码;模块来源来自进程,才说明这次实际导入了什么;LoRA 附着数量来自权重加载阶段;动作遵从来自生成输出。缺少后面的观察,不应由前面的准备材料代填。

如果记录中的模块来源不符,先处理执行路径,不通过修改动作提示来掩盖身份不明。如果源码特征存在但文件被改过,保留改动与版本,不把特征命中包装成完整官方等价。如果身份与接口都核对正确,权重附着仍失败,就把问题留在权重与对象匹配阶段。到此已足以指导下一次观察,无需先运行一整套性能测试来证明自己使用了哪份代码。

最终能带走的判断很有限,也很实用:H3-World 的权重文件、被补丁修改的 pipeline 与本次进程导入的对象必须分别有依据。官方预检提供了具体入口和特征,但有条件、有覆盖边界;它通过也不等于目标机器已能正确控制动作。先把这次运行的代码身份与调用路径记录清楚,再用实际输出回答行为问题。

来源与范围

原 ZIP 的 03_CODE_AUDIT.md 只提供选题线索。官方作者注释、标准库静态解析、作者未执行记录与未知运行效果分别保留;未播放仓库演示视频,不据其推导成功率、延迟或实时控制能力。