187 lines
6.3 KiB
Python
187 lines
6.3 KiB
Python
|
|
#!/usr/bin/env python3
|
|||
|
|
"""
|
|||
|
|
检测模型的head_size,如果不在vLLM支持列表中,则patch到vllm代码中
|
|||
|
|
"""
|
|||
|
|
import os
|
|||
|
|
import json
|
|||
|
|
import re
|
|||
|
|
import shutil
|
|||
|
|
from typing import Optional, List
|
|||
|
|
|
|||
|
|
# vLLM默认支持的head_size列表
|
|||
|
|
DEFAULT_SUPPORTED_HEAD_SIZES = [64, 80, 96, 112, 120, 128, 192, 256]
|
|||
|
|
|
|||
|
|
# vLLM需要patch的文件路径
|
|||
|
|
VLLM_PAGED_ATTN_PATH = "/usr/local/corex/lib64/python3/dist-packages/vllm/attention/ops/paged_attn.py"
|
|||
|
|
|
|||
|
|
|
|||
|
|
def get_model_head_size(model_dir: str) -> Optional[int]:
|
|||
|
|
"""
|
|||
|
|
从模型config.json中获取head_size
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
model_dir: 模型目录路径
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
head_size,如果无法获取则返回None
|
|||
|
|
"""
|
|||
|
|
config_path = os.path.join(model_dir, "config.json")
|
|||
|
|
|
|||
|
|
if not os.path.exists(config_path):
|
|||
|
|
print(f"[detect_head_size] config.json not found in {model_dir}")
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
with open(config_path, 'r') as f:
|
|||
|
|
config = json.load(f)
|
|||
|
|
|
|||
|
|
# 尝试多种方式获取head_size
|
|||
|
|
# 1. 直接从head_dim字段获取
|
|||
|
|
if 'head_dim' in config:
|
|||
|
|
head_size = config['head_dim']
|
|||
|
|
print(f"[detect_head_size] Found head_dim in config: {head_size}")
|
|||
|
|
return head_size
|
|||
|
|
|
|||
|
|
# 2. 从hidden_size和num_attention_heads计算
|
|||
|
|
if 'hidden_size' in config and 'num_attention_heads' in config:
|
|||
|
|
hidden_size = config['hidden_size']
|
|||
|
|
num_heads = config['num_attention_heads']
|
|||
|
|
head_size = hidden_size // num_heads
|
|||
|
|
print(f"[detect_head_size] Calculated from hidden_size({hidden_size}) / num_attention_heads({num_heads}) = {head_size}")
|
|||
|
|
return head_size
|
|||
|
|
|
|||
|
|
# 3. 对于GPTJ等模型,可能使用n_embd和n_head
|
|||
|
|
if 'n_embd' in config and 'n_head' in config:
|
|||
|
|
n_embd = config['n_embd']
|
|||
|
|
n_head = config['n_head']
|
|||
|
|
head_size = n_embd // n_head
|
|||
|
|
print(f"[detect_head_size] Calculated from n_embd({n_embd}) / n_head({n_head}) = {head_size}")
|
|||
|
|
return head_size
|
|||
|
|
|
|||
|
|
# 4. 对于T5等模型,可能使用d_kv和num_heads
|
|||
|
|
if 'd_kv' in config:
|
|||
|
|
head_size = config['d_kv']
|
|||
|
|
print(f"[detect_head_size] Found d_kv in config: {head_size}")
|
|||
|
|
return head_size
|
|||
|
|
|
|||
|
|
print(f"[detect_head_size] Cannot determine head_size from config.json")
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"[detect_head_size] Error reading config.json: {e}")
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
|
|||
|
|
def patch_vllm_head_sizes(new_head_size: int) -> bool:
|
|||
|
|
"""
|
|||
|
|
将新的head_size patch到vLLM的get_supported_head_sizes方法中
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
new_head_size: 需要添加的新head_size
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
是否成功patch
|
|||
|
|
"""
|
|||
|
|
if not os.path.exists(VLLM_PAGED_ATTN_PATH):
|
|||
|
|
print(f"[patch] vLLM file not found: {VLLM_PAGED_ATTN_PATH}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# 备份原文件
|
|||
|
|
backup_path = VLLM_PAGED_ATTN_PATH + ".backup"
|
|||
|
|
if not os.path.exists(backup_path):
|
|||
|
|
shutil.copy2(VLLM_PAGED_ATTN_PATH, backup_path)
|
|||
|
|
print(f"[patch] Backed up original file to {backup_path}")
|
|||
|
|
|
|||
|
|
# 读取文件内容
|
|||
|
|
with open(VLLM_PAGED_ATTN_PATH, 'r') as f:
|
|||
|
|
content = f.read()
|
|||
|
|
|
|||
|
|
# 找到get_supported_head_sizes方法并patch
|
|||
|
|
pattern = r'(@staticmethod\s+def get_supported_head_sizes\(\)\s*->\s*List\[int\]:\s*return\s*\[)([^\]]+)(\])'
|
|||
|
|
match = re.search(pattern, content, re.MULTILINE | re.DOTALL)
|
|||
|
|
|
|||
|
|
if not match:
|
|||
|
|
print(f"[patch] Could not find get_supported_head_sizes method in {VLLM_PAGED_ATTN_PATH}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
prefix = match.group(1)
|
|||
|
|
current_list = match.group(2)
|
|||
|
|
suffix = match.group(3)
|
|||
|
|
|
|||
|
|
# 解析当前列表
|
|||
|
|
current_sizes = [int(x.strip()) for x in current_list.split(',') if x.strip()]
|
|||
|
|
|
|||
|
|
# 添加新的head_size并排序
|
|||
|
|
if new_head_size not in current_sizes:
|
|||
|
|
current_sizes.append(new_head_size)
|
|||
|
|
current_sizes.sort()
|
|||
|
|
|
|||
|
|
# 生成新的列表字符串
|
|||
|
|
new_list_str = ', '.join(str(size) for size in current_sizes)
|
|||
|
|
|
|||
|
|
# 替换原方法
|
|||
|
|
new_method = f"{prefix}{new_list_str}{suffix}"
|
|||
|
|
new_content = re.sub(pattern, new_method, content, flags=re.MULTILINE | re.DOTALL)
|
|||
|
|
|
|||
|
|
# 写回文件
|
|||
|
|
with open(VLLM_PAGED_ATTN_PATH, 'w') as f:
|
|||
|
|
f.write(new_content)
|
|||
|
|
|
|||
|
|
print(f"[patch] Successfully added head_size {new_head_size} to supported list: {current_sizes}")
|
|||
|
|
return True
|
|||
|
|
else:
|
|||
|
|
print(f"[patch] head_size {new_head_size} already in list, skipping")
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"[patch] Error patching vLLM file: {e}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
|
|||
|
|
def main():
|
|||
|
|
"""主函数"""
|
|||
|
|
try:
|
|||
|
|
model_dir = os.environ.get("MODEL_DIR", "/model")
|
|||
|
|
|
|||
|
|
print(f"[detect_head_size] Checking model in: {model_dir}")
|
|||
|
|
|
|||
|
|
# 获取head_size
|
|||
|
|
head_size = get_model_head_size(model_dir)
|
|||
|
|
|
|||
|
|
if head_size is None:
|
|||
|
|
print(f"[detect_head_size] Could not determine head_size, skipping")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
print(f"[detect_head_size] Model head_size: {head_size}")
|
|||
|
|
|
|||
|
|
# 检查是否需要patch
|
|||
|
|
if head_size in DEFAULT_SUPPORTED_HEAD_SIZES:
|
|||
|
|
print(f"[detect_head_size] head_size {head_size} is already supported by vLLM, skipping patch")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
print(f"[detect_head_size] head_size {head_size} is NOT in default supported list: {DEFAULT_SUPPORTED_HEAD_SIZES}")
|
|||
|
|
print(f"[detect_head_size] Attempting to patch vLLM...")
|
|||
|
|
|
|||
|
|
# 执行patch
|
|||
|
|
success = patch_vllm_head_sizes(head_size)
|
|||
|
|
|
|||
|
|
if success:
|
|||
|
|
print(f"[detect_head_size] Successfully patched vLLM to support head_size {head_size}")
|
|||
|
|
else:
|
|||
|
|
print(f"[detect_head_size] Failed to patch vLLM for head_size {head_size}")
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"[detect_head_size] Error during head_size detection/patch: {e}")
|
|||
|
|
print(f"[detect_head_size] Continuing with vLLM startup anyway...")
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
try:
|
|||
|
|
main()
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"[detect_head_size] Unexpected error: {e}")
|
|||
|
|
print(f"[detect_head_size] This will not prevent vLLM from starting")
|
|||
|
|
import sys
|
|||
|
|
sys.exit(0) # 总是成功退出,不阻止vLLM启动
|