LTX-2.3 本地部署完整复盘

先把结论放前面:LTX-2.3(22B)这条 pipeline 在 4×RTX 3090(24GB)这套硬件上,按官方默认推理方式基本跑不起来。我最终得到的不是“没跑通”,而是一个更有价值的结果:把它为什么跑不起来、卡在哪、该怎么判断“物理不可行”,完整验证了一遍。

这篇文章是一次本地部署的工程复盘:从模型文件下载、依赖链补齐、环境和代码层踩坑,到显存拆分、多卡 device 规划,再到最终 OOM 的边界判断。希望你在遇到类似“看起来只要把权重放进去就能跑”的大模型工程时,可以少走很多弯路。


TL;DR(1 分钟读完)

  • LTX-2.3 不是单模型,而是一个多组件 pipeline:文本编码器(Gemma)+ 视频 diffusion 主模型(22B)+ 可能的增强/上采样等。
  • 仅下载 ltx-2.3-22b-distilled.safetensors(≈46GB)并不够,运行时会继续依赖 HuggingFace 生态的一整套配置文件与子模型。
  • 我在 3090 上遇到两类 OOM:
    • Gemma(12B)加载阶段先爆一次(prompt encoder 直接吃掉接近一张 3090 的可用显存)。
    • diffusion 主模型推理阶段仍然爆(不是调小分辨率/帧数就能解决的那种)。
  • 做到“能跑”的三条现实路线:
    1. CPU / NVMe offload(能出结果但会很慢);
    2. 真正意义上的模型并行/张量并行(需要改造实现,不是简单多卡);
    3. 换更现实的模型/蒸馏版本。

1. 背景

目标很直接:

  • 在本地 GPU 服务器上部署 LTX-2.3(22B)视频生成模型
  • 硬件:4×RTX 3090(每张 24GB)
  • 系统:Ubuntu 24.04
  • 网络受限:需要手工下载模型(避免 git-lfs 拉爆、断点续传等)

我以为这会是一件“把权重下载下来、装依赖、跑脚本”的事。事实证明:当模型规模跨过某个阈值后,部署问题本质上会从“软件配置”变成“系统工程 + 资源规划”。


2. 一个关键认知:LTX 是 pipeline,不是“一个模型”

很多部署失败,是从最初的心理模型就错了。

LTX-2.3 的推理链路更像这样:

Prompt(文本)
  ↓
Text Encoder(Gemma)
  ↓
Diffusion 主模型(视频生成)
  ↓
(可选) Upsampler / Enhancer
  ↓
Video Output

也就是说你不是在部署一个 .safetensors,而是在部署一条 由多个模型 + 多套配置 + 多段推理代码组成的 pipeline。

只要这条链上的任何一环缺文件、缺 config、版本不匹配,你就会看到“看似随机”的报错。


3. 第一步:下载主权重,但很快发现“这只是开始”

我先手工下载了 diffusion 主模型的权重:

ltx-2.3-22b-distilled.safetensors  ≈ 46GB

这一步本身没什么技术含量,但它会带来一个误导:你会以为“权重有了就能跑”。

很快,运行就报错(典型的第一类缺失):

找不到 preprocessor_config.json

这意味着:代码实际上在按 HuggingFace 的模型目录结构去找东西——不仅是权重文件,还包括 tokenizer / processor / config 这一整套元数据。


4. 最大的坑:Gemma 依赖与版本匹配

继续顺着报错和代码路径往下挖,我定位到:

PromptEncoder → GemmaTextEncoder

结论很明确:必须提供 Gemma 模型。并且不是“随便一个 Gemma”。

我走了三次典型的错误分支:

4.1 gemma-2:架构不匹配

这类错误的特征是:你能加载一部分,然后在模型结构对齐/层数/命名上出现不兼容。

4.2 gemma-3-4b:尺寸不匹配

报错类似:

hidden_size mismatch(2560 vs 3840)

这基本可以断定:上游代码/权重期望的是更大规格的文本编码器。

4.3 gemma-3-12b-it:终于对上

最终匹配的版本是 gemma-3-12b-it。除了分片权重外,还必须补齐:

  • tokenizer
  • processor
  • config
  • preprocessor_config.json(关键)

到这一步,依赖链才算完整。


5. 环境与依赖:不是“装不装得上”,而是“能不能稳定复现”

我这次遇到的环境问题并不花哨,但非常常见:

5.1 Conda strict priority 导致依赖不可解

典型报错:

LibMambaUnsatisfiableError

如果你用了国内镜像(例如开启 strict priority),很容易把解空间卡死。处理方式也很工程化:

  • 切回默认源,或
  • 调整 channel priority 策略

5.2 uv / pip / .venv 混用导致“看起来装了但其实没装”

我这里踩到的是:项目自带 .venv 与我的 conda 环境产生混乱。一个简单但重要的结论是:

  • 项目自带 .venv 不等于必须使用
  • 你完全可以用 conda + pip 管理,只要保证解释器和 site-packages 指向一致

6. 第一次真正跑起来前:Gemma 先 OOM

当依赖补齐之后,我第一次遇到的硬性问题是:Gemma(12B)加载/初始化阶段直接 OOM。

报错类似:

CUDA out of memory(发生在 PromptEncoder)

直觉上也合理:

  • Gemma 12B 本身就接近 20GB+ 的显存消耗
  • 3090 标称 24GB,但可用显存会被 buffer、碎片和 runtime 开销进一步挤压

6.1 解决:手动做“分卡”而不是幻想多卡自动分摊

我采取的策略:

  • GPU1:Gemma / PromptEncoder
  • GPU0:LTX diffusion 主模型

我在 DistilledPipeline.__init__ 里增加了类似 text_device 的参数,把 prompt encoder 显式放到 cuda:1,而 diffusion 放到 cuda:0

这类改动的意义在于:多卡不是自动解决显存问题的。你必须明确:每个大组件在哪张卡上。


7. 代码层的“工程坑”(不难但很耗时间)

在继续推进时,我遇到了一组很典型的“不是算法问题,但会让你卡很久”的坑:

  • SyntaxError:复制/粘贴残留(例如多余的引号、括号)
  • TabError:tabs vs spaces(Python 最经典的坑)
    • 我用 sed -i 's/\\t/ /g' 统一掉缩进
  • __init__:后定义覆盖前定义,导致你以为参数传进去了但实际没生效
  • CUDA device ordinal:你以为有 cuda:1,但其实进程只看见一张卡
    • 典型原因是设置了 CUDA_VISIBLE_DEVICES=0
    • 解决:unset CUDA_VISIBLE_DEVICES

这些问题都不“高级”,但它们会把你从“调模型”拖进“调工程”泥潭。


8. 第二次 OOM:主模型推理阶段依然爆

当 Gemma 分卡后,我以为最难的部分过去了。但真正决定“能不能跑”的,是 diffusion 主模型推理阶段的显存峰值。

即使我做了:

  • 降分辨率
  • 减少帧数
  • 尝试进一步拆分

依然出现 OOM,且位置更接近主生成阶段。

这里的核心结论是:

LTX-2.3 22B 的权重(≈46GB)只是显存需求的一部分。

推理时还会叠加:

  • 激活(diffusion 的迭代过程会放大峰值)
  • 各类中间 buffer
  • 可能的 KV cache / attention 临时张量
  • 后处理/增强组件(如果启用)

最终,你会发现 24GB 这条线对它来说太窄了。


9. 最终结论:这是一次“边界验证”

我最终得到的最重要结论是:

  • 在 4×3090(24GB)上,按当前实现与默认推理方式,LTX-2.3 22B 很难稳定跑通
  • 这不是“参数没调好”,而是资源约束与实现方式共同决定的不可行

这类项目最怕的是:你投入很多时间,最后只留下一个模糊的“没跑通”。我希望这篇复盘留下的是一个可复用的方法论:

  1. 先把系统拆成 pipeline(谁在吃资源、谁是硬依赖)
  2. 对每一段做“可测量的资源预算”(显存、CPU、磁盘、带宽)
  3. 多卡要显式规划(组件级 device placement),不要期待自动分摊
  4. 当 OOM 多次落在主模型推理峰值时,要学会及时判断“物理不可行”,避免无效优化

10. 后续可行路线(现实版)

如果你也在类似硬件上想“出结果”,大致只有三条路:

  1. CPU/NVMe offload:能跑,但速度会非常慢
  2. 真正的模型并行:需要系统性改造(不是简单把 .to(cuda:1)
  3. 换模型/换规模:选择更适合 24GB 单卡推理的版本(或更激进的蒸馏/量化方案)

结语

这次复盘对我最有价值的点,不是“搞定了一个部署”,而是把一条大模型 pipeline 的依赖链、资源峰值、以及多卡拆分的工程边界走了一遍。

一句话总结:

从“我能不能跑这个模型”
到“这个模型在这套硬件上按当前实现物理不可行”

Read more

三台机器部署 ClickHouse 高可用集群实战记录

本文是一份可发布版部署记录。真实 IP、域名、账号、密码、下载链接、业务目录名、机器唯一标识等敏感信息已经替换为占位符。命令中的 <...> 需要按自己的环境替换。 目标与拓扑 这次目标是用三台数据节点部署一套 ClickHouse 高可用集群,拓扑采用: 1 shard x 3 replicas 含义是:集群只有一个逻辑分片,三台机器都保存同一份数据的完整副本。任意一台数据节点宕机时,只要 ClickHouse Keeper 仍然有多数派,剩余节点仍可继续提供读写服务。 规划节点如下: 主机名示例地址角色ch-01<ch-01-ip>ClickHouse Server + ClickHouse Keeperch-02<ch-02-ip>ClickHouse Server + ClickHouse Keeperch-03<ch-03-ip&

By ladydd

折腾记(二):接入火山引擎实时语音 API,家庭语音助手体验直接拉满

接上篇 上一篇用全开源组件(Whisper + Hermes + Edge-TTS)搭了个语音助手,能跑,但体验就是"能用"二字: * 中文识别只有 70 分,方言基本歇菜 * 英文唤醒词"Alexa"喊着别扭 * 说完到回复要等 4-8 秒 * 它说话的时候你插不了嘴 这些问题靠堆开源组件很难根治。于是我去试了火山引擎(字节跳动)的语音服务,结果直接换了条路。 这篇分两段:先讲怎么用火山引擎的 ASR/TTS 替换掉开源组件(小改),再讲怎么上端到端实时语音模型(大改)。 第一段:先把 ASR 和 TTS 换成火山引擎 为什么换 我用豆包输入法的时候发现它语音识别准得离谱。一查,豆包用的就是字节自家的火山引擎 Seed-ASR。开通后有免费额度(

By ladydd

折腾记(一):用全开源组件给家里搭一个语音助手,对接自己的 Hermes Agent

起因 事情是从一块 ESP32-S3 开发板开始的。 我手上有一块 Seeed Studio XIAO ESP32-S3 Sense,带摄像头和麦克风。最初的想法很美好:用这块板子做一个无线语音终端,对着它说话,连到我服务器上跑的 Hermes Agent(一个自托管的 AI agent),让它回答我。 但折腾到一半我突然意识到一件事:我的麦克风、音响、服务器全在家里,为什么要绕一圈用 ESP32?直接把麦克风和音响插到服务器上不就行了? ESP32 那条路(做无线拾音终端)当然也有价值,但那是"为了学嵌入式而学",不是解决问题的最短路径。于是这个项目就从"嵌入式项目"变成了"在服务器上拼一个语音助手"。这篇就记录后者。 教训零:先想清楚你要解决的是什么问题。很多时候最优解比你最初设想的简单得多。 目标

By ladydd

Kiro 的三种代理设置方法:本地、服务端、Remote

作为kiro的骨灰级用户,这篇是我自己折腾 Kiro / Kiro Remote / Ubuntu Server 代理问题后的复盘。 核心不是“怎么配一个代理”,而是先判断:到底是谁在访问外网? 谁访问外网,代理就要配给谁。 0. 先说结论 Kiro 相关代理大概分三类: 场景真正访问外网的进程在哪里代理应该配在哪里本地 KiroWindows / Mac 本机本机 Clash / Proxifier / 系统代理服务端 Kiro / CLIUbuntu Server 上的 shell、CLI、node、kiro 进程Ubuntu 的环境变量,比如 HTTP_PROXY / HTTPS_PROXYKiro Remote远程 Ubuntu 上的 ~/.kiro-server 和 extensionHost远程 Ubuntu 的 Kiro Server

By ladydd
陕公网安备61011302002223号 | 陕ICP备2025083092号