| # Copyright (c) Meta Platforms, Inc. and affiliates. |
| # All rights reserved. |
| # |
| # This source code is licensed under the BSD-style license found in the |
| # LICENSE file in the root directory of this source tree. |
| |
| import unittest |
| |
| import torch |
| from executorch.examples.models import MODEL_NAME_TO_MODEL |
| from executorch.examples.models.model_factory import EagerModelFactory |
| |
| from executorch.extension.export_util.utils import export_to_edge |
| |
| from executorch.extension.pybindings.portable_lib import ( # @manual |
| _load_for_executorch_from_buffer, |
| ) |
| |
| |
| class ExportTest(unittest.TestCase): |
| def collect_executorch_and_eager_outputs( |
| self, |
| eager_model: torch.nn.Module, |
| example_inputs, |
| ): |
| """ |
| Compares the output of the given eager mode PyTorch model with the output |
| of the equivalent executorch model, both provided with example inputs. |
| Returns a tuple containing the outputs of the eager mode model and the executorch mode model. |
| """ |
| eager_model = eager_model.eval() |
| model = torch.export.export_for_training(eager_model, example_inputs).module() |
| edge_model = export_to_edge(model, example_inputs) |
| |
| executorch_prog = edge_model.to_executorch() |
| |
| pte_model = _load_for_executorch_from_buffer(executorch_prog.buffer) |
| |
| with torch.no_grad(): |
| eager_output = eager_model(*example_inputs) |
| with torch.no_grad(): |
| executorch_output = pte_model.run_method("forward", example_inputs) |
| |
| return (eager_output, executorch_output) |
| |
| def validate_tensor_allclose( |
| self, eager_output, executorch_output, rtol=1e-5, atol=1e-5 |
| ): |
| self.assertTrue( |
| isinstance(eager_output, type(executorch_output)), |
| f"Outputs are not of the same type: eager type: {type(eager_output)}, executorch type: {type(executorch_output)}", |
| ) |
| self.assertTrue( |
| len(eager_output) == len(executorch_output), |
| f"len(eager_output)={len(eager_output)}, len(executorch_output)={len(executorch_output)}", |
| ) |
| result = True |
| for i in range(len(eager_output)): |
| result = torch.allclose( |
| eager_output[i], |
| executorch_output[i], |
| rtol=rtol, |
| atol=atol, |
| ) |
| if not result: |
| print(f"eager output[{i}]: {eager_output[i]}") |
| print(f"executorch output[{i}]: {executorch_output[i]}") |
| break |
| return self.assertTrue(result) |
| |
| def test_mv3_export_to_executorch(self): |
| eager_model, example_inputs, _ = EagerModelFactory.create_model( |
| *MODEL_NAME_TO_MODEL["mv3"] |
| ) |
| eager_output, executorch_output = self.collect_executorch_and_eager_outputs( |
| eager_model, example_inputs |
| ) |
| # TODO(T166083470): Fix accuracy issue |
| self.validate_tensor_allclose( |
| eager_output, executorch_output[0], rtol=1e-3, atol=1e-5 |
| ) |
| |
| def test_mv2_export_to_executorch(self): |
| eager_model, example_inputs, _ = EagerModelFactory.create_model( |
| *MODEL_NAME_TO_MODEL["mv2"] |
| ) |
| eager_output, executorch_output = self.collect_executorch_and_eager_outputs( |
| eager_model, example_inputs |
| ) |
| self.validate_tensor_allclose(eager_output, executorch_output[0]) |
| |
| def test_vit_export_to_executorch(self): |
| eager_model, example_inputs, _ = EagerModelFactory.create_model( |
| *MODEL_NAME_TO_MODEL["vit"] |
| ) |
| eager_output, executorch_output = self.collect_executorch_and_eager_outputs( |
| eager_model, example_inputs |
| ) |
| # TODO(T166083470): Fix accuracy, detected on Arm64 |
| self.validate_tensor_allclose( |
| eager_output, executorch_output[0], rtol=1e-2, atol=1e-2 |
| ) |
| |
| def test_w2l_export_to_executorch(self): |
| eager_model, example_inputs, _ = EagerModelFactory.create_model( |
| *MODEL_NAME_TO_MODEL["w2l"] |
| ) |
| eager_output, executorch_output = self.collect_executorch_and_eager_outputs( |
| eager_model, example_inputs |
| ) |
| self.validate_tensor_allclose(eager_output, executorch_output[0]) |
| |
| def test_ic3_export_to_executorch(self): |
| eager_model, example_inputs, _ = EagerModelFactory.create_model( |
| *MODEL_NAME_TO_MODEL["ic3"] |
| ) |
| eager_output, executorch_output = self.collect_executorch_and_eager_outputs( |
| eager_model, example_inputs |
| ) |
| # TODO(T166083470): Fix accuracy issue |
| self.validate_tensor_allclose( |
| eager_output, executorch_output[0], rtol=1e-3, atol=1e-5 |
| ) |
| |
| def test_resnet18_export_to_executorch(self): |
| eager_model, example_inputs, _ = EagerModelFactory.create_model( |
| *MODEL_NAME_TO_MODEL["resnet18"] |
| ) |
| eager_output, executorch_output = self.collect_executorch_and_eager_outputs( |
| eager_model, example_inputs |
| ) |
| self.validate_tensor_allclose(eager_output, executorch_output[0]) |
| |
| def test_resnet50_export_to_executorch(self): |
| eager_model, example_inputs, _ = EagerModelFactory.create_model( |
| *MODEL_NAME_TO_MODEL["resnet50"] |
| ) |
| eager_output, executorch_output = self.collect_executorch_and_eager_outputs( |
| eager_model, example_inputs |
| ) |
| self.validate_tensor_allclose(eager_output, executorch_output[0]) |
| |
| def test_dl3_export_to_executorch(self): |
| eager_model, example_inputs, _ = EagerModelFactory.create_model( |
| *MODEL_NAME_TO_MODEL["dl3"] |
| ) |
| eager_output, executorch_output = self.collect_executorch_and_eager_outputs( |
| eager_model, example_inputs |
| ) |
| self.validate_tensor_allclose(list(eager_output.values()), executorch_output) |