当前位置: 首页 > news >正文

[nano-vllm] LLMEngine类 | generate循环调用step | add_request

第1章:LLM引擎

想象一下,你想与一个人工智能(AI)模型对话,比如让它"讲一个关于勇敢骑士的故事"

  • 你输入请求后,AI就会为你生成一个完整的故事。这背后是如何实现的?特别是当许多人可能同时向AI提问时,系统如何运作?

这就是LLM引擎发挥作用的地方

LLM引擎就像一个精密工厂的中央控制室。这个"工厂"旨在使用强大的大型语言模型(LLM)生成文本。它的主要工作是接收请求,确保它们被高效处理,发送给实际的AI模型(我们工厂中的"工人"),然后收集所有生成的部分,给你最终的完整文本。

它解决了什么问题?

如果没有LLM引擎,运行AI模型进行文本生成将变得复杂且缓慢。你需要手动:

  1. 将文本转换为计算机理解的数字(标记化)
  2. 将这些数字发送给AI模型
  3. 管理许多进行中的请求而不让它们纠缠在一起
  4. 逐块(逐个标记)处理文本生成
  5. 将所有块重新组合成可读文本
  6. 可能需要同时使用多个强大的图形处理器(GPU)

LLM引擎处理所有这些复杂任务,让你只需专注于提问并快速获得答案。它是一个协调者,确保即使在处理多个并发请求和跨多个GPU时,也能实现流畅、快速和高效的文本生成

如何使用LLM引擎

nano-vllm中,你通过一个名为LLM的简单类与LLM引擎交互。让我们看看如何使用它来生成文本。

首先,需要创建一个LLM实例,告诉它使用哪个AI模型。

from nanovllm import LLM, SamplingParams
import os
from transformers import AutoTokenizer# 下载的AI模型路径(例如Qwen3-0.6B)
model_path = os.path.expanduser("~/huggingface/Qwen3-0.6B/")# 创建LLM引擎实例
llm = LLM(model_path)# 你还需要一个标记器来准备文本
tokenizer = AutoTokenizer.from_pretrained(model_path)

在这个代码片段中,我们通过指向本地AI模型来设置LLM

LLM类处理启动引擎所需的所有内部设置。我们还获取了一个tokenizer,这对于将人类可读文本转换为AI模型理解的数字(标记)以及反向转换至关重要。

接下来,你定义想要生成的文本类型(你的提示)以及希望AI如何生成它(采样参数)。

# 定义AI应如何生成文本(例如,它应该有多大的创造性)
sampling_params = SamplingParams(temperature=0.6, max_tokens=256)# 你对AI的问题或提示
prompts = ["introduce yourself","list all prime numbers within 100",
]# (可选但推荐做法)为聊天模型格式化提示
prompts = [tokenizer.apply_chat_template([{"role": "user", "content": prompt}],tokenize=False, # 我们还不标记化,LLM引擎会处理add_generation_prompt=True,)for prompt in prompts
]

这里,SamplingParams让你控制temperature(输出有多创造性或"随机")和max_tokens(生成响应的最大长度)等参数。我们还准备了提示,确保它们为我们的AI模型正确格式化。

最后,使用generate方法让AI生成文本:

# 生成文本!
outputs = llm.generate(prompts, sampling_params)# 打印结果
for prompt, output in zip(prompts, outputs):print("\n")print(f"Prompt: {prompt!r}")print(f"Completion: {output['text']!r}")

示例输出

Prompt: '<s>User\nintroduce yourself'
Completion: '你好!我是一个大型语言模型,由Google训练。我在这里帮助你完成各种任务,并尽我所能提供信息。今天我能为你做什么?'Prompt: '<s>User\nlist all prime numbers within 100'
Completion: '100以内的质数有:2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97。'

如你所见,llm.generate()方法接收你的提示和采样设置,并返回AI生成的文本。中间所有复杂步骤都由LLM引擎自动处理。

内部机制:LLM引擎如何工作

让我们揭开帷幕,看看当调用llm.generate()时发生了什么。

将LLM引擎想象成管弦乐队的指挥。当要求它生成文本时,它协调几个专门的部分(其他组件)来产生最终的表演。

以下是事件的简化序列:

在这里插入图片描述

这个图表显示LLM引擎持续循环,协调生成过程

询问请求调度器哪些请求已准备好,将它们发送给模型运行器进行计算,然后用新生成的标记更新请求。这个循环重复,直到所有请求完全生成。

🎢LLM类:入口点

LLM类设计为你与nano-vllm库交互的主要方式。它非常简单:

# nanovllm/llm.py
from nanovllm.engine.llm_engine import LLMEngineclass LLM(LLMEngine):pass

nanovllm中的LLM类只是继承LLMEngine。这意味着LLM直接获得LLMEngine的所有强大功能

实例化LLM,但所有繁重的工作都在LLMEngine类中完成。

设计理念

LLM类作为与nano-vllm库交互的主要接口,采用设计仅继承LLMEngine,实现了以下目标:

  • 简化用户接口
    通过继承LLMEngineLLM类直接暴露底层引擎的全部功能,用户无需关注实现细节,仅需实例化LLM即可调用所有能力。

  • 维护与扩展解耦
    将核心逻辑保留在LLMEngine中,LLM作为轻量级包装层。未来引擎升级或修改时,用户代码无需调整,保证兼容性。

  • 清晰的职责分离
    LLM类仅承担交互入口职责,功能实现完全委托给父类,符合单一职责原则。这种设计也便于测试和模块替换。

LLMEngine类:真正的指挥家

让我们看看nanovllm/engine/llm_engine.pyLLMEngine的核心:

# nanovllm/engine/llm_engine.py
import torch.multiprocessing as mp
from nanovllm.config import Config
from nanovllm.engine.scheduler import Scheduler
from nanovllm.engine.model_runner import ModelRunner
from transformers import AutoTokenizerclass LLMEngine:def __init__(self, model, **kwargs):# 1. 加载配置和模型详情config = Config(model, **kwargs)# 2. 设置模型运行器(可能跨多个GPU)self.ps = [] # 用于并行进程self.events = [] # 用于进程间通信ctx = mp.get_context("spawn")for i in range(1, config.tensor_parallel_size):# 为其他GPU创建单独的进程process = ctx.Process(target=ModelRunner, args=(config, i, event))process.start()self.ps.append(process)self.events.append(event)# 主GPU的主模型运行器self.model_runner = ModelRunner(config, 0, self.events)# 3. 初始化标记器和调度器self.tokenizer = AutoTokenizer.from_pretrained(config.model, use_fast=True)config.eos = self.tokenizer.eos_token_id # 序列结束标记self.scheduler = Scheduler(config) # 管理传入请求# 确保程序退出时正确清理atexit.register(self.exit)

LLMEngine初始化时:

  1. 配置(Config):它加载重要设置,如模型路径、一次处理的最大标记数和GPU内存使用情况。
  2. 模型运行器设置:这是关键步骤!LLM引擎准备一个或多个模型运行器实例。如果你有tensor_parallel_size > 1(意味着你想使用多个GPU进行更快处理),它会巧妙地启动每个额外GPU的单独进程。这允许AI模型的不同部分同时在不同GPU上运行。self.model_runner是主要的运行器,与其他运行器协调。
  3. 标记器和调度器:它初始化一个tokenizer将文本转换为数字标记并反向转换。它还设置了请求调度器,它将负责管理和优先处理所有文本生成请求。

添加新请求(add_request)

当要求LLM引擎生成文本时,请求首先通过add_request

# nanovllm/engine/llm_engine.py
from nanovllm.sampling_params import SamplingParams
from nanovllm.engine.sequence import Sequenceclass LLMEngine:# ... (init方法) ...def add_request(self, prompt: str | list[int], sampling_params: SamplingParams):# 如果提示是纯文本,将其转换为数字(标记)if isinstance(prompt, str):prompt = self.tokenizer.encode(prompt)# 创建一个"Sequence"对象来跟踪这个特定的生成seq = Sequence(prompt, sampling_params)# 将序列交给调度器管理self.scheduler.add(seq)

add_request中:

  • 文本提示使用tokenizer转换为数字列表(标记)。
  • 创建一个Sequence对象。这个对象保存关于你特定文本生成请求的所有信息,包括原始提示、迄今为止生成的标记和SamplingParams。你可以在生成序列中了解更多。
  • 这个Sequence然后传递给请求调度器,它将管理其被处理的顺序。

生成循环(stepgenerate)

  • 直接调用的generate方法实际上运行一个循环,反复调用step方法
  • step方法是每个周期中标记生成的实际工作发生的地方。
# nanovllm/engine/llm_engine.py
# ... (导入和类定义) ...def step(self):# 1. 询问调度器哪些序列已准备好处理seqs, is_prefill = self.scheduler.schedule()# 2. 将这些序列发送给模型运行器以生成下一个标记# 模型运行器处理实际的AI模型计算token_ids = self.model_runner.call("run", seqs, is_prefill)# 3. 用新生成的标记更新序列self.scheduler.postprocess(seqs, token_ids)# 4. 识别并收集任何已完成生成的序列outputs = [(seq.seq_id, seq.completion_token_ids) for seq in seqs if seq.is_finished]# 返回完成的输出和处理的标记数以进行性能跟踪return outputs, len(seqs)def is_finished(self):# 检查调度器是否已完成所有请求return self.scheduler.is_finished()def generate(self, prompts, sampling_params, use_tqdm: bool = True) -> list[str]:# 将所有初始提示添加为请求for prompt, sp in zip(prompts, sampling_params):self.add_request(prompt, sp)outputs = {} # 存储已完成序列的结果# 持续执行步骤直到所有请求完成while not self.is_finished():output, num_tokens = self.step() # 调用主要工作方法# 存储任何完成的输出for seq_id, token_ids in output:outputs[seq_id] = token_idsif use_tqdm:# 如果启用,更新进度条pass # 为简洁起见简化# 将最终标记ID解码回人类可读文本outputs = [outputs[seq_id] for seq_id in sorted(outputs.keys())]outputs = [{"text": self.tokenizer.decode(token_ids), "token_ids": token_ids} for token_ids in outputs]return outputs

generate方法充当保持整个生成过程运行的顶层循环

  • 反复调用self.step(),直到self.is_finished()告诉它所有请求的文本生成已完成

step内部:

  1. 调度(self.scheduler.schedule()):请求调度器决定哪些活动请求(序列)应接下来发送给AI模型。这对于高效批处理和许多请求的处理至关重要。
  2. 模型执行(self.model_runner.call("run", ...)):选定的序列传递给模型运行器。这是实际神经网络(AI模型)在GPU上进行计算并生成下一组标记的地方。
  3. 后处理(self.scheduler.postprocess(...)):新生成的标记然后发送回请求调度器,它更新相应的Sequence对象。它还检查是否有任何序列已完成生成(例如,达到max_tokens或生成序列结束标记)。

这个schedule -> run model -> postprocess的持续循环是LLM引擎如何协调所有活动请求逐标记生成文本的方式。

结论

刚刚完成了对nano-vllm的第一次探索

现在明白LLM引擎是协调整个文本生成过程的中央大脑。它接收你的提示,管理它们,将它们发送给底层AI模型,并高效地收集结果。

看到了如何使用LLM类生成文本,并瞥见了LLMEngine如何设置关键组件,如请求调度器和模型运行器,并管理文本生成的持续循环。

在下一章中,我们将聚焦于关键角色之一:生成序列。这是LLM引擎用来跟踪每个单独请求在生成过程中进展的方式

生成序列

http://www.dtcms.com/a/589749.html

相关文章:

  • 快速建站公司怎么样中山优秀网站建设
  • C++中的CRTP(Curiously Recurring Template Pattern,奇异递归模板模式)
  • SQL 子查询:解锁复杂查询的秘密
  • 临沂网络网站建设长尾关键词举例
  • vs code jupyter连gpu结点kernel
  • 设计网站用户需求分析报告甘肃seo网站
  • QUSB BULK和Qualcomm HS-USB QDLoader 9008的区别是什么?
  • 读后感:《解析极限编程:拥抱变化》
  • 【愚公系列】《MCP协议与AI Agent开发》008-MCP的基本原理(MCP的状态管理与中间态控制)
  • 天津网站建设方案咨询深圳世展建设公司
  • 企业碳资产的清算(核算)、审计和交易全过程
  • 珠海学网站开发网页可视化编辑
  • 简单网站建设模板微信扫码即可打开的网站如何做
  • 企业网站设计模板网站建设策划实施要素有哪些
  • AIGC|广州AI优化企业新榜单与选择指南
  • 不同种类(如红参、白参)或不同产地人参的化学成分有何差异?
  • 南昌网站建设公司案例wordpress火车头免密发布模块接口
  • 网站网页框架构架图怎么做南通网站推广排名
  • Git Tag 用法记录
  • 《论文写作》笔记
  • 三明网站制作上传网站怎么安装
  • fastmcp 纳米AI 报错 uniq_id Unexpected keyword argument 多余参数报错
  • 黄石市下陆区建设管理局网站专业网站建设制作公司哪家好
  • 视觉信息如何被大脑处理?
  • 建设银行流水网站wordpress documentation
  • 【本地持久化到磁盘的模式】
  • 土巴兔网站开发技术软件开发的七个流程
  • 给公司做网站软件网站备案点不进去
  • 11、Linux 文本流处理命令
  • 微信网站建设方案ppt湖北省住房和城乡建设网站