mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-07-20 19:12:24 +00:00
### Description This PR contains fusion-level and kernel-level optimizations for [Meta's LLaMA-2](https://blogs.microsoft.com/blog/2023/07/18/microsoft-and-meta-expand-their-ai-partnership-with-llama-2-on-azure-and-windows/). Some of the added optimizations include: - SimplifiedLayerNorm changes - Fusions for multiple variants - SkipSimplifiedLayerNorm changes - Kernel support for CPU - Rotary embeddings (previously did not exist) - Fusions for multiple variants - CPU and CUDA kernels - Supports interleaving and non-interleaving in the same kernels - Optimized cache that requires half of its originally exported sizes - Reduced from `(max_sequence_length, head_size)` to `(max_sequence_length, head_size / 2)` - Multi-head attention - Support for 2D and 3D attention masks - Group query attention (for FP16 CUDA and INT4 CUDA) - Integration with flash attention v2 and past-present buffer sharing - Removes need for `attention_mask` input as it is supported in the kernel - 4 bit quantization - `block_size` parameter is available for customizing - Support the new changes for [Microsoft version](https://github.com/microsoft/Llama-2-Onnx) - Support combinations of the below variants (ex: export ORT version and run with Optimum) Supported variants of LLaMA-2 include: - [ORT version](https://github.com/microsoft/onnxruntime/tree/main/onnxruntime/python/tools/transformers/models/llama) - Produces one ONNX file that is already optimized (and quantized if requested) - Integrates with Optimum - [Another Microsoft version](https://github.com/microsoft/Llama-2-Onnx) - Already exported and available off-the-shelf - Faster versions of those models will be uploaded there soon - [Hugging Face version](https://huggingface.co/meta-llama) - Models that end with `-hf` - Some older and current versions of [`transformers`](https://github.com/huggingface/transformers) and [`optimum`](https://github.com/huggingface/optimum) that export the model to ONNX differently - Note that while some older versions are supported, it is recommended to use the latest package versions. ### Usage To use the optimizations, please see `README.md` for details. Please note the various `requirements.txt` files for the package versions recommended in order to use these changes. To run the ORT transformer optimizer separately, run the script as follows: ``` $ cd onnxruntime/onnxruntime/python/tools/transformers/ $ python3 optimizer.py --input <filename>.onnx --output <filename>.onnx --model_type gpt2 --num_heads <number of attention heads> --hidden_size <attention hidden size> --use_external_data_format --opt_level 0 ``` ### Motivation and Context This PR helps the following issues: - https://github.com/microsoft/onnxruntime/issues/14997 - https://github.com/microsoft/onnxruntime/issues/16254 - https://github.com/microsoft/onnxruntime/issues/17681 - https://github.com/microsoft/onnxruntime/issues/17925 - https://github.com/microsoft/onnxruntime-inference-examples/issues/320 This PR uses changes from the following PRs: - https://github.com/pytorch/pytorch/pull/104468 - https://github.com/pytorch/pytorch/pull/109759 - https://github.com/microsoft/onnxruntime/pull/17020 - https://github.com/microsoft/onnxruntime/pull/17674 - https://github.com/microsoft/onnxruntime/pull/17890 - https://github.com/microsoft/onnxruntime/pull/17920 - https://github.com/huggingface/transformers/pull/26162 - https://github.com/huggingface/optimum/pull/1257 - https://github.com/huggingface/optimum/pull/1289 - https://github.com/huggingface/optimum/pull/1462 ### New TorchDynamo Exporter (experimental stage) This PR uses changes from the following issues and PRs to begin supporting the [new TorchDynamo exporter](https://pytorch.org/docs/stable/onnx.html#torchdynamo-based-onnx-exporter): - https://github.com/huggingface/transformers/pull/26307 - https://github.com/pytorch/pytorch/issues/104903 - https://github.com/pytorch/pytorch/pull/105040 - https://github.com/microsoft/onnxscript/pull/847 - https://github.com/microsoft/onnxscript/pull/862 - https://github.com/microsoft/onnxscript/issues/493
194 lines
8.5 KiB
Python
194 lines
8.5 KiB
Python
# -------------------------------------------------------------------------
|
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
# Licensed under the MIT License. See License.txt in the project root for
|
|
# license information.
|
|
# --------------------------------------------------------------------------
|
|
|
|
import os
|
|
import unittest
|
|
|
|
import onnx
|
|
from parity_utilities import find_transformers_source
|
|
from whisper_model_generator import (
|
|
create_whisper_decoder_attention,
|
|
create_whisper_decoder_multihead_attention,
|
|
create_whisper_decoder_with_past_multihead_cross_attention,
|
|
create_whisper_decoder_with_past_multihead_self_attention,
|
|
create_whisper_encoder_attention,
|
|
)
|
|
|
|
if find_transformers_source():
|
|
from fusion_options import FusionOptions
|
|
from onnx_model import OnnxModel
|
|
from optimizer import optimize_model
|
|
else:
|
|
from onnxruntime.transformers.fusion_options import FusionOptions
|
|
from onnxruntime.transformers.onnx_model import OnnxModel
|
|
from onnxruntime.transformers.optimizer import optimize_model
|
|
|
|
|
|
class TestFusion(unittest.TestCase):
|
|
def verify_fusion(self, optimized_model, expected_model_filename):
|
|
optimized_model.topological_sort(is_deterministic=True)
|
|
|
|
expected_model_path = os.path.join(
|
|
os.path.dirname(__file__), "test_data", "models", "whisper", expected_model_filename
|
|
)
|
|
expected_model = OnnxModel(onnx.load(expected_model_path))
|
|
expected_model.topological_sort(is_deterministic=True)
|
|
|
|
nodes = optimized_model.model.graph.node
|
|
self.assertEqual(len(nodes), len(expected_model.model.graph.node))
|
|
|
|
for i in range(len(nodes)):
|
|
self.assertEqual(nodes[i], expected_model.model.graph.node[i])
|
|
|
|
for expected_initializer in expected_model.model.graph.initializer:
|
|
self.assertTrue(
|
|
OnnxModel.has_same_value(
|
|
optimized_model.get_initializer(expected_initializer.name), expected_initializer
|
|
)
|
|
)
|
|
|
|
# Attention type #1 in fusion_bart_attention.py
|
|
def test_encoder_attention_fusion_with_skiplayernorm(self):
|
|
num_heads = 4
|
|
hidden_size = 64
|
|
model = create_whisper_encoder_attention(
|
|
num_heads=num_heads, hidden_size=hidden_size, add_before_layernorm=False
|
|
)
|
|
dir = "."
|
|
model_path = os.path.join(dir, "whisper_encoder_attention_sln.onnx")
|
|
onnx.save(model, model_path)
|
|
options = FusionOptions("bart")
|
|
optimized_model = optimize_model(
|
|
model_path, model_type="bart", num_heads=num_heads, hidden_size=hidden_size, optimization_options=options
|
|
)
|
|
os.remove(model_path)
|
|
self.verify_fusion(optimized_model, "encoder_attention_with_sln_fused.onnx")
|
|
|
|
# Attention type #2 in fusion_bart_attention.py
|
|
def test_decoder_attention_fusion_with_skiplayernorm(self):
|
|
num_heads = 4
|
|
hidden_size = 64
|
|
model = create_whisper_decoder_attention(
|
|
num_heads=num_heads, hidden_size=hidden_size, add_before_layernorm=False
|
|
)
|
|
dir = "."
|
|
model_path = os.path.join(dir, "whisper_decoder_attention_sln.onnx")
|
|
onnx.save(model, model_path)
|
|
options = FusionOptions("bart")
|
|
optimized_model = optimize_model(
|
|
model_path, model_type="bart", num_heads=num_heads, hidden_size=hidden_size, optimization_options=options
|
|
)
|
|
os.remove(model_path)
|
|
self.verify_fusion(optimized_model, "decoder_attention_with_sln_fused.onnx")
|
|
|
|
# Attention type #4 in fusion_bart_attention.py
|
|
def test_decoder_multihead_attention_fusion(self):
|
|
num_heads = 4
|
|
hidden_size = 64
|
|
model = create_whisper_decoder_multihead_attention(num_heads=num_heads, hidden_size=hidden_size)
|
|
dir = "."
|
|
model_path = os.path.join(dir, "whisper_decoder_mha.onnx")
|
|
onnx.save(model, model_path)
|
|
options = FusionOptions("bart")
|
|
options.use_multi_head_attention = True
|
|
optimized_model = optimize_model(
|
|
model_path, model_type="bart", num_heads=num_heads, hidden_size=hidden_size, optimization_options=options
|
|
)
|
|
os.remove(model_path)
|
|
self.verify_fusion(optimized_model, "decoder_mha_fused.onnx")
|
|
|
|
# Attention type #3 in fusion_bart_attention.py
|
|
def test_decoder_with_past_multihead_self_attention_fusion_with_skiplayernorm(self):
|
|
num_heads = 4
|
|
hidden_size = 64
|
|
model = create_whisper_decoder_with_past_multihead_self_attention(
|
|
num_heads=num_heads, hidden_size=hidden_size, add_before_layernorm=False
|
|
)
|
|
dir = "."
|
|
model_path = os.path.join(dir, "whisper_decoder_with_past_self_mha.onnx")
|
|
onnx.save(model, model_path)
|
|
options = FusionOptions("bart")
|
|
options.use_multi_head_attention = True
|
|
optimized_model = optimize_model(
|
|
model_path, model_type="bart", num_heads=num_heads, hidden_size=hidden_size, optimization_options=options
|
|
)
|
|
os.remove(model_path)
|
|
self.verify_fusion(optimized_model, "decoder_with_past_self_mha_fused.onnx")
|
|
|
|
# Attention type #5 in fusion_bart_attention.py
|
|
def test_decoder_with_past_multihead_cross_attention_fusion(self):
|
|
num_heads = 4
|
|
hidden_size = 64
|
|
model = create_whisper_decoder_with_past_multihead_cross_attention(num_heads=num_heads, hidden_size=hidden_size)
|
|
dir = "."
|
|
model_path = os.path.join(dir, "whisper_decoder_with_past_cross_mha.onnx")
|
|
onnx.save(model, model_path)
|
|
options = FusionOptions("bart")
|
|
options.use_multi_head_attention = True
|
|
optimized_model = optimize_model(
|
|
model_path, model_type="bart", num_heads=num_heads, hidden_size=hidden_size, optimization_options=options
|
|
)
|
|
os.remove(model_path)
|
|
self.verify_fusion(optimized_model, "decoder_with_past_cross_mha_fused.onnx")
|
|
|
|
# Attention type #4 in fusion_bart_attention.py
|
|
def test_decoder_multihead_attention_split_bias_fusion(self):
|
|
num_heads = 4
|
|
hidden_size = 64
|
|
model = create_whisper_decoder_multihead_attention(num_heads=num_heads, hidden_size=hidden_size)
|
|
dir = "."
|
|
model_path = os.path.join(dir, "whisper_decoder_mha.onnx")
|
|
onnx.save(model, model_path)
|
|
options = FusionOptions("bart")
|
|
options.use_multi_head_attention = True
|
|
options.disable_multi_head_attention_bias = True
|
|
optimized_model = optimize_model(
|
|
model_path, model_type="bart", num_heads=num_heads, hidden_size=hidden_size, optimization_options=options
|
|
)
|
|
os.remove(model_path)
|
|
self.verify_fusion(optimized_model, "decoder_mha_split_bias_fused.onnx")
|
|
|
|
# Attention type #3 in fusion_bart_attention.py
|
|
def test_decoder_with_past_multihead_self_attention_split_bias_fusion_with_skiplayernorm(self):
|
|
num_heads = 4
|
|
hidden_size = 64
|
|
model = create_whisper_decoder_with_past_multihead_self_attention(
|
|
num_heads=num_heads, hidden_size=hidden_size, add_before_layernorm=False
|
|
)
|
|
dir = "."
|
|
model_path = os.path.join(dir, "whisper_decoder_with_past_self_mha.onnx")
|
|
onnx.save(model, model_path)
|
|
options = FusionOptions("bart")
|
|
options.use_multi_head_attention = True
|
|
options.disable_multi_head_attention_bias = True
|
|
|
|
optimized_model = optimize_model(
|
|
model_path, model_type="bart", num_heads=num_heads, hidden_size=hidden_size, optimization_options=options
|
|
)
|
|
os.remove(model_path)
|
|
self.verify_fusion(optimized_model, "decoder_with_past_self_mha_split_bias_fused.onnx")
|
|
|
|
# Attention type #5 in fusion_bart_attention.py
|
|
def test_decoder_with_past_multihead_cross_attention_split_bias_fusion(self):
|
|
num_heads = 4
|
|
hidden_size = 64
|
|
model = create_whisper_decoder_with_past_multihead_cross_attention(num_heads=num_heads, hidden_size=hidden_size)
|
|
dir = "."
|
|
model_path = os.path.join(dir, "whisper_decoder_with_past_cross_mha.onnx")
|
|
onnx.save(model, model_path)
|
|
options = FusionOptions("bart")
|
|
options.use_multi_head_attention = True
|
|
options.disable_multi_head_attention_bias = True
|
|
|
|
optimized_model = optimize_model(
|
|
model_path, model_type="bart", num_heads=num_heads, hidden_size=hidden_size, optimization_options=options
|
|
)
|
|
os.remove(model_path)
|
|
self.verify_fusion(optimized_model, "decoder_with_past_cross_mha_split_bias_fused.onnx")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|