130 lines
4.3 KiB
Python
130 lines
4.3 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|||
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
import torch
|
||
|
|
from vllm.model_executor.layers.linear import (
|
||
|
|
ColumnParallelLinear,
|
||
|
|
QKVParallelLinear,
|
||
|
|
RowParallelLinear,
|
||
|
|
)
|
||
|
|
|
||
|
|
from vllm_omni.quantization import svdquant_config as svdquant
|
||
|
|
|
||
|
|
pytestmark = [pytest.mark.core_model, pytest.mark.cpu, pytest.mark.diffusion]
|
||
|
|
|
||
|
|
|
||
|
|
def _linear_without_init(linear_type: type[torch.nn.Module], tp_rank: int):
|
||
|
|
layer = object.__new__(linear_type)
|
||
|
|
torch.nn.Module.__init__(layer)
|
||
|
|
layer.tp_rank = tp_rank
|
||
|
|
return layer
|
||
|
|
|
||
|
|
|
||
|
|
def _values(shape: tuple[int, ...], dtype: torch.dtype) -> torch.Tensor:
|
||
|
|
values = torch.arange(torch.Size(shape).numel(), dtype=torch.int64).remainder(127)
|
||
|
|
return values.reshape(shape).to(dtype)
|
||
|
|
|
||
|
|
|
||
|
|
def _method(monkeypatch: pytest.MonkeyPatch):
|
||
|
|
monkeypatch.setattr(svdquant, "_assert_supported", lambda: None)
|
||
|
|
return svdquant.DiffusionSVDQuantLinearMethod(svdquant.DiffusionSVDQuantConfig(rank=8))
|
||
|
|
|
||
|
|
|
||
|
|
def test_column_parallel_loader_shards_svdquant_outputs(
|
||
|
|
monkeypatch: pytest.MonkeyPatch,
|
||
|
|
) -> None:
|
||
|
|
layer = _linear_without_init(ColumnParallelLinear, tp_rank=1)
|
||
|
|
method = _method(monkeypatch)
|
||
|
|
method.create_weights(
|
||
|
|
layer,
|
||
|
|
input_size_per_partition=128,
|
||
|
|
output_partition_sizes=[32],
|
||
|
|
input_size=128,
|
||
|
|
output_size=64,
|
||
|
|
params_dtype=torch.bfloat16,
|
||
|
|
weight_loader=layer.weight_loader,
|
||
|
|
)
|
||
|
|
|
||
|
|
loaded_tensors = {
|
||
|
|
"qweight": _values((64, 64), torch.int8),
|
||
|
|
"wscales": _values((8, 64), torch.float8_e4m3fn),
|
||
|
|
"proj_up": _values((64, 8), torch.bfloat16),
|
||
|
|
"wcscales": _values((64,), torch.bfloat16),
|
||
|
|
}
|
||
|
|
for name, loaded in loaded_tensors.items():
|
||
|
|
parameter = getattr(layer, name)
|
||
|
|
parameter.weight_loader(parameter, loaded)
|
||
|
|
expected = loaded.narrow(parameter.output_dim, 32, 32)
|
||
|
|
torch.testing.assert_close(parameter, expected)
|
||
|
|
|
||
|
|
|
||
|
|
def test_row_parallel_loader_shards_svdquant_inputs(
|
||
|
|
monkeypatch: pytest.MonkeyPatch,
|
||
|
|
) -> None:
|
||
|
|
layer = _linear_without_init(RowParallelLinear, tp_rank=1)
|
||
|
|
method = _method(monkeypatch)
|
||
|
|
method.create_weights(
|
||
|
|
layer,
|
||
|
|
input_size_per_partition=64,
|
||
|
|
output_partition_sizes=[64],
|
||
|
|
input_size=128,
|
||
|
|
output_size=64,
|
||
|
|
params_dtype=torch.bfloat16,
|
||
|
|
weight_loader=layer.weight_loader,
|
||
|
|
)
|
||
|
|
|
||
|
|
loaded_tensors = {
|
||
|
|
"qweight": _values((64, 64), torch.int8),
|
||
|
|
"wscales": _values((8, 64), torch.float8_e4m3fn),
|
||
|
|
"proj_down": _values((128, 8), torch.bfloat16),
|
||
|
|
"smooth_factor": _values((128,), torch.bfloat16),
|
||
|
|
}
|
||
|
|
for name, loaded in loaded_tensors.items():
|
||
|
|
parameter = getattr(layer, name)
|
||
|
|
parameter.weight_loader(parameter, loaded)
|
||
|
|
shard_size = parameter.shape[parameter.input_dim]
|
||
|
|
expected = loaded.narrow(parameter.input_dim, shard_size, shard_size)
|
||
|
|
torch.testing.assert_close(parameter, expected)
|
||
|
|
|
||
|
|
|
||
|
|
def test_qkv_parallel_loader_shards_fused_svdquant_outputs(
|
||
|
|
monkeypatch: pytest.MonkeyPatch,
|
||
|
|
) -> None:
|
||
|
|
layer = _linear_without_init(QKVParallelLinear, tp_rank=1)
|
||
|
|
layer.total_num_heads = 4
|
||
|
|
layer.total_num_kv_heads = 2
|
||
|
|
layer.num_heads = 2
|
||
|
|
layer.num_kv_heads = 1
|
||
|
|
layer.num_kv_head_replicas = 1
|
||
|
|
layer.head_size = 8
|
||
|
|
layer.v_head_size = 8
|
||
|
|
method = _method(monkeypatch)
|
||
|
|
method.create_weights(
|
||
|
|
layer,
|
||
|
|
input_size_per_partition=128,
|
||
|
|
output_partition_sizes=[16, 8, 8],
|
||
|
|
input_size=128,
|
||
|
|
output_size=64,
|
||
|
|
params_dtype=torch.bfloat16,
|
||
|
|
weight_loader=layer.weight_loader,
|
||
|
|
)
|
||
|
|
|
||
|
|
loaded_tensors = {
|
||
|
|
"qweight": _values((64, 64), torch.int8),
|
||
|
|
"wscales": _values((8, 64), torch.float8_e4m3fn),
|
||
|
|
"proj_up": _values((64, 8), torch.bfloat16),
|
||
|
|
"wcscales": _values((64,), torch.bfloat16),
|
||
|
|
}
|
||
|
|
expected_global_outputs = [slice(16, 32), slice(40, 48), slice(56, 64)]
|
||
|
|
for name, loaded in loaded_tensors.items():
|
||
|
|
parameter = getattr(layer, name)
|
||
|
|
parameter.weight_loader(parameter, loaded)
|
||
|
|
expected = torch.cat(
|
||
|
|
[
|
||
|
|
loaded.narrow(parameter.output_dim, part.start, part.stop - part.start)
|
||
|
|
for part in expected_global_outputs
|
||
|
|
],
|
||
|
|
dim=parameter.output_dim,
|
||
|
|
)
|
||
|
|
torch.testing.assert_close(parameter, expected)
|