| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 |
- #!/usr/bin/env python3
- """
- 验证已下载的模型是否完整可用
- 支持文件完整性检查 + 实际推理测试
- """
- import json
- import fnmatch
- from pathlib import Path
- PROJECT_ROOT = Path(__file__).parent.parent.resolve()
- WEIGHTS_DIR = PROJECT_ROOT / "model" / "weights"
- # 必需的核心文件
- REQUIRED_FILES = {
- "config.json",
- "tokenizer.json",
- "tokenizer_config.json",
- }
- # 模型权重文件(至少有一种)
- WEIGHT_FILES = {
- "pytorch_model.bin",
- "model.safetensors",
- "tf_model.h5",
- "model.onnx",
- }
- # 分片权重文件模式
- SHARDED_WEIGHT_PATTERNS = [
- "model-*.safetensors",
- "pytorch_model-*.bin",
- "model-*.bin",
- ]
- def verify_model(model_dir: Path) -> dict:
- """验证单个模型目录"""
- result = {
- "name": model_dir.name,
- "path": str(model_dir),
- "exists": model_dir.exists(),
- "files": [],
- "missing_required": [],
- "has_weights": False,
- "weight_files": [],
- "valid": False,
- "error": None
- }
- if not model_dir.exists():
- result["error"] = "模型目录不存在"
- return result
- # 列出所有文件
- all_files = {f.name for f in model_dir.iterdir() if f.is_file()}
- result["files"] = sorted(all_files)
- # 检查必需文件
- for req_file in REQUIRED_FILES:
- if req_file not in all_files:
- found = False
- for sub_dir in model_dir.iterdir():
- if sub_dir.is_dir():
- sub_files = {f.name for f in sub_dir.iterdir() if f.is_file()}
- if req_file in sub_files:
- found = True
- break
- if not found:
- result["missing_required"].append(req_file)
- # 检查权重文件
- for weight_file in WEIGHT_FILES:
- if weight_file in all_files:
- result["weight_files"].append(weight_file)
- result["has_weights"] = True
- else:
- for sub_dir in model_dir.iterdir():
- if sub_dir.is_dir():
- sub_files = {f.name for f in sub_dir.iterdir() if f.is_file()}
- if weight_file in sub_files:
- result["weight_files"].append(f"{sub_dir.name}/{weight_file}")
- result["has_weights"] = True
- break
- # 检查分片权重文件
- for pattern in SHARDED_WEIGHT_PATTERNS:
- for f in model_dir.iterdir():
- if f.is_file() and fnmatch.fnmatch(f.name, pattern):
- result["weight_files"].append(f.name)
- result["has_weights"] = True
- # 验证完整性
- if not result["missing_required"] and result["has_weights"]:
- result["valid"] = True
- return result
- def check_model_integrity(model_dir: Path) -> bool:
- """检查模型文件的完整性"""
- errors = []
- for json_file in model_dir.glob("*.json"):
- try:
- with open(json_file, 'r', encoding='utf-8') as f:
- json.load(f)
- except json.JSONDecodeError as e:
- errors.append(f"{json_file.name}: JSON 解析错误 - {e}")
- except Exception as e:
- errors.append(f"{json_file.name}: 读取错误 - {e}")
- # 显示权重文件大小
- for weight_pattern in ["*.bin", "*.safetensors", "*.onnx", "*.h5"]:
- for weight_file in model_dir.glob(weight_pattern):
- size = weight_file.stat().st_size
- if size == 0:
- errors.append(f"{weight_file.name}: 文件大小为 0")
- elif size > 1024 * 1024 * 1024:
- size_gb = size / (1024 * 1024 * 1024)
- print(f" {weight_file.name}: {size_gb:.2f} GB")
- elif size > 1024 * 1024:
- size_mb = size / (1024 * 1024)
- print(f" {weight_file.name}: {size_mb:.2f} MB")
- else:
- size_kb = size / 1024
- print(f" {weight_file.name}: {size_kb:.2f} KB")
- if errors:
- for err in errors:
- print(f" ✗ {err}")
- return False
- return True
- def test_qwen_inference(model_dir: Path):
- """使用 Qwen 模型进行实际推理测试"""
- print("\n" + "=" * 60)
- print("Qwen 模型推理测试")
- print("=" * 60)
- try:
- from transformers import AutoModelForCausalLM, AutoTokenizer
- import torch
- except ImportError as e:
- print(f"✗ 缺少依赖:{e}")
- print("请运行:pip install transformers torch")
- return False
- print(f"\n模型路径:{model_dir}")
- print("-" * 60)
- # 步骤 1: 加载分词器
- print("\n[1/4] 加载分词器...")
- try:
- tokenizer = AutoTokenizer.from_pretrained(
- str(model_dir),
- trust_remote_code=True
- )
- print(f" ✓ 分词器加载成功")
- print(f" - 词表大小:{len(tokenizer):,}")
- except Exception as e:
- print(f" ✗ 分词器加载失败:{e}")
- return False
- # 步骤 2: 加载模型
- print("\n[2/4] 加载模型...")
- try:
- model = AutoModelForCausalLM.from_pretrained(
- str(model_dir),
- trust_remote_code=True,
- torch_dtype=torch.float16, # 使用半精度节省显存
- device_map="auto" # 自动选择设备
- )
- print(f" ✓ 模型加载成功")
- print(f" - 参数量:{sum(p.numel() for p in model.parameters()):,}")
- print(f" - 设备:{model.device}")
- except Exception as e:
- print(f" ✗ 模型加载失败:{e}")
- return False
- # 步骤 3: 构建测试输入
- print("\n[3/4] 准备测试输入...")
- test_prompts = [
- "你好,请介绍一下你自己。",
- "What is the capital of France?",
- ]
- for prompt in test_prompts:
- print(f"\n 输入:{prompt}")
- # 步骤 4: 执行推理
- print("\n[4/4] 执行推理测试...")
- try:
- for prompt in test_prompts:
- inputs = tokenizer(prompt, return_tensors="pt")
- inputs = inputs.to(model.device)
- with torch.no_grad():
- outputs = model.generate(
- **inputs,
- max_new_tokens=50,
- do_sample=True,
- temperature=0.7,
- top_p=0.9,
- )
- response = tokenizer.decode(outputs[0], skip_special_tokens=True)
- # 只显示 prompt + 部分回复
- response_text = response[len(prompt):].strip()
- if len(response_text) > 100:
- response_text = response_text[:100] + "..."
- print(f" 输出:{response_text}")
- print("\n ✓ 推理测试通过")
- return True
- except Exception as e:
- print(f" ✗ 推理失败:{e}")
- return False
- def main():
- import argparse
- parser = argparse.ArgumentParser(description="模型验证工具")
- parser.add_argument(
- "--model",
- type=str,
- default=None,
- help="指定要验证的模型名称 (默认验证所有)"
- )
- parser.add_argument(
- "--test-inference",
- action="store_true",
- help="对 Qwen 模型进行推理测试"
- )
- args = parser.parse_args()
- print("=" * 60)
- print("模型验证工具")
- print("=" * 60)
- print(f"模型目录:{WEIGHTS_DIR}\n")
- if not WEIGHTS_DIR.exists():
- print("✗ 模型目录不存在")
- return
- model_dirs = [d for d in WEIGHTS_DIR.iterdir() if d.is_dir()]
- if not model_dirs:
- print("未找到任何模型")
- return
- # 过滤指定模型
- if args.model:
- model_dirs = [d for d in model_dirs if args.model in d.name]
- if not model_dirs:
- print(f"✗ 未找到包含 '{args.model}' 的模型")
- return
- print(f"找到 {len(model_dirs)} 个模型:\n")
- all_valid = True
- for model_dir in sorted(model_dirs):
- print("-" * 60)
- print(f"\n模型:{model_dir.name}")
- print(f"路径:{model_dir}")
- result = verify_model(model_dir)
- print(f"\n文件列表 ({len(result['files'])} 个):")
- for f in result['files'][:15]:
- print(f" - {f}")
- if len(result['files']) > 15:
- print(f" ... 还有 {len(result['files']) - 15} 个文件")
- print(f"\n验证结果:")
- if result['valid']:
- print(f" ✓ 模型完整")
- else:
- print(f" ✗ 模型不完整")
- all_valid = False
- if result['missing_required']:
- print(f" 缺少必需文件:{', '.join(result['missing_required'])}")
- if not result['has_weights']:
- print(f" 未找到权重文件")
- print(f"\n权重文件:")
- if result['weight_files']:
- for wf in result['weight_files']:
- print(f" - {wf}")
- else:
- print(f" 未找到权重文件")
- print(f"\n完整性检查:")
- if check_model_integrity(model_dir):
- print(f" ✓ 文件完整性通过")
- else:
- print(f" ✗ 文件完整性失败")
- all_valid = False
- print("\n" + "=" * 60)
- if all_valid:
- print("✓ 所有模型文件验证通过!")
- else:
- print("✗ 部分模型验证失败")
- print("=" * 60)
- # 推理测试
- if args.test_inference:
- qwen_dir = WEIGHTS_DIR / "Qwen2.5-7B-Instruct"
- if qwen_dir.exists():
- success = test_qwen_inference(qwen_dir)
- if success:
- print("\n" + "=" * 60)
- print("✓ Qwen 模型推理测试成功!")
- print("=" * 60)
- else:
- print("\n" + "=" * 60)
- print("✗ Qwen 模型推理测试失败")
- print("=" * 60)
- else:
- print("\n✗ 未找到 Qwen 模型,跳过推理测试")
- if __name__ == "__main__":
- main()
|