Skip to content

Commit 9f6b586

Browse files
xingguo01Copilot
andauthored
LLM extension: add ethosu 8w16a and quantize scope plumbing (#19876)
- adds the `ethosu_8w16a` PT2E quantization mode - introduces shared `quantization.quantize_scope` handling for Arm backends - wires the Arm quantize scope through the LLM export path - passes Ethos-U system config and memory mode through the partitioner setup cc @digantdesai @freddan80 @per @zingo @oscarandersson8218 @mansnils @Sebastian-Larsson @robell @rascani --------- Signed-off-by: Xingguo Li <xingguo.li@arm.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 9c48e9d commit 9f6b586

4 files changed

Lines changed: 84 additions & 14 deletions

File tree

examples/models/llama/export_llama_lib.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ def build_args_parser() -> argparse.ArgumentParser:
231231
"vulkan_8w",
232232
"tosa_8a8w",
233233
"ethosu_8a8w",
234+
"ethosu_16a8w",
234235
"vgf_8a8w",
235236
"vgf_16a8w",
236237
],
@@ -845,17 +846,29 @@ def get_quantizer_and_quant_params(llm_config):
845846
llm_config.quantization.pt2e_quantize.value
846847
)
847848
quantizers.append(coreml_quantizer)
849+
arm_quantize_scope = llm_config.quantization.quantize_scope.value
850+
if (
851+
arm_quantize_scope == "full"
852+
and llm_config.backend.vgf.enabled
853+
and llm_config.backend.vgf.quantize_scope.value != "full"
854+
):
855+
arm_quantize_scope = llm_config.backend.vgf.quantize_scope.value
856+
848857
if llm_config.backend.tosa.enabled and llm_config.quantization.pt2e_quantize:
849858
tosa_quantizer = get_tosa_quantizer(
850-
llm_config.backend.tosa.version, llm_config.quantization.pt2e_quantize.value
859+
llm_config.backend.tosa.version,
860+
llm_config.quantization.pt2e_quantize.value,
861+
arm_quantize_scope,
851862
)
852863
quantizers.append(tosa_quantizer)
853864
if llm_config.backend.ethosu.enabled and llm_config.quantization.pt2e_quantize:
854865
ethosu_quantizer = get_ethosu_quantizer(
855866
llm_config.backend.ethosu.target,
856867
llm_config.backend.ethosu.system_config,
857868
llm_config.backend.ethosu.memory_mode,
869+
llm_config.backend.ethosu.extra_flags,
858870
llm_config.quantization.pt2e_quantize.value,
871+
arm_quantize_scope,
859872
)
860873
quantizers.append(ethosu_quantizer)
861874
if llm_config.backend.vgf.enabled and llm_config.quantization.pt2e_quantize:
@@ -1054,6 +1067,7 @@ def _to_edge_and_lower_llama_arm(
10541067
llm_config.backend.ethosu.target,
10551068
llm_config.backend.ethosu.system_config,
10561069
llm_config.backend.ethosu.memory_mode,
1070+
llm_config.backend.ethosu.extra_flags,
10571071
)
10581072
)
10591073
modelname = f"ethosu_{modelname}"

extension/llm/export/config/llm_config.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ class Pt2eQuantize(str, Enum):
377377
vulkan_8w = "vulkan_8w"
378378
tosa_8a8w = "tosa_8a8w"
379379
ethosu_8a8w = "ethosu_8a8w"
380+
ethosu_16a8w = "ethosu_16a8w"
380381
vgf_8a8w = "vgf_8a8w"
381382
vgf_16a8w = "vgf_16a8w"
382383

@@ -386,6 +387,11 @@ class SpinQuant(str, Enum):
386387
native = "native"
387388

388389

390+
class QuantizeScope(str, Enum):
391+
full = "full"
392+
linear = "linear"
393+
394+
389395
@dataclass
390396
class QuantizationConfig:
391397
"""
@@ -403,6 +409,9 @@ class QuantizationConfig:
403409
use_spin_quant: Which spin quant mode to use. If unspecified, don't use
404410
spin quant.
405411
use_qat: Whether the checkpoint is quantization-awarely trained.
412+
quantize_scope: Scope for Arm PT2E quantization. "full" quantizes the
413+
full supported graph, while "linear" limits quantization to
414+
torch.nn.Linear modules.
406415
calibration_tasks: Tasks for GPTQ calibration from lm_eval.
407416
calibration_limit: Number of samples used for calibration from lm_eval.
408417
calibration_seq_length: Sequence length for GPTQ calibration from lm_eval.
@@ -427,6 +436,7 @@ class QuantizationConfig:
427436
group_size: Optional[int] = None
428437
use_spin_quant: Optional[SpinQuant] = None
429438
use_qat: bool = False
439+
quantize_scope: QuantizeScope = QuantizeScope.full
430440
calibration_tasks: Optional[List[str]] = None
431441
calibration_limit: Optional[int] = None
432442
calibration_seq_length: Optional[int] = None
@@ -587,6 +597,7 @@ class EthosUConfig:
587597
target: str = "ethos-u85-128" # Default target, can be overridden.
588598
memory_mode: str = "default"
589599
system_config: str = "default"
600+
extra_flags: List[str] = field(default_factory=list)
590601

591602

592603
class VgfQuantizeScope(str, Enum):
@@ -832,7 +843,9 @@ def from_args(cls, args: argparse.Namespace) -> "LlmConfig": # noqa: C901
832843
llm_config.backend.vgf.quantize_scope = VgfQuantizeScope(
833844
args.vgf_quantize_scope
834845
)
835-
846+
llm_config.quantization.quantize_scope = QuantizeScope(
847+
args.vgf_quantize_scope
848+
)
836849
# TorchAoKernels
837850
if any(
838851
hasattr(args, a)

extension/llm/export/partitioner_lib.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ def get_ethosu_partitioner(
252252
target: str,
253253
system_config: Optional[str] = None,
254254
memory_mode: Optional[str] = None,
255+
extra_flags: Optional[List[str]] = None,
255256
):
256257
from executorch.backends.arm.ethosu.compile_spec import EthosUCompileSpec
257258
from executorch.backends.arm.ethosu.partitioner import EthosUPartitioner
@@ -260,6 +261,7 @@ def get_ethosu_partitioner(
260261
target,
261262
system_config=None if system_config == "default" else system_config,
262263
memory_mode=None if memory_mode == "default" else memory_mode,
264+
extra_flags=extra_flags,
263265
)
264266

265267
return EthosUPartitioner(compile_spec)

extension/llm/export/quantizer_lib.py

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ def get_vulkan_quantizer(pt2e_quantize: str):
323323
return quantizer
324324

325325

326-
def get_tosa_quantizer(version: str, pt2e_quantize: str):
326+
def get_tosa_quantizer(version: str, pt2e_quantize: str, quantize_scope: str):
327327
from executorch.backends.arm.quantizer.arm_quantizer import (
328328
get_symmetric_quantization_config,
329329
TOSAQuantizer,
@@ -335,34 +335,76 @@ def get_tosa_quantizer(version: str, pt2e_quantize: str):
335335
quantizer = TOSAQuantizer(compile_spec)
336336

337337
if pt2e_quantize == "tosa_8a8w":
338-
quantizer.set_global(get_symmetric_quantization_config())
338+
quantization_config = get_symmetric_quantization_config()
339339
else:
340340
raise ValueError(f"Unsupported quantizer specification {pt2e_quantize}")
341341

342+
_apply_arm_quantize_scope(
343+
quantizer,
344+
quantization_config=quantization_config,
345+
quantize_scope=quantize_scope,
346+
backend_name="TOSA",
347+
)
342348
return quantizer
343349

344350

345351
def get_ethosu_quantizer(
346-
target: str, system_config: str, memory_mode: str, pt2e_quantize: str
352+
target: str,
353+
system_config: str,
354+
memory_mode: str,
355+
extra_flags: Optional[List[str]],
356+
pt2e_quantize: str,
357+
quantize_scope: str,
347358
):
348359
from executorch.backends.arm.ethosu.compile_spec import EthosUCompileSpec
349360
from executorch.backends.arm.quantizer.arm_quantizer import (
350361
EthosUQuantizer,
362+
get_symmetric_a16w8_quantization_config,
351363
get_symmetric_quantization_config,
352364
)
353365

354-
compile_spec = EthosUCompileSpec(target, system_config, memory_mode)
366+
compile_spec = EthosUCompileSpec(
367+
target=target,
368+
system_config=None if system_config == "default" else system_config,
369+
memory_mode=None if memory_mode == "default" else memory_mode,
370+
extra_flags=extra_flags,
371+
)
355372

356373
quantizer = EthosUQuantizer(compile_spec)
357374

358375
if pt2e_quantize == "ethosu_8a8w":
359-
quantizer.set_global(get_symmetric_quantization_config())
376+
quantization_config = get_symmetric_quantization_config()
377+
elif pt2e_quantize == "ethosu_16a8w":
378+
quantization_config = get_symmetric_a16w8_quantization_config()
360379
else:
361380
raise ValueError(f"Unsupported quantizer specification {pt2e_quantize}")
362381

382+
_apply_arm_quantize_scope(
383+
quantizer,
384+
quantization_config=quantization_config,
385+
quantize_scope=quantize_scope,
386+
backend_name="Ethos-U",
387+
)
363388
return quantizer
364389

365390

391+
def _apply_arm_quantize_scope(
392+
quantizer,
393+
*,
394+
quantization_config,
395+
quantize_scope: str,
396+
backend_name: str,
397+
):
398+
if quantize_scope == "full":
399+
quantizer.set_global(quantization_config)
400+
elif quantize_scope == "linear":
401+
quantizer.set_module_type(torch.nn.Linear, quantization_config)
402+
else:
403+
raise ValueError(
404+
f"Unsupported {backend_name} quantization scope {quantize_scope}"
405+
)
406+
407+
366408
def get_vgf_quantizer(
367409
compile_spec: Optional[str],
368410
compiler_flags: Optional[List[str]],
@@ -392,11 +434,10 @@ def get_vgf_quantizer(
392434
else:
393435
raise ValueError(f"Unsupported quantizer specification {pt2e_quantize}")
394436

395-
if quantize_scope == "full":
396-
quantizer.set_global(quantization_config)
397-
elif quantize_scope == "linear":
398-
quantizer.set_module_type(torch.nn.Linear, quantization_config)
399-
else:
400-
raise ValueError(f"Unsupported VGF quantization scope {quantize_scope}")
401-
437+
_apply_arm_quantize_scope(
438+
quantizer,
439+
quantization_config=quantization_config,
440+
quantize_scope=quantize_scope,
441+
backend_name="VGF",
442+
)
402443
return quantizer

0 commit comments

Comments
 (0)