mirror of
https://github.com/hiyouga/LLaMA-Factory.git
synced 2026-08-17 05:25:44 +08:00
155 lines
6.1 KiB
Python
155 lines
6.1 KiB
Python
# Copyright 2025 the LlamaFactory team.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import sys
|
|
from functools import partial
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
import torch.multiprocessing as mp
|
|
from torch import nn
|
|
from transformers import AutoModelForCausalLM
|
|
|
|
|
|
def _original_fla_op(*args, **kwargs):
|
|
return args, kwargs
|
|
|
|
|
|
class _LinearAttention(nn.Module):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.chunk_gated_delta_rule = _original_fla_op
|
|
self.recurrent_gated_delta_rule = _original_fla_op
|
|
|
|
|
|
class _FLAModel(nn.Module):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.linear_attn = _LinearAttention()
|
|
|
|
|
|
def _apply_kernel(rank) -> None:
|
|
with patch("torch.accelerator.current_accelerator") as mock_get_accelerator:
|
|
mock_device = MagicMock()
|
|
setattr(mock_device, "type", "npu")
|
|
mock_get_accelerator.return_value = mock_device
|
|
|
|
model = AutoModelForCausalLM.from_pretrained("llamafactory/tiny-random-qwen3")
|
|
original_rmsnorm_forward = model.model.layers[0].input_layernorm.forward
|
|
original_swiglu_forward = model.model.layers[0].mlp.forward
|
|
|
|
with patch.dict(sys.modules, {"torch_npu": MagicMock()}):
|
|
# Reload kernel modules so dependency checks use the mocked NPU environment.
|
|
for k in list(sys.modules.keys()):
|
|
if k.startswith("llamafactory.v1.plugins.model_plugins.kernels"):
|
|
del sys.modules[k]
|
|
|
|
from llamafactory.v1.plugins.model_plugins.kernels.interface import apply_kernels
|
|
|
|
model = apply_kernels(model=model, config={"name": "npu_fused_rmsnorm"})
|
|
|
|
assert model.model.layers[0].input_layernorm.forward.__func__ is not original_rmsnorm_forward.__func__
|
|
assert model.model.layers[0].mlp.forward.__func__ is original_swiglu_forward.__func__
|
|
|
|
|
|
def _apply_all_kernels(rank) -> None:
|
|
with patch("torch.accelerator.current_accelerator") as mock_get_accelerator:
|
|
mock_device = MagicMock()
|
|
setattr(mock_device, "type", "npu")
|
|
mock_get_accelerator.return_value = mock_device
|
|
|
|
model = AutoModelForCausalLM.from_pretrained("llamafactory/tiny-random-qwen3")
|
|
original_rmsnorm_forward = model.model.layers[0].input_layernorm.forward
|
|
original_swiglu_forward = model.model.layers[0].mlp.forward
|
|
|
|
with patch.dict(sys.modules, {"torch_npu": MagicMock()}):
|
|
# Reload kernel modules so dependency checks use the mocked NPU environment.
|
|
for k in list(sys.modules.keys()):
|
|
if k.startswith("llamafactory.v1.plugins.model_plugins.kernels"):
|
|
del sys.modules[k]
|
|
|
|
from llamafactory.v1.plugins.model_plugins.kernels.interface import apply_kernels
|
|
|
|
model = apply_kernels(model=model, config={"name": "auto"})
|
|
|
|
assert model.model.layers[0].input_layernorm.forward.__func__ is not original_rmsnorm_forward.__func__
|
|
assert model.model.layers[0].mlp.forward.__func__ is not original_swiglu_forward.__func__
|
|
|
|
|
|
def test_apply_kernel():
|
|
mp.spawn(_apply_kernel)
|
|
|
|
|
|
def test_apply_all_kernels():
|
|
mp.spawn(_apply_all_kernels)
|
|
|
|
|
|
@pytest.mark.runs_on(["npu"])
|
|
def test_flash_linear_attention_kernels_compose_with_auto(monkeypatch):
|
|
import fsdp_turbo.ops.fla # noqa: F401
|
|
from fsdp_turbo.ops import get_op
|
|
|
|
from llamafactory.v1.plugins.model_plugins.kernels import interface
|
|
from llamafactory.v1.plugins.model_plugins.kernels.ops.linear_attention.fla import (
|
|
FlashLinearAttentionKernel,
|
|
)
|
|
|
|
model = _FLAModel()
|
|
auto_calls = []
|
|
|
|
monkeypatch.setattr(
|
|
interface,
|
|
"_apply_auto_kernels",
|
|
lambda model, **kwargs: auto_calls.append((model, kwargs)) or model,
|
|
)
|
|
# FLA execution is outside this bridge test; its external runtime is not required.
|
|
monkeypatch.setattr(FlashLinearAttentionKernel, "check_deps", staticmethod(lambda: None))
|
|
|
|
config = {
|
|
"name": "auto, flash-linear-attention",
|
|
"include_kernels": "fused_recurrent_gated_delta_rule, chunk_gated_delta_rule",
|
|
"chunk_size": 32,
|
|
}
|
|
assert interface.apply_kernels(model, config) is model
|
|
assert auto_calls == [(model, {"config": config, "require_logits": False})]
|
|
assert get_op("chunk_gated_delta_rule").__module__ == "fsdp_turbo.ops.fla"
|
|
|
|
chunk_op = model.linear_attn.chunk_gated_delta_rule
|
|
assert isinstance(chunk_op, partial)
|
|
assert chunk_op.func.__module__ == "fsdp_turbo.ops.fla"
|
|
assert chunk_op.keywords == {"chunk_size": 32}
|
|
assert model.linear_attn.recurrent_gated_delta_rule.__module__ == "fsdp_turbo.ops.fla"
|
|
|
|
with pytest.raises(RuntimeError, match="did not match any model module attributes"):
|
|
FlashLinearAttentionKernel.apply(
|
|
model=nn.Linear(2, 2),
|
|
config={"include_kernels": "chunk_gated_delta_rule", "chunk_size": 32},
|
|
)
|
|
|
|
|
|
def test_flash_linear_attention_kernel_validates_config(monkeypatch):
|
|
from llamafactory.v1.plugins.model_plugins.kernels.ops.linear_attention.fla import (
|
|
FlashLinearAttentionKernel,
|
|
)
|
|
|
|
model = nn.Sequential(nn.Linear(2, 2))
|
|
monkeypatch.setattr(FlashLinearAttentionKernel, "check_device", staticmethod(lambda: None))
|
|
monkeypatch.setattr(FlashLinearAttentionKernel, "check_deps", staticmethod(lambda: None))
|
|
|
|
with pytest.raises(ValueError, match="chunk_size"):
|
|
FlashLinearAttentionKernel.apply(model=model, config={"include_kernels": "auto", "chunk_size": 48})
|
|
|
|
with pytest.raises(ValueError, match="Unsupported Flash Linear Attention kernels"):
|
|
FlashLinearAttentionKernel.apply(model=model, config={"include_kernels": "not_a_kernel"})
|