展开代码# 更新系统包(避免旧版本兼容性问题) sudo apt update && sudo apt upgrade -y # 安装编译工具链(后续安装 Python 包需要) sudo apt install -y build-essential python3-pip curl git wget \ libssl-dev zlib1g-dev libbz2-dev libreadline-dev \ libsqlite3-dev llvm libncurses5-dev libncursesw5-dev \ xz-utils tk-dev libffi-dev liblzma-dev
关键说明:
build-essential:提供 GCC/G++,编译 PyTorch 等库必需假设已完成:
- 驱动版本 ≥ 525.60.13(CUDA 12.x 兼容)
nvidia-smi正常输出nvcc --version显示 CUDA 12.x
验证命令:
展开代码nvidia-smi # 应显示 GPU 型号、温度、显存占用 nvcc -V # 应显示 release 12.x
| 方案 | 优点 | 缺点 |
|---|---|---|
| 系统 Python | 简单 | 包冲突、无法多版本并存 |
| virtualenv | 轻量 | 编译依赖复杂 |
| Miniconda | 预编译包、环境隔离、CUDA 自动匹配 | 体积稍大 |
| Docker | 完全隔离 | 调试麻烦、GPU 穿透配置繁琐 |
展开代码# 下载官方安装脚本(约 100MB) wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh # 静默安装到用户目录(-b 跳过交互,-p 指定路径) bash Miniconda3-latest-Linux-x86_64.sh -b -p $HOME/miniconda3 # 加入环境变量(当前 shell 生效) echo 'export PATH=$HOME/miniconda3/bin:$PATH' >> ~/.bashrc source ~/.bashrc # 验证 conda --version # 应显示 conda 23.x 或更高
展开代码# 创建 Python 3.10 环境(LLM 生态最稳定版本) conda create -n llm python=3.10 -y # 激活环境(后续所有操作都在此环境) conda activate llm # 升级基础工具 pip install --upgrade pip setuptools wheel
环境管理速查:
展开代码conda env list # 查看所有环境 conda deactivate # 退出当前环境 conda remove -n llm --all # 删除环境(危险)
展开代码# 核心推理栈 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 # HuggingFace 生态(模型加载、量化、加速) pip install transformers accelerate bitsandbytes # 可选:FlashAttention 加速(需 CUDA 11.6+,A100/H100 推荐) pip install flash-attn --no-build-isolation
| 包名 | 作用 | 关键特性 |
|---|---|---|
torch | PyTorch 深度学习框架 | CUDA 加速、自动求导 |
transformers | HuggingFace 模型库 | AutoModel、AutoTokenizer |
accelerate | 多卡/混合精度训练推理 | device_map="auto" |
bitsandbytes | 8-bit/4-bit 量化 | load_in_4bit=True 省显存 |
flash-attn | 内存高效 Attention | 2-4× 速度提升,支持长序列 |
展开代码python -c " import torch print(f'PyTorch: {torch.__version__}') print(f'CUDA available: {torch.cuda.is_available()}') print(f'CUDA version: {torch.version.cuda}') print(f'GPU: {torch.cuda.get_device_name(0)}') print(f'GPU memory: {torch.cuda.get_device_properties(0).total_memory / 1e9:.1f} GB') "
预期输出:
展开代码PyTorch: 2.3.0+cu121 CUDA available: True CUDA version: 12.1 GPU: NVIDIA A100-SXM4-80GB GPU memory: 80.0 GB
| 渠道 | 优点 | 缺点 |
|---|---|---|
| HuggingFace | 最全 | 国内访问慢,易中断 |
| ModelScope | 国内 CDN、速度快、免登录 | 部分新模型滞后 |
| 官方 GitHub | 源码完整 | 无 LFS 加速,大文件慢 |
| 百度网盘/迅雷 | 民间镜像 | 版本混乱、安全风险 |
展开代码# 安装魔搭客户端 pip install modelscope # 设置国内镜像(可选,自动识别) export MODELSCOPE_CACHE=/home/roots/models # 自定义缓存目录
展开代码# 创建模型目录 mkdir -p /home/roots/models/DeepSeek-R1-Distill-Llama-70B # 最好使用终端复用工具(如tmux)进行下载,防止断开下载连接 # 开始下载(约 140GB,根据网速 30-120 分钟) # 支持断点续传,中断后重新执行会自动跳过已下载 modelscope download \ --local_dir /home/roots/models/DeepSeek-R1-Distill-Llama-70B \ --model deepseek-ai/DeepSeek-R1-Distill-Llama-70B
下载进度监控:
展开代码# 另开终端查看 watch -n 5 'du -sh /home/roots/models/DeepSeek-R1-Distill-Llama-70B'
展开代码ls -lh /home/roots/models/DeepSeek-R1-Distill-Llama-70B
预期输出:
展开代码total 140G -rw-r--r-- 1 roots roots 1.5K config.json -rw-r--r-- 1 roots roots 4.8K generation_config.json -rw-r--r-- 1 roots roots 9.0G model-00001-of-00015.safetensors -rw-r--r-- 1 roots roots 9.0G model-00002-of-00015.safetensors ... -rw-r--r-- 1 roots roots 9.0G model-00015-of-00015.safetensors -rw-r--r-- 1 roots roots 16K model.safetensors.index.json -rw-r--r-- 1 roots roots 4.9M tokenizer.json -rw-r--r-- 1 roots roots 1.1M tokenizer.model -rw-r--r-- 1 roots roots 51K tokenizer_config.json
关键文件:
config.json:模型架构配置(层数、隐藏维度、注意力头数)*.safetensors:模型权重(HuggingFace 安全格式,替代 pickle)tokenizer.*:分词器(BPE 算法,将文本转为 token ID)展开代码cat > /home/roots/run.py <<'EOF' #!/usr/bin/env python3 """ 最小化 DeepSeek-R1-Distill-Llama 推理脚本 支持 4-bit 量化,单卡/多卡自动分配 """ import torch from transformers import AutoModelForCausalLM, AutoTokenizer import warnings warnings.filterwarnings("ignore") # ==================== 配置 ==================== MODEL_PATH = "/home/roots/models/DeepSeek-R1-Distill-Llama-70B" # 若显存不足,换 8B 版本: # MODEL_PATH = "/home/roots/models/DeepSeek-R1-Distill-Llama-8B" QUERY = "你是谁?你能做些什么?请用中文回答。" # ==================== 加载分词器 ==================== print(f"[1/3] 正在加载分词器: {MODEL_PATH}") tokenizer = AutoTokenizer.from_pretrained( MODEL_PATH, trust_remote_code=True, # DeepSeek 有自定义代码 padding_side="left" # 生成模型通常左填充 ) # 设置 pad_token(Llama 系列默认无 pad) if tokenizer.pad_token is None: tokenizer.pad_token = tokenizer.eos_token # ==================== 加载模型 ==================== print("[2/3] 正在加载模型(4-bit 量化,自动分配设备)...") model = AutoModelForCausalLM.from_pretrained( MODEL_PATH, torch_dtype=torch.float16, # 权重 FP16,计算 FP16 device_map="auto", # 自动分配到所有 GPU load_in_4bit=True, # 4-bit 量化,显存省 4× bnb_4bit_compute_dtype=torch.float16, # 计算时还原 FP16 bnb_4bit_use_double_quant=True, # 嵌套量化,再省 0.4 bit trust_remote_code=True ) print(f" 模型设备分布:") for name, device in model.hf_device_map.items(): print(f" - {name}: {device}") # ==================== 推理 ==================== print("[3/3] 开始推理...") inputs = tokenizer(QUERY, return_tensors="pt").to("cuda") # 生成参数 generate_kwargs = { "max_new_tokens": 512, # 最多生成 512 个新 token "temperature": 0.7, # 采样温度(0=确定,1=随机) "top_p": 0.9, # 核采样,保留累积概率 90% "repetition_penalty": 1.1, # 惩罚重复,1.0=无惩罚 "do_sample": True, # 启用采样(False=贪心解码) "pad_token_id": tokenizer.pad_token_id } with torch.no_grad(): outputs = model.generate(**inputs, **generate_kwargs) response = tokenizer.decode(outputs[0], skip_special_tokens=True) print(f"\n{'='*50}") print(f"用户: {QUERY}") print(f"AI: {response[len(QUERY):].strip()}") # 去掉输入部分 print(f"{'='*50}") # 显存统计 if torch.cuda.is_available(): print(f"\n显存占用:") for i in range(torch.cuda.device_count()): mem = torch.cuda.memory_allocated(i) / 1e9 max_mem = torch.cuda.max_memory_allocated(i) / 1e9 print(f" GPU {i}: {mem:.2f} GB / {max_mem:.2f} GB (峰值)") EOF
展开代码# 确保在 llm 环境 conda activate llm # 运行(首次加载需 2-5 分钟,从磁盘读 140GB) python /home/roots/run.py
预期输出:
展开代码[1/3] 正在加载分词器: /home/roots/models/DeepSeek-R1-Distill-Llama-70B [2/3] 正在加载模型(4-bit 量化,自动分配设备)... 模型设备分布: - model.embed_tokens: cuda:0 - model.layers.0-39: cuda:0 - model.layers.40-79: cuda:1 - model.norm: cuda:1 - lm_head: cuda:1 [3/3] 开始推理... ================================================== 用户: 你是谁?你能做些什么?请用中文回答。 AI: 我是 DeepSeek-R1 蒸馏后的 Llama 模型,由 DeepSeek 团队训练。我可以帮助你进行文本生成、代码编写、数学推理、知识问答等任务。我的回答基于训练数据中的模式,但请注意核实重要信息。 ================================================== 显存占用: GPU 0: 38.5 GB / 40.2 GB (峰值) GPU 1: 38.2 GB / 39.8 GB (峰值)
| 特性 | Transformers | vLLM |
|---|---|---|
| 吞吐量 | 低(逐序列生成) | 高(连续批处理 + PagedAttention) |
| 并发支持 | 差 | 优秀(动态批处理) |
| 显存效率 | 一般(KV-Cache 碎片) | 高(分页管理) |
| API 兼容性 | 无 | OpenAI 兼容 |
| 生产适用性 | 实验/调试 | 在线服务 |
展开代码conda activate llm # 安装指定版本(与 CUDA 12.1 匹配) pip install vllm==0.5.1 # 或最新版(可能需编译) pip install -U vllm
展开代码# 8B 模型,单卡启动 python -m vllm.entrypoints.openai.api_server \ --model /home/roots/models/DeepSeek-R1-Distill-Llama-8B \ --host 0.0.0.0 \ --port 36000 \ --dtype half \ --gpu-memory-utilization 0.9 \ --trust-remote-code # 70B 模型,双卡张量并行(需 2×A100-80GB 或 4×A100-40GB) python -m vllm.entrypoints.openai.api_server \ --model /home/roots/models/DeepSeek-R1-Distill-Llama-70B \ --host 0.0.0.0 \ --port 36000 \ --dtype half \ --tensor-parallel-size 2 \ --quantization bitsandbytes \ --load-format safetensors \ --gpu-memory-utilization 0.95 \ --trust-remote-code \ --max-model-len 8192 \ --max-num-seqs 16
参数详解:
| 参数 | 说明 | 建议值 |
|---|---|---|
--model | 本地模型路径 | 绝对路径 |
--host | 监听地址 | 0.0.0.0(允许外部访问) |
--port | 服务端口 | 36000 |
--dtype | 权重精度 | half(FP16)或 bfloat16 |
--tensor-parallel-size | 张量并行卡数 | 2/4/8,需整除注意力头数 |
--quantization | 量化方式 | bitsandbytes(8bit/4bit) |
--gpu-memory-utilization | 显存利用率上限 | 0.9-0.95,留余量给 KV-Cache |
--max-model-len | 最大序列长度 | 模型支持的最大上下文 |
--max-num-seqs | 最大并发序列数 | 根据显存调整 |
首次启动:会自动编译 CUDA kernel,持续 2-3 分钟,看到 Uvicorn running on http://0.0.0.0:36000 即成功。
展开代码curl http://localhost:36000/v1/completions \ -H "Content-Type: application/json" \ -d '{ "model": "/home/roots/models/DeepSeek-R1-Distill-Llama-8B", "prompt": "如何查看GPU温度?", "max_tokens": 80, "temperature": 0.7 }'
响应:
展开代码{ "id": "cmpl-abc123", "object": "text_completion", "created": 1699999999, "model": "/home/roots/models/DeepSeek-R1-Distill-Llama-8B", "choices": [{ "text": "你可以使用 nvidia-smi 命令查看 GPU 温度、功耗和显存占用。", "index": 0, "logprobs": null, "finish_reason": "stop" }], "usage": { "prompt_tokens": 12, "completion_tokens": 25, "total_tokens": 37 } }
展开代码curl http://localhost:36000/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer token-abc123" \ -d '{ "model": "/home/roots/models/DeepSeek-R1-Distill-Llama-8B", "messages": [ {"role": "system", "content": "你是一个 helpful 的 AI 助手。"}, {"role": "user", "content": "你好,你能干什么呢?用中文回答我"} ], "max_tokens": 200, "temperature": 0.7, "top_p": 0.9 }'
字段详解:
| 字段 | 类型 | 说明 |
|---|---|---|
model | string | 本地模型绝对路径(vLLM 直接当模型名用) |
messages | array | OpenAI 标准格式,system/user/assistant |
max_tokens | int | 最多生成多少新 token(约 0.75 汉字/token) |
temperature | float | 0-2,越高越随机,0.7 平衡创意与稳定 |
top_p | float | 核采样,0.9 表示从累积概率前 90% 采样 |
展开代码# 安装压测工具 pip install aiohttp # 并发压测脚本(100 并发,1000 请求) python -c " import asyncio, aiohttp, time, json async def request(session): async with session.post( 'http://localhost:36000/v1/chat/completions', json={ 'model': '/home/roots/models/DeepSeek-R1-Distill-Llama-8B', 'messages': [{'role': 'user', 'content': '你好'}], 'max_tokens': 50 } ) as resp: return await resp.json() async def main(): async with aiohttp.ClientSession() as session: start = time.time() tasks = [request(session) for _ in range(1000)] await asyncio.gather(*tasks) print(f'1000 请求完成,耗时: {time.time()-start:.2f}s') print(f'平均 QPS: {1000/(time.time()-start):.2f}') asyncio.run(main())
展开代码sudo tee /etc/systemd/system/vllm-deepseek.service <<'EOF' [Unit] Description=vLLM DeepSeek-R1 Service After=network.target [Service] Type=simple User=roots Group=roots WorkingDirectory=/home/roots Environment="PATH=/home/roots/miniconda3/envs/llm/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin" Environment="CUDA_VISIBLE_DEVICES=0,1" Environment="VLLM_LOGGING_LEVEL=INFO" ExecStart=/home/roots/miniconda3/envs/llm/bin/python -m vllm.entrypoints.openai.api_server \ --model /home/roots/models/DeepSeek-R1-Distill-Llama-70B \ --host 0.0.0.0 \ --port 36000 \ --dtype half \ --tensor-parallel-size 2 \ --quantization bitsandbytes \ --gpu-memory-utilization 0.95 \ --trust-remote-code \ --max-model-len 8192 Restart=always RestartSec=10 [Install] WantedBy=multi-user.target EOF sudo systemctl daemon-reload sudo systemctl enable --now vllm-deepseek sudo systemctl status vllm-deepseek
展开代码# /etc/nginx/sites-available/vllm server { listen 443 ssl http2; server_name llm.yourdomain.com; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; location / { proxy_pass http://localhost:36000; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; # 限流:每分钟 100 请求 limit_req zone=api_limit burst=20 nodelay; } } # 限流配置(http 块) limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/m;
展开代码# vLLM 自带 metrics 端点 curl http://localhost:36000/metrics # 关键指标 vllm:gpu_cache_usage_perc # KV-Cache 使用率 vllm:num_requests_running # 正在处理的请求数 vllm:time_to_first_token_seconds # 首 token 延迟 vllm:time_per_output_token_seconds # 生成速度
| 现象 | 可能原因 | 解决 |
|---|---|---|
CUDA out of memory | 模型太大,显存不足 | 加 --quantization bitsandbytes 或换小模型 |
RuntimeError: CUDA error | 驱动/CUDA 版本不匹配 | 重装对应版本 PyTorch |
| 生成速度慢 | 未使用 FP16/BF16 | 加 --dtype half |
| 首 token 延迟高 | 模型未预热,KV-Cache 空 | 发送 warm-up 请求 |
| 并发一高就 OOM | max-num-seqs 太大 | 降低 --max-num-seqs |
| 中文乱码 | 分词器配置错误 | 检查 tokenizer_config.json |
本文作者:zzz
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!