Skip to content

Commit d7dfd79

Browse files
committed
Update
[ghstack-poisoned]
1 parent 62dd59b commit d7dfd79

4 files changed

Lines changed: 143 additions & 0 deletions

File tree

examples/models/llama/export_llama_lib.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,19 @@ def build_args_parser() -> argparse.ArgumentParser:
565565
help="Use SpinQuant for better quantization performance. Only support cuda and native.",
566566
)
567567

568+
parser.add_argument(
569+
"--use_moe_quantized_op",
570+
action="store_true",
571+
default=False,
572+
help=(
573+
"Replace eager MoE feed-forward modules with the "
574+
"`llama::quantized_moe_ffn` portable-runtime custom op (INT4 "
575+
"weights, INT8 dyn-quant activations via torchao). On aarch64 "
576+
"with ENABLE_QUANTIZED_MOE_FFN the optimized torchao kernel is "
577+
"used; otherwise a portable reference fallback runs."
578+
),
579+
)
580+
568581
parser.add_argument(
569582
"-qat",
570583
"--use_qat",
@@ -809,6 +822,7 @@ def _prepare_for_llama_export(llm_config: LlmConfig) -> LLMEdgeManager:
809822
use_torchao_kernels_linear=llm_config.backend.torchao.use_torchao_kernels_linear,
810823
use_torchao_kernels_tied_embedding=llm_config.backend.torchao.use_torchao_kernels_tied_embedding,
811824
quantize_with_hqq=llm_config.quantization.use_hqq,
825+
use_moe_quantized_op=llm_config.model.use_moe_quantized_op,
812826
)
813827
)
814828

@@ -1712,6 +1726,7 @@ def _get_source_transforms( # noqa
17121726
use_torchao_kernels_linear: bool = False,
17131727
use_torchao_kernels_tied_embedding: bool = False,
17141728
quantize_with_hqq: bool = True,
1729+
use_moe_quantized_op: bool = False,
17151730
) -> List[Callable[[torch.nn.Module], torch.nn.Module]]:
17161731
"""
17171732
Return a list of functions that transform a graph.
@@ -1769,6 +1784,17 @@ def _get_source_transforms( # noqa
17691784

17701785
transforms.append(inject_fast_hadamard_transform_native_for_spin_quant)
17711786

1787+
if use_moe_quantized_op:
1788+
from .source_transformation.moe import replace_moe_with_quantized_op
1789+
1790+
transforms.append(
1791+
partial(
1792+
replace_moe_with_quantized_op,
1793+
group_size=group_size or 32,
1794+
weight_nbit=4,
1795+
)
1796+
)
1797+
17721798
if embedding_quantize:
17731799
"""
17741800
When this option is selected, it finds all embedding layers and transforms

extension/llm/custom_ops/BUCK

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ fbcode_target(_kind = runtime.python_test,
100100
preload_deps = [
101101
":custom_ops_aot_lib_mkl_noomp",
102102
":custom_ops_aot_py",
103+
# The Python source transform calls
104+
# `torch.ops.torchao._pack_8bit_act_4bit_weight` to pack each
105+
# expert's INT4 weights, so preload the torchao AOT library too.
103106
"//pytorch/ao/torchao/csrc/cpu/shared_kernels/linear_8bit_act_xbit_weight:op_linear_8bit_act_xbit_weight_aten",
104107
],
105108
deps = [
@@ -108,5 +111,7 @@ fbcode_target(_kind = runtime.python_test,
108111
"//executorch/examples/models/llama:llama_transformer",
109112
"//executorch/examples/models/llama:transformer_modules",
110113
"//executorch/examples/models/llama:source_transformation",
114+
"//executorch/examples/models/llama:export_library",
115+
"//executorch/extension/llm/export/config:llm_config",
111116
],
112117
)

extension/llm/custom_ops/test_quantized_moe.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,3 +585,110 @@ def test_no_shared_expert_is_none(self) -> None:
585585
wrapper.m = moe
586586
replace_moe_with_quantized_op(wrapper, group_size=32, weight_nbit=4)
587587
self.assertIsNone(wrapper.m.shared_expert)
588+
589+
590+
class TestExportPipelineWiring(unittest.TestCase):
591+
"""The export pipeline correctly includes the MoE transform."""
592+
593+
def test_get_source_transforms_includes_moe_when_enabled(self) -> None:
594+
from functools import partial
595+
596+
from executorch.examples.models.llama.export_llama_lib import (
597+
_get_source_transforms,
598+
)
599+
600+
transforms = _get_source_transforms(
601+
dtype_override=torch.float32, use_moe_quantized_op=True
602+
)
603+
moe_transforms = [
604+
t
605+
for t in transforms
606+
if isinstance(t, partial)
607+
and t.func.__name__ == "replace_moe_with_quantized_op"
608+
]
609+
self.assertEqual(len(moe_transforms), 1)
610+
self.assertEqual(moe_transforms[0].keywords["group_size"], 32)
611+
self.assertEqual(moe_transforms[0].keywords["weight_nbit"], 4)
612+
613+
def test_get_source_transforms_excludes_moe_when_disabled(self) -> None:
614+
from functools import partial
615+
616+
from executorch.examples.models.llama.export_llama_lib import (
617+
_get_source_transforms,
618+
)
619+
620+
transforms = _get_source_transforms(
621+
dtype_override=torch.float32, use_moe_quantized_op=False
622+
)
623+
moe_transforms = [
624+
t
625+
for t in transforms
626+
if isinstance(t, partial)
627+
and hasattr(t.func, "__name__")
628+
and t.func.__name__ == "replace_moe_with_quantized_op"
629+
]
630+
self.assertEqual(len(moe_transforms), 0)
631+
632+
def test_get_source_transforms_passes_custom_group_size(self) -> None:
633+
from functools import partial
634+
635+
from executorch.examples.models.llama.export_llama_lib import (
636+
_get_source_transforms,
637+
)
638+
639+
transforms = _get_source_transforms(
640+
dtype_override=torch.float32,
641+
use_moe_quantized_op=True,
642+
group_size=64,
643+
)
644+
moe_transforms = [
645+
t
646+
for t in transforms
647+
if isinstance(t, partial)
648+
and t.func.__name__ == "replace_moe_with_quantized_op"
649+
]
650+
self.assertEqual(len(moe_transforms), 1)
651+
self.assertEqual(moe_transforms[0].keywords["group_size"], 64)
652+
653+
def test_sentinel_op_is_registered(self) -> None:
654+
self.assertTrue(hasattr(torch.ops.llama, "_quantized_moe_ffn_active"))
655+
self.assertTrue(torch.ops.llama._quantized_moe_ffn_active())
656+
657+
658+
class TestLlmConfigMoeFlag(unittest.TestCase):
659+
"""llm_config wires the --use_moe_quantized_op flag correctly."""
660+
661+
def test_from_args_sets_flag(self) -> None:
662+
import argparse
663+
664+
from executorch.extension.llm.export.config.llm_config import LlmConfig
665+
666+
args = argparse.Namespace(use_moe_quantized_op=True)
667+
config = LlmConfig.from_args(args)
668+
self.assertTrue(config.model.use_moe_quantized_op)
669+
670+
def test_default_is_false(self) -> None:
671+
from executorch.extension.llm.export.config.llm_config import ModelConfig
672+
673+
self.assertFalse(ModelConfig().use_moe_quantized_op)
674+
675+
def test_from_args_missing_field_defaults_false(self) -> None:
676+
import argparse
677+
678+
from executorch.extension.llm.export.config.llm_config import LlmConfig
679+
680+
args = argparse.Namespace()
681+
config = LlmConfig.from_args(args)
682+
self.assertFalse(config.model.use_moe_quantized_op)
683+
684+
def test_argparser_flag_true(self) -> None:
685+
from executorch.examples.models.llama.export_llama_lib import build_args_parser
686+
687+
args = build_args_parser().parse_args(["--use_moe_quantized_op"])
688+
self.assertTrue(args.use_moe_quantized_op)
689+
690+
def test_argparser_flag_default_false(self) -> None:
691+
from executorch.examples.models.llama.export_llama_lib import build_args_parser
692+
693+
args = build_args_parser().parse_args([])
694+
self.assertFalse(args.use_moe_quantized_op)

extension/llm/export/config/llm_config.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,9 @@ class ModelConfig:
205205
use_kv_cache: bool = False
206206
quantize_kv_cache: bool = False
207207
local_global_attention: Optional[List[int]] = None
208+
# Replace eager MOEFeedForward modules with the
209+
# `llama::quantized_moe_ffn` portable-runtime custom op.
210+
use_moe_quantized_op: bool = False
208211

209212
def __post_init__(self):
210213
self._validate_attention_sink()
@@ -728,6 +731,8 @@ def from_args(cls, args: argparse.Namespace) -> "LlmConfig": # noqa: C901
728731
llm_config.model.quantize_kv_cache = args.quantize_kv_cache
729732
if hasattr(args, "local_global_attention"):
730733
llm_config.model.local_global_attention = args.local_global_attention
734+
if hasattr(args, "use_moe_quantized_op"):
735+
llm_config.model.use_moe_quantized_op = args.use_moe_quantized_op
731736

732737
# ExportConfig
733738
if hasattr(args, "max_seq_length"):

0 commit comments

Comments
 (0)