语言大模型
概念
- 基于海量文本数据训练的深度学习模型,能够理解和生成自然语言
- 每个词都是向量,语义相近的词,向量也较接近
- 根据概率,依次生成下一个词
开始部署
下载
环境
# NVIDIA显卡,查看驱动版本
nvidia-smi
# 查看CUDA版本,没有就去NVIDIA官网安装CUDA
nvcc --version
# 查看CUDA版本对应的pytorch版本
https://pytorch.org/get-started/locally
# 安装
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu126
pip install transformers
# 验证
print(f"PyTorch版本: {torch.__version__}")
print(f"CUDA是否可用: {torch.cuda.is_available()}")
使用
pip install accelerate
# 环境
python:3.13
accelerate:1.13.0
torch:2.10.0+cu126
tokenizers:0.22.2
transformers:5.3.0
peft:0.18.1
datasets:4.8.2
# demo
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
# 装了模型所有文件的上级文件夹路径
model_name = "./model"
# 加载分词器
tokenizer = AutoTokenizer.from_pretrained(model_name)
# 加载模型AutoModelForCausalLM会自动识别模型类型并加载对应的因果语言模型
model = AutoModelForCausalLM.from_pretrained(
model_name,
dtype=torch.float16,
device_map="auto"
)
if "cuda" in str(model.device):
print(f"模型运行在GPU上: {model.device}")
print(f"显存使用量: {torch.cuda.memory_allocated() / 1024 ** 3} GB")
else:
print("模型运行在CPU上")
# 准备输入
text = "星期四要吃什么?"
inputs = tokenizer(text, return_tensors="pt").to(model.device)
# 生成,禁用梯度计算
with torch.no_grad():
outputs = model.generate(
**inputs,
num_return_sequences=3,
max_new_tokens=999, # 最大生成token数量
do_sample=True, # 使用采样而不是贪婪解码
temperature=0.5, # 采样温度,控制随机性
top_p=0.9 # 核采样参数,控制生成多样性
)
# 解码并打印结果
generated_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
print(generated_text)
LoRA微调
微调
# lora微调
import torch
from transformers import (
AutoModelForCausalLM,
AutoTokenizer,
TrainingArguments,
Trainer,
DataCollatorForLanguageModeling
)
from peft import LoraConfig, get_peft_model, TaskType
from datasets import Dataset
# 加载lora权重,PeftModel.from_pretrained(base_model, "./lora_qwen_output/checkpoint-24")
from peft import PeftModel
# 查看训练日志:tensorboard --logdir ./logs
import tensorboard
# 模型路径
model_name_or_path = "./model"
# 训练输出目录
output_dir = "./lora_qwen_output"
# 配置Lora超参数
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM, # 因果语言模型任务
r=8,
lora_alpha=32,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"], # 要应用LoRA的模块
lora_dropout=0.1, # Dropout概率
bias="none" # 不训练bias
)
# 训练参数
epochs = 3 # 训练轮数
batch_size = 1 # 每GPU批次大小
gradient_accumulation_steps = 4 # 梯度累积步数
learning_rate = 2e-4 # 学习率
max_length = 512 # 输入最大长度
logging_steps = 10 # 日志打印间隔
save_steps = 50 # 每多少步保存一次
training_args = TrainingArguments(
output_dir=output_dir,
num_train_epochs=epochs,
max_steps=120,
per_device_train_batch_size=batch_size,
gradient_accumulation_steps=gradient_accumulation_steps,
learning_rate=learning_rate,
logging_steps=logging_steps,
logging_dir="./logs",
save_steps=save_steps,
save_total_limit=2, # 只保留最近2个checkpoint
fp16=True, # 使用混合精度训练(与模型半精度一致)
remove_unused_columns=False, # 保留所有列,因为collator需要"input_ids"等
report_to="tensorboard", # 不报告到外部工具(如wandb),保持精简
)
tokenizer = AutoTokenizer.from_pretrained(
model_name_or_path,
trust_remote_code=True
)
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token
# 训练数据
dialogs = []
for i in range(5):
dialogs.append({
"messages": [
{"role": "user", "content": "你好!我是秦始皇!"},
{"role": "assistant", "content": "陛下!"}
]
})
dialogs.append({
"messages": [
{"role": "user", "content": "什么是语言大模型?"},
{"role": "assistant", "content": "就是接龙"}
]
})
dialogs.append({
"messages": [
{"role": "user", "content": "阿姆罗的宿敌是谁?"},
{"role": "assistant", "content": "夏亚"}
]
})
def format_dialog(example):
text = tokenizer.apply_chat_template(
example["messages"],
tokenize=False, # 不进行tokenize,只返回字符串
add_generation_prompt=False # 训练时不添加生成提示,让模型学习生成assistant部分
)
return {"text": text}
# 创建HuggingFace Dataset对象
dataset = Dataset.from_list(dialogs)
dataset = dataset.map(format_dialog, remove_columns=["messages"])
# 对文本进行tokenize
def tokenize_function(examples):
return tokenizer(
examples["text"],
truncation=True,
max_length=max_length,
padding=False, # 不需要padding,DataCollator会动态处理
return_tensors=None # 返回list,由collator处理
)
dataset = dataset.map(tokenize_function, batched=True, remove_columns=["text"])
# 此时dataset包含"input_ids"和"attention_mask"字段
# 以半精度加载模型
model = AutoModelForCausalLM.from_pretrained(
model_name_or_path,
dtype=torch.float16, # 半精度,显存减半
device_map=None,
trust_remote_code=True
)
model = model.cuda()
# 加载lora权重
# model = PeftModel.from_pretrained(model, "./lora_qwen_output/checkpoint-27")
# 启用梯度检查点(可选,进一步节省显存,但会减慢速度)
# model.gradient_checkpointing_enable()
# 将Lora应用到模型上
model = get_peft_model(model, lora_config)
# 打印可训练参数数量,确认Lora生效
model.print_trainable_parameters()
# 创建数据收集器 因果语言建模的数据收集器,会自动对批次进行padding,并创建labels(与input_ids相同)
data_collator = DataCollatorForLanguageModeling(
tokenizer=tokenizer,
mlm=False, # 不是掩码语言模型,而是因果LM
)
# 开始训练
trainer = Trainer(
model=model,
args=training_args,
train_dataset=dataset,
data_collator=data_collator,
)
# 从头训练
trainer.train(resume_from_checkpoint=None)
# 恢复训练
# trainer.train(resume_from_checkpoint="./lora_qwen_output/checkpoint-114")
# 保存模型
model.save_pretrained(output_dir)
# 保存分词器
tokenizer.save_pretrained(output_dir)
print(f"训练完成!保存到 {output_dir}")
使用
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
# 基座模型路径
base_model_path = "./model" # 或本地路径
lora_weights_path = "./lora_qwen_output" # 你保存LoRA权重的目录
# 加载基座模型和分词器
tokenizer = AutoTokenizer.from_pretrained(base_model_path, trust_remote_code=True)
base_model = AutoModelForCausalLM.from_pretrained(
base_model_path,
torch_dtype=torch.float16,
device_map=None,
trust_remote_code=True
)
base_model = base_model.cuda()
# 加载LoRA权重并合并或直接使用PeftModel
lora_model = PeftModel.from_pretrained(base_model, lora_weights_path)
# 如果想永久合并权重(变成普通模型):
# merged_model = lora_model.merge_and_unload()
# 推理函数
def generate_response(model, prompt):
messages = [{"role": "user", "content": prompt}]
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
inputs = tokenizer(text, return_tensors="pt").to(model.device)
outputs = model.generate(
**inputs,
max_new_tokens=128,
do_sample=True,
temperature=0.7
)
response = tokenizer.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokens=True)
return response
# 测试
test_prompts = [
"你好!我是秦始皇!",
# "什么是语言大模型?",
# "阿姆罗的宿敌是谁?"
]
print("=== 微调前模型 ===")
for p in test_prompts:
print(f"in: {p}")
print(f"out: {generate_response(base_model, p)}\n")
print("=== 微调后模型 ===")
for p in test_prompts:
print(f"in: {p}")
print(f"out: {generate_response(lora_model, p)}\n")
生产部署
环境
ubuntu-22.04
# 内核:5.15.0-173-generic
# 查看驱动
nvidia-smi
# 如果内核和nvidia驱动不兼容,卸载nvidia驱动
/usr/bin/nvidia-uninstall
# 安装当前内核的头文件(驱动编译时需要)
apt install linux-headers-$(uname -r)
# 添加NVIDIA 驱动仓库
add-apt-repository ppa:graphics-drivers/ppa
# 查看推荐驱动
ubuntu-drivers devices
# 安装
apt install nvidia-driver-580
reboot
# 确认
nvidia-smi
# CUDA Version: 13.0
# 确认 DKMS(更新内核时,自动重新编译)
dkms status | grep nvidia
python环境
# 进入虚拟环境
apt install python3-pip
# 镜像源
pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
pip3 install torch torchvision
pip install transformers
# 验证
print(f"PyTorch版本: {torch.__version__}")
print(f"CUDA是否可用: {torch.cuda.is_available()}")
运行demo
pip install accelerate
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
model_name = "./dfdfdf/dfdf/baichuan"
model = AutoModelForCausalLM.from_pretrained(
model_name,
device_map="auto",
dtype=torch.float16,
trust_remote_code=True
)
tokenizer = AutoTokenizer.from_pretrained(model_name)
# 2. Input prompt text
prompt = "今天重庆天气怎么样?"
# 3. Encode the input text for the model
messages = [
{"role": "user", "content": prompt}
]
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
thinking_mode='off' # on/off/auto
)
model_inputs = tokenizer([text], return_tensors="pt").to(model.device)
# 4. Generate text
generated_ids = model.generate(
**model_inputs,
max_new_tokens=1024
)
output_ids = [
output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
][0].tolist()
# 5. parsing thinking content
try:
# rindex finding 151668 (</think>)
index = len(output_ids) - output_ids[::-1].index(151668)
except ValueError:
index = 0
# thinking_content = tokenizer.decode(output_ids[:index], skip_special_tokens=True).strip("")
content = tokenizer.decode(output_ids[index:], skip_special_tokens=True).strip("")
# print("thinking content:", thinking_content)
print(content)
项目搭建
- 项目结构
-configs/
--config.yaml
-models/
--base/
--lora/
-scripts/
--demo.py
-data/
-logs/
- 配置文件
model:
baichuan:
base_path: "/xx/xxx/models/Baichuan-M2-32B/base"
lora_save_path: "/xxx/xxx/models/Baichuan-M2-32B/lora"
data:
processed_path: "/xx/xxx/data/processed/my_dataset"
training:
log_dir: "/xxx/xxx/logs"
- tool
import yaml
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline,GenerationConfig
from langchain_huggingface import HuggingFacePipeline, ChatHuggingFace
from langchain_openai import ChatOpenAI
def get_config():
with open('../configs/config.yaml','r') as f:
return yaml.safe_load(f)
def load_model_baichuan():
model_name = get_config()['model']['baichuan']['base_path']
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
model_name,
dtype=torch.float16,
device_map="auto",
trust_remote_code=True
)
if "cuda" in str(model.device):
print(f"模型运行在GPU上: {model.device}")
print(f"显存使用量: {torch.cuda.memory_allocated() / 1024 ** 3:.2f} GB")
else:
print("模型运行在CPU上")
gen_config = GenerationConfig(
max_new_tokens=1024,
max_length=None, # 显式覆盖该值以消除冲突警告
do_sample=True,
temperature=0.2
)
# 创建pipeline(支持chat模板模型)
pipe = pipeline(
"text-generation",
model=model,
tokenizer=tokenizer
)
llm_pipeline = HuggingFacePipeline(
pipeline=pipe,
model_kwargs={
"generation_config": gen_config
}
)
return ChatHuggingFace(llm=llm_pipeline)
def remote_model():
base_url = "https://xxx.xxx.cn/v1/"
api_key = "xxxx"
model_id = "xxxxc"
llm = ChatOpenAI(
model=model_id, # 对应你调用时的 model
temperature=0.2,
top_p=0.9,
frequency_penalty=0,
openai_api_key=api_key,
openai_api_base=base_url,
max_retries=2,
timeout=60,
)
return llm
- 使用
# pip install langchain
from langchain.agents import create_agent
from langchain.tools import tool
import langchain
from langchain_core.prompts import PromptTemplate
from pub_tool import load_model_baichuan
# 加载大模型
llm = load_model_baichuan()
# ============ chain ============
def simple_chain():
prompt = PromptTemplate.from_template(
"请为一个新式 {product} 设计一个有创意、有幽默感的名称,解释这样设计的含义"
)
chain = prompt | llm
result = chain.invoke({"product": "智能水杯"})
print(result.content)
# ============ 逐一输出 ============
def stream_print():
for chunk in llm.stream("为什么夏亚阿兹纳布尔加入扎比家吉翁军?"):
for char in chunk.content:
print(char, end="|", flush=True)
simple_agent()
vllm部署
- 快速、易于使用的 LLM 推理和服务库
- 快速部署本地大模型
- 原生支持 OpenAI 兼容的 API 服务器
- 内置PagedAttention 显存管理、并发请求调度等功能
# 安装
pip install vllm
# 验证
python -c "import vllm; print(vllm.__version__)"
# 编写启动脚本,启动
./vllm.sh
- 启动配置脚本
# vllm.sh
# ================================
# vLLM OpenAI 服务器启动脚本
# ================================
# ---------- 基础模型配置 ----------
BASE_MODEL_PATH="./xxx" # 基础模型路径
SERVED_MODEL_NAME="xxxx"
DTYPE="float16" # 数据类型 half/float16/bfloat16
GPU_IDS="0,1,2,3" # 使用的 GPU,多卡用逗号分隔
TENSOR_PARALLEL_SIZE=4 # 张量并行度
GPU_MEMORY_UTIL=0.95 # GPU 显存使用比例
MAX_MODEL_LEN=2048 # 最大序列长度
MAX_NUM_SEQS=2 # 最大并发请求数
MAX_NUM_BATCHED_TOKENS=1024 # 批处理最大 token 数
# ---------- LoRA 配置 ----------
# 格式:lora_name1=/path/to/lora1 lora_name2=/path/to/lora2
LORA_MODULES=""
# 是否启用 LoRA
ENABLE_LORA=true
MAX_LORA_RANK=64 # 根据你的 LoRA rank 设置
MAX_CPU_LORAS=4 # 最多在 CPU 缓存几个 LoRA
# ---------- 服务器配置 ----------
API_KEY="cousin"
PORT=8000
LOG_DIR="./logs"
LOG_FILE="${LOG_DIR}/vllm_$(date +%Y%m%d_%H%M%S).log"
# ---------- 其他额外参数 ----------
EXTRA_ARGS="--enable-auto-tool-choice --tool-call-parser hermes"
# ---------- 检查基础模型路径 ----------
if [ ! -d "${BASE_MODEL_PATH}" ]; then
echo "错误:基础模型路径 '${BASE_MODEL_PATH}' 不存在。"
exit 1
fi
# ---------- 构建启动命令 ----------
CMD="python -m vllm.entrypoints.openai.api_server \
--model ${BASE_MODEL_PATH} \
--served-model-name ${SERVED_MODEL_NAME} \
--dtype ${DTYPE} \
--api-key ${API_KEY} \
--port ${PORT} \
--gpu-memory-utilization ${GPU_MEMORY_UTIL} \
--max-model-len ${MAX_MODEL_LEN} \
--max-num-seqs ${MAX_NUM_SEQS} \
--max-num-batched-tokens ${MAX_NUM_BATCHED_TOKENS}"
# 多卡并行
if [ ${TENSOR_PARALLEL_SIZE} -gt 1 ]; then
CMD="${CMD} --tensor-parallel-size ${TENSOR_PARALLEL_SIZE}"
fi
# LoRA 相关参数
if [ "${ENABLE_LORA}" = true ] && [ -n "${LORA_MODULES}" ]; then
# 将空格分隔的多个 "name=path" 拼接到参数中
CMD="${CMD} --enable-lora --lora-modules ${LORA_MODULES}"
CMD="${CMD} --max-lora-rank ${MAX_LORA_RANK}"
CMD="${CMD} --max-cpu-loras ${MAX_CPU_LORAS}"
echo "LoRA 已启用,模块:${LORA_MODULES}"
fi
# 额外参数
if [ -n "${EXTRA_ARGS}" ]; then
CMD="${CMD} ${EXTRA_ARGS}"
fi
# ---------- 设置 GPU 环境 ----------
export CUDA_VISIBLE_DEVICES=${GPU_IDS}
# ---------- 启动信息 ----------
mkdir -p ${LOG_DIR}
echo "=========================================="
echo "启动 vLLM 服务器"
echo "基础模型: ${BASE_MODEL_PATH}"
echo "LoRA 模块: ${LORA_MODULES:-无}"
echo "端口: ${PORT} | API Key: ${API_KEY}"
echo "GPU: ${GPU_IDS} (并行度 ${TENSOR_PARALLEL_SIZE})"
echo "显存比例: ${GPU_MEMORY_UTIL} | 最大并发: ${MAX_NUM_SEQS}"
echo "日志: ${LOG_FILE}"
echo "=========================================="
echo "命令:"
echo "${CMD}"
echo "=========================================="
# 启动(后台运行)
nohup ${CMD} >> ${LOG_FILE} 2>&1 &
echo "vLLM 服务已启动,PID: $!"
- 客户端调用
def local_chat_baichuan():
base_url = "http://localhost:8000/v1"
api_key = "xxxxx"
model_id = "xxxxx"
llm = ChatOpenAI(
model=model_id,
temperature=0.2,
top_p=0.9,
frequency_penalty=0,
openai_api_key=api_key,
openai_api_base=base_url,
max_retries=2,
timeout=60,
)
return llm
# 加载大模型
llm = local_chat_baichuan()
llm.invoke("你是什么大模型")
量化版本部署
环境
pip install gptqmodel # 可能报错:'QuantizeConfig' is not defined,依赖不适配,下载对应版本的gptqmodel:https://github.com/ModelCloud/GPTQModel
# pip install ./gptqmodel-4.2.5+cu12.torch2.7-cp310-cp310-linux_x86_64.whl
pip install optimum
pip install auto-gptq
from transformers import AutoTokenizer, AutoModelForCausalLM, GPTQConfig
import torch
import yaml
def get_config():
with open('../configs/config.yaml','r') as f:
return yaml.safe_load(f)
model_name = get_config()['model']['baichuan']['int4_path']
tokenizer = AutoTokenizer.from_pretrained(model_name)
gptq_config = GPTQConfig(
bits=4, # 量化位数(Int8=8,Int4=4)
group_size=128, # 量化分组大小,默认128
dataset="c4", # 量化时使用的数据集(不影响加载,填默认值即可)
tokenizer=tokenizer,
trust_remote_code=True
)
model = AutoModelForCausalLM.from_pretrained(
model_name,
quantization_config=gptq_config,
device_map="auto",
trust_remote_code=True
)
# 2. Input prompt text
prompt = "今天重庆天气怎么样?"
# 3. Encode the input text for the model
messages = [
{"role": "user", "content": prompt}
]
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
thinking_mode='off' # on/off/auto
)
model_inputs = tokenizer([text], return_tensors="pt").to(model.device)
# 4. Generate text
generated_ids = model.generate(
**model_inputs,
max_new_tokens=1024
)
output_ids = [
output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
][0].tolist()
# 5. parsing thinking content
try:
# rindex finding 151668 (</think>)
index = len(output_ids) - output_ids[::-1].index(151668)
except ValueError:
index = 0
# thinking_content = tokenizer.decode(output_ids[:index], skip_special_tokens=True).strip("")
content = tokenizer.decode(output_ids[index:], skip_special_tokens=True).strip("")
# print("thinking content:", thinking_content)
print(content)
vllm配置
- 启动脚本
#!/bin/bash
# ================================
# vLLM OpenAI 服务器启动脚本
# ================================
# ---------- 基础模型配置 ----------
BASE_MODEL_PATH="/xxxx/Baichuan-M2-32B/INT4/" # 基础模型路径
SERVED_MODEL_NAME="baichuan"
DTYPE="auto" # 数据类型 half/float16/bfloat16
GPU_IDS="2,3" # 使用的 GPU,多卡用逗号分隔
TENSOR_PARALLEL_SIZE=2 # 张量并行度
PIPELINE_PARALLEL_SIZE=1 # 流水线并行
GPU_MEMORY_UTIL=0.95 # GPU 显存使用比例
MAX_MODEL_LEN=32768 # 最大序列长度
MAX_NUM_SEQS=2 # 最大并发请求数
MAX_NUM_BATCHED_TOKENS=2048 # 批处理最大 token 数
# ---------- LoRA 配置 ----------
# 格式:lora_name1=/path/to/lora1 lora_name2=/path/to/lora2
# 如果只有一个 LoRA,按下面示例填写;多个用空格分隔。
LORA_MODULES=""
# 是否启用 LoRA(如果上面有值,会自动启用;也可以手动强制 false)
ENABLE_LORA=true
MAX_LORA_RANK=64 # 根据你的 LoRA rank 设置
MAX_CPU_LORAS=4 # 最多在 CPU 缓存几个 LoRA
# ---------- 服务器配置 ----------
API_KEY="xxx"
PORT=xxxx
LOG_DIR="/xxxxx"
LOG_FILE="${LOG_DIR}/vllm_$(date +%Y%m%d_%H%M%S).log"
# ---------- 其他额外参数 ----------
EXTRA_ARGS="--quantization gptq_marlin --reasoning-parser qwen3 --enable-auto-tool-choice --tool-call-parser hermes"
# --enforce-eager
# ---------- 检查基础模型路径 ----------
if [ ! -d "${BASE_MODEL_PATH}" ]; then
echo "错误:基础模型路径 '${BASE_MODEL_PATH}' 不存在。"
exit 1
fi
# ---------- 构建启动命令 ----------
CMD="python -m vllm.entrypoints.openai.api_server \
--model ${BASE_MODEL_PATH} \
--served-model-name ${SERVED_MODEL_NAME} \
--dtype ${DTYPE} \
--api-key ${API_KEY} \
--port ${PORT} \
--gpu-memory-utilization ${GPU_MEMORY_UTIL} \
--max-model-len ${MAX_MODEL_LEN} \
--max-num-seqs ${MAX_NUM_SEQS} \
--max-num-batched-tokens ${MAX_NUM_BATCHED_TOKENS}"
# 多卡并行
if [ ${TENSOR_PARALLEL_SIZE} -gt 1 ]; then
CMD="${CMD} --tensor-parallel-size ${TENSOR_PARALLEL_SIZE}"
fi
if [ ${PIPELINE_PARALLEL_SIZE} -gt 1 ]; then
CMD="${CMD} --pipeline-parallel-size ${PIPELINE_PARALLEL_SIZE}"
fi
# LoRA 相关参数(仅当 ENABLE_LORA=true 且 LORA_MODULES 非空时添加)
if [ "${ENABLE_LORA}" = true ] && [ -n "${LORA_MODULES}" ]; then
# 将空格分隔的多个 "name=path" 拼接到参数中
CMD="${CMD} --enable-lora --lora-modules ${LORA_MODULES}"
CMD="${CMD} --max-lora-rank ${MAX_LORA_RANK}"
CMD="${CMD} --max-cpu-loras ${MAX_CPU_LORAS}"
echo "LoRA 已启用,模块:${LORA_MODULES}"
fi
# 额外参数
if [ -n "${EXTRA_ARGS}" ]; then
CMD="${CMD} ${EXTRA_ARGS}"
fi
# ---------- 设置 GPU 环境 ----------
export CUDA_VISIBLE_DEVICES=${GPU_IDS}
# ---------- 打印启动信息 ----------
mkdir -p ${LOG_DIR}
echo "=========================================="
echo "启动 vLLM 服务器"
echo "基础模型: ${BASE_MODEL_PATH}"
echo "LoRA 模块: ${LORA_MODULES:-无}"
echo "端口: ${PORT} | API Key: ${API_KEY}"
echo "GPU: ${GPU_IDS} (并行度 ${TENSOR_PARALLEL_SIZE})"
echo "显存比例: ${GPU_MEMORY_UTIL} | 最大并发: ${MAX_NUM_SEQS}"
echo "日志: ${LOG_FILE}"
echo "=========================================="
echo "命令:"
echo "${CMD}"
echo "=========================================="
# 启动(后台运行)
nohup ${CMD} >> ${LOG_FILE} 2>&1 &
echo "vLLM 服务已启动,PID: $!"