跳到内容

什么是逐层(重)加载?

逐层重加载(Layerwise reloading)是用于处理将新权重数据加载到现有权重数据目标位置的系统,而无需触发 CUDA 图(cuda graph)和其他运行时制品的重新编译。该系统用于支持 QeRL 风格的训练后流程,其中全精度训练器权重被量化并加载到目标 vLLM 实例中,以实现快速、高探索性的 rollout。核心实现可以在 layerwise.py 中找到。

Layerwise

用于 QeRL 的逐层重加载

为了将新权重加载到现有权重数据目标中,权重必须经过以下操作:

  • 传输(Transfer):权重必须从训练器模型传输到目标节点/设备
  • 融合(Fuse):权重分区必须进行融合,例如 qkv/gate_up
  • 处理(Process):这通常意味着在线量化以及特定于算子的填充(padding)或跨步(striding)
  • 切分(Shard):权重必须根据选定的并行策略进行切分
  • 复制(Copy):权重必须复制到现有的权重数据目标中

逐层重加载通过以下步骤实现这一目标:

  1. 权重从训练器传输到目标(参见 weight_transfer
  2. 权重通过 model.load_weights 加载,期间进行切分融合
  3. 一旦某一层的所有权重都加载完毕,权重就会以在线方式进行处理
  4. 权重被复制到现有的权重数据目标中

有关实现的更多信息,请参阅 底层级 layerwise API

带有在线量化的逐层加载

在线量化(Online quantization)是指用户提供全精度权重,而这些权重在加载到模型中时即时进行量化。逐层重加载系统通过将在线量化视为一个处理步骤来处理它,该步骤在首次加载和重加载期间都以在线方式处理。典型的在线量化方法实现应如下所示

class Fp8PerTensorOnlineLinearMethod(LinearMethodBase):
    """Online version of FP8 per-tensor quantization which loads a full
    precision checkpoint and quantizes weights during loading."""

    uses_meta_device: bool = True

    def create_weights(self, layer: torch.nn.Module, ...):
        # weight is materialized and processed during loading
        layer.weight = ModelWeightParameter(
            data=torch.empty(..., device="meta"),
            weight_loader=weight_loader,
        )

        # set up online processing
        initialize_online_processing(layer)

    def process_weights_after_loading(self, layer: Module) -> None:
        if getattr(layer, "_already_called_process_weights_after_loading", False):
            return

        layer.weight, layer.weight_scale = ops.scaled_fp8_quant(layer.weight)

        # Prevent duplicate processing (e.g., during weight reload)
        layer._already_called_process_weights_after_loading = True

用法示例

高层级权重传输 API

逐层重加载系统已与训练后权重传输系统集成。要将逐层重加载与权重传输系统结合使用,请参考 这里的示例。检查点格式的权重传输引擎(例如 NCCL 和 IPC 后端)会在其 start_weight_update/finish_weight_update 生命周期内自动运行逐层重加载。

中层级 reload_weights API

逐层重加载也可以通过 reload_weights API 暴露。此接口可以使用以下代码调用

from vllm import LLM

llm = LLM("Qwen/Qwen3-0.6B")
llm.collective_rpc("reload_weights")

此接口还允许指定 weights_path,用于选择要加载的检查点路径

from vllm import LLM

# fine tuned model checkpoints for testing
mul_path = "inference-optimization/Qwen3-0.6B-debug-multiply"
add_path = "inference-optimization/Qwen3-0.6B-debug-add"

llm = LLM("Qwen/Qwen3-0.6B")
llm.collective_rpc("reload_weights", kwargs={"weights_path": mul_path})
llm.generate("3 4 = ")  # 12

llm.collective_rpc("reload_weights", kwargs={"weights_path": add_path})
llm.generate("3 4 = ")  # 7

最后,可以直接提供 weights_iterator。此迭代器可以是惰性定义的,也可以是立即定义的。

from vllm import LLM

weights_iterator = [("q_proj", ...), ("k_proj", ...), ...]

llm = LLM("Qwen/Qwen3-0.6B")
llm.collective_rpc("reload_weights", kwargs={"weights_iterator": weights_iterator})

底层级 layerwise API

layerwise.py 实现了以下函数以执行其生命周期:

函数 目的 量化重加载 在线量化
record_metadata_for_reloading 记录张量元数据,以便在元设备(meta device)上还原层 BaseModelLoader 调用 BaseModelLoader 调用
restore_layer_on_meta 在重加载开始时将层还原为模型格式 initialize_layerwise_reload 调用 未调用。在线量化权重已通过 ...OnlineLinearMethod.create_weights 在元设备上启动
initialize_online_processing 使用 online_process_loader 封装器封装权重加载器,该封装器会缓冲权重,直到加载完所有层权重 initialize_layerwise_reload 调用 ...OnlineLinearMethod.create_weights 调用
_layerwise_process 一旦所有权重加载完毕,立即处理该层 加载期间由 online_process_loader 调用 加载期间由 online_process_loader 调用
_copy_and_restore_kernel_tensors 将处理后的权重复制到原始张量位置,以影响已编译的 CUDA 图等。 process_weights_after_loading 之后由 _layerwise_process 调用 未调用。目前还没有编译好的 CUDA 图
finalize_layerwise_processing 捕获任何未加载全部权重的层(例如注意力权重或带填充的权重) BaseModelLoader 调用 BaseModelLoader 调用

你可以通过调用 initialize_layerwise_reload、加载权重,然后调用 finalize_layerwise_processing 来直接接入此生命周期

from vllm import LLM
from vllm.model_executor.model_loader.reload import initialize_layerwise_reload, finalize_layerwise_processing

llm = LLM("Qwen/Qwen3-0.6B")

# this model path requires `VLLM_ENABLE_V1_MULTIPROCESSING=0` and is not stable
model = llm.llm_engine.engine_core.engine_core.model_executor.driver_worker.worker.get_model()

# layerwise reload
initialize_layerwise_reload(model)
model.load_weights(...)
finalize_layerwise_processing(model, llm.model_config)

排查内存占用过高问题

逐层重加载允许用户在权重加载到模型时增量地加载和处理权重。该系统依赖于在设备上缓冲层权重,直到该层的所有权重都已加载。然而,在没有卸载(offloading)的情况下,如果权重加载顺序混乱,这种方法必然会导致过度缓冲。

因此,用户在重新加载到模型时必须注意权重的顺序。权重应“按顺序”加载,这意味着在开始加载下一层的权重之前,每一层的权重都已完全加载。“乱序”加载会导致某些层权重在其他层权重加载时保持缓冲状态,从而导致内存占用过高。在下面的示例中,q_proj、k_proj、v_proj 和 up_proj 同时被缓冲,比在 q_proj、k_proj 和 v_proj 之后加载 up_proj 消耗更多内存。

正确的加载方式 错误的加载方式
Layerwise Layerwise

如果权重乱序加载,用户将看到如下警告。

WARNING [layerwise.py:198] Allocating 28.5 MB of device memory to buffers to load ["QKVParallelLinear", "MergedColumnParallelLinear"] layers. This extra memory usage can be avoided by ordering weights by their parent layer when reloading.