Sun Ruoxi a53f6a02d0
All checks were successful
Docker Build and Push / docker (push) Successful in 1m4s
add log in loading models
Signed-off-by: Sun Ruoxi <sunruoxi@4paradigm.com>
2026-08-18 10:35:53 +08:00
2026-07-23 11:55:57 +08:00
2026-07-23 11:55:57 +08:00
2026-08-18 10:35:53 +08:00
2026-07-28 14:10:43 +08:00
2026-07-23 11:55:57 +08:00
2026-07-28 14:10:43 +08:00

vLLM 自动修复方案

Tokenizer修复 + Head Size补丁 + Ixformer Ops补丁

1. 背景

在使用 vLLM 部署部分模型时,可能会遇到如下报错:


ValueError: Tokenizer class TokenizersBackend does not exist or is not currently imported.

该问题通常由 transformers 的 tokenizer 加载机制导致:

  • tokenizer_config.json 中指定了不存在或不兼容的 tokenizer_class
  • 开启 trust_remote_code=True 时transformers 会强制加载该 class
  • vLLM 无法通过参数 override tokenizer class

另外,某些模型的 head_size 可能不在 vLLM 默认支持的列表中64, 80, 96, 112, 120, 128, 192, 256导致运行时错误。

此外,某些模型需要特定的 ixformer 操作函数(如 gelu_tanh_and_mul),但这些函数可能不在默认的 ixformer 库中。


2. 方案目标

本方案实现:


无需修改模型文件
无需修改启动命令
自动修复 tokenizer 并启动 vLLM
自动检测并 patch 不支持的 head_size


3. 核心思路

在容器启动时:


entrypoint.sh
↓
检测 tokenizer 是否异常
↓
复制 tokenizer 文件 → /tmp/fixed_tokenizer
↓
修复 tokenizer_config.json
↓
检测模型 head_size
↓
如果 head_size 不在支持列表中patch vLLM 代码
↓
vllm serve --tokenizer /tmp/fixed_tokenizer

注意Ixformer ops 的 patch 在镜像构建时完成,不需要在容器启动时执行。


4. 支持的自动修复场景

原 tokenizer_class 修复为
TokenizersBackend PreTrainedTokenizerFast
TiktokenTokenizer GPT2TokenizerFast
缺失 tokenizer_config 自动生成
SentencePiece LlamaTokenizer

修复 extra_special_tokens 格式

extra_special_tokens 为 list 格式时,自动转换为 dict 格式:

// 修复前
"extra_special_tokens": ["<|im_start|>", "<|im_end|>", "<|box_start|>", "<|box_end|>", ...]

// 修复后
"extra_special_tokens": {
  "<|im_start|>": "<|im_start|>",
  "<|im_end|>": "<|im_end|>",
  "<|box_start|>": "<|box_start|>",
  "<|box_end|>": "<|box_end|>",
  ...
}

5. 生成的 tokenizer 目录

/tmp/fixed_tokenizer/
├── tokenizer.json
├── tokenizer_config.json   (已修复)
├── special_tokens_map.json (可选)
├── vocab.json / merges.txt (如需要)

6. 日志说明

正常情况

[entrypoint] tokenizer OK, skip fix

自动修复

[entrypoint] fixing tokenizer...
[fix] override bad tokenizer_class: TokenizersBackend → PreTrainedTokenizerFast
[fix] converted extra_special_tokens from list (13 items) to dict format

触发条件AUTO_FIX=auto 时):

  • tokenizer_config.json 包含 TokenizersBackendTiktokenTokenizer
  • tokenizer_config.json 中 extra_special_tokens 为 list 格式("extra_special_tokens": [

7. 验证方法

进入容器执行:

from transformers import AutoTokenizer

tok = AutoTokenizer.from_pretrained("/tmp/fixed_tokenizer")

print(tok.encode("hello world"))
print(tok.decode(tok.encode("hello world")))

确保:

encode → decode 可逆

8. 注意事项

⚠️ 1. tokenizer 文件必须存在

至少需要:

类型 必需文件
Fast tokenizer tokenizer.json
BPE vocab.json + merges.txt
SentencePiece tokenizer.model

⚠️ 2. 不影响模型推理

本方案:

仅影响 tokenizer文本 ↔ token
不影响模型计算attention / KV cache

⚠️ 3. 特殊 token 风险

需确认:

bos_token / eos_token / pad_token 一致

否则可能影响生成结果


9. Head Size 自动补丁

问题

vLLM 默认只支持以下 head_size 值:

[64, 80, 96, 112, 120, 128, 192, 256]

如果模型的 head_size 不在此列表中,会导致运行时错误。

检测逻辑

系统会自动从模型的 config.json 中检测 head_size支持以下多种配置格式

  1. 直接读取 head_dim 字段

    {
      "head_dim": 128
    }
    
  2. hidden_size / num_attention_heads 计算

    {
      "hidden_size": 2048,
      "num_attention_heads": 16
    }
    // head_size = 2048 / 16 = 128
    
  3. n_embd / n_head 计算GPTJ等模型

    {
      "n_embd": 2048,
      "n_head": 16
    }
    
  4. 直接读取 d_kv 字段T5等模型

    {
      "d_kv": 128
    }
    

补丁逻辑

如果检测到的 head_size 不在支持列表中,系统会:

  1. 备份原文件

    /usr/local/corex/lib64/python3/dist-packages/vllam/attention/ops/paged_attn.py.backup
    
  2. 修改 get_supported_head_sizes 方法

    @staticmethod
    def get_supported_head_sizes() -> List[int]:
        return [64, 80, 96, 112, 120, 128, 192, 256, YOUR_NEW_SIZE]
    
  3. 保持列表排序 新的 head_size 会被插入到正确的位置,保持列表升序排列。

日志示例

无需补丁的情况

[entrypoint] checking model head_size...
[detect_head_size] Found head_dim in config: 128
[detect_head_size] Model head_size: 128
[detect_head_size] head_size 128 is already supported by vLLM, skipping patch

需要补丁的情况

[entrypoint] checking model head_size...
[detect_head_size] Calculated from hidden_size(4096) / num_attention_heads(32) = 128
[detect_head_size] Model head_size: 128
[detect_head_size] head_size 160 is NOT in default supported list: [64, 80, 96, 112, 120, 128, 192, 256]
[detect_head_size] Attempting to patch vLLM...
[patch] Backed up original file to /usr/local/.../paged_attn.py.backup
[patch] Successfully added head_size 160 to supported list: [64, 80, 96, 112, 120, 128, 160, 192, 256]
[detect_head_size] Successfully patched vLLM to support head_size 160

补丁恢复

如需恢复原始文件:

cp /usr/local/corex/lib64/python3/dist-packages/vllm/attention/ops/paged_attn.py.backup \
   /usr/local/corex/lib64/python3/dist-packages/vllm/attention/ops/paged_attn.py

容错机制

重要head_size 检测和 patch 功能具有完整的容错机制:

  • 检测失败不影响启动:如果无法检测 head_sizevLLM 仍会正常启动
  • Patch 失败不影响启动:如果 patch 过程出错vLLM 仍会正常启动
  • 代码异常不影响启动如果检测脚本本身出现异常vLLM 仍会正常启动

这确保了:

本来能跑的模型 → 即使 head_size 检测失败 → 仍然能跑

日志示例

检测失败的情况仍会启动vLLM

[entrypoint] checking model head_size...
[detect_head_size] Error during head_size detection/patch: config.json not found
[detect_head_size] Continuing with vLLM startup anyway...
[entrypoint] head_size check failed, but continuing with vLLM startup
[entrypoint] starting vLLM...

10. Ixformer Ops 自动补丁

问题

某些模型需要特定的 ixformer 操作函数,如 gelu_tanh_and_mul,但这些函数可能不在默认的 ixformer 库中。

补丁时机

重要Ixformer ops 的 patch 在镜像构建时执行,而不是在容器启动时执行。这意味着:

  • 性能优化:容器启动速度更快,不需要每次都执行 patch 操作
  • 一次构建,多次运行patch 操作只在构建镜像时执行一次
  • 符合最佳实践:将构建时操作放在 Dockerfile 中,运行时操作放在 entrypoint 中

通用解决方案

重要特性:系统采用通用扫描机制,无需每次修改代码:

  1. 自动扫描:自动扫描 patched_ops 目录中的所有 .py 文件
  2. 批量处理:批量复制所有文件到目标目录
  3. 自动生成import:为每个文件自动生成对应的 from .xxx import * 语句
  4. 智能去重自动检测已存在的import避免重复添加

使用方法

只需将需要补丁的 ops 文件放入 patched_ops 目录即可:

patched_ops/
├── gelu_tanh_and_mul.py      # 第一个ops文件
├── another_op.py              # 第二个ops文件
├── third_operation.py         # 第三个ops文件
└── ...

系统会在镜像构建时自动:

  • 扫描所有 .py 文件(排除 __init__.py
  • 复制到 /usr/local/corex/lib64/python3/dist-packages/ixformer/functions/
  • __init__.py 中添加对应的 import 语句

补丁逻辑

系统会在镜像构建时自动执行以下操作:

  1. 扫描源目录

    源目录: /opt/patched_ops/
    自动查找所有 .py 文件(排除 __init__.py
    
  2. 批量复制文件

    源文件: /opt/patched_ops/*.py
    目标: /usr/local/corex/lib64/python3/dist-packages/ixformer/functions/
    
  3. 自动生成并添加 import 语句

    目标文件: /usr/local/corex/lib64/python3/dist-packages/ixformer/functions/__init__.py
    自动生成: from .gelu_tanh_and_mul import *
             from .another_op import *
             from .third_operation import *
             ```
    
    
  4. 自动备份 在修改前会自动备份原始的 __init__.py 文件为 __init__.py.backup

构建时日志示例

成功的批量补丁操作

Step 4/8 : COPY patched_ops /opt/patched_ops/
 ---> Using cache
 ---> 1234567890ab
Step 5/8 : RUN python3 /opt/patch_ops.py &&     chmod +x /opt/entrypoint.sh
 ---> Running in 9876543210fe
[patch_ops] Starting ixformer ops patch...
[patch_ops] Found 3 ops file(s): gelu_tanh_and_mul.py, another_op.py, third_operation.py
[patch_ops] Backed up /usr/local/.../__init__.py to /usr/local/.../__init__.py.backup
[patch_ops] Copied gelu_tanh_and_mul.py to /usr/local/.../ixformer/functions/
[patch_ops] Copied another_op.py to /usr/local/.../ixformer/functions/
[patch_ops] Copied third_operation.py to /usr/local/.../ixformer/functions/
[patch_ops] Added 3 import statement(s) to /usr/local/.../__init__.py
[patch_ops] Successfully patched ixformer ops
[patch_ops] Patch completed successfully
 ---> Removed intermediate container
 ---> abcdef123456

重复构建已存在import

Step 5/8 : RUN python3 /opt/patch_ops.py &&     chmod +x /opt/entrypoint.sh
 ---> Running in 1234567890ab
[patch_ops] Starting ixformer ops patch...
[patch_ops] Found 3 ops file(s): gelu_tanh_and_mul.py, another_op.py, third_operation.py
[patch_ops] Backup already exists: /usr/local/.../__init__.py.backup
[patch_ops] Copied gelu_tanh_and_mul.py to /usr/local/.../ixformer/functions/
[patch_ops] Copied another_op.py to /usr/local/.../ixformer/functions/
[patch_ops] Copied third_operation.py to /usr/local/.../ixformer/functions/
[patch_ops] All imports already exist in /usr/local/.../__init__.py, skipping modification
[patch_ops] Successfully patched ixformer ops

补丁恢复

如需恢复原始的 __init__.py 文件:

cp /usr/local/corex/lib64/python3/dist-packages/ixformer/functions/__init__.py.backup \
   /usr/local/corex/lib64/python3/dist-packages/ixformer/functions/__init__.py

容错机制

由于 patch 在镜像构建时执行,如果构建失败:

  • 构建失败会立即发现:不会发布有问题的镜像
  • 部分失败不影响整体:即使某个文件处理失败,其他文件仍会继续处理
  • 清晰的错误日志:构建日志会明确显示失败原因

11. 总结

本方案通过在容器启动阶段引入多种自动修复和补丁逻辑,实现:

"模型不动,运行时自适应兼容"

主要功能:

  • 自动修复不兼容的 tokenizer 配置
  • 自动检测并补丁不支持的 head_size
  • 自动补丁 ixformer ops如 gelu_tanh_and_mul- 在镜像构建时执行
  • 无需修改模型文件,无需修改启动命令
  • 完全透明,不影响正常模型部署
  • 完整的容错机制,确保本来能跑的模型不受影响

启动流程总结

镜像构建时

复制 patched_ops 文件
↓
执行 patch_ops.py一次
↓
补丁 ixformer ops 到目标目录

容器启动时

容器启动
↓
修复 tokenizer如需要
↓
检查并 patch head_size如需要
↓
启动 vLLM

每个步骤都有完整的容错机制,确保即使某个步骤失败,也不会影响后续步骤的执行。

Description
No description provided
Readme 51 KiB
Languages
Python 88.6%
Dockerfile 6.6%
Shell 4.8%