# 1. 推理服务的性能优化
# 1.1 推理服务的优化方向
随着LLM的不断发展和应用,如何提高模型推理性能成为了一个重要的研究方向。推理性能受到显存带宽而不是计算能力的限制,服务吞吐量受到推理batch_size的限制。针对推理性能和服务吞吐量的优化,可以从三个角度开展:推理引擎层、服务层优化以及量化技术。
推理引擎层主要是针对计算性能进行优化,例如KernelFusion、KV-Cache、FlashAttention、TP+PP、PagedAttention等技术;
服务层优化主要关注吞吐量的提升,包括Dynamic-Batching、Continous-Batching等技术。
此外,还有针对特定场景的优化技术,例如流式、交互式及持续生成,以及长序列推理等。
模型量化方面主要涉及Weight-Only、int8、int4以及KV-Cache量化等。
对于以上这些技术基础理论的介绍,详见这篇文章:大模型推理-2-推理引擎和服务性能优化 (opens new window)
# 1.2 基于vLLM加速大模型推理
# 1.2.1 vLLM项目简介
vLLM是一个大型语言模型推理加速工具,它通过优化内存管理、连续批处理、CUDA核心优化和分布式推理支持等技术手段,显著提高了大型语言模型的推理速度和效率。在官方实验中,vLLM 的吞吐量比 HF 高出 24 倍,比 TGI 高出 3.5 倍。
- 项目地址:https://github.com/vllm-project/vllm (opens new window)
- 论文地址:https://dl.acm.org/doi/pdf/10.1145/3600006.3613165 (opens new window)
- 官方博客:vLLM: Easy, Fast, and Cheap LLM Serving with PagedAttention (opens new window)
注:vLLM官方目前还不支持AutoGPTQ的量化模型、ChatGLM-1模型,后续官方可能会改进优化,使用时注意一下兼容性问题。
# 1.2.2 vLLM基本原理
vLLM是LLM推理和服务引擎,支持多种模型,具有极高的性能,PagedAttention是vLLM背后的核心技术。
作者发现大模型推理的性能瓶颈主要来自于内存。一是自回归过程中缓存的K和V张量非常大,在LLaMA-13B中,单个序列输入进来需要占用1.7GB内存。二是内存占用是动态的,取决于输入序列的长度。由于碎片化和过度预留,现有的系统浪费了60%-80%的内存。
PagedAttention灵感来自于操作系统中虚拟内存和分页的经典思想,它可以允许在非连续空间立存储连续的KV张量。具体来说,PagedAttention把每个序列的KV缓存进行了分块,每个块包含固定长度的token,而在计算attention时可以高效地找到并获取那些块。

每个固定长度的块可以看成虚拟内存中的页,token可以看成字节,序列可以看成进程。那么通过一个块表就可以将连续的逻辑块映射到非连续的物理块,而物理块可以根据新生成的token按需分配。
所以序列在分块之后,只有最后一个块可能会浪费内存(实际中浪费的内存低于4%)。高效利用内存的好处很明显:系统可以在一个batch中同时输入更多的序列,提升GPU的利用率,显著地提升吞吐量。
PagedAttention的另外一个好处是高效内存共享。例如,在并行采样的时候,一个prompt需要生成多个输出序列。这种情况下,对于这个prompt的计算和内存可以在输出序列之间共享。
通过块表可以自然地实现内存共享。类似进程之间共享物理页,在PagedAttention中的不同序列通过将逻辑块映射到一样的物理块上可以实现共享块。为了确保安全共享,PagedAttention跟踪物理块的引用计数,并实现了Copy-on-Write机制。内存共享减少了55%内存使用量,大大降低了采样算法的内存开销,同时提升了高达2.2倍的吞吐量。
# 1.3 大模型量化与显存估算
# 1.3.1 大模型量化及压缩技术
近年来,随着Transformer、MOE架构的提出,使得深度学习模型轻松突破上万亿规模参数,从而导致模型变得越来越大,因此,我们需要一些大模型压缩技术来降低模型部署的成本,并提升模型的推理性能。大模型压缩主要分为如下几类:剪枝(Pruning)、知识蒸馏(Knowledge Distillation)、量化(Quantization)、低秩分解(Low-Rank Factorization)。
[1] 模型量化
模型量化是指以较低的推理精度损失将连续取值(通常为float32或者大量可能的离散值)的浮点型权重近似为有限多个离散值(通常为int8)的过程。通过以更少的位数表示浮点数据,模型量化可以减少模型尺寸,进而减少在推理时的内存消耗,并且在一些低精度运算较快的处理器上可以增加推理速度。下图中,[-T, T]是量化前的数据范围,[-127, 127]是量化后的数据范围。
[2] 量化比特
计算机中不同数据类型的占用比特数及其表示的数据范围各不相同。可以根据实际业务需求将原模型量化成不同比特数的模型,一般深度神经网络的模型用单精度浮点数表示,如果能用有符号整数来近似原模型的参数,那么被量化的权重参数存储大小就可以降到原先的四分之一,用来量化的比特数越少,量化后的模型压缩率越高。
工业界目前最常用的量化位数是8比特,低于8比特的量化被称为低比特量化。1比特是模型压缩的极限,可以将模型压缩为1/32,在推理时也可以使用高效的XNOR和BitCount位运算来提升推理速度。
[3] 量化对象
模型量化的对象主要包括以下几个方面:
- 权重(weight):weight的量化是最常规也是最常见的,量化weight可达到减少模型大小内存和占用空间。
- 激活(activation):实际中activation往往是占内存使用的大头,因此量化activation不仅可以大大减少内存占用。更重要的是,结合weight的量化可以充分利用整数计算获得性能提升。
- KV cache:量化 KV 缓存对于提高长序列生成的吞吐量至关重要。
- 梯度(Gradients):相对小众一些,在训练深度学习模型时,梯度通常是浮点数,它主要作用是在分布式计算中减少通信开销,同时,也可以减少backward时的开销。
[4] 量化形式
根据量化数据表示的原始数据范围是否均匀,可以将量化方法分为线性量化和非线性量化。实际的深度神经网络的权重和激活值通常是不均匀的,因此理论上使用非线性量化导致的精度损失更小,但在实际推理中非线性量化的计算复杂度较高,通常使用线性量化。
[5] 量化分类
根据应用量化压缩模型的阶段,可以将模型量化分为:
- 量化感知训练(Quantization Aware Training, QAT):在模型训练过程中加入伪量化算子,通过训练时统计输入输出的数据范围可以提升量化后模型的精度,适用于对模型精度要求较高的场景,其量化目标无缝地集成到模型的训练过程中。这种方法使LLM在训练过程中适应低精度表示,增强其处理由量化引起的精度损失的能力。这种适应旨在量化过程之后保持更高性能。
- 量化感知微调(Quantization-Aware Fine-tuning,QAF):在微调过程中对LLM进行量化。主要目标是确保经过微调的LLM在量化为较低位宽后仍保持性能。通过将量化感知整合到微调中,以在模型压缩和保持性能之间取得平衡。
- 训练后量化(Post Training Quantization, PTQ):在LLM训练完成后对其参数进行量化,只需要少量校准数据,适用于追求高易用性和缺乏训练资源的场景。主要目标是减少LLM的存储和计算复杂性,而无需对LLM架构进行修改或进行重新训练。PTQ的主要优势在于其简单性和高效性,但PTQ可能会在量化过程中引入一定程度的精度损失。
# 1.3.2 估算大模型显存占用
方式一:参数量估算法,按照 fp16 格式运行,需要 VRAM 大小(GB) = 参数量(billion) x 2
- 例1:llama-2-70B, 70 billion * 2 bytes = 140 GB
- 例2:llama-2-13B, 13 billion * 2 bytes = 26 GB
- 例3:llama-2-7B, 7 billion * 2 bytes = 14 GB
方式二:使用 HuggingFace Accelerate 工具估算
$ pip3 install git+https://github.com/huggingface/accelerate.git
$ pip3 install jaxlib
$ pip3 install bitsandbytes
$ huggingface-cli login // 填写Token值,从Huggingface官网的个人账号设置处生成即可
$ accelerate estimate-memory baichuan-inc/Baichuan2-13B-Chat --trust-remote-code
┌────────────────────────────────────────────────────────────┐
│ Memory Usage for loading `baichuan-inc/Baichuan2-13B-Chat` │
├───────┬─────────────┬──────────┬───────────────────────────┤
│ dtype │Largest Layer│Total Size│ Training using Adam │
├───────┼─────────────┼──────────┼───────────────────────────┤
│float32│ 2.4 GB │ 51.77 GB │ 207.08 GB │
│float16│ 1.2 GB │ 25.88 GB │ 103.54 GB │
│ int8 │ 613.75 MB │ 12.94 GB │ N/A │
│ int4 │ 306.88 MB │ 6.47 GB │ N/A │
└───────┴─────────────┴──────────┴───────────────────────────┘
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1.3.3 大模型参数量B的含义
"B"代表“Billion”,即“十亿”。这是模型训练中所使用到参数的数量。大模型中的参数通常是指神经网络中的各个权重和偏置值,这些参数决定了模型在训练过程中如何学习和适应输入数据,从而影响模型的预测性能。
下面以一个简单的线性回归模型为例,说明大模型中的参数是什么样子。
- 假设我们有一个线性回归模型,用于预测房价。这个模型的目标是找到一个线性关系,使得给定房屋的面积(特征),我们可以预测出对应的房价(目标变量)。线性回归模型可以表示为:y = wx + b。其中,y是房价,w是权重,x是房屋面积,b是偏置。这个模型有3个参数:w、x和b。
- 在训练过程中,我们需要通过大量的房屋面积和房价数据来调整这些参数,使得模型的预测结果尽可能接近实际房价。这个过程通常使用梯度下降等优化算法来实现。我们首先随机初始化权重w和偏置b,然后计算模型对这10组数据的预测结果。接下来,我们计算预测结果与实际房价之间的误差,并使用梯度下降算法更新参数w和b,使得误差最小化。这个过程可能需要进行多次迭代,直到模型的预测性能达到满意的程度。大模型中的参数w和b就是我们需要学习的权重和偏置值。通过不断地调整这些参数,我们的线性回归模型可以逐渐学会如何根据房屋面积来预测房价。
注:现在LLM的参数大小为什么都设计成6/7B、13/14B、72B、130B等规模?——答案:就是为了匹配显存。
- 6B模型可以在在12/16/24G显存的消费级显卡部署和训练。如果一个公司的模型不打算在消费级显卡部署,通常不会训6B这个规模。而且通常还会有一个1.4B或者2.8B,这个是比较适合在手机、车载端量化部署的尺寸。
- 13B模型按照4k长度组织数据,数据并行=2,刚好占满一个8卡机,并且可以量化部署在A10甚至4090。目前更大模型有16B、34B、52B、56B、65B、70B、100B、130B、170B、220B这几个规模,基本都是刚好占满某种规格的算力,要么是训练要么是推理。如果需要加快训练速度,只需要倍增卡数即可。
# 1.4 大模型推理过程可视化
一个大模型推理过程可视化的开源项目。左侧是模型结构总览图,包括模型的整体架构以及构成模型的组件。选择模型整体或某个组件时,右侧可通过鼠标进行交互,并且显示对应详细信息。
- 项目地址:https://github.com/bbycroft/llm-viz (opens new window)(在线体验地址:https://bbycroft.net/llm (opens new window))
$ yarn
$ yarn dev
2
# 2. 需求分析及技术选型
# 2.1 需求分析
# 2.1.1 并行推理加速
支持目前主流的开源大模型(如ChatGLM、Baichuan、Qwen、LLaMA),支持流式输出。对推理的并行处理做了优化,能够在较快响应速度的前提下支持较多的并发量。
# 2.1.2 CPU-Offload
显存溢出时让它使用内存,这样只是推理变慢而不是爆显存挂掉。CPU Offload技术可将对象卸载到外部资源或将其分布在多种设备上以减少主设备的内存负载,常用于在GPU显存有限的设备上优化内存使用。比如:将GPU显存中的权重卸载到CPU内存、NVMe/Disk。
# 2.2 技术选型
调研了很多开源项目,发现还是LLaMA-Factory最合适。它是一个主要用于大模型微调训练的项目,里面自带推理服务,在2024.03.07的版本在推理服务里支持了vLLM技术,可用来部署高效的推理服务。

CPU Offload的特性,在vLLM的issues里已有人实现,并已经其合并到了主分支,推理时带上 --cpu-offload-gb 传参即可。
# 3. 部署高效的推理服务
实验环境:实体GPU服务器,NVIDIA A800 / 80GB,Debian 12,Docker version 24.0.7,Anaconda3-2019.03,CUDA 12.6
如果没有GPU服务器,可以租用AutoDL等平台的。服务器的租用及基础环节的安装这里就不赘述了,详见我的另一篇博客:常用深度学习平台的使用指南 (opens new window)
# 3.1 LLaMA-Factory部署推理服务
# 3.1.1 使用Conda方式部署
Step1:拉取项目并安装依赖
拉取项目并安装依赖,并准备模型文件(这里以 Qwen/Qwen2.5-14B-Instruct (opens new window) 为例)
$ conda create -n vllm python=3.10
$ conda activate vllm
$ git clone https://github.com/hiyouga/LLaMA-Factory.git
$ cd LLaMA-Factory
$ pip3 install -r requirements.txt
$ pip3 install vllm==0.5.0
2
3
4
5
6
注:vllm版本建议按照官方建议的来,在官方建议还在0.5.0时,我尝试将vllm升级到0.6.3,在不修改代码的情况下运行遇到了报错。
Step2:启动支持vLLM的推理服务
启动支持vLLM的推理服务:
$ CUDA_VISIBLE_DEVICES=0 API_PORT=8000 python3 src/api.py \
--model_name_or_path /root/llm_models/Qwen/Qwen2-0_5B/ \
--template qwen \
--infer_backend vllm \
--vllm_gpu_util 0.9 \
--vllm_maxlen 32768 \
--max_new_tokens 4096 \
--vllm_enforce_eager True \
--infer_dtype float16
2
3
4
5
6
7
8
9
参数含义解释:
--template qwen
:指定模板类型,用于在推理时对输入输出进行特定格式化。--infer_backend vllm
:设置推理引擎的后端,此处使用vllm
作为推理引擎。--vllm_gpu_util 0.9
:指定vllm推理引擎的GPU利用率,范围为0到1,0.9表示允许GPU利用率为90%。--vllm_maxlen 32768
:指定vllm引擎的最大上下文长度,这里设为模型推理时可以支持最大32768个token。--max_new_tokens 4096
:控制生成输出的最大新tokens数量,这里设置生成内容的最大token数上限为4096。--vllm_enforce_eager True
:设置vllm推理为同步推理模式,这样可以减少等待时间,提高响应速度。--infer_dtype float16
:指定推理模型时使用的精度,这里采用的是全精度。
注:vllm_gpu_util 参数用于控制显存占用比例,默认值为0.9,详见 /LLaMA-Factory/src/llmtuner/hparams/model_args.py
from dataclasses import dataclass, field, fields
from typing import Any, Dict, Literal, Optional, Union
import torch
from typing_extensions import Self
@dataclass
class QuantizationArguments:
r"""
Arguments pertaining to the quantization method.
"""
quantization_method: Literal["bitsandbytes", "hqq", "eetq"] = field(
default="bitsandbytes",
metadata={"help": "Quantization method to use for on-the-fly quantization."},
)
quantization_bit: Optional[int] = field(
default=None,
metadata={"help": "The number of bits to quantize the model using on-the-fly quantization."},
)
quantization_type: Literal["fp4", "nf4"] = field(
default="nf4",
metadata={"help": "Quantization data type to use in bitsandbytes int4 training."},
)
double_quantization: bool = field(
default=True,
metadata={"help": "Whether or not to use double quantization in bitsandbytes int4 training."},
)
quantization_device_map: Optional[Literal["auto"]] = field(
default=None,
metadata={"help": "Device map used to infer the 4-bit quantized model, needs bitsandbytes>=0.43.0."},
)
@dataclass
class ProcessorArguments:
r"""
Arguments pertaining to the image processor.
"""
image_resolution: int = field(
default=512,
metadata={"help": "Keeps the height or width of image below this resolution."},
)
video_resolution: int = field(
default=128,
metadata={"help": "Keeps the height or width of video below this resolution."},
)
video_fps: float = field(
default=2.0,
metadata={"help": "The frames to sample per second for video inputs."},
)
video_maxlen: int = field(
default=64,
metadata={"help": "The maximum number of sampled frames for video inputs."},
)
@dataclass
class ExportArguments:
r"""
Arguments pertaining to the model export.
"""
export_dir: Optional[str] = field(
default=None,
metadata={"help": "Path to the directory to save the exported model."},
)
export_size: int = field(
default=1,
metadata={"help": "The file shard size (in GB) of the exported model."},
)
export_device: Literal["cpu", "auto"] = field(
default="cpu",
metadata={"help": "The device used in model export, use `auto` to accelerate exporting."},
)
export_quantization_bit: Optional[int] = field(
default=None,
metadata={"help": "The number of bits to quantize the exported model."},
)
export_quantization_dataset: Optional[str] = field(
default=None,
metadata={"help": "Path to the dataset or dataset name to use in quantizing the exported model."},
)
export_quantization_nsamples: int = field(
default=128,
metadata={"help": "The number of samples used for quantization."},
)
export_quantization_maxlen: int = field(
default=1024,
metadata={"help": "The maximum length of the model inputs used for quantization."},
)
export_legacy_format: bool = field(
default=False,
metadata={"help": "Whether or not to save the `.bin` files instead of `.safetensors`."},
)
export_hub_model_id: Optional[str] = field(
default=None,
metadata={"help": "The name of the repository if push the model to the Hugging Face hub."},
)
@dataclass
class VllmArguments:
r"""
Arguments pertaining to the vLLM worker.
"""
vllm_maxlen: int = field(
default=2048,
metadata={"help": "Maximum sequence (prompt + response) length of the vLLM engine."},
)
vllm_gpu_util: float = field(
default=0.9,
metadata={"help": "The fraction of GPU memory in (0,1) to be used for the vLLM engine."},
)
vllm_enforce_eager: bool = field(
default=False,
metadata={"help": "Whether or not to disable CUDA graph in the vLLM engine."},
)
vllm_max_lora_rank: int = field(
default=32,
metadata={"help": "Maximum rank of all LoRAs in the vLLM engine."},
)
@dataclass
class ModelArguments(QuantizationArguments, ProcessorArguments, ExportArguments, VllmArguments):
r"""
Arguments pertaining to which model/config/tokenizer we are going to fine-tune or infer.
"""
model_name_or_path: Optional[str] = field(
default=None,
metadata={
"help": "Path to the model weight or identifier from huggingface.co/models or modelscope.cn/models."
},
)
adapter_name_or_path: Optional[str] = field(
default=None,
metadata={
"help": (
"Path to the adapter weight or identifier from huggingface.co/models. "
"Use commas to separate multiple adapters."
)
},
)
adapter_folder: Optional[str] = field(
default=None,
metadata={"help": "The folder containing the adapter weights to load."},
)
cache_dir: Optional[str] = field(
default=None,
metadata={"help": "Where to store the pre-trained models downloaded from huggingface.co or modelscope.cn."},
)
use_fast_tokenizer: bool = field(
default=True,
metadata={"help": "Whether or not to use one of the fast tokenizer (backed by the tokenizers library)."},
)
resize_vocab: bool = field(
default=False,
metadata={"help": "Whether or not to resize the tokenizer vocab and the embedding layers."},
)
split_special_tokens: bool = field(
default=False,
metadata={"help": "Whether or not the special tokens should be split during the tokenization process."},
)
new_special_tokens: Optional[str] = field(
default=None,
metadata={"help": "Special tokens to be added into the tokenizer. Use commas to separate multiple tokens."},
)
model_revision: str = field(
default="main",
metadata={"help": "The specific model version to use (can be a branch name, tag name or commit id)."},
)
low_cpu_mem_usage: bool = field(
default=True,
metadata={"help": "Whether or not to use memory-efficient model loading."},
)
rope_scaling: Optional[Literal["linear", "dynamic"]] = field(
default=None,
metadata={"help": "Which scaling strategy should be adopted for the RoPE embeddings."},
)
flash_attn: Literal["auto", "disabled", "sdpa", "fa2"] = field(
default="auto",
metadata={"help": "Enable FlashAttention for faster training and inference."},
)
shift_attn: bool = field(
default=False,
metadata={"help": "Enable shift short attention (S^2-Attn) proposed by LongLoRA."},
)
mixture_of_depths: Optional[Literal["convert", "load"]] = field(
default=None,
metadata={"help": "Convert the model to mixture-of-depths (MoD) or load the MoD model."},
)
use_unsloth: bool = field(
default=False,
metadata={"help": "Whether or not to use unsloth's optimization for the LoRA training."},
)
use_unsloth_gc: bool = field(
default=False,
metadata={"help": "Whether or not to use unsloth's gradient checkpointing."},
)
enable_liger_kernel: bool = field(
default=False,
metadata={"help": "Whether or not to enable liger kernel for faster training."},
)
moe_aux_loss_coef: Optional[float] = field(
default=None,
metadata={"help": "Coefficient of the auxiliary router loss in mixture-of-experts model."},
)
disable_gradient_checkpointing: bool = field(
default=False,
metadata={"help": "Whether or not to disable gradient checkpointing."},
)
upcast_layernorm: bool = field(
default=False,
metadata={"help": "Whether or not to upcast the layernorm weights in fp32."},
)
upcast_lmhead_output: bool = field(
default=False,
metadata={"help": "Whether or not to upcast the output of lm_head in fp32."},
)
train_from_scratch: bool = field(
default=False,
metadata={"help": "Whether or not to randomly initialize the model weights."},
)
infer_backend: Literal["huggingface", "vllm"] = field(
default="huggingface",
metadata={"help": "Backend engine used at inference."},
)
offload_folder: str = field(
default="offload",
metadata={"help": "Path to offload model weights."},
)
use_cache: bool = field(
default=True,
metadata={"help": "Whether or not to use KV cache in generation."},
)
infer_dtype: Literal["auto", "float16", "bfloat16", "float32"] = field(
default="auto",
metadata={"help": "Data type for model weights and activations at inference."},
)
hf_hub_token: Optional[str] = field(
default=None,
metadata={"help": "Auth token to log in with Hugging Face Hub."},
)
ms_hub_token: Optional[str] = field(
default=None,
metadata={"help": "Auth token to log in with ModelScope Hub."},
)
om_hub_token: Optional[str] = field(
default=None,
metadata={"help": "Auth token to log in with Modelers Hub."},
)
print_param_status: bool = field(
default=False,
metadata={"help": "For debugging purposes, print the status of the parameters in the model."},
)
compute_dtype: Optional[torch.dtype] = field(
default=None,
init=False,
metadata={"help": "Torch data type for computing model outputs, derived from `fp/bf16`. Do not specify it."},
)
device_map: Optional[Union[str, Dict[str, Any]]] = field(
default=None,
init=False,
metadata={"help": "Device map for model placement, derived from training stage. Do not specify it."},
)
model_max_length: Optional[int] = field(
default=None,
init=False,
metadata={"help": "The maximum input length for model, derived from `cutoff_len`. Do not specify it."},
)
block_diag_attn: bool = field(
default=False,
init=False,
metadata={"help": "Whether use block diag attention or not, derived from `neat_packing`. Do not specify it."},
)
def __post_init__(self):
if self.model_name_or_path is None:
raise ValueError("Please provide `model_name_or_path`.")
if self.split_special_tokens and self.use_fast_tokenizer:
raise ValueError("`split_special_tokens` is only supported for slow tokenizers.")
if self.adapter_name_or_path is not None: # support merging multiple lora weights
self.adapter_name_or_path = [path.strip() for path in self.adapter_name_or_path.split(",")]
if self.new_special_tokens is not None: # support multiple special tokens
self.new_special_tokens = [token.strip() for token in self.new_special_tokens.split(",")]
if self.export_quantization_bit is not None and self.export_quantization_dataset is None:
raise ValueError("Quantization dataset is necessary for exporting.")
@classmethod
def copyfrom(cls, source: "Self", **kwargs) -> "Self":
init_args, lazy_args = {}, {}
for attr in fields(source):
if attr.init:
init_args[attr.name] = getattr(source, attr.name)
else:
lazy_args[attr.name] = getattr(source, attr.name)
init_args.update(kwargs)
result = cls(**init_args)
for name, value in lazy_args.items():
setattr(result, name, value)
return result
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
不同vllm_gpu_util参数设置的显存占用对比:
# 3.1.2 使用Docker方式部署
Step1:准备代码并修改Dockerfile
$ git clone https://github.com/hiyouga/LLaMA-Factory.git
$ cd LLaMA-Factory
$ cp ./docker/docker-cuda/Dockerfile .
$ vim Dockerfile
2
3
4
这里我只需要部署推理服务,因此对这个原始的Dockerfile做了一些改动,并删去了一些不需要的步骤。
- 需要安装vLLM,所以需要把Dockerfile里的INSTALL_VLLM设置成true。
- 为了加速下载,可以把PIP_INDEX设置成
https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple
。
# Default use the NVIDIA official image with PyTorch 2.3.0
# https://docs.nvidia.com/deeplearning/frameworks/pytorch-release-notes/index.html
ARG BASE_IMAGE=nvcr.io/nvidia/pytorch:24.02-py3
FROM ${BASE_IMAGE}
# Define environments
ENV MAX_JOBS=4
ENV FLASH_ATTENTION_FORCE_BUILD=False
ENV VLLM_WORKER_MULTIPROC_METHOD=spawn
# Define installation arguments
ARG INSTALL_BNB=false
ARG INSTALL_VLLM=true
ARG INSTALL_DEEPSPEED=false
ARG INSTALL_FLASHATTN=false
ARG INSTALL_LIGER_KERNEL=false
ARG INSTALL_HQQ=false
ARG INSTALL_EETQ=false
ARG PIP_INDEX=https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple
# Set the working directory
WORKDIR /app
# Install the requirements
COPY requirements.txt /app
RUN pip config set global.index-url "$PIP_INDEX" && \
pip config set global.extra-index-url "$PIP_INDEX" && \
python -m pip install --upgrade pip && \
python -m pip install -r requirements.txt
# Copy the rest of the application into the image
COPY . /app
# Install the LLaMA Factory
RUN EXTRA_PACKAGES="metrics"; \
if [ "$INSTALL_BNB" == "true" ]; then \
EXTRA_PACKAGES="${EXTRA_PACKAGES},bitsandbytes"; \
fi; \
if [ "$INSTALL_VLLM" == "true" ]; then \
EXTRA_PACKAGES="${EXTRA_PACKAGES},vllm"; \
fi; \
if [ "$INSTALL_DEEPSPEED" == "true" ]; then \
EXTRA_PACKAGES="${EXTRA_PACKAGES},deepspeed"; \
fi; \
if [ "$INSTALL_LIGER_KERNEL" == "true" ]; then \
EXTRA_PACKAGES="${EXTRA_PACKAGES},liger-kernel"; \
fi; \
if [ "$INSTALL_HQQ" == "true" ]; then \
EXTRA_PACKAGES="${EXTRA_PACKAGES},hqq"; \
fi; \
if [ "$INSTALL_EETQ" == "true" ]; then \
EXTRA_PACKAGES="${EXTRA_PACKAGES},eetq"; \
fi; \
pip install -e ".[$EXTRA_PACKAGES]"
# Rebuild flash attention
RUN pip uninstall -y transformer-engine flash-attn && \
if [ "$INSTALL_FLASHATTN" == "true" ]; then \
pip uninstall -y ninja && pip install ninja && \
pip install --no-cache-dir flash-attn --no-build-isolation; \
fi
# Rebuild vllm
RUN pip uninstall -y vllm
RUN pip install vllm==0.6.2
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
Step2:构建镜像并运行容器
$ docker build -t llamafactory_image .
$ docker run -itd \
--gpus=all \
--shm-size 32G \
-p 8000:8000 \
-v /home/DeepSeek-R1-Distill-Qwen-32B/:/app/models/model \
-e CUDA_VISIBLE_DEVICES=1,2 \
-e API_PORT=8000 \
-e NCCL_P2P_DISABLE=1 \
-e NCCL_IB_DISABLE=1 \
--name deepseek_qwen32b \
llamafactory_image:latest \
python src/api.py \
--model_name_or_path /app/models/model \
--template qwen \
--infer_backend vllm \
--vllm_enforce_eager True \
--vllm_gpu_util 0.95 \
--vllm_maxlen 16384
$ docker logs -f deepseek_qwen32b
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
参数的含义解释:
docker run -itd
--gpus=all # 容器可见的显卡
--shm-size 32G # 容器共享内存设置
-p 8000:8000 # 把服务器端口和容器内端口映射,这里是服务器上的8000端口和容器内8000对应
-v /home/DeepSeek-R1-Distill-Qwen-32B/:/app/models/model # 把服务器上的模型挂载到容器里
-e CUDA_VISIBLE_DEVICES=1,2 # 使用的显卡
-e API_PORT=8000 # 容器内API端口设置
-e NCCL_P2P_DISABLE=1 # 有些显卡需要这么设置避免报错
-e NCCL_IB_DISABLE=1 # 有些显卡需要这么设置避免报错
--name deepseek_qwen32b # 容器名
llamafactory_image:latest
python src/api.py
--model_name_or_path /app/models/model \ # 容器内挂载的模型,需要把模型挂载到这里
--template qwen \ # chat模型的对话模板,需根据模型类型不同更改
--infer_backend vllm \ # 推理后端,选vllm或是huggingface
--vllm_enforce_eager True \ # vllm的一个选项,可以省显存
--vllm_gpu_util 0.95 \ # vllm显存占用设置
--vllm_maxlen 16384 # 模型输入加输出最大长度
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 3.2 vLLM和Ray实现分布式推理
2台8*A800服务器,之间网络互通,文件系统通过NFS共享。准备每台机器4卡,共8卡来部署Qwen2.5-72B-Instruct模型做实验。
# 3.2.1 拉取vLLM的镜像
两台机器拉取vLLM最新的Docker镜像,此时最新的vLLM是v0.7.2。
$ docker pull vllm/vllm-openai // 官方源
$ docker pull docker.1ms.run/vllm/vllm-openai // 国内受限的话,可以用这个镜像源
2
# 3.2.2 编写集群启动脚本
分别给两个机器编写集群启动脚本,CUDA_VISIBLE_DEVICES按照当前机器的空余显卡来写。
run_cluster.sh
#!/bin/bash
if [ $# -lt 2 ]; then
echo "Usage: $0 head_node_address --head|--worker [additional_args...]"
exit 1
fi
HEAD_NODE_ADDRESS="$1"
NODE_TYPE="$2"
shift 2
ADDITIONAL_ARGS=("$@")
if [ "${NODE_TYPE}" != "--head" ] && [ "${NODE_TYPE}" != "--worker" ]; then
echo "Error: Node type must be --head or --worker"
exit 1
fi
cleanup() {
if [ -e /var/lib/docker/containers/vllm ]; then
docker stop vllm
docker rm vllm
fi
}
trap cleanup EXIT
RAY_START_CMD="ray start --block"
if [ "${NODE_TYPE}" == "--head" ]; then
RAY_START_CMD+=" --head --port=6379"
else
RAY_START_CMD+=" --address=${HEAD_NODE_ADDRESS}:6379"
fi
docker run -itd \
--privileged \
--entrypoint /bin/bash \
--network host \
--name vllm \
--shm-size 10000000g \
--gpus all \
-v /root/Qwen2.5-72B-Instruct/:/mnt \
"${ADDITIONAL_ARGS[@]}" \
-e CUDA_VISIBLE_DEVICES=0,1,2,3 \
docker.1ms.run/vllm/vllm-openai:latest \
-c "${RAY_START_CMD}"
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
关键参数解释:
-v /root/Qwen2.5-72B-Instruct/:/mnt # 将Qwen2.5-72B-Instruct挂载到/mnt目录
-e CUDA_VISIBLE_DEVICES=0,1,2,3 # 指定显卡
2
# 3.2.3 启动Ray集群节点
随便选一台服务器作为head节点,另一台作为worker节点。
head节点容器启动
$ bash run_cluster_head.sh 111.111.111.111 --head
worker节点容器启动
$ bash run_cluster_worker.sh 111.111.111.111 --worker
注:这里的111.111.111.111写你的head节点真实IP地址。
从任意服务器进入容器内,检查是否连接上了Ray集群。
$ docker exec -it vllm /bin/bash
$ ray status
2
发现两个节点都在,说明没问题。

# 3.2.4 vLLM启动推理服务
在任意服务器的vllm容器内执行以下命令:
$ vllm serve /mnt --tensor-parallel-size 8 --trust-remote-code --gpu-memory-utilization 0.8 --max-model-len 32768 --enforce-eager --served-model-name qwen72B
启动参数的解释:
--tensor-parallel-size 8 # 和卡的数量对应
--trust-remote-code # 是否信任huggingface上的代码
--gpu-memory-utilization 0.8 # 设置每张卡显存占用率
--max-model-len 32768 # 模型上下文长度(包含prompt+response)
--enforce-eager # 禁用CUDA图,节约显存。
--served-model-name # 指定openai格式API中需要填写的model字段
2
3
4
5
6
发现模型在加载了,说明没有问题。
下面这样就说明部署好了:
注:需要注意的是,虽然这种方式部署的也是OpenAI格式的接口,但它是严格校验model传参的。
# 3.3 对部署的推理服务进行测试
# 3.3.1 查看接口文档
使用Chrome浏览器打开此地址:http://<your_server_ip>:8000/docs
,可以访问到接口文档。
# 3.3.2 测试流式输出
在接口文档那里,可以看到 curl 命令,把 stream 改成 true 即可变成流式输出。
$ curl --location 'http://<your_server_ip>:8000/v1/chat/completions' \
--header 'Content-Type: application/json' \
--data '{
"model": "Qwen2.5-14B-Instruct",
"messages": [
{
"role": "user",
"content": "解释一下量子计算"
}
],
"temperature": 0.1,
"stream": true
}'
2
3
4
5
6
7
8
9
10
11
12
13
# 3.4 OpenAI风格的大模型推理服务
# 3.4.1 大模型服务参数含义
OpenAI格式的大模型推理服务已经成为了业内规范,常见的开源大模型和商业大模型几乎都支持以这种格式进行部署。
支持的完整参数可参考:https://platform.openai.com/docs/api-reference/making-requests (opens new window)
[1] temperature
该参数用于控制生成文本的随机性和多样性,其实是调整了模型输出的概率分布。
- temperature 较高时,会更平均地分配概率给各个 token,这导致生成的文本更具随机性和多样性。
- temperatur 较低接近 0 时,会倾向于选择概率最高的 token,从而使生成的文本更加确定和集中。
- temperature=1 时表示不使用此方式。
[2] top_k
用于在生成下一个 token 时,限制模型只能考虑前 k 个概率最高的 token,这个策略可以降低模型生成无意义或重复的输出的概率,同时提高模型的生成速度和效率。
[3] top_p
通常用一个介于 0 到 1 之间的值来表示生成下一个 token 时,在概率分布中选择的最高概率的累积阈值。
- top_p 较高时比如 0.9,这意味着前 90% 的概率的 token 会被考虑在抽样中,这样会允许更多的 token 参与抽样,增加生成文本的多样性。
- top_p 较低时比如 0.1,这意味着只有前 10% 最高概率的 token 会被考虑在抽样中,这样会限制生成文本的可能性,使生成的文本更加确定和集中。
- top_p=1 时表示不使用此方式。
[4] repetition_penalty
目标是在这个概率分布中对先前生成过的 token,又重复的生成了该 token 进行惩罚(降低概率),以减少生成文本中的重复性。
[5] no_repeat_ngram_size
当设为大于 0 的整数时,生成的文本中不会出现指定大小的重复 n-gram(n个连续的token),可以使生成的文本更加多样化,避免出现重复的短语或句子结构。
[6] do_sample
对模型计算出来的概率要不要进行多项式采样。
多项式采样是一种用于从一个具有多个可能结果的离散概率分布中进行随机抽样的方法。在多项式采样中,概率高的结果更有可能被选中,但不同于确定性的选择,每个结果(概率较低但仍然可能的词)仍然有一定概率被选中。
[7] num_beams
用于束搜索(beam search)算法,用途是控制生成的多个候选句子的数量,该参数控制的是每个生成步要保留的生成结果的数量。
[8] num_beam_groups
beam search 算法的改进,叫做 Diverse Beam Search (DBS)。核心是分组机制,如果设置 num_beams=2,num_beam_groups=2,那就是说分成2个组,每个组里的 beam 可以相似,但组和组之间要有足够的多样性。
[9] diversity_penalty
多样性惩罚参数只有在启用了“num_beam_groups”(组束搜索)时才有效,在这些组之间应用多样性惩罚,以确保每个组生成的内容尽可能不同。
[10] length_penalty
长度惩罚参数也是用于束搜索过程中,作用是将生成序列的长度应用于得分的分母,从而影响候选序列的得分。
- length_penalty > 1.0 时,较长的序列得到更大的惩罚,鼓励生成较短的序列。
- length_penalty < 1.0 时,较短的序列得到更大的惩罚,鼓励生成较长的序列。
- 默认为1,不受惩罚。
[11] use_cache
设置为True时,则模型会利用之前计算得到的注意力权重的缓存,当下一个token需要被生成时,模型可以通过缓存的注意力权重来重用之前计算的信息,而不需要重新计算一次,有效地跳过重复计算的步骤,从而减少计算负担,提高生成速度和效率。
[12] max_length
生成的 token 的最大长度 = 输入 prompt 的长度 + max_new_tokens。如果同时设置了 max_new_tokens,则会覆盖此参数。
[13] max_new_tokens
生成的最大 token 的数量,不考虑输入 prompt 中的 token 数。
[14] min_length
生成的 token 的最小长度 = 输入 prompt 的长度 + min_new_tokens。如果同时设置了 min_new_tokens,则会覆盖此参数。
[15] min_new_tokens
生成的最小 token 的数量,不考虑输入 prompt 中的 token 数。
[16] bad_words_ids
包含词汇 id 的列表,用于指定不允许在生成文本中出现的词汇,如果生成的文本包含任何在这个列表中的词汇,它们将被被替换或排除在最终生成的文本之外。
[17] force_words_ids
包含词汇 id 的列表,用于指定必须包含在生成文本中的词汇,如果给定一个列表,生成的文本将包含这些词汇。
[18] constraints
自定义约束条件,可以指定约束条件,这些约束条件可以是必须出现的关键词、短语、特定术语或其他文本元素,和 force_words_ids 差不多,在代码实现也是一样的。
# 3.4.2 普通输出与流式输出
请求地址:https://api.openai.com/v1/chat/completions (opens new window)
请求方法:POST
[1] 非流式输出
$ curl --location 'https://api.openai.com/v1/chat/completions' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer sk-xxx' \
--data '{
"model": "gpt-4o",
"messages": [
{
"role": "user",
"content": "你是谁"
}
],
"temperature": 0.2,
"stream": false
}'
>>> 返回结果:
{
"id": "chatcmpl-ANEuxC362ZhkQIzOgrvfsAkJo8bMd",
"object": "chat.completion",
"created": 1730102251,
"model": "gpt-4o-2024-08-06",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "我是你的AI助手,可以帮助回答问题、提供信息或协助解决问题。有需要请随时告诉我!",
"refusal": null
},
"logprobs": null,
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 9,
"completion_tokens": 24,
"total_tokens": 33,
"prompt_tokens_details": {
"cached_tokens": 0
},
"completion_tokens_details": {
"reasoning_tokens": 0
}
},
"system_fingerprint": "fp_d54531d9eb"
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
[2] 流式输出
$ curl --location 'https://api.openai.com/v1/chat/completions' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer sk-xxx' \
--data '{
"model": "gpt-4o",
"messages": [
{
"role": "user",
"content": "你是谁"
}
],
"temperature": 0.2,
"stream": true
}'
>>> 返回结果:
data: {"id":"chatcmpl-ANEwAKU935M3a9rRgmPRtUzu0BmMG","object":"chat.completion.chunk","created":1730102326,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_d54531d9eb","choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}]}
data: {"id":"chatcmpl-ANEwAKU935M3a9rRgmPRtUzu0BmMG","object":"chat.completion.chunk","created":1730102326,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_d54531d9eb","choices":[{"index":0,"delta":{"content":"你好"},"logprobs":null,"finish_reason":null}]}
...[此处省略中间内容]
data: {"id":"chatcmpl-ANEwAKU935M3a9rRgmPRtUzu0BmMG","object":"chat.completion.chunk","created":1730102326,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_d54531d9eb","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]}
data: [DONE]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
流式输出对接的注意事项:
[1] 末尾还有个额外的[DONE],注意兼容。这两个字段同时出现并不冲突,作用都是表明请求已成功完成且没有更多的数据要传输。
- finish_reason:"stop":这个字段表示生成任务已经结束,模型认为它已经完整地回答了请求。这种情况下,模型不需要继续生成更多内容,所以返回了"stop"。
- data: [DONE]:这是一个流式接口的常见标记,用来表明数据流已经完成。这意味着服务器已经传输了所有的响应数据,并且不会再有更多的数据发送过来。
[2] 流式输出有时会包含ping心跳包,通常用于保持连接活跃及检测网络连接状态,前端对接接口时要注意兼容,不要出现截断情况。
# 3.4.3 多轮会话的格式示例
多轮对话的格式示例:
{
"model": "gpt-4o",
"messages": [
{
"role": "system",
"content": "我将为您提供一个在职业生涯中寻求指导的人,您的任务是帮助他们根据自己的技能、兴趣和经验确定最适合的职业。您还应该对可用的各种选项进行研究,解释不同行业的就业市场趋势,并就哪些资格对追求特定领域有益提出建议。"
},
{
"role": "user",
"content": "我目前是一名软件开发人员,我对数据分析和人工智能感兴趣,想转向这个方向。您能给我一些建议吗?"
},
{
"role": "assistant",
"content": "根据您的背景,转向数据分析和人工智能是一个很自然的选择。以下是一些建议和方向,您可以考虑:\n\n1. **数据科学家/数据分析师**:结合您现有的编程技能,您可以进一步学习数据分析工具和技术(如SQL、Pandas、NumPy)。同时,学习如何使用统计学和数据可视化工具(如Tableau、PowerBI)也非常有帮助。市场上对数据分析师的需求在不断增长,尤其是在金融、科技和健康等行业。\n\n2. **机器学习工程师**:如果您对人工智能尤其感兴趣,机器学习工程师是一个有前景的职业。您可以开始学习常用的机器学习框架,如TensorFlow和PyTorch,进一步掌握机器学习算法(如回归、分类、聚类等)。\n\n3. **行业趋势**:目前,数据科学和人工智能在多个行业都具有良好的就业前景,特别是在科技、金融、医疗和电商领域。根据不同国家的就业市场,这些职位的薪资和需求都有增长趋势,特别是对拥有编程和数据处理能力的人才需求旺盛。\n\n4. **资格建议**:为了增强您的竞争力,建议您获得相关的认证或文凭,例如数据科学、机器学习或人工智能领域的课程认证(Coursera、edX 等平台提供很多相关课程)。您还可以考虑参加 Kaggle 等平台上的数据竞赛,提升实战经验。"
},
{
"role": "user",
"content": "那关于上一个的第二个方向,是否需要精通数学和统计学?"
},
{
"role": "assistant",
"content": "是的,机器学习工程师的职位需要掌握一定的数学和统计学知识。特别是线性代数、概率论和微积分是核心基础。虽然上手可以依赖框架,但理解这些理论会帮助您更好地优化模型和调试算法。因此,建议至少掌握这些领域的基本概念,再通过实际项目逐步深化。"
}
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
角色解释:
- system:这是用于设定对话规则或提供对话指导的角色。它通常定义了对话的初始状态和期望的行为,比如要求助手如何处理用户的输入。
- user:这个角色表示用户的输入,通常是提问、查询或某种交互请求。用户可以发送各种消息,比如问题、命令或需求。
- assistant:这是对话中的助手角色,负责根据
system
定义的规则和user
的输入,提供相关的回应或解答。
# 3.5 大模型服务重复输出问题
# 3.5.1 问题情形的描述
由于大模型是生成式的模型,有的时候会像复读机一样重复生成内容,可以分为三个级别:
- 词汇和短语重复:模型生成的文本中会出现同一个词或短语反复出现,比如同一句话在一段文字中被多次重复。
- 段落级别重复:在生成较长文本时,模型可能生成重复的句子或段落,甚至生成与前面几乎相同的内容段。
- 内容结构重复:在有结构化回答需求的情况下,模型可能多次以相似的结构回答或解释,导致输出显得冗余。
# 3.5.2 问题的产生原因
当输入的条件文本很长,生成的文本又很少时可能出现不断重复的现象。很多大模型是自回归模型,通过前T-1个Token作为条件,来预测第T个Token的是哪个,当前面的条件文本过长时,大模型的输出的几个短文本会被原始的很长的条件文本淹没,继续预测下一个Token的话,在模型看起来可能条件仍然是差不多的,新生成的内容被忽视了,于是就重复生成内容了。
外在的影响因素可以归结为下面内容:
[1] 输入提示设计问题
- 模糊或不明确的提示:当提示不够清晰时,模型可能会生成冗长的、相似的内容,造成重复。
- 缺乏上下文:如果模型在生成响应时缺乏足够的上下文,它可能无法理解你想要的信息,因此可能会重复之前提到的内容。
[2] 模型设置
- 温度系数参数过低:模型在低温度设置下(如温度接近 0)会生成保守且确定性的输出,可能导致重复生成相似的内容。
- Top-k 或 Top-p 采样:如果这些采样策略没有设置合理,可能会导致模型只选择高概率的词汇,造成重复。
[3] 数据问题
- 训练数据的局限性:模型是基于大量文本进行训练的,如果数据集中存在重复信息,模型可能会在生成响应时重复这些信息。
- 上下文的重复:在交互中,如果多次提及相同的主题或问题,模型可能会基于之前的回答进行重复。
# 3.5.3 缓解问题的方案
[1] Prompt提示词优化
Prompt提示词的设计对于输出的质量至关重要。为了避免重复,可以在提示词中明确要求输出不同的信息或新的观点。
- 指定不同的角度:在提示中要求模型从不同的视角或方面进行回答。
- 避免泛泛的问题:提供更具体的问题能够帮助模型生成不重复的信息。
[2] 调整模型参数
如果有权访问模型的参数,可以通过调整参数来减少重复现象。
- 提高温度系数:适度提高温度(如 0.7-0.9)可增加输出的随机性,鼓励模型生成更多不同的内容。温度的设置影响到概率分布的平滑程度。低温度值(如接近0)会使得生成的文本更具确定性,更倾向于选择概率最高的单词。高温度值(如接近1)则会增加随机性,使模型更有可能选择概率较低的单词,从而产生更多样化和创造性的结果。
- 使用 Top-p 采样:调整 Top-p 采样可以让模型在生成时从更多不同的词汇中进行选择,而不仅仅选择高概率的词。Top-p 参数通过设置概率阈值p来限制模型的选择范围,而不是只选取最高概率的词。比如,设置p=0.9,意味着模型会从概率累计达到90%的词中随机选择下一个词,而不会只局限于最可能的那几个选项。一般来说,p 值在0.85到0.95之间效果较佳。p=0.9通常是一个比较平衡的设置,能兼顾多样性和连贯性。若设置较低的 p 值(如 0.8),生成内容会更加丰富,但可能也会出现不相关或略微随机的词汇选择。较高的 p 值(接近1.0)则趋向于常规的高概率选择,生成的文本更安全和精确,但重复性可能也略高。
- 适当调整输出长度:避免设置过短的最大输出长度,确保模型有足够的空间生成完整且不重复的回答。
- 使用repetition_penalty惩罚模型重复输出:repetition_penalty 参数的工作原理是,在生成新内容时惩罚模型重复使用之前生成过的词汇和短语。这会促使模型在生成时更加倾向使用新的词汇或表达方式。通常repetition_penalty的值可以在1.01到2.0之间选择,值越高,惩罚越重。一般来说,像1.05这样略微的惩罚值有助于减少简单重复,同时保持生成的流畅度和逻辑性。对于更强的惩罚,可以尝试更高的值(例如1.2),但可能会影响文本的连贯性。
[3] 分段提问
将问题分解为更小的子问题,逐步获取更多细节,避免模型在单一长回答中重复。
不佳的提示:
请告诉我所有关于气候变化的信息。
优化后的提问:
1. 请简述气候变化的定义及主要原因。
2. 气候变化如何影响海洋生态系统?
3. 气候变化对农业有何影响?
2
3
通过逐步提问,不仅减少了信息重复,还可以获得更结构化的内容。
[4] 控制输出长度与约束
在提示中明确要求回答不要重复,并限制生成的长度。
在提示中指定避免重复:
请描述气候变化的影响,但避免重复之前提到的内容。
限制输出长度:可以在某些情况下限制生成的字数,以避免长篇回答中不必要的重复,但是让大模型控制输出内容长度不一定有效。
在150字以内回答这个问题,避免冗长和重复。
# 4. 评估推理服务的性能
# 4.1 推理服务性能测试概述
# 4.1.1 推理服务性能测试工具
为了测试LLM服务的推理性能,评估是否满足生产需求,可以使用 Eval-Scope 项目中的性能测试工具Perf。
- Eval-Scope中的性能测试工具Perf:https://github.com/modelscope/eval-scope/tree/main/llmuses/perf (opens new window)
llmuses perf --help
usage: llmuses <command> [<args>] perf [-h] --model MODEL [--url URL] [--connect-timeout CONNECT_TIMEOUT] [--read-timeout READ_TIMEOUT] [-n NUMBER] [--parallel PARALLEL] [--rate RATE]
[--log-every-n-query LOG_EVERY_N_QUERY] [--headers KEY1=VALUE1 [KEY1=VALUE1 ...]] [--wandb-api-key WANDB_API_KEY] [--name NAME] [--debug] [--tokenizer-path TOKENIZER_PATH]
[--api API] [--max-prompt-length MAX_PROMPT_LENGTH] [--min-prompt-length MIN_PROMPT_LENGTH] [--prompt PROMPT] [--query-template QUERY_TEMPLATE] [--dataset DATASET]
[--dataset-path DATASET_PATH] [--frequency-penalty FREQUENCY_PENALTY] [--logprobs] [--max-tokens MAX_TOKENS] [--n-choices N_CHOICES] [--seed SEED] [--stop STOP] [--stream]
[--temperature TEMPERATURE] [--top-p TOP_P]
options:
-h, --help show this help message and exit
--model MODEL The test model name.
--url URL
--connect-timeout CONNECT_TIMEOUT
The network connection timeout
--read-timeout READ_TIMEOUT
The network read timeout
-n NUMBER, --number NUMBER
How many requests to be made, if None, will will send request base dataset or prompt.
--parallel PARALLEL Set number of concurrency request, default 1
--rate RATE Number of requests per second. default None, if it set to -1,then all the requests are sent at time 0. Otherwise, we use Poisson process to synthesize the request arrival times. Mutual exclusion
with parallel
--log-every-n-query LOG_EVERY_N_QUERY
Logging every n query.
--headers KEY1=VALUE1 [KEY1=VALUE1 ...]
Extra http headers accepts by key1=value1 key2=value2. The headers will be use for each query.You can use this parameter to specify http authorization and other header.
--wandb-api-key WANDB_API_KEY
The wandb api key, if set the metric will be saved to wandb.
--name NAME The wandb db result name and result db name, default: {model_name}_{current_time}
--debug Debug request send.
--tokenizer-path TOKENIZER_PATH
Specify the tokenizer weight path, used to calculate the number of input and output tokens,usually in the same directory as the model weight.
--api API Specify the service api, current support [openai|dashscope]you can define your custom parser with python, and specify the python file path, reference api_plugin_base.py,
--max-prompt-length MAX_PROMPT_LENGTH
Maximum input prompt length
--min-prompt-length MIN_PROMPT_LENGTH
Minimum input prompt length.
--prompt PROMPT Specified the request prompt, all the query will use this prompt, You can specify local file via @file_path, the prompt will be the file content.
--query-template QUERY_TEMPLATE
Specify the query template, should be a json string, or local file,with local file, specified with @local_file_path,will will replace model and prompt in the template.
--dataset DATASET Specify the dataset [openqa|longalpaca|line_by_line]you can define your custom dataset parser with python, and specify the python file path, reference dataset_plugin_base.py,
--dataset-path DATASET_PATH
Path to the dataset file, Used in conjunction with dataset. If dataset is None, each line defaults to a prompt.
--frequency-penalty FREQUENCY_PENALTY
The frequency_penalty value.
--logprobs The logprobs.
--max-tokens MAX_TOKENS
The maximum number of tokens can be generated.
--n-choices N_CHOICES
How may chmpletion choices to generate.
--seed SEED The random seed.
--stop STOP The stop generating tokens.
--stop-token-ids Set the stop token ids.
--stream Stream output with SSE.
--temperature TEMPERATURE
The sample temperature.
--top-p TOP_P Sampling top p.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# 4.1.2 推理服务关键指标及影响
对于提供公共推理服务,提高吞吐率优先级比较高,而在一些专用的业务场景,则对首包延迟和整体请求延迟有着较高要求。
- Throughput:总吞吐量,可以提高总的服务承载能力。
- Time to First Token(TTFT):返回的第一个Token的时间,在Stream输出模式下,对体验影响较大。
- Time per output token:生成每个Token的时间,影响体验。
- Latency:处理完整请求用时。
- QPS:每秒处理完成的请求数。
# 4.2 准备推理性能测试环境
# 4.2.1 准备测试环境及模型数据
准备测试环境及模型数据,用来对比。这里使用的是老版本 LLaMA-Factory 和 vllm 进行的测试,新版本还会有一定性能提升。
- 测试服务器:采用 NVIDIA RTX 4090 服务器。
- 推理引擎:推理引擎这里只测试“开启vLLM”、“未开启vLLM”的推理性能。
- 测试数据集:准备正常上下文、长上下文两类数据集进行评测。
- 测试大模型:测试Qwen2-0.5B在不同请求长度以及并发下的性能,推理引擎参数使用默认值,未针对性调参,不代表推理引擎最优性能。
# 4.2.2 部署用于测试的推理服务
使用 LLaMA-Factory 在不同显卡上分别部署了“开启vLLM”(vllm版本为0.4.0,vllm_gpu_util设置为0.9)的推理服务和“未开启vLLM”的推理服务。
$ conda activate llama_factory
$ cd LLaMA-Factory
$ CUDA_VISIBLE_DEVICES=0 API_PORT=8000 python3 src/api_demo.py \
--model_name_or_path /root/llm_models/Qwen/Qwen2-0_5B/ \
--template default \
--infer_backend vllm \
--vllm_maxlen 128000 \
--vllm_gpu_util 0.9
$ CUDA_VISIBLE_DEVICES=1 API_PORT=8001 python3 src/api_demo.py \
--model_name_or_path /root/llm_models/Qwen/Qwen2-0_5B/ \
--template default
2
3
4
5
6
7
8
9
10
11
注:对比启动前后的显存占用(还未进行推理请求,正式使用的时候显存占用会更大),“开启vLLM”的推理服务占用了21639MB显存,而“未开启vLLM”的推理服务占用了2147MB显存。
# 4.3 进行推理服务性能测试
# 4.3.1 准备Eval-Scope及数据集
安装 Eval-Scope 评测工具:https://github.com/modelscope/eval-scope (opens new window)
$ pip3 install llmuses
下载评测数据集:
- 正常上下文数据集: https://huggingface.co/datasets/Hello-SimpleAI/HC3-Chinese/blob/main/open_qa.jsonl (opens new window)
- 长上下文数据集:https://huggingface.co/datasets/Yukang/LongAlpaca-12k/blob/main/LongAlpaca-12k.json (opens new window)
# 4.3.2 使用Eval-Scope测试推理性能
对两个服务分别执行类似如下的命令,只有url的端口号有差异:
$ llmuses perf --url 'http://127.0.0.1:8000/v1/chat/completions' --parallel 1 --model 'qwen2-0.5b' --log-every-n-query 10 --read-timeout=120 --dataset-path '/root/data/open_qa.jsonl' -n 100 --max-prompt-length 128000 --api openai --dataset openqa
注意事项:不要开启 --stream 流式输出,开启之后无法将压测数据写入db文件内。
参数含义:以下是参数含义的解释。
--url 'http://127.0.0.1:8000/v1/chat/completions'
:设置了性能测试请求的API。--parallel 1
:性能测试将会顺序发送请求,不会并行发送。--model 'qwen2-0.5b'
:指定了正在测试的模型名称。--log-every-n-query 10
:每10个查询记录一次信息,帮助跟踪测试进度。--read-timeout=120
:为请求设置了120秒的超时时间。--dataset-path '/root/data/open_qa.jsonl'
:用于性能测试的数据集文件的路径。-n 100
:性能测试共发送100个请求。--max-prompt-length 128000
:设置了可以发送到模型的输入提示的最大Token长度为128000。--api openai
:指定了使用 OpenAI 格式的大模型推理服务。--dataset openqa
:指定了要使用的数据集类型为openqa。
运行日志里会打印性能测试的统计信息,输出结果是一个db文件(可以用Navicat工具打开SQLite文件来查看)
# 4.4 Eval-Scope的测试结果
# 4.4.1 评测open_qa正常上下文
[1] 单并发情形
$ llmuses perf --url 'http://127.0.0.1:8000/v1/chat/completions' --parallel 1 --model 'qwen2-0.5b' --log-every-n-query 10 --read-timeout=120 --dataset-path '/root/data/open_qa.jsonl' -n 100 --max-prompt-length 128000 --api openai --dataset openqa
$ llmuses perf --url 'http://127.0.0.1:8001/v1/chat/completions' --parallel 1 --model 'qwen2-0.5b' --log-every-n-query 10 --read-timeout=120 --dataset-path '/root/data/open_qa.jsonl' -n 100 --max-prompt-length 128000 --api openai --dataset openqa
2
3
“开启vllm”、“单并发”大模型服务测试“open_qa正常上下文”的结果:
Benchmarking summary:
Time taken for tests: 92.500 seconds
Expected number of requests: 100
Number of concurrency: 1
Total requests: 100
Succeed requests: 100
Failed requests: 0
Average QPS: 1.081
Average latency: 0.920
Throughput(average output tokens per second): 322.420
Average time to first token: 0.920
Average input tokens per request: 27.740
Average output tokens per request: 298.240
Average time per output token: 0.00310
Average package per request: 1.000
Average package latency: 0.920
Percentile of time to first token:
p50: 0.8986
p66: 1.5066
p75: 1.5469
p80: 1.5564
p90: 1.5937
p95: 1.7404
p98: 1.7507
p99: 1.8698
Percentile of request latency:
p50: 0.8986
p66: 1.5066
p75: 1.5469
p80: 1.5564
p90: 1.5937
p95: 1.7404
p98: 1.7507
p99: 1.8698
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
“未开启vllm”、“单并发”大模型服务测试“open_qa正常上下文”的结果:
Benchmarking summary:
Time taken for tests: 376.490 seconds
Expected number of requests: 100
Number of concurrency: 1
Total requests: 100
Succeed requests: 100
Failed requests: 0
Average QPS: 0.266
Average latency: 3.755
Throughput(average output tokens per second): 81.877
Average time to first token: 3.755
Average input tokens per request: 27.740
Average output tokens per request: 308.260
Average time per output token: 0.01221
Average package per request: 1.000
Average package latency: 3.755
Percentile of time to first token:
p50: 4.0598
p66: 5.7397
p75: 5.9918
p80: 6.0867
p90: 6.3599
p95: 6.6001
p98: 7.3478
p99: 7.4545
Percentile of request latency:
p50: 4.0598
p66: 5.7397
p75: 5.9918
p80: 6.0867
p90: 6.3599
p95: 6.6001
p98: 7.3478
p99: 7.4545
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
[2] 多并发情形
$ llmuses perf --url 'http://127.0.0.1:8000/v1/chat/completions' --parallel 10 --model 'qwen2-0.5b' --log-every-n-query 10 --read-timeout=120 --dataset-path '/root/data/open_qa.jsonl' -n 100 --max-prompt-length 128000 --api openai --dataset openqa
$ llmuses perf --url 'http://127.0.0.1:8001/v1/chat/completions' --parallel 10 --model 'qwen2-0.5b' --log-every-n-query 10 --read-timeout=120 --dataset-path '/root/data/open_qa.jsonl' -n 100 --max-prompt-length 128000 --api openai --dataset openqa
2
3
“开启vllm”、“10并发”大模型服务测试“open_qa正常上下文”结果:
Benchmarking summary:
Time taken for tests: 22.336 seconds
Expected number of requests: 100
Number of concurrency: 10
Total requests: 100
Succeed requests: 100
Failed requests: 0
Average QPS: 4.477
Average latency: 1.997
Throughput(average output tokens per second): 1360.801
Average time to first token: 1.997
Average input tokens per request: 27.740
Average output tokens per request: 303.950
Average time per output token: 0.00073
Average package per request: 1.000
Average package latency: 1.997
Percentile of time to first token:
p50: 2.2662
p66: 3.2062
p75: 3.2765
p80: 3.2969
p90: 3.6703
p95: 3.7885
p98: 3.7956
p99: 3.8169
Percentile of request latency:
p50: 2.2662
p66: 3.2062
p75: 3.2765
p80: 3.2969
p90: 3.6703
p95: 3.7885
p98: 3.7956
p99: 3.8169
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
“未开启vllm”、“10并发”大模型服务测试“open_qa正常上下文”结果:
Benchmarking summary:
Time taken for tests: 372.361 seconds
Expected number of requests: 100
Number of concurrency: 10
Total requests: 100
Succeed requests: 100
Failed requests: 0
Average QPS: 0.269
Average latency: 35.401
Throughput(average output tokens per second): 82.052
Average time to first token: 35.401
Average input tokens per request: 27.740
Average output tokens per request: 305.530
Average time per output token: 0.01219
Average package per request: 1.000
Average package latency: 35.401
Percentile of time to first token:
p50: 35.5600
p66: 37.5228
p75: 40.2956
p80: 42.3999
p90: 46.3894
p95: 47.7985
p98: 49.0284
p99: 52.4676
Percentile of request latency:
p50: 35.5600
p66: 37.5228
p75: 40.2956
p80: 42.3999
p90: 46.3894
p95: 47.7985
p98: 49.0284
p99: 52.4676
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 4.4.2 评测LongAlpaca-12K长上下文
修改的地方有--dataset-path、--dataset,需要注意的是启动 vllm 的推理服务时,要将 --vllm_maxlen 指定的大一些,否则将使用2048的默认值,无法成功请求。
[1] 单并发情形
$ llmuses perf --url 'http://127.0.0.1:8000/v1/chat/completions' --parallel 1 --model 'qwen2-0.5b' --log-every-n-query 10 --read-timeout=120 --dataset-path '/root/data/LongAlpaca-12k.json' -n 100 --max-prompt-length 128000 --api openai --dataset longalpaca
$ llmuses perf --url 'http://127.0.0.1:8001/v1/chat/completions' --parallel 1 --model 'qwen2-0.5b' --log-every-n-query 10 --read-timeout=120 --dataset-path '/root/data/LongAlpaca-12k.json' -n 100 --max-prompt-length 128000 --api openai --dataset longalpaca
2
3
“开启vllm”、“单并发”大模型服务测试“LongAlpaca-12K长上下文”的结果:
Benchmarking summary:
Time taken for tests: 162.214 seconds
Expected number of requests: 100
Number of concurrency: 1
Total requests: 100
Succeed requests: 100
Failed requests: 0
Average QPS: 0.616
Average latency: 1.618
Throughput(average output tokens per second): 234.043
Average time to first token: 1.618
Average input tokens per request: 7370.820
Average output tokens per request: 379.650
Average time per output token: 0.00427
Average package per request: 1.000
Average package latency: 1.618
Percentile of time to first token:
p50: 1.6618
p66: 1.8991
p75: 2.0094
p80: 2.1071
p90: 2.6250
p95: 3.4559
p98: 3.6226
p99: 3.8583
Percentile of request latency:
p50: 1.6618
p66: 1.8991
p75: 2.0094
p80: 2.1071
p90: 2.6250
p95: 3.4559
p98: 3.6226
p99: 3.8583
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
“未开启vllm”、“单并发”大模型服务测试“LongAlpaca-12K长上下文”的结果:
Benchmarking summary:
Time taken for tests: 424.913 seconds
Expected number of requests: 100
Number of concurrency: 1
Total requests: 100
Succeed requests: 88
Failed requests: 12
Average QPS: 0.207
Average latency: 4.804
Throughput(average output tokens per second): 78.362
Average time to first token: 4.804
Average input tokens per request: 7256.273
Average output tokens per request: 378.375
Average time per output token: 0.01276
Average package per request: 1.000
Average package latency: 4.804
Percentile of time to first token:
p50: 5.8342
p66: 6.2719
p75: 6.4467
p80: 6.5855
p90: 6.7999
p95: 7.1027
p98: 7.2525
p99: 7.2756
Percentile of request latency:
p50: 5.8342
p66: 6.2719
p75: 6.4467
p80: 6.5855
p90: 6.7999
p95: 7.1027
p98: 7.2525
p99: 7.2756
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
[2] 多并发情形
$ llmuses perf --url 'http://127.0.0.1:8000/v1/chat/completions' --parallel 10 --model 'qwen2-0.5b' --log-every-n-query 10 --read-timeout=120 --dataset-path '/root/data/LongAlpaca-12k.json' -n 100 --max-prompt-length 128000 --api openai --dataset longalpaca
$ llmuses perf --url 'http://127.0.0.1:8001/v1/chat/completions' --parallel 10 --model 'qwen2-0.5b' --log-every-n-query 10 --read-timeout=120 --dataset-path '/root/data/LongAlpaca-12k.json' -n 100 --max-prompt-length 128000 --api openai --dataset longalpaca
2
3
“开启vllm”、“10并发”大模型服务测试“LongAlpaca-12K长上下文”的结果:
Benchmarking summary:
Time taken for tests: 74.949 seconds
Expected number of requests: 100
Number of concurrency: 10
Total requests: 100
Succeed requests: 100
Failed requests: 0
Average QPS: 1.334
Average latency: 7.321
Throughput(average output tokens per second): 528.829
Average time to first token: 7.321
Average input tokens per request: 7370.820
Average output tokens per request: 396.350
Average time per output token: 0.00189
Average package per request: 1.000
Average package latency: 7.321
Percentile of time to first token:
p50: 8.3670
p66: 9.9169
p75: 9.9974
p80: 10.1820
p90: 10.4507
p95: 10.6263
p98: 10.9379
p99: 10.9593
Percentile of request latency:
p50: 8.3670
p66: 9.9169
p75: 9.9974
p80: 10.1820
p90: 10.4507
p95: 10.6263
p98: 10.9379
p99: 10.9593
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
“未开启vllm”、“10并发”大模型服务测试“LongAlpaca-12K长上下文”的结果:
Benchmarking summary:
Time taken for tests: 497.981 seconds
Expected number of requests: 100
Number of concurrency: 10
Total requests: 100
Succeed requests: 98
Failed requests: 2
Average QPS: 0.197
Average latency: 47.973
Throughput(average output tokens per second): 78.352
Average time to first token: 47.973
Average input tokens per request: 7356.724
Average output tokens per request: 398.143
Average time per output token: 0.01276
Average package per request: 1.000
Average package latency: 47.973
Percentile of time to first token:
p50: 49.6310
p66: 52.7080
p75: 53.9384
p80: 54.5298
p90: 55.9694
p95: 57.6743
p98: 58.7529
p99: 58.9002
Percentile of request latency:
p50: 49.6310
p66: 52.7080
p75: 53.9384
p80: 54.5298
p90: 55.9694
p95: 57.6743
p98: 58.7529
p99: 58.9002
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 4.4.3 Eval-Scope测试结果的对比
以表格的形式对上述测试结果进行汇总对比,可以看出来开启vLLM之后对于响应速度、并发推理的提升是非常明显的。
# 4.5 Qwen官方的推理性能评估
为了更权威的对比开启vLLM之后对于推理性能的提升,这里可以查看Qwen模型的官方推理测试结果。结论是大参数模型相较于小参数模型的推理速度要慢很多,开启vLLM之后,推理速度均有较大提升。
Qwen模型官方推理测试结果的链接:https://qwen.readthedocs.io/zh-cn/latest/benchmark/speed_benchmark.html (opens new window)
# 5. 参考资料
[1] vLLM是一个大型语言模型推理加速工具 from Github (opens new window)
[2] vLLM: Easy, Fast, and Cheap LLM Serving with PagedAttention from vLLM官方文档 (opens new window)
[3] vLLM:给大模型提提速,支持高并发吞吐量提高24倍,同时推理速度最少提高 8 倍 from CSDN (opens new window)
[4] vLLM Feature:Offload Model Weights to CPU from Github issues (opens new window)
[5] 如何让vLLM适配一个新模型 from 知乎 (opens new window)
[7] 如何解决LLM大语言模型的并发问题 from 知乎 (opens new window)
[8] 大模型的N种高效部署方法:以LLama2为例 from 美熙科技说 (opens new window)
[9] LightLLM:纯Python超轻量高性能LLM推理框架 from AI文摘 (opens new window)
[10] 大模型推理百倍加速之KV cache篇 from 知乎 (opens new window)
[11] 大模型推理-2-推理引擎和服务性能优化 from 知乎 (opens new window)
[12] 在 Triton 中部署 vLLM 模型 from Github (opens new window)
[13] VLLM推理加速与部署 from Github (opens new window)
[14] Triton Inference Server教程2 from CSDN (opens new window)
[15] 使用本地模型替代 OpenAI:多模型并发推理框架 from 知乎 (opens new window)
[16] 怎么在我们项目中使用vLLM推理 from Github issues (opens new window)
[17] LLaMA-Factory统一 100 多个 LLM 的高效微调 from Github (opens new window)
[18] ZeRO-Inference: Democratizing massive model inference from Deepspeed官方文档 (opens new window)
[19] 图解大模型计算加速系列:vLLM源码解析1,整体架构 from AINLP (opens new window)
[20] 量化模型能否用vllm部署 from Github issues (opens new window)
[21] Would it be possible to support LoRA fine-tuned models from Github issues (opens new window)
[22] Support LoRA adapter from Github issues (opens new window)
[23] 大模型部署综述 from 吃果冻不吐果冻皮 (opens new window)
[24] LLM后端推理引擎性能大比拼 from 吃果冻不吐果冻皮 (opens new window)
[25] LLM推理引擎性能评测:vllm、lmdeploy、tensorrt-llm from 微信公众号 (opens new window)
[26] eval-scope里的大模型推理性能测试工具perf from Github (opens new window)
[27] 图解大模型计算加速系列:vLLM源码解析1,整体架构 from 吃果冻不吐果冻皮 (opens new window)
[28] SGLang:LLM推理引擎发展新方向 from 微信公众号 (opens new window)
[29] 内网环境使用Docker部署Qwen2模型-vLLM篇 from 微信公众号 (opens new window)
[30] Qwen推理效率评估 from Qwen官方文档 (opens new window)
[31] 是时候更新vllm了,新版吞吐提升2倍 from 微信公众号 (opens new window)
[32] 解密vLLM推理快的原因 from 微信公众号 (opens new window)
[33] vLLM这一年的新特性以及后续规划(总结版)from 吃果冻不吐果冻皮 (opens new window)
[34] 大模型重复输出怎么办?from CSDN (opens new window)
[35] 大模型推理参数 from 姳楽的开发分享 (opens new window)
[36] Qwen2.5-LLM:扩展大型语言模型的边界 from Qwen官方文档 (opens new window)
[37] 大模型量化概述 from 吃果冻不吐果冻皮 (opens new window)
[38] NLP(十一):大语言模型的模型量化(INT8/INT4)技术 from 知乎 (opens new window)
[39] 模型量化(int8)系统知识导读 from CSDN (opens new window)
[40] 模型量化技术综述:揭示大型语言模型压缩的前沿技术 from 知乎 (opens new window)
[41] LLM实践系列-昇腾910B上进行Qwen2.5推理 from 微信公众号 (opens new window)
[42] 如何估计大模型所需要的显存大小 from AI魔法学院 (opens new window)
[43] deepseek r1满血版本部署 from 微信公众号 (opens new window)
[44] 分布式推理和服务 from vLLM官方文档 (opens new window)
[45] LightLLM:一个基于 Python 的 LLM推理和服务框架,以其轻量级设计、易于扩展和高速性能而著称 from Github (opens new window)
[46] FastLLM:一个纯c++的全平台llm加速库,支持python调用 from Github (opens new window)
[47] ServiceStreamer:将服务请求排队组成一个完整的batch,再送进GPU运算,极大提高GPU利用率 from Github (opens new window)
[48] Mosec:一个高性能 ML 模型服务框架,提供动态批处理和 CPU/GPU 管道,以充分利用计算资源 from Github (opens new window)
[49] triton-inference-server/vllm_backend:vLLM设计,旨在vLLM引擎上运行支持的模型 from Github (opens new window)
[50] imitater:基于 vllm 和 infinity 构建的统一语言模型服务器 from Github (opens new window)
[51] SGLang:比vLLM吞吐还要大5倍的推理引擎 from 微信公众号 (opens new window)
[52] 大语言模型引擎全解析:Transformers、vLLM、Llama.cpp、SGLang、MLX 和 Ollama from 微信公众号 (opens new window)