diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c6041bef..260f06f38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,13 @@ - **修复 Git 更新收走模型和训练集(#356)**:停止自动 stash,自动补齐旧包缺失的程序文件;新包保留完整浅克隆工作树,并在打包前执行数据保留测试。已经受影响的用户请保留整个旧目录(含 `.git`),参见 [恢复说明](https://github.com/wochenlong/lora-scripts-next/issues/356)。 +### Musubi-tuner 新增 Ideogram 4(#424) + +- Musubi 引擎支持 **Ideogram 4 LoRA**:训练页新增模型入口(`/lora/ideogram4.html`),三阶段任务(缓存 latents → 缓存文本编码器输出 → 训练)与任务队列、日志、预览沿用现有能力 +- 组件只发**量化权重(FP8)**、许可为 **非商用**,整合包不预置;「训练用模型」区提供 conditional / unconditional DiT、Qwen3-VL-8B 文本编码器、Flux2 VAE 与 tokenizer 的清单与下载入口 +- 上游快照补丁:修正文本编码器 rotary embedding 在 `to_empty()` 后未初始化的问题(上游 v0.3.6 修复),并把 tokenizer 指向本地目录,离线可用 +- 引擎能力、预检与配置导入识别 `ideogram4-lora`(跨页导入自动跳转到对应训练页),详见 [Ideogram 4 训练指南](docs/ideogram4-musubi.md) + ### 数据集标签编辑(#423) - **编辑页重做**:默认只展示图片墙,选中图片后才展开编辑面板 diff --git a/README-zh.md b/README-zh.md index 0440b6d3d..682b9e9da 100644 --- a/README-zh.md +++ b/README-zh.md @@ -36,7 +36,7 @@ Next Trainer 把数据集管理、打标、标签编辑、训练配置与任务 | --- | --- | --- | | Kohya | SD 1.5、SDXL、Flux、Anima | 支持范围随模型而异;已接入独立环境管理 | | Anima Fast | Anima 2B / 2.9B LoRA | 独立运行环境;两种规格有明确的底模路径入口 | -| Musubi | Krea 2 LoRA | 可选安装;Linux 多卡见专项指南 | +| Musubi | Krea 2 / Ideogram 4 LoRA | 可选安装;Ideogram 4 为非商用许可的量化权重,需自行下载;Linux 多卡见专项指南 | | DiffSynth | Qwen-Image-2.1 文生图 / 图像编辑 LoRA | 当前为单卡 BF16;Edit 使用 Batch 1 | | AI Toolkit | SDXL、Flux.1 Dev、Klein 4B / 9B、Krea 2 RAW、Anima、Qwen-Image-2.1 LoRA | 可选安装、独立环境;Klein 支持 base / distilled,Klein 与 Qwen 提供图像编辑入口 | diff --git a/README.md b/README.md index 0938f6369..35380772d 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Settings are organised as **UI, Training Engines, API, Plugins, Advanced** and * | --- | --- | --- | | Kohya | SD 1.5, SDXL, Flux, Anima | Capabilities vary by model; standalone environment management is integrated | | Anima Fast | Anima 2B / 2.9B LoRA | Isolated runtime with explicit base-model path selection | -| Musubi | Krea 2 LoRA | Optional installation; see the Linux multi-GPU guide | +| Musubi | Krea 2 / Ideogram 4 LoRA | Optional installation; Ideogram 4 ships non-commercial quantized weights you fetch yourself; see the Linux multi-GPU guide | | DiffSynth | Qwen-Image-2.1 text-to-image / image-editing LoRA | Single-GPU BF16; Edit currently uses batch size 1 | | AI Toolkit | SDXL, Flux.1 Dev, Klein 4B / 9B, Krea 2 RAW, Anima, Qwen-Image-2.1 LoRA | Optional installation with an isolated runtime; Klein supports base / distilled variants, and Klein and Qwen offer image-editing entry points | diff --git a/docs/dev-progress.md b/docs/dev-progress.md index 1cbd5ce8f..faa2b1cec 100644 --- a/docs/dev-progress.md +++ b/docs/dev-progress.md @@ -8,6 +8,7 @@ | 方向 | 已集成内容 | 边界 | | --- | --- | --- | +| Musubi / Ideogram 4(#424) | Musubi 引擎新增 Ideogram 4 LoRA:训练页模型入口、三阶段缓存与训练、条件化/非条件化 DiT 与采样参数、配置导入识别;本仓补丁修正上游文本编码器 rotary 未初始化问题并支持本地 tokenizer | 官方仅发 FP8 量化权重且为**非商用**许可,整合包不预置,需自行下载;LoRA 不能合并回 FP8 基座;上游为 experimental,真机验收见 #424 | | 标签编辑与中文释义(#423) | 编辑页重做(图片墙优先、标签/自由两种编辑模式、批量操作与历史记录);内置 Danbooru 中文词典(约 33 万条),翻译来源为词库 / 本地模型 / API;翻译设置与 API 配置进入全局设置 | 词库可完全离线;本地模型与 API 来源依赖用户自行安装或配置;全局 API 配置的存储统一仍在推进,打标页当前只选择来源 | | AI Toolkit(#399 及验收修复) | 独立环境、多模型 LoRA 入口、本地模型组件、文生图与图像编辑、统一任务和预览 | Qwen-Image-2.1 两种模式各通过 3 步真机冒烟及 20 步预览采样;不等于全模型、浏览器全流程或长期稳定性验收 | | 引擎列表(#398) | 搜索、安装状态筛选、置顶与排序 | 顺序按浏览器及访问地址保存,不同步到其他设备,也不改变默认训练引擎 | @@ -55,6 +56,13 @@ AI Toolkit 本轮修复包括 GPU 显示名称转设备编号、Windows 中文 Anima 底模选择适用于 Kohya LoRA、Anima Fast LoRA 和 Anima 全量微调。Anima Fast 另有明确的训练时长模式及验证集拆分配置;引擎参数与限制见 [Anima Fast 指南](anima-fast.md)。 +### Musubi / Ideogram 4 + +- Musubi 引擎的训练类型从 Krea 2 扩展为 Krea 2 / Ideogram 4 两个模型族:训练页模型入口、三阶段缓存与训练、模型族专属参数与约束(量化-only 基座、非对称 CFG 采样开关、blocks_to_swap 上限 33)。 +- 组件只发 FP8 量化权重(非商用许可),整合包不预置;「训练用模型」区提供清单与下载入口,tokenizer 建议本地放置以支持离线。 +- 本仓补丁修正上游快照中文本编码器 rotary embedding 未初始化的问题(上游 v0.3.6 修复);文档见 [Ideogram 4 训练指南](ideogram4-musubi.md)。 +- 真机验收(3 步冒烟、缓存复用、显存实测)在 [#424](https://github.com/wochenlong/lora-scripts-next/issues/424) 跟踪,不代表已完成。 + ### DiffSynth / Qwen-Image-2.1 - 支持完整模型目录或受支持的 BF16 DiT、文本编码器与 VAE 组件,Processor 资源自动管理。 diff --git a/docs/ideogram4-musubi.md b/docs/ideogram4-musubi.md new file mode 100644 index 000000000..284057809 --- /dev/null +++ b/docs/ideogram4-musubi.md @@ -0,0 +1,79 @@ +# Ideogram 4(Musubi-Tuner)训练指南 + +Ideogram 4 通过 **Musubi-Tuner** 引擎接入,训练入口:训练页选择 **模型 = Ideogram 4,引擎 = Musubi-Tuner,目标 = LoRA**,或直接访问 `/lora/ideogram4.html`。 + +当前支持 **LoRA 训练**;上游尚未支持全量微调,也 **不支持把 LoRA 合并回 FP8 底模**。 + +## 许可与权重(务必先读) + +- 模型许可为 **Ideogram Non-Commercial**:仅限非商业用途,微调同样受此限制;禁止使用其输出训练与 Ideogram 竞争的模型。 +- 官方仅发布 **量化权重(FP8 / NVFP4)**,没有 BF16/FP16 底模。DiT 始终以预量化 FP8 加载作为冻结基座,LoRA 以计算精度运行——这是唯一的工作模式,不是可选项。 +- 本项目的整合包 **不预置这些权重**。请在「训练用模型」区下载或手动放置,组件取自 Comfy-Org 单文件布局: + +| 组件 | 文件 | +| --- | --- | +| conditional DiT | `ideogram4_fp8_scaled.safetensors` | +| unconditional DiT(非对称 CFG,可选) | `ideogram4_unconditional_fp8_scaled.safetensors` | +| 文本编码器(Qwen3-VL-8B,FP8) | `qwen3vl_8b_fp8_scaled.safetensors` | +| VAE(Flux2) | `flux2-vae.safetensors` | +| tokenizer(Qwen3-VL-8B) | 目录,建议本地放置 | + +默认路径:`sd-models/ideogram4/`,tokenizer 目录为 `sd-models/ideogram4/qwen3-vl-8b-tokenizer`。 + +### 需要多少磁盘空间 + +官方只发量化权重,**有 FP8 版本,而且 FP8 就是我们默认使用、也是 musubi 训练脚本唯一支持的加载格式**。Comfy-Org 镜像上主要的文件体积(2026-10 实测): + +| 文件 | 体积 | 是否必需 | +| --- | --- | --- | +| `ideogram4_fp8_scaled.safetensors`(conditional DiT) | 8.64 GB | 必需 | +| `qwen3vl_8b_fp8_scaled.safetensors`(Qwen3-VL-8B 文本编码器) | 9.86 GB | 必需 | +| `flux2-vae.safetensors` | 0.31 GB | 必需 | +| tokenizer(Qwen3-VL-8B 目录) | 约 0.02 GB | 必需 | +| `ideogram4_unconditional_fp8_scaled.safetensors` | 8.64 GB | 可选(非对称 CFG 推理 / 显式开启的训练采样) | + +镜像里还有 **NVFP4**(DiT 5.11 GB + 文本编码器 5.87 GB)与 **int8_convrot**(8.93 GB)变体,体积更小;我们的默认资源清单与上游文档都按 FP8 单文件路径配置,其它量化格式 **未做过验证**。 + +配套空间按需预留: + +- **权重最低要求**:约 **18.8 GB**(必需四项);需要无条件 DiT 再 +8.6 GB(合计约 27.5 GB)。 +- **文本缓存**:每 token 存 53248 通道——BF16 下 128 token ≈ 13.6 MB/图,1000 张约 **13.6 GB**;`text_cache_dtype=fp8_e4m3fn` 可减半。 +- **latent 缓存**:每图 MB 级,可忽略。 +- **引擎环境**:若本机还没装 musubi-tuner(含 torch 等),安装另需约 **8–12 GB**。 + +**建议预留 60–80 GB**(权重 + 一份缓存 + 引擎与产物余量);已有 musubi 环境且数据集只有几百张图时,40 GB 也够。下载期间会同时在目标目录写入一份文件,不要贴着剩余空间下限操作。 + + +## 运行流程与显存 + +训练按三阶段任务组执行:**缓存 latents → 缓存文本编码器输出 → 训练**。缓存阶段固定使用第一张 GPU。 + +- **文本缓存很大**:每个 token 存 53248 通道,BF16 下 128 token ≈ 13.6 MB/图(1k 图 ≈ 13.6 GB),256 token 翻倍,512 token ≈ 54.5 GB。可把 `text_cache_dtype` 设为 `fp8_e4m3fn` 减半,但属于精度权衡。 +- 基座为 9.3B FP8 DiT + Qwen3-VL-8B 文本编码器。显存不足时按顺序尝试:`blocks_to_swap`(上限 **33**)、`gradient_checkpointing`、关闭训练中采样预览。 +- 预检会在显存低于 16 GB 时提示,并检查已安装的 musubi-tuner 快照是否包含 Ideogram 4 脚本;缺失时请在「设置 → 训练引擎」修复/重装。 + +## 参数要点 + +- **时间步采样**:默认 `ideogram4_shift`(官方对齐的分辨率感知采样器),通常无需修改。上游另有 `ideogram4_timestep_mu/std` 两个兼容参数,但明确忽略其取值,界面不提供。 +- **损失**:纯 MSE flow matching,`weighting_scheme` 只能是 `none`(界面不提供该选项,导入其它值时会被忽略并提示)。 +- **caption**:官方用结构化 JSON(`high_level_description` / `style_description` / `compositional_deconstruction`)。纯文本也能训练;开启 `validate_caption_structure` 会校验结构,配合 `warn_on_caption_issues` 可只警告不中断。 +- **LoRA 目标**:仅训练 conditional transformer(`attention.qkv`、`attention.o`、`feed_forward.w1/w2/w3`)。 +- **注意力**:`sdpa` / `flash_attn` / `xformers` 可用;**SageAttention 不可用**(head 维度 256 超出其 int8/fp8 核支持范围)。 +- 精度:`mixed_precision` 仅 bf16;`dit_dtype` 默认 bfloat16(底模始终 FP8 加载)。 + +## 训练中采样 + +- 需要同时给出 `text_encoder` 与 `vae` 路径。 +- 采样调度由 `sampler_preset` 决定:`V4_DEFAULT_20`(18+2 步,默认)、`V4_QUALITY_48`(45+3 步)、`V4_TURBO_12`(11+1 步);`initial_sigma` 默认 1.004。 +- 默认只用 conditional DiT 采样(LoRA 只挂在 conditional DiT 上)。若要使用官方非对称 CFG,需要提供 unconditional DiT 并开启对应开关。 +- 官方结构化 JSON prompt 也可用于采样,界面默认给出一个三键示例。 + +## 本仓的上游补丁 + +我们固定的上游快照(2026-08-13)中,`ideogram4_utils.py` 的文本编码器加载在 `to_empty()` 之后 **没有重建 rotary embedding**,非持久化的 `inv_freq` 缓冲会保持未初始化,导致文本缓存是垃圾值。上游在 v0.3.6 修复。本仓不升级 pin,而是在启动训练前对该文件打等价补丁(幂等、写入前做语法校验),同时把 tokenizer 指向本地目录以支持离线运行。 + +## 已知限制 + +- 上游标注该模型支持为 **experimental**;上游 issue 报告过文本编码器预缓存阶段 OOM 与 resume 变慢,长训练前请先小步验证。 +- 只能 LoRA;LoRA 不能合并回 FP8 权重,生成时通过 forward hook 加载,结果可能与把 LoRA 合并进权重的工具(如 ComfyUI)不同。 +- 真机验收(3 步冒烟 + 缓存复用)在 [#424](https://github.com/wochenlong/lora-scripts-next/issues/424) 跟踪。 diff --git a/frontend/src/engines/catalog.ts b/frontend/src/engines/catalog.ts index 1ee3a4e40..2fba17632 100644 --- a/frontend/src/engines/catalog.ts +++ b/frontend/src/engines/catalog.ts @@ -75,7 +75,7 @@ export const ENGINE_CATALOG: readonly EngineDefinition[] = [ requiresGpu: true, managesRuntime: true, mark: "M", - tags: ["lora", "krea2", "nvidia"], + tags: ["lora", "krea2", "ideogram4", "nvidia"], version: "plugin", updatedAt: "2026-08", }, diff --git a/frontend/src/i18n/messages/en-US.ts b/frontend/src/i18n/messages/en-US.ts index 17bd7e188..4a15428a2 100644 --- a/frontend/src/i18n/messages/en-US.ts +++ b/frontend/src/i18n/messages/en-US.ts @@ -49,6 +49,7 @@ export default { flux: "Flux", lumina: "Lumina 2", krea2: "Krea 2", + ideogram4: "Ideogram 4", klein: "Klein (FLUX.2)", "qwen-image-21": "Qwen-Image-2.1", }, @@ -208,6 +209,7 @@ export default { "flux-lora": { title: "Flux LoRA", area: "Flux · Kohya-ss · LoRA" }, "lumina2-lora": { title: "Lumina 2 LoRA", area: "Lumina 2 · Kohya-ss · LoRA" }, "krea2-lora": { title: "Krea 2 LoRA", area: "Krea 2 · Musubi-Tuner · LoRA" }, + "ideogram4-lora": { title: "Ideogram 4 LoRA", area: "Ideogram 4 · Musubi-Tuner · LoRA" }, "qwen-image-21-lora": { title: "Qwen-Image-2.1 LoRA", area: "Qwen-Image-2.1 · DiffSynth-Studio · LoRA" }, "ai-toolkit-sdxl-lora": { title: "SDXL LoRA", area: "SDXL · AI Toolkit · LoRA" }, "ai-toolkit-flux-lora": { title: "Flux LoRA", area: "Flux · AI Toolkit · LoRA" }, @@ -711,6 +713,7 @@ export default { flux: "Flux", anima: "Anima", krea2: "Krea 2", + ideogram4: "Ideogram 4", klein: "Klein", "qwen-image-21": "Qwen-Image-2.1", nvidia: "NVIDIA", @@ -806,7 +809,7 @@ export default { }, musubi: { name: "Musubi-Tuner", - summary: "Optional engine for Krea 2 and other image-model LoRA.", + summary: "Optional engine for Krea 2, Ideogram 4 and other image-model LoRA.", sizeHint: "Large download (usually several GB); extra disk space and download time required", }, "ai-toolkit": { diff --git a/frontend/src/i18n/messages/zh-CN.ts b/frontend/src/i18n/messages/zh-CN.ts index 873a24fad..6e1dec2d2 100644 --- a/frontend/src/i18n/messages/zh-CN.ts +++ b/frontend/src/i18n/messages/zh-CN.ts @@ -49,6 +49,7 @@ export default { flux: "Flux", lumina: "Lumina 2", krea2: "Krea 2", + ideogram4: "Ideogram 4", klein: "Klein (FLUX.2)", "qwen-image-21": "Qwen-Image-2.1", }, @@ -208,6 +209,7 @@ export default { "flux-lora": { title: "Flux LoRA", area: "Flux · Kohya-ss · LoRA" }, "lumina2-lora": { title: "Lumina 2 LoRA", area: "Lumina 2 · Kohya-ss · LoRA" }, "krea2-lora": { title: "Krea 2 LoRA", area: "Krea 2 · Musubi-Tuner · LoRA" }, + "ideogram4-lora": { title: "Ideogram 4 LoRA", area: "Ideogram 4 · Musubi-Tuner · LoRA" }, "qwen-image-21-lora": { title: "Qwen-Image-2.1 LoRA", area: "Qwen-Image-2.1 · DiffSynth-Studio · LoRA" }, "ai-toolkit-sdxl-lora": { title: "SDXL LoRA", area: "SDXL · AI Toolkit · LoRA" }, "ai-toolkit-flux-lora": { title: "Flux LoRA", area: "Flux · AI Toolkit · LoRA" }, @@ -711,6 +713,7 @@ export default { flux: "Flux", anima: "Anima", krea2: "Krea 2", + ideogram4: "Ideogram 4", klein: "Klein", "qwen-image-21": "Qwen-Image-2.1", nvidia: "NVIDIA", @@ -806,7 +809,7 @@ export default { }, musubi: { name: "Musubi-Tuner", - summary: "可选训练引擎,支持 Krea 2 等图像模型 LoRA。", + summary: "可选训练引擎,支持 Krea 2、Ideogram 4 等图像模型 LoRA。", sizeHint: "体积较大(通常数 GB),需额外磁盘与下载时间", }, "ai-toolkit": { diff --git a/frontend/src/pages/MusubiGatePage.test.ts b/frontend/src/pages/MusubiGatePage.test.ts index d6b25e269..a76452892 100644 --- a/frontend/src/pages/MusubiGatePage.test.ts +++ b/frontend/src/pages/MusubiGatePage.test.ts @@ -21,15 +21,17 @@ const installing: EngineStatus = { id: "musubi", state: "installing", featureEna const notInstalled: EngineStatus = { id: "musubi", state: "not_installed", featureEnabled: true } const trainingPageStub = { - template: '
', + props: ["schemaName", "title", "area"], + template: '
', } const routerLinkStub = { props: ["to"], template: '', } -function mountPage() { +function mountPage(props: Record = {}) { return mount(MusubiGatePage, { + props, global: { plugins: [i18n], stubs: { TrainingPage: trainingPageStub, RouterLink: routerLinkStub } }, }) } @@ -49,10 +51,20 @@ describe("MusubiGatePage", () => { expect(status).toHaveBeenCalledWith("musubi") expect(setInterval).not.toHaveBeenCalled() expect(wrapper.find(".training-page-stub").exists()).toBe(true) + expect(wrapper.get(".training-page-stub").attributes("data-schema")).toBe("krea2-lora") expect(wrapper.get('[data-testid="engine-ready-chip"]').text()).toContain("训练环境准备就绪") wrapper.unmount() }) + it("forwards the selected musubi schema to the training form", async () => { + status.mockResolvedValue(ready) + const wrapper = mountPage({ schemaName: "ideogram4-lora" }) + await flushPromises() + + expect(wrapper.get(".training-page-stub").attributes("data-schema")).toBe("ideogram4-lora") + wrapper.unmount() + }) + it("shows the install view instead of the form when musubi is not installed", async () => { status.mockResolvedValue(notInstalled) const wrapper = mountPage() diff --git a/frontend/src/pages/MusubiGatePage.vue b/frontend/src/pages/MusubiGatePage.vue index 6af3c7d4b..9e0db1fe8 100644 --- a/frontend/src/pages/MusubiGatePage.vue +++ b/frontend/src/pages/MusubiGatePage.vue @@ -8,7 +8,10 @@ import EngineStatusBar from "../components/EngineStatusBar.vue" import TrainingPage from "./TrainingPage.vue" import { SCHEMA_META } from "../training/modules" -withDefaults(defineProps<{ bare?: boolean }>(), { bare: false }) +const props = withDefaults(defineProps<{ bare?: boolean; schemaName?: string }>(), { + bare: false, + schemaName: "krea2-lora", +}) const status = ref({ id: "musubi", state: "unknown", featureEnabled: true }) const loading = ref(false) @@ -20,9 +23,9 @@ let logSource: EventSource | undefined let progressSource: EventSource | undefined const { t } = useI18n() -const meta = SCHEMA_META["krea2-lora"] -const title = computed(() => t(meta.titleKey)) -const area = computed(() => t(meta.areaKey)) +const meta = computed(() => SCHEMA_META[props.schemaName] ?? SCHEMA_META["krea2-lora"]) +const title = computed(() => t(meta.value.titleKey)) +const area = computed(() => t(meta.value.areaKey)) const ready = computed(() => status.value.state === "ready") const working = computed(() => ["installing", "auditing"].includes(status.value.state)) const label = computed(() => { @@ -133,7 +136,7 @@ onBeforeUnmount(() => { v-if="ready" :title="title" :area="area" - schema-name="krea2-lora" + :schema-name="schemaName" :bare="bare" > - +