From ea7760261aac0f09e0a011ea93bc5880627ba6ce Mon Sep 17 00:00:00 2001 From: Xinqi Li Date: Thu, 31 Jul 2025 08:23:16 +0000 Subject: [PATCH 1/7] support checking model redundancy --- graph_net/test/vision_model_test.py | 4 +- graph_net/torch/__init__.py | 3 +- .../torch/check_redundant_incrementally.py | 64 +++++++++++++++++ graph_net/torch/extractor.py | 17 ++++- .../torch/remove_redundant_incrementally.py | 69 +++++++++++++++++++ graph_net/torch/samples_util.py | 5 ++ graph_net/torch/single_device_runner.py | 67 +++++++++++++----- graph_net/torch/validate.py | 14 +++- samples/torchvision/alexnet/graph_hash.txt | 1 + .../torchvision/convnext_base/graph_hash.txt | 1 + .../torchvision/convnext_large/graph_hash.txt | 1 + .../torchvision/convnext_small/graph_hash.txt | 1 + .../torchvision/convnext_tiny/graph_hash.txt | 1 + .../torchvision/densenet121/graph_hash.txt | 1 + .../torchvision/densenet161/graph_hash.txt | 1 + .../torchvision/densenet169/graph_hash.txt | 1 + .../torchvision/densenet201/graph_hash.txt | 1 + .../efficientnet_b0/graph_hash.txt | 1 + .../efficientnet_b1/graph_hash.txt | 1 + samples/torchvision/googlenet/graph_hash.txt | 1 + .../torchvision/inception_v3/graph_hash.txt | 1 + samples/torchvision/mnasnet0_5/graph_hash.txt | 1 + .../torchvision/mnasnet0_75/graph_hash.txt | 1 + samples/torchvision/mnasnet1_0/graph_hash.txt | 1 + samples/torchvision/mnasnet1_3/graph_hash.txt | 1 + .../torchvision/mobilenet_v2/graph_hash.txt | 1 + .../mobilenet_v3_large/graph_hash.txt | 1 + .../mobilenet_v3_small/graph_hash.txt | 1 + samples/torchvision/r3d_18/graph_hash.txt | 1 + .../torchvision/regnet_x_16gf/graph_hash.txt | 1 + .../torchvision/regnet_x_1_6gf/graph_hash.txt | 1 + .../torchvision/regnet_x_32gf/graph_hash.txt | 1 + .../torchvision/regnet_x_3_2gf/graph_hash.txt | 1 + .../torchvision/regnet_x_400mf/graph_hash.txt | 1 + .../torchvision/regnet_x_800mf/graph_hash.txt | 1 + .../torchvision/regnet_x_8gf/graph_hash.txt | 1 + .../torchvision/regnet_y_128gf/graph_hash.txt | 1 + samples/torchvision/resnet101/graph_hash.txt | 1 + samples/torchvision/resnet152/graph_hash.txt | 1 + samples/torchvision/resnet18/graph_hash.txt | 1 + samples/torchvision/resnet34/graph_hash.txt | 1 + samples/torchvision/resnet50/graph_hash.txt | 1 + .../resnext101_32x8d/graph_hash.txt | 1 + .../resnext101_64x4d/graph_hash.txt | 1 + .../resnext50_32x4d/graph_hash.txt | 1 + .../torchvision/squeezenet1_0/graph_hash.txt | 1 + .../torchvision/squeezenet1_1/graph_hash.txt | 1 + samples/torchvision/vgg11/graph_hash.txt | 1 + samples/torchvision/vgg11_bn/graph_hash.txt | 1 + samples/torchvision/vgg13/graph_hash.txt | 1 + samples/torchvision/vgg13_bn/graph_hash.txt | 1 + samples/torchvision/vgg16/graph_hash.txt | 1 + samples/torchvision/vgg16_bn/graph_hash.txt | 1 + samples/torchvision/vgg19/graph_hash.txt | 1 + samples/torchvision/vgg19_bn/graph_hash.txt | 1 + .../distilbert-base-uncased/graph_hash.txt | 1 + 56 files changed, 268 insertions(+), 23 deletions(-) create mode 100644 graph_net/torch/check_redundant_incrementally.py create mode 100644 graph_net/torch/remove_redundant_incrementally.py create mode 100644 graph_net/torch/samples_util.py create mode 100644 samples/torchvision/alexnet/graph_hash.txt create mode 100644 samples/torchvision/convnext_base/graph_hash.txt create mode 100644 samples/torchvision/convnext_large/graph_hash.txt create mode 100644 samples/torchvision/convnext_small/graph_hash.txt create mode 100644 samples/torchvision/convnext_tiny/graph_hash.txt create mode 100644 samples/torchvision/densenet121/graph_hash.txt create mode 100644 samples/torchvision/densenet161/graph_hash.txt create mode 100644 samples/torchvision/densenet169/graph_hash.txt create mode 100644 samples/torchvision/densenet201/graph_hash.txt create mode 100644 samples/torchvision/efficientnet_b0/graph_hash.txt create mode 100644 samples/torchvision/efficientnet_b1/graph_hash.txt create mode 100644 samples/torchvision/googlenet/graph_hash.txt create mode 100644 samples/torchvision/inception_v3/graph_hash.txt create mode 100644 samples/torchvision/mnasnet0_5/graph_hash.txt create mode 100644 samples/torchvision/mnasnet0_75/graph_hash.txt create mode 100644 samples/torchvision/mnasnet1_0/graph_hash.txt create mode 100644 samples/torchvision/mnasnet1_3/graph_hash.txt create mode 100644 samples/torchvision/mobilenet_v2/graph_hash.txt create mode 100644 samples/torchvision/mobilenet_v3_large/graph_hash.txt create mode 100644 samples/torchvision/mobilenet_v3_small/graph_hash.txt create mode 100644 samples/torchvision/r3d_18/graph_hash.txt create mode 100644 samples/torchvision/regnet_x_16gf/graph_hash.txt create mode 100644 samples/torchvision/regnet_x_1_6gf/graph_hash.txt create mode 100644 samples/torchvision/regnet_x_32gf/graph_hash.txt create mode 100644 samples/torchvision/regnet_x_3_2gf/graph_hash.txt create mode 100644 samples/torchvision/regnet_x_400mf/graph_hash.txt create mode 100644 samples/torchvision/regnet_x_800mf/graph_hash.txt create mode 100644 samples/torchvision/regnet_x_8gf/graph_hash.txt create mode 100644 samples/torchvision/regnet_y_128gf/graph_hash.txt create mode 100644 samples/torchvision/resnet101/graph_hash.txt create mode 100644 samples/torchvision/resnet152/graph_hash.txt create mode 100644 samples/torchvision/resnet18/graph_hash.txt create mode 100644 samples/torchvision/resnet34/graph_hash.txt create mode 100644 samples/torchvision/resnet50/graph_hash.txt create mode 100644 samples/torchvision/resnext101_32x8d/graph_hash.txt create mode 100644 samples/torchvision/resnext101_64x4d/graph_hash.txt create mode 100644 samples/torchvision/resnext50_32x4d/graph_hash.txt create mode 100644 samples/torchvision/squeezenet1_0/graph_hash.txt create mode 100644 samples/torchvision/squeezenet1_1/graph_hash.txt create mode 100644 samples/torchvision/vgg11/graph_hash.txt create mode 100644 samples/torchvision/vgg11_bn/graph_hash.txt create mode 100644 samples/torchvision/vgg13/graph_hash.txt create mode 100644 samples/torchvision/vgg13_bn/graph_hash.txt create mode 100644 samples/torchvision/vgg16/graph_hash.txt create mode 100644 samples/torchvision/vgg16_bn/graph_hash.txt create mode 100644 samples/torchvision/vgg19/graph_hash.txt create mode 100644 samples/torchvision/vgg19_bn/graph_hash.txt create mode 100644 samples/transformers-auto-model/distilbert-base-uncased/graph_hash.txt diff --git a/graph_net/test/vision_model_test.py b/graph_net/test/vision_model_test.py index 498a9c207e..0002cbec65 100644 --- a/graph_net/test/vision_model_test.py +++ b/graph_net/test/vision_model_test.py @@ -29,9 +29,9 @@ model.to(device) normalized_input = normalized_input.to(device) - model = graph_net.torch.extract(name="resnet18")(model) + model = graph_net.torch.extract(name="resnet18", placeholder_auto_rename=True)(model) print("Running inference...") print("Input shape:", normalized_input.shape) output = model(normalized_input) - print("Inference finished. Output shape:", output.shape) \ No newline at end of file + print("Inference finished. Output shape:", output.shape) diff --git a/graph_net/torch/__init__.py b/graph_net/torch/__init__.py index 2fc126c2ca..eaafa44978 100644 --- a/graph_net/torch/__init__.py +++ b/graph_net/torch/__init__.py @@ -3,5 +3,6 @@ """ from .extractor import extract +from .samples_util import get_default_samples_directory -__all__ = ['extract'] \ No newline at end of file +__all__ = ['extract', 'get_default_samples_directory'] diff --git a/graph_net/torch/check_redundant_incrementally.py b/graph_net/torch/check_redundant_incrementally.py new file mode 100644 index 0000000000..d07686f038 --- /dev/null +++ b/graph_net/torch/check_redundant_incrementally.py @@ -0,0 +1,64 @@ +from . import utils +import argparse +import importlib.util +import inspect +import torch +from pathlib import Path +from typing import Type, Any +import sys +import os +import os.path +from dataclasses import dataclass +from contextlib import contextmanager +import time +import glob + +def get_recursively_model_pathes(root_dir): + if is_single_model_dir(root_dir): + yield root_dir + return + for sub_dir in get_immediate_subdirectory_paths(root_dir): + if is_single_model_dir(sub_dir): + yield sub_dir + else: + yield from get_recursively_model_pathes(sub_dir) + +def get_immediate_subdirectory_paths(parent_dir): + return [ + sub_dir + for name in os.listdir(parent_dir) + for sub_dir in [os.path.join(parent_dir, name)] + if os.path.isdir(sub_dir) + ] + +def is_single_model_dir(model_dir): + return os.path.isfile(f"{model_dir}/graph_net.json") + +def main(args): + assert os.path.isdir(args.model_path) + assert os.path.isdir(args.graph_net_samples_path) + current_model_graph_hash_pathes = set( + graph_hash_path + for model_path in get_recursively_model_pathes(args.model_path) + for graph_hash_path in [f"{model_path}/graph_hash.txt"] + ) + graph_hash2graph_net_model_path = { + graph_hash:graph_hash_path + for model_path in get_recursively_model_pathes(args.graph_net_samples_path) + for graph_hash_path in [f"{model_path}/graph_hash.txt"] + if os.path.isfile(graph_hash_path) + if graph_hash_path not in current_model_graph_hash_pathes + for graph_hash in [open(graph_hash_path).read()] + } + for current_model_graph_hash_path in current_model_graph_hash_pathes: + graph_hash = open(current_model_graph_hash_path).read() + assert graph_hash not in graph_hash2graph_net_model_path, f"Redundant models detected. old-model-path:{current_model_graph_hash_path}, new-model-path:{graph_hash2graph_net_model_path[graph_hash]}." + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="Test compiler performance.") + parser.add_argument("--model-path", type=str, required=True, + help="Path to model file(s), each subdirectory containing graph_net.json will be regarded as a model") + parser.add_argument("--graph-net-samples-path", type=str, required=False, default='default', + help="Path to GraphNet samples") + args = parser.parse_args() + main(args=args) diff --git a/graph_net/torch/extractor.py b/graph_net/torch/extractor.py index 744221236b..535f99fde4 100644 --- a/graph_net/torch/extractor.py +++ b/graph_net/torch/extractor.py @@ -5,7 +5,7 @@ from . import utils -def extract(name, dynamic=True): +def extract(name, dynamic=True, mut_graph_codes=None, placeholder_auto_rename=False): """ A decorator for PyTorch functions to capture the computation graph. @@ -30,16 +30,31 @@ def extractor(gm: torch.fx.GraphModule, sample_inputs): # 2. Get full params params = {} input_idx = 0 + unique_id = 0 + def try_rename_placeholder(node): + assert node.op == "placeholder" + if not placeholder_auto_rename: + return + nonlocal unique_id + node.target = f"v{unique_id}" + unique_id += 1 + node.name = f"v{unique_id}" + unique_id += 1 for node in gm.graph.nodes: if node.op == "placeholder": + try_rename_placeholder(node) input = sample_inputs[input_idx] if isinstance(input, torch.SymInt): input = torch.tensor(4) params[node.target] = input input_idx += 1 assert input_idx == len(sample_inputs) + if mut_graph_codes is not None: + assert isinstance(mut_graph_codes, list) + mut_graph_codes.append(gm.code) # 3. Generate and save model code base_code = gm.code + # gm.graph.print_tabular() write_code = utils.apply_templates(base_code) with open(os.path.join(model_path, "model.py"), "w") as fp: fp.write(write_code) diff --git a/graph_net/torch/remove_redundant_incrementally.py b/graph_net/torch/remove_redundant_incrementally.py new file mode 100644 index 0000000000..2cf8e137f6 --- /dev/null +++ b/graph_net/torch/remove_redundant_incrementally.py @@ -0,0 +1,69 @@ +from . import utils +import argparse +import importlib.util +import inspect +import torch +from pathlib import Path +from typing import Type, Any +import sys +import os +import os.path +from dataclasses import dataclass +from contextlib import contextmanager +import time +import glob +import shutil + +def get_recursively_model_pathes(root_dir): + if is_single_model_dir(root_dir): + yield root_dir + return + for sub_dir in get_immediate_subdirectory_paths(root_dir): + if is_single_model_dir(sub_dir): + yield sub_dir + else: + yield from get_recursively_model_pathes(sub_dir) + +def get_immediate_subdirectory_paths(parent_dir): + return [ + sub_dir + for name in os.listdir(parent_dir) + for sub_dir in [os.path.join(parent_dir, name)] + if os.path.isdir(sub_dir) + ] + +def is_single_model_dir(model_dir): + return os.path.isfile(f"{model_dir}/graph_net.json") + +def main(args): + assert os.path.isdir(args.model_path) + assert os.path.isdir(args.graph_net_samples_path) + current_model_graph_hash_pathes = set( + graph_hash_path + for model_path in get_recursively_model_pathes(args.model_path) + for graph_hash_path in [f"{model_path}/graph_hash.txt"] + ) + graph_hash2graph_net_model_path = { + graph_hash:graph_hash_path + for model_path in get_recursively_model_pathes(args.graph_net_samples_path) + for graph_hash_path in [f"{model_path}/graph_hash.txt"] + if os.path.isfile(graph_hash_path) + if graph_hash_path not in current_model_graph_hash_pathes + for graph_hash in [open(graph_hash_path).read()] + } + for current_model_graph_hash_path in current_model_graph_hash_pathes: + graph_hash = open(current_model_graph_hash_path).read() + if graph_hash not in graph_hash2graph_net_model_path: + continue + directory = os.path.dirname(current_model_graph_hash_path) + shutil.rmtree(directory) + os.makedirs(directory, exist_ok=True) + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="Test compiler performance.") + parser.add_argument("--model-path", type=str, required=True, + help="Path to model file(s), each subdirectory containing graph_net.json will be regarded as a model") + parser.add_argument("--graph-net-samples-path", type=str, required=False, default='default', + help="Path to GraphNet samples") + args = parser.parse_args() + main(args=args) diff --git a/graph_net/torch/samples_util.py b/graph_net/torch/samples_util.py new file mode 100644 index 0000000000..3fa7ea910d --- /dev/null +++ b/graph_net/torch/samples_util.py @@ -0,0 +1,5 @@ +import graph_net +import os + +def get_default_samples_directory(): + return f"{os.path.dirname(graph_net.__file__)}/../samples" diff --git a/graph_net/torch/single_device_runner.py b/graph_net/torch/single_device_runner.py index aedf3aeafe..732d9a8817 100644 --- a/graph_net/torch/single_device_runner.py +++ b/graph_net/torch/single_device_runner.py @@ -7,6 +7,8 @@ from typing import Type, Any import sys from graph_net.torch.extractor import extract +import hashlib +from contextlib import contextmanager def load_class_from_file(file_path: str, class_name: str) -> Type[torch.nn.Module]: @@ -15,32 +17,61 @@ def load_class_from_file(file_path: str, class_name: str) -> Type[torch.nn.Modul spec.loader.exec_module(unnamed) model_class = getattr(unnamed, class_name, None) return model_class + +def _get_sha_hash(content): + m = hashlib.sha256() + m.update(content.encode()) + return m.hexdigest() -def main(args): - model_path = args.model_path - model_class = load_class_from_file(f"{model_path}/model.py", class_name="GraphModule") - assert model_class is not None - model = model_class() - print(f'{model_path=}') - if args.enable_extract: - assert args.extract_name is not None - model = extract(name=args.extract_name)(model) - - inputs_params = utils.load_converted_from_text(f'{model_path}') - params = inputs_params["weight_info"] - state_dict = { - k: utils.replay_tensor(v) for k, v in params.items() +def _save_to_model_path(dump_dir, hash_text): + file_path = f"{dump_dir}/graph_hash.txt" + with open(file_path, 'w') as f: + f.write(hash_text) + +@contextmanager +def _dump_graph_hash_key_ctx(cmd_args): + if not cmd_args.dump_graph_hash_key: + yield {} + return + mut_graph_codes = [] + extractor_kwarg = { + 'placeholder_auto_rename': True, + 'mut_graph_codes': mut_graph_codes } - - y = model(**state_dict)[0] + yield extractor_kwarg + if len(mut_graph_codes) > 0: + assert len(mut_graph_codes) == 1, f"{len(mut_graph_codes)=}" + _save_to_model_path(cmd_args.model_path, _get_sha_hash(mut_graph_codes[0])) + + +def main(args): + with _dump_graph_hash_key_ctx(args) as dump_graph_options: + model_path = args.model_path + model_class = load_class_from_file(f"{model_path}/model.py", class_name="GraphModule") + assert model_class is not None + model = model_class() + print(f'{model_path=}') + if args.enable_extract: + assert args.extract_name is not None + kwargs = dict(name=args.extract_name, **dump_graph_options) + model = extract(**kwargs)(model) + + inputs_params = utils.load_converted_from_text(f'{model_path}') + params = inputs_params["weight_info"] + state_dict = { + k: utils.replay_tensor(v) for k, v in params.items() + } + + y = model(**state_dict)[0] - print(torch.argmin(y), torch.argmax(y)) - print(y.shape) + print(torch.argmin(y), torch.argmax(y)) + print(y.shape) if __name__ == '__main__': parser = argparse.ArgumentParser(description="load and run model") parser.add_argument("--model-path", type=str, required=True, help="Path to folder e.g '../../samples/torch/resnet18'") + parser.add_argument("--dump-graph-hash-key", action='store_true', default=False, help="Dump graph hash key") parser.add_argument("--enable-extract", type=bool, required=False, default=False, help="Enable extract") parser.add_argument("--extract-name", type=str, required=False, default=None, help="Extracted graph's name") args = parser.parse_args() diff --git a/graph_net/torch/validate.py b/graph_net/torch/validate.py index f6c7c68246..7da67ccf07 100644 --- a/graph_net/torch/validate.py +++ b/graph_net/torch/validate.py @@ -3,6 +3,7 @@ import tempfile import sys import contextlib +import graph_net @contextlib.contextmanager def temp_workspace(): @@ -17,12 +18,21 @@ def main(args): model_path = args.model_path with temp_workspace() as tmp_dir_name: extract_name = "temp" - cmd = f'{sys.executable} -m graph_net.torch.single_device_runner --model-path {model_path} --enable-extract True --extract-name {extract_name}' + print("Check extractability ...") + cmd = f'{sys.executable} -m graph_net.torch.single_device_runner --model-path {model_path} --enable-extract True --extract-name {extract_name} --dump-graph-hash-key True' cmd_ret = os.system(cmd) assert cmd_ret == 0, f'{cmd_ret=}, {cmd=}' cmd = f'{sys.executable} -m graph_net.torch.single_device_runner --model-path {tmp_dir_name}/{extract_name}' cmd_ret = os.system(cmd) assert cmd_ret == 0, f'{cmd_ret=}, {cmd=}' + if not args.no_check_redundancy: + print("Check redundancy ...") + graph_net_samples_path = graph_net.torch.samples_util.get_default_samples_directory() + cmd = f"{sys.executable} -m graph_net.torch.check_redundant_incrementally --model-path {args.model_path} --graph-net-samples-path {graph_net_samples_path}" + cmd_ret = os.system(cmd) + rm_cmd = f"{sys.executable} -m graph_net.torch.remove_redundant_incrementally --model-path {args.model_path} --graph-net-samples-path {graph_net_samples_path}" + assert cmd_ret == 0, f"\nPlease use the following command to remove redundant model directories:\n\n{rm_cmd}\n" + print(f'Validation success, {model_path=}') @@ -30,6 +40,8 @@ def main(args): parser = argparse.ArgumentParser(description="load and run model") parser.add_argument("--model-path", type=str, required=True, help="Path to folder e.g '../../samples/torch/resnet18'") + parser.add_argument("--no-check-redundancy", action='store_true', + help="whether check model graph redundancy") args = parser.parse_args() main(args=args) diff --git a/samples/torchvision/alexnet/graph_hash.txt b/samples/torchvision/alexnet/graph_hash.txt new file mode 100644 index 0000000000..582f90575e --- /dev/null +++ b/samples/torchvision/alexnet/graph_hash.txt @@ -0,0 +1 @@ +9f9d4addeb6cb388deab98fdc9c7b23a86972575b2b40c616a0ad944cd6bc1fe \ No newline at end of file diff --git a/samples/torchvision/convnext_base/graph_hash.txt b/samples/torchvision/convnext_base/graph_hash.txt new file mode 100644 index 0000000000..9ef960a15d --- /dev/null +++ b/samples/torchvision/convnext_base/graph_hash.txt @@ -0,0 +1 @@ +714c33398060d08f52081e798ab9f9644b5199e72ef4693df17edbd55a81b8df \ No newline at end of file diff --git a/samples/torchvision/convnext_large/graph_hash.txt b/samples/torchvision/convnext_large/graph_hash.txt new file mode 100644 index 0000000000..49f0694a1f --- /dev/null +++ b/samples/torchvision/convnext_large/graph_hash.txt @@ -0,0 +1 @@ +701eb1e25733659c94cf2125568e8703fd1b318acd496b2ed6d87b77ad89f027 \ No newline at end of file diff --git a/samples/torchvision/convnext_small/graph_hash.txt b/samples/torchvision/convnext_small/graph_hash.txt new file mode 100644 index 0000000000..47a487c12f --- /dev/null +++ b/samples/torchvision/convnext_small/graph_hash.txt @@ -0,0 +1 @@ +e5b3c7e5c7f50a53d4d1b898f88506f01398347c7244cee9d75bbb401de35104 \ No newline at end of file diff --git a/samples/torchvision/convnext_tiny/graph_hash.txt b/samples/torchvision/convnext_tiny/graph_hash.txt new file mode 100644 index 0000000000..97ca975028 --- /dev/null +++ b/samples/torchvision/convnext_tiny/graph_hash.txt @@ -0,0 +1 @@ +a254270566b45214bf8de7994ad9692f326484bd15db6ce08ec55eb2dea35411 \ No newline at end of file diff --git a/samples/torchvision/densenet121/graph_hash.txt b/samples/torchvision/densenet121/graph_hash.txt new file mode 100644 index 0000000000..21678187e4 --- /dev/null +++ b/samples/torchvision/densenet121/graph_hash.txt @@ -0,0 +1 @@ +a218d776dfc4fc897dcb36c2bac96153bcb468cd581cd1b5f53e24b0365eb130 \ No newline at end of file diff --git a/samples/torchvision/densenet161/graph_hash.txt b/samples/torchvision/densenet161/graph_hash.txt new file mode 100644 index 0000000000..2bbe1ffdf2 --- /dev/null +++ b/samples/torchvision/densenet161/graph_hash.txt @@ -0,0 +1 @@ +a0e6158f429c8206b59e2b7afd58728ca151216a7eb03a0c84a0111bb5ab7a11 \ No newline at end of file diff --git a/samples/torchvision/densenet169/graph_hash.txt b/samples/torchvision/densenet169/graph_hash.txt new file mode 100644 index 0000000000..17f7b1e1cf --- /dev/null +++ b/samples/torchvision/densenet169/graph_hash.txt @@ -0,0 +1 @@ +bdfb22fd4a337e568baab559647b72d3da717cb978154eec316ef8969910c77f \ No newline at end of file diff --git a/samples/torchvision/densenet201/graph_hash.txt b/samples/torchvision/densenet201/graph_hash.txt new file mode 100644 index 0000000000..124b07737f --- /dev/null +++ b/samples/torchvision/densenet201/graph_hash.txt @@ -0,0 +1 @@ +973964bc7dd5bc2d33b009de4ba19d87034d74292ac9859757a9ffc678b18caf \ No newline at end of file diff --git a/samples/torchvision/efficientnet_b0/graph_hash.txt b/samples/torchvision/efficientnet_b0/graph_hash.txt new file mode 100644 index 0000000000..76d24228ef --- /dev/null +++ b/samples/torchvision/efficientnet_b0/graph_hash.txt @@ -0,0 +1 @@ +c2cae9c443d22d14ae37dd70f233030e2d20d0c132e0282dabd6de45c0f678a8 \ No newline at end of file diff --git a/samples/torchvision/efficientnet_b1/graph_hash.txt b/samples/torchvision/efficientnet_b1/graph_hash.txt new file mode 100644 index 0000000000..d45814531f --- /dev/null +++ b/samples/torchvision/efficientnet_b1/graph_hash.txt @@ -0,0 +1 @@ +104b21c6844482eb728ea323f0c01e279f1bb0b89f68870d10ae7e9238fa6130 \ No newline at end of file diff --git a/samples/torchvision/googlenet/graph_hash.txt b/samples/torchvision/googlenet/graph_hash.txt new file mode 100644 index 0000000000..b751047aad --- /dev/null +++ b/samples/torchvision/googlenet/graph_hash.txt @@ -0,0 +1 @@ +67ae6c6cd0497cba3ab8549b012eb711f4278e1f1c7d6be58e41ff5064238ea4 \ No newline at end of file diff --git a/samples/torchvision/inception_v3/graph_hash.txt b/samples/torchvision/inception_v3/graph_hash.txt new file mode 100644 index 0000000000..bbdbae6eeb --- /dev/null +++ b/samples/torchvision/inception_v3/graph_hash.txt @@ -0,0 +1 @@ +cd9047b1958d5f9bc0638965adb73847bdd997418c4f269a02857f7c44286c69 \ No newline at end of file diff --git a/samples/torchvision/mnasnet0_5/graph_hash.txt b/samples/torchvision/mnasnet0_5/graph_hash.txt new file mode 100644 index 0000000000..844df41856 --- /dev/null +++ b/samples/torchvision/mnasnet0_5/graph_hash.txt @@ -0,0 +1 @@ +bf4e7567be3a21c38277b12adb46fe2122b713f897b88a5050f0138c852fb537 \ No newline at end of file diff --git a/samples/torchvision/mnasnet0_75/graph_hash.txt b/samples/torchvision/mnasnet0_75/graph_hash.txt new file mode 100644 index 0000000000..f5687c39e3 --- /dev/null +++ b/samples/torchvision/mnasnet0_75/graph_hash.txt @@ -0,0 +1 @@ +f306d203bfef03a0dc879ed0064f9c9a11ae40d89e49d21a162ba86b2ffe4f67 \ No newline at end of file diff --git a/samples/torchvision/mnasnet1_0/graph_hash.txt b/samples/torchvision/mnasnet1_0/graph_hash.txt new file mode 100644 index 0000000000..210e5eb69d --- /dev/null +++ b/samples/torchvision/mnasnet1_0/graph_hash.txt @@ -0,0 +1 @@ +c630c94e3b73740215fc2e5621348c7f8b89826018fe8faafe4ed8b3ae860833 \ No newline at end of file diff --git a/samples/torchvision/mnasnet1_3/graph_hash.txt b/samples/torchvision/mnasnet1_3/graph_hash.txt new file mode 100644 index 0000000000..54ce079c4a --- /dev/null +++ b/samples/torchvision/mnasnet1_3/graph_hash.txt @@ -0,0 +1 @@ +4b443a19e694bf7409af76dc66ce0bb49154109cf798f1d30fd15f7c878a4a04 \ No newline at end of file diff --git a/samples/torchvision/mobilenet_v2/graph_hash.txt b/samples/torchvision/mobilenet_v2/graph_hash.txt new file mode 100644 index 0000000000..5c36dd3cc4 --- /dev/null +++ b/samples/torchvision/mobilenet_v2/graph_hash.txt @@ -0,0 +1 @@ +c8e857abd33ff165958fc373cad510dd8e9ea5ed85a942c7b127b09c7b96978a \ No newline at end of file diff --git a/samples/torchvision/mobilenet_v3_large/graph_hash.txt b/samples/torchvision/mobilenet_v3_large/graph_hash.txt new file mode 100644 index 0000000000..05459c859f --- /dev/null +++ b/samples/torchvision/mobilenet_v3_large/graph_hash.txt @@ -0,0 +1 @@ +e599ad974d8bdf45566ddc3e784f6a380a09c34dc121cadb1340935bf034c712 \ No newline at end of file diff --git a/samples/torchvision/mobilenet_v3_small/graph_hash.txt b/samples/torchvision/mobilenet_v3_small/graph_hash.txt new file mode 100644 index 0000000000..c26fe8efb8 --- /dev/null +++ b/samples/torchvision/mobilenet_v3_small/graph_hash.txt @@ -0,0 +1 @@ +6f069533f581a2a10a2cc5b9744da88fe96a4a2998a5165fdcc12076382b90fd \ No newline at end of file diff --git a/samples/torchvision/r3d_18/graph_hash.txt b/samples/torchvision/r3d_18/graph_hash.txt new file mode 100644 index 0000000000..6d0703cecb --- /dev/null +++ b/samples/torchvision/r3d_18/graph_hash.txt @@ -0,0 +1 @@ +5d11a010c31a695dc64121c46e16dd33cd9903192a57f60fd90820238d2e5a46 \ No newline at end of file diff --git a/samples/torchvision/regnet_x_16gf/graph_hash.txt b/samples/torchvision/regnet_x_16gf/graph_hash.txt new file mode 100644 index 0000000000..eedc9899ca --- /dev/null +++ b/samples/torchvision/regnet_x_16gf/graph_hash.txt @@ -0,0 +1 @@ +82d4abbf7ed005f4d4fe451b713f3f90438ab09e0e1b6c93c57ea0b60ede989d \ No newline at end of file diff --git a/samples/torchvision/regnet_x_1_6gf/graph_hash.txt b/samples/torchvision/regnet_x_1_6gf/graph_hash.txt new file mode 100644 index 0000000000..4eab7a7596 --- /dev/null +++ b/samples/torchvision/regnet_x_1_6gf/graph_hash.txt @@ -0,0 +1 @@ +afe4ad7c79375b6dec61d20b60d346e0a848abbba2f44ab44d81a912653ddb05 \ No newline at end of file diff --git a/samples/torchvision/regnet_x_32gf/graph_hash.txt b/samples/torchvision/regnet_x_32gf/graph_hash.txt new file mode 100644 index 0000000000..387d8209aa --- /dev/null +++ b/samples/torchvision/regnet_x_32gf/graph_hash.txt @@ -0,0 +1 @@ +c48e822ac367b90e1f6fdd8533d3751606f71fe1bb10959a7a7a0a0d8e4a45ff \ No newline at end of file diff --git a/samples/torchvision/regnet_x_3_2gf/graph_hash.txt b/samples/torchvision/regnet_x_3_2gf/graph_hash.txt new file mode 100644 index 0000000000..23cf8eec80 --- /dev/null +++ b/samples/torchvision/regnet_x_3_2gf/graph_hash.txt @@ -0,0 +1 @@ +51079e8fcf3e2c9e4e5422290d693de770b0ba1651b8fda9afc64082006db261 \ No newline at end of file diff --git a/samples/torchvision/regnet_x_400mf/graph_hash.txt b/samples/torchvision/regnet_x_400mf/graph_hash.txt new file mode 100644 index 0000000000..e437ad3621 --- /dev/null +++ b/samples/torchvision/regnet_x_400mf/graph_hash.txt @@ -0,0 +1 @@ +b9bb661182c5f360458630f6c54c47be4503547feac48c03fdd0995d03647445 \ No newline at end of file diff --git a/samples/torchvision/regnet_x_800mf/graph_hash.txt b/samples/torchvision/regnet_x_800mf/graph_hash.txt new file mode 100644 index 0000000000..1820e63daf --- /dev/null +++ b/samples/torchvision/regnet_x_800mf/graph_hash.txt @@ -0,0 +1 @@ +3665fd276f34a63a9f1a36b0a8b4d645ed3d82352f1f514cbb4c6bcbb63d4d9e \ No newline at end of file diff --git a/samples/torchvision/regnet_x_8gf/graph_hash.txt b/samples/torchvision/regnet_x_8gf/graph_hash.txt new file mode 100644 index 0000000000..879bcaa21b --- /dev/null +++ b/samples/torchvision/regnet_x_8gf/graph_hash.txt @@ -0,0 +1 @@ +0663ed7feea418ff45015f88a1c93402be76b84031cfbbfd6b1d4b66f8f55cda \ No newline at end of file diff --git a/samples/torchvision/regnet_y_128gf/graph_hash.txt b/samples/torchvision/regnet_y_128gf/graph_hash.txt new file mode 100644 index 0000000000..b53533d7d5 --- /dev/null +++ b/samples/torchvision/regnet_y_128gf/graph_hash.txt @@ -0,0 +1 @@ +1513c5f929c4b0557b4020293f3cdd667169ffc8c8d05320a08d8fdb8a50f285 \ No newline at end of file diff --git a/samples/torchvision/resnet101/graph_hash.txt b/samples/torchvision/resnet101/graph_hash.txt new file mode 100644 index 0000000000..ef24b0a924 --- /dev/null +++ b/samples/torchvision/resnet101/graph_hash.txt @@ -0,0 +1 @@ +0cbc99e22d6c50c11fb02aca0ded3f01a0f7dd3474d2f65e12f956a11b08b2cb \ No newline at end of file diff --git a/samples/torchvision/resnet152/graph_hash.txt b/samples/torchvision/resnet152/graph_hash.txt new file mode 100644 index 0000000000..8778f1e228 --- /dev/null +++ b/samples/torchvision/resnet152/graph_hash.txt @@ -0,0 +1 @@ +e6285a1eea52cf08f0ac621086e773ab4763f2a1d07fe155a1f009e0bebcc272 \ No newline at end of file diff --git a/samples/torchvision/resnet18/graph_hash.txt b/samples/torchvision/resnet18/graph_hash.txt new file mode 100644 index 0000000000..a43bbde050 --- /dev/null +++ b/samples/torchvision/resnet18/graph_hash.txt @@ -0,0 +1 @@ +9d1568d6617d5b2d5d3762b24b3c296f63fe423cfecdc27d6ecd425c0290de74 \ No newline at end of file diff --git a/samples/torchvision/resnet34/graph_hash.txt b/samples/torchvision/resnet34/graph_hash.txt new file mode 100644 index 0000000000..c47fd8879e --- /dev/null +++ b/samples/torchvision/resnet34/graph_hash.txt @@ -0,0 +1 @@ +ebab7b3df841647c90ec576ffd128b64c04de03a89325407110f3a9e3047fc66 \ No newline at end of file diff --git a/samples/torchvision/resnet50/graph_hash.txt b/samples/torchvision/resnet50/graph_hash.txt new file mode 100644 index 0000000000..c4d4c065af --- /dev/null +++ b/samples/torchvision/resnet50/graph_hash.txt @@ -0,0 +1 @@ +87396dbda3e3e3177c581d5b79fe44c45f5dc1fb8432e1d2ea21aee4febe0c4f \ No newline at end of file diff --git a/samples/torchvision/resnext101_32x8d/graph_hash.txt b/samples/torchvision/resnext101_32x8d/graph_hash.txt new file mode 100644 index 0000000000..916aa88a00 --- /dev/null +++ b/samples/torchvision/resnext101_32x8d/graph_hash.txt @@ -0,0 +1 @@ +52a11486e82cb4a71b6393d7af95da2218927178f0c60baa8c391825dca9fa44 \ No newline at end of file diff --git a/samples/torchvision/resnext101_64x4d/graph_hash.txt b/samples/torchvision/resnext101_64x4d/graph_hash.txt new file mode 100644 index 0000000000..a0407b9f05 --- /dev/null +++ b/samples/torchvision/resnext101_64x4d/graph_hash.txt @@ -0,0 +1 @@ +024a2573cbd5ebb5300cac0faf62b73b682a3651bcb0f6eea089c3eb6683a2a0 \ No newline at end of file diff --git a/samples/torchvision/resnext50_32x4d/graph_hash.txt b/samples/torchvision/resnext50_32x4d/graph_hash.txt new file mode 100644 index 0000000000..b4d4c6051e --- /dev/null +++ b/samples/torchvision/resnext50_32x4d/graph_hash.txt @@ -0,0 +1 @@ +2e3e9ce73f4693d66c60727f4285fe8bd8757b4103f5832354a95e750533ab62 \ No newline at end of file diff --git a/samples/torchvision/squeezenet1_0/graph_hash.txt b/samples/torchvision/squeezenet1_0/graph_hash.txt new file mode 100644 index 0000000000..813f399662 --- /dev/null +++ b/samples/torchvision/squeezenet1_0/graph_hash.txt @@ -0,0 +1 @@ +9ad67491386ba2cd1b77ac21ef76624e5b9c58f6992b3411f40f2fe9a2153f55 \ No newline at end of file diff --git a/samples/torchvision/squeezenet1_1/graph_hash.txt b/samples/torchvision/squeezenet1_1/graph_hash.txt new file mode 100644 index 0000000000..9c2ce5a6cd --- /dev/null +++ b/samples/torchvision/squeezenet1_1/graph_hash.txt @@ -0,0 +1 @@ +06ae5589119525532e61d159a8173eb94f56faacd3e823342bd471233c958404 \ No newline at end of file diff --git a/samples/torchvision/vgg11/graph_hash.txt b/samples/torchvision/vgg11/graph_hash.txt new file mode 100644 index 0000000000..40f5988649 --- /dev/null +++ b/samples/torchvision/vgg11/graph_hash.txt @@ -0,0 +1 @@ +68ad60dba65c7769563ebba85aa8981038338b062af8f913d7bce421284b11ce \ No newline at end of file diff --git a/samples/torchvision/vgg11_bn/graph_hash.txt b/samples/torchvision/vgg11_bn/graph_hash.txt new file mode 100644 index 0000000000..1963d3c0a4 --- /dev/null +++ b/samples/torchvision/vgg11_bn/graph_hash.txt @@ -0,0 +1 @@ +ec7a8669233e90e10dbae7b9fb4d72b7e59edd6564abf418431ed048919e54bd \ No newline at end of file diff --git a/samples/torchvision/vgg13/graph_hash.txt b/samples/torchvision/vgg13/graph_hash.txt new file mode 100644 index 0000000000..7b4c7fd097 --- /dev/null +++ b/samples/torchvision/vgg13/graph_hash.txt @@ -0,0 +1 @@ +8b01e935f3fe706b8590e93c8c81a415927115dd7b261424a6b995b1eaec30eb \ No newline at end of file diff --git a/samples/torchvision/vgg13_bn/graph_hash.txt b/samples/torchvision/vgg13_bn/graph_hash.txt new file mode 100644 index 0000000000..7db8f1c0e0 --- /dev/null +++ b/samples/torchvision/vgg13_bn/graph_hash.txt @@ -0,0 +1 @@ +c8c3cf0e5b103707fd6db3bd6caa2da73642ee7b06a788e66e01dcd28d6b815d \ No newline at end of file diff --git a/samples/torchvision/vgg16/graph_hash.txt b/samples/torchvision/vgg16/graph_hash.txt new file mode 100644 index 0000000000..f2f2b33546 --- /dev/null +++ b/samples/torchvision/vgg16/graph_hash.txt @@ -0,0 +1 @@ +7877919aec2a5accc5be74c1145585a5b71407d81633ebc919d102ea54781f90 \ No newline at end of file diff --git a/samples/torchvision/vgg16_bn/graph_hash.txt b/samples/torchvision/vgg16_bn/graph_hash.txt new file mode 100644 index 0000000000..a80927aa25 --- /dev/null +++ b/samples/torchvision/vgg16_bn/graph_hash.txt @@ -0,0 +1 @@ +95aa20ff842033ee57afd421c4237019307c520a1ae8e62490d3ea298e55607e \ No newline at end of file diff --git a/samples/torchvision/vgg19/graph_hash.txt b/samples/torchvision/vgg19/graph_hash.txt new file mode 100644 index 0000000000..7370a3412c --- /dev/null +++ b/samples/torchvision/vgg19/graph_hash.txt @@ -0,0 +1 @@ +3396d4d7859b75da9f131f47d184c885bdfae13c070f2ad80c975b7b118b923c \ No newline at end of file diff --git a/samples/torchvision/vgg19_bn/graph_hash.txt b/samples/torchvision/vgg19_bn/graph_hash.txt new file mode 100644 index 0000000000..ca8131ca2c --- /dev/null +++ b/samples/torchvision/vgg19_bn/graph_hash.txt @@ -0,0 +1 @@ +e6c3807886040e406a69dff874a542ce634c00370c84de3672e09b2cd1ae574c \ No newline at end of file diff --git a/samples/transformers-auto-model/distilbert-base-uncased/graph_hash.txt b/samples/transformers-auto-model/distilbert-base-uncased/graph_hash.txt new file mode 100644 index 0000000000..2acaca7324 --- /dev/null +++ b/samples/transformers-auto-model/distilbert-base-uncased/graph_hash.txt @@ -0,0 +1 @@ +b657cdf3f52b9717c7a8c5a76b380e19be40fddf843fefe4eee488a10429d8e1 \ No newline at end of file From c21cb49e5ca8aa544bd9f3d83ee11a5821d6e5ea Mon Sep 17 00:00:00 2001 From: Xinqi Li Date: Thu, 31 Jul 2025 08:25:19 +0000 Subject: [PATCH 2/7] revert change of vision_model_test --- graph_net/test/vision_model_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graph_net/test/vision_model_test.py b/graph_net/test/vision_model_test.py index 0002cbec65..c2c41c7dea 100644 --- a/graph_net/test/vision_model_test.py +++ b/graph_net/test/vision_model_test.py @@ -29,7 +29,7 @@ model.to(device) normalized_input = normalized_input.to(device) - model = graph_net.torch.extract(name="resnet18", placeholder_auto_rename=True)(model) + model = graph_net.torch.extract(name="resnet18")(model) print("Running inference...") print("Input shape:", normalized_input.shape) From ca9017f281c5b535897e7b03e613d606cc52d312 Mon Sep 17 00:00:00 2001 From: Xinqi Li Date: Thu, 31 Jul 2025 08:28:19 +0000 Subject: [PATCH 3/7] reformat python code. --- graph_net/__init__.py | 2 +- graph_net/config.py | 40 ++- graph_net/pack.py | 44 +-- graph_net/status.py | 17 +- graph_net/test/bert_model_test.py | 11 +- graph_net/test/vision_model_test.py | 7 +- graph_net/torch/__init__.py | 2 +- .../torch/check_redundant_incrementally.py | 30 +- graph_net/torch/extractor.py | 2 + .../torch/remove_redundant_incrementally.py | 26 +- graph_net/torch/samples_util.py | 3 +- graph_net/torch/single_device_runner.py | 59 ++-- graph_net/torch/test_compiler.py | 271 ++++++++++-------- graph_net/torch/utils.py | 73 +++-- graph_net/torch/validate.py | 46 +-- graph_net/unpack.py | 25 +- 16 files changed, 400 insertions(+), 258 deletions(-) diff --git a/graph_net/__init__.py b/graph_net/__init__.py index c5d3fb2251..0a618d53c2 100644 --- a/graph_net/__init__.py +++ b/graph_net/__init__.py @@ -1 +1 @@ -import graph_net.torch as torch \ No newline at end of file +import graph_net.torch as torch diff --git a/graph_net/config.py b/graph_net/config.py index c690141572..b4b72e2a22 100644 --- a/graph_net/config.py +++ b/graph_net/config.py @@ -7,53 +7,51 @@ def load_env_workspace(): - ws = os.environ.get('GRAPH_NET_EXTRACT_WORKSPACE') + ws = os.environ.get("GRAPH_NET_EXTRACT_WORKSPACE") if not ws: - print('Error: GRAPH_NET_EXTRACT_WORKSPACE is not set', file=sys.stderr) + print("Error: GRAPH_NET_EXTRACT_WORKSPACE is not set", file=sys.stderr) sys.exit(1) return ws def write_config(path: str, data: dict): # Ensure parent dir exists - + os.makedirs(os.path.dirname(path), exist_ok=True) - with open(path, 'w', encoding='utf-8') as f: + with open(path, "w", encoding="utf-8") as f: json.dump(data, f, indent=2) - print(f'Config written to {path}') + print(f"Config written to {path}") def main(): parser = argparse.ArgumentParser( - prog='python -m graph_net.config', - description='Set user config for graph_net' + prog="python -m graph_net.config", description="Set user config for graph_net" + ) + parser.add_argument( + "--global", + dest="global_", + action="store_true", + help="Write config globally to ~/.graph_net/config.json", ) - parser.add_argument('--global', dest='global_', action='store_true', - help='Write config globally to ~/.graph_net/config.json') - parser.add_argument('--username', required=True, - help='Username to set in config') - parser.add_argument('--email', required=True, - help='Email to set in config') + parser.add_argument("--username", required=True, help="Username to set in config") + parser.add_argument("--email", required=True, help="Email to set in config") args = parser.parse_args() # Prepare config data - config_data = { - 'username': args.username, - 'email': args.email - } + config_data = {"username": args.username, "email": args.email} if args.global_: # global config - home = os.path.expanduser('~') - config_path = os.path.join(home, '.graph_net', 'config.json') + home = os.path.expanduser("~") + config_path = os.path.join(home, ".graph_net", "config.json") else: # workspace config ws = load_env_workspace() - config_path = os.path.join(ws, 'config.json') + config_path = os.path.join(ws, "config.json") write_config(config_path, config_data) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/graph_net/pack.py b/graph_net/pack.py index a24b5b4544..44e6aaa914 100644 --- a/graph_net/pack.py +++ b/graph_net/pack.py @@ -10,7 +10,9 @@ def pack_directory(src_dir: str, output_path: str): Pack all files and subdirectories under src_dir into a ZIP file at output_path. """ src_dir = os.path.normpath(src_dir) - with zipfile.ZipFile(output_path, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=True) as zf: + with zipfile.ZipFile( + output_path, "w", compression=zipfile.ZIP_DEFLATED, allowZip64=True + ) as zf: for root, _, files in os.walk(src_dir): for fname in files: full_path = os.path.join(root, fname) @@ -31,7 +33,7 @@ def clear_directory(src_dir: str): elif os.path.isdir(path): shutil.rmtree(path) except Exception as e: - print(f'Failed to remove {path}: {e}', file=sys.stderr) + print(f"Failed to remove {path}: {e}", file=sys.stderr) print(f'Cleared contents of "{src_dir}"') @@ -40,44 +42,48 @@ def parse_boolean_flag(value: str) -> bool: Parse a string to boolean, accepting 'True' or 'False' (case-insensitive). """ lower = value.lower() - if lower == 'true': + if lower == "true": return True - if lower == 'false': + if lower == "false": return False - raise argparse.ArgumentTypeError(f"clear_after_pack must be 'True' or 'False', got '{value}'") + raise argparse.ArgumentTypeError( + f"clear_after_pack must be 'True' or 'False', got '{value}'" + ) def main(): parser = argparse.ArgumentParser( - prog='python -m graph_net.pack', - description='Pack the $GRAPH_NET_EXTRACT_WORKSPACE directory into ZIP (clear_after_pack is required)' + prog="python -m graph_net.pack", + description="Pack the $GRAPH_NET_EXTRACT_WORKSPACE directory into ZIP (clear_after_pack is required)", ) parser.add_argument( - '--output', - metavar='OUTPUT_PATH', - help='Specify the output ZIP file path (default is .zip)', + "--output", + metavar="OUTPUT_PATH", + help="Specify the output ZIP file path (default is .zip)", ) parser.add_argument( - '--clear-after-pack', - dest='clear_after_pack', + "--clear-after-pack", + dest="clear_after_pack", required=True, type=parse_boolean_flag, - help="Specify whether to clear workspace after packing: 'True' or 'False'" + help="Specify whether to clear workspace after packing: 'True' or 'False'", ) args = parser.parse_args() - ws = os.environ.get('GRAPH_NET_EXTRACT_WORKSPACE') + ws = os.environ.get("GRAPH_NET_EXTRACT_WORKSPACE") if not ws: - parser.error('Environment variable GRAPH_NET_EXTRACT_WORKSPACE is not set') + parser.error("Environment variable GRAPH_NET_EXTRACT_WORKSPACE is not set") if not os.path.isdir(ws): - parser.error(f'The path specified by GRAPH_NET_EXTRACT_WORKSPACE ("{ws}") is not a valid directory') + parser.error( + f'The path specified by GRAPH_NET_EXTRACT_WORKSPACE ("{ws}") is not a valid directory' + ) # Determine output path if args.output: output_path = args.output else: - base = os.path.basename(ws.rstrip(os.sep)) or 'workspace' + base = os.path.basename(ws.rstrip(os.sep)) or "workspace" output_path = f"{base}.zip" # Perform pack @@ -88,5 +94,5 @@ def main(): clear_directory(ws) -if __name__ == '__main__': - main() \ No newline at end of file +if __name__ == "__main__": + main() diff --git a/graph_net/status.py b/graph_net/status.py index 95953b245f..ed8cce15de 100644 --- a/graph_net/status.py +++ b/graph_net/status.py @@ -4,6 +4,7 @@ import sys import argparse + def list_directory(src_dir: str): """ List all files and subdirectories directly under src_dir. @@ -21,20 +22,24 @@ def list_directory(src_dir: str): for name in sorted(entries): print(name) + def main(): parser = argparse.ArgumentParser( - prog='python -m graph_net.status', - description='List contents of the $GRAPH_NET_EXTRACT_WORKSPACE directory (like ls)' + prog="python -m graph_net.status", + description="List contents of the $GRAPH_NET_EXTRACT_WORKSPACE directory (like ls)", ) args = parser.parse_args() - ws = os.environ.get('GRAPH_NET_EXTRACT_WORKSPACE') + ws = os.environ.get("GRAPH_NET_EXTRACT_WORKSPACE") if not ws: - parser.error('Environment variable GRAPH_NET_EXTRACT_WORKSPACE is not set') + parser.error("Environment variable GRAPH_NET_EXTRACT_WORKSPACE is not set") if not os.path.isdir(ws): - parser.error(f'The path specified by GRAPH_NET_EXTRACT_WORKSPACE ("{ws}") is not a valid directory') + parser.error( + f'The path specified by GRAPH_NET_EXTRACT_WORKSPACE ("{ws}") is not a valid directory' + ) list_directory(ws) -if __name__ == '__main__': + +if __name__ == "__main__": main() diff --git a/graph_net/test/bert_model_test.py b/graph_net/test/bert_model_test.py index 71eb5a25f1..ab818c647f 100644 --- a/graph_net/test/bert_model_test.py +++ b/graph_net/test/bert_model_test.py @@ -1,18 +1,21 @@ import torch from transformers import AutoModel, AutoTokenizer -import graph_net.torch +import graph_net.torch import os + def get_model_name(): return "distilbert-base-uncased" + def create_model(): model = AutoModel.from_pretrained(get_model_name()) model.eval() return model.to(device) -if __name__ == '__main__': - + +if __name__ == "__main__": + tokenizer = AutoTokenizer.from_pretrained(get_model_name()) text = "Hello world" @@ -25,5 +28,5 @@ def create_model(): model = graph_net.torch.extract(name=get_model_name())(model) print("Running inference...") - output = model(**inputs) + output = model(**inputs) print("Inference finished. Output shape:", output.last_hidden_state.shape) diff --git a/graph_net/test/vision_model_test.py b/graph_net/test/vision_model_test.py index c2c41c7dea..ea51bc5334 100644 --- a/graph_net/test/vision_model_test.py +++ b/graph_net/test/vision_model_test.py @@ -7,11 +7,10 @@ import os import graph_net -if __name__ == '__main__': +if __name__ == "__main__": # Normalization parameters for ImageNet normalize = transforms.Normalize( - mean=[0.485, 0.456, 0.406], - std=[0.229, 0.224, 0.225] + mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225] ) # Create dummy input @@ -28,7 +27,7 @@ device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model.to(device) normalized_input = normalized_input.to(device) - + model = graph_net.torch.extract(name="resnet18")(model) print("Running inference...") diff --git a/graph_net/torch/__init__.py b/graph_net/torch/__init__.py index eaafa44978..67f7a71202 100644 --- a/graph_net/torch/__init__.py +++ b/graph_net/torch/__init__.py @@ -5,4 +5,4 @@ from .extractor import extract from .samples_util import get_default_samples_directory -__all__ = ['extract', 'get_default_samples_directory'] +__all__ = ["extract", "get_default_samples_directory"] diff --git a/graph_net/torch/check_redundant_incrementally.py b/graph_net/torch/check_redundant_incrementally.py index d07686f038..747a710d4d 100644 --- a/graph_net/torch/check_redundant_incrementally.py +++ b/graph_net/torch/check_redundant_incrementally.py @@ -13,6 +13,7 @@ import time import glob + def get_recursively_model_pathes(root_dir): if is_single_model_dir(root_dir): yield root_dir @@ -23,6 +24,7 @@ def get_recursively_model_pathes(root_dir): else: yield from get_recursively_model_pathes(sub_dir) + def get_immediate_subdirectory_paths(parent_dir): return [ sub_dir @@ -31,9 +33,11 @@ def get_immediate_subdirectory_paths(parent_dir): if os.path.isdir(sub_dir) ] + def is_single_model_dir(model_dir): return os.path.isfile(f"{model_dir}/graph_net.json") + def main(args): assert os.path.isdir(args.model_path) assert os.path.isdir(args.graph_net_samples_path) @@ -43,7 +47,7 @@ def main(args): for graph_hash_path in [f"{model_path}/graph_hash.txt"] ) graph_hash2graph_net_model_path = { - graph_hash:graph_hash_path + graph_hash: graph_hash_path for model_path in get_recursively_model_pathes(args.graph_net_samples_path) for graph_hash_path in [f"{model_path}/graph_hash.txt"] if os.path.isfile(graph_hash_path) @@ -52,13 +56,25 @@ def main(args): } for current_model_graph_hash_path in current_model_graph_hash_pathes: graph_hash = open(current_model_graph_hash_path).read() - assert graph_hash not in graph_hash2graph_net_model_path, f"Redundant models detected. old-model-path:{current_model_graph_hash_path}, new-model-path:{graph_hash2graph_net_model_path[graph_hash]}." + assert ( + graph_hash not in graph_hash2graph_net_model_path + ), f"Redundant models detected. old-model-path:{current_model_graph_hash_path}, new-model-path:{graph_hash2graph_net_model_path[graph_hash]}." -if __name__ == '__main__': + +if __name__ == "__main__": parser = argparse.ArgumentParser(description="Test compiler performance.") - parser.add_argument("--model-path", type=str, required=True, - help="Path to model file(s), each subdirectory containing graph_net.json will be regarded as a model") - parser.add_argument("--graph-net-samples-path", type=str, required=False, default='default', - help="Path to GraphNet samples") + parser.add_argument( + "--model-path", + type=str, + required=True, + help="Path to model file(s), each subdirectory containing graph_net.json will be regarded as a model", + ) + parser.add_argument( + "--graph-net-samples-path", + type=str, + required=False, + default="default", + help="Path to GraphNet samples", + ) args = parser.parse_args() main(args=args) diff --git a/graph_net/torch/extractor.py b/graph_net/torch/extractor.py index 535f99fde4..9ed82d381f 100644 --- a/graph_net/torch/extractor.py +++ b/graph_net/torch/extractor.py @@ -31,6 +31,7 @@ def extractor(gm: torch.fx.GraphModule, sample_inputs): params = {} input_idx = 0 unique_id = 0 + def try_rename_placeholder(node): assert node.op == "placeholder" if not placeholder_auto_rename: @@ -40,6 +41,7 @@ def try_rename_placeholder(node): unique_id += 1 node.name = f"v{unique_id}" unique_id += 1 + for node in gm.graph.nodes: if node.op == "placeholder": try_rename_placeholder(node) diff --git a/graph_net/torch/remove_redundant_incrementally.py b/graph_net/torch/remove_redundant_incrementally.py index 2cf8e137f6..81a6b3d902 100644 --- a/graph_net/torch/remove_redundant_incrementally.py +++ b/graph_net/torch/remove_redundant_incrementally.py @@ -14,6 +14,7 @@ import glob import shutil + def get_recursively_model_pathes(root_dir): if is_single_model_dir(root_dir): yield root_dir @@ -24,6 +25,7 @@ def get_recursively_model_pathes(root_dir): else: yield from get_recursively_model_pathes(sub_dir) + def get_immediate_subdirectory_paths(parent_dir): return [ sub_dir @@ -32,9 +34,11 @@ def get_immediate_subdirectory_paths(parent_dir): if os.path.isdir(sub_dir) ] + def is_single_model_dir(model_dir): return os.path.isfile(f"{model_dir}/graph_net.json") + def main(args): assert os.path.isdir(args.model_path) assert os.path.isdir(args.graph_net_samples_path) @@ -44,7 +48,7 @@ def main(args): for graph_hash_path in [f"{model_path}/graph_hash.txt"] ) graph_hash2graph_net_model_path = { - graph_hash:graph_hash_path + graph_hash: graph_hash_path for model_path in get_recursively_model_pathes(args.graph_net_samples_path) for graph_hash_path in [f"{model_path}/graph_hash.txt"] if os.path.isfile(graph_hash_path) @@ -59,11 +63,21 @@ def main(args): shutil.rmtree(directory) os.makedirs(directory, exist_ok=True) -if __name__ == '__main__': + +if __name__ == "__main__": parser = argparse.ArgumentParser(description="Test compiler performance.") - parser.add_argument("--model-path", type=str, required=True, - help="Path to model file(s), each subdirectory containing graph_net.json will be regarded as a model") - parser.add_argument("--graph-net-samples-path", type=str, required=False, default='default', - help="Path to GraphNet samples") + parser.add_argument( + "--model-path", + type=str, + required=True, + help="Path to model file(s), each subdirectory containing graph_net.json will be regarded as a model", + ) + parser.add_argument( + "--graph-net-samples-path", + type=str, + required=False, + default="default", + help="Path to GraphNet samples", + ) args = parser.parse_args() main(args=args) diff --git a/graph_net/torch/samples_util.py b/graph_net/torch/samples_util.py index 3fa7ea910d..a6b7870e01 100644 --- a/graph_net/torch/samples_util.py +++ b/graph_net/torch/samples_util.py @@ -1,5 +1,6 @@ import graph_net import os + def get_default_samples_directory(): - return f"{os.path.dirname(graph_net.__file__)}/../samples" + return f"{os.path.dirname(graph_net.__file__)}/../samples" diff --git a/graph_net/torch/single_device_runner.py b/graph_net/torch/single_device_runner.py index 732d9a8817..72d89c1f5e 100644 --- a/graph_net/torch/single_device_runner.py +++ b/graph_net/torch/single_device_runner.py @@ -17,17 +17,20 @@ def load_class_from_file(file_path: str, class_name: str) -> Type[torch.nn.Modul spec.loader.exec_module(unnamed) model_class = getattr(unnamed, class_name, None) return model_class - + + def _get_sha_hash(content): m = hashlib.sha256() m.update(content.encode()) return m.hexdigest() + def _save_to_model_path(dump_dir, hash_text): file_path = f"{dump_dir}/graph_hash.txt" - with open(file_path, 'w') as f: + with open(file_path, "w") as f: f.write(hash_text) + @contextmanager def _dump_graph_hash_key_ctx(cmd_args): if not cmd_args.dump_graph_hash_key: @@ -35,8 +38,8 @@ def _dump_graph_hash_key_ctx(cmd_args): return mut_graph_codes = [] extractor_kwarg = { - 'placeholder_auto_rename': True, - 'mut_graph_codes': mut_graph_codes + "placeholder_auto_rename": True, + "mut_graph_codes": mut_graph_codes, } yield extractor_kwarg if len(mut_graph_codes) > 0: @@ -47,32 +50,54 @@ def _dump_graph_hash_key_ctx(cmd_args): def main(args): with _dump_graph_hash_key_ctx(args) as dump_graph_options: model_path = args.model_path - model_class = load_class_from_file(f"{model_path}/model.py", class_name="GraphModule") + model_class = load_class_from_file( + f"{model_path}/model.py", class_name="GraphModule" + ) assert model_class is not None model = model_class() - print(f'{model_path=}') + print(f"{model_path=}") if args.enable_extract: assert args.extract_name is not None kwargs = dict(name=args.extract_name, **dump_graph_options) model = extract(**kwargs)(model) - inputs_params = utils.load_converted_from_text(f'{model_path}') + inputs_params = utils.load_converted_from_text(f"{model_path}") params = inputs_params["weight_info"] - state_dict = { - k: utils.replay_tensor(v) for k, v in params.items() - } - + state_dict = {k: utils.replay_tensor(v) for k, v in params.items()} + y = model(**state_dict)[0] print(torch.argmin(y), torch.argmax(y)) print(y.shape) -if __name__ == '__main__': + +if __name__ == "__main__": parser = argparse.ArgumentParser(description="load and run model") - parser.add_argument("--model-path", type=str, required=True, - help="Path to folder e.g '../../samples/torch/resnet18'") - parser.add_argument("--dump-graph-hash-key", action='store_true', default=False, help="Dump graph hash key") - parser.add_argument("--enable-extract", type=bool, required=False, default=False, help="Enable extract") - parser.add_argument("--extract-name", type=str, required=False, default=None, help="Extracted graph's name") + parser.add_argument( + "--model-path", + type=str, + required=True, + help="Path to folder e.g '../../samples/torch/resnet18'", + ) + parser.add_argument( + "--dump-graph-hash-key", + action="store_true", + default=False, + help="Dump graph hash key", + ) + parser.add_argument( + "--enable-extract", + type=bool, + required=False, + default=False, + help="Enable extract", + ) + parser.add_argument( + "--extract-name", + type=str, + required=False, + default=None, + help="Extracted graph's name", + ) args = parser.parse_args() main(args=args) diff --git a/graph_net/torch/test_compiler.py b/graph_net/torch/test_compiler.py index 31e44eaef2..15534685e0 100644 --- a/graph_net/torch/test_compiler.py +++ b/graph_net/torch/test_compiler.py @@ -18,168 +18,209 @@ def load_class_from_file(file_path: str, class_name: str) -> Type[torch.nn.Modul file = Path(file_path).resolve() module_name = file.stem - with open(file_path, 'r', encoding='utf-8') as f: + with open(file_path, "r", encoding="utf-8") as f: original_code = f.read() - import_stmt= "import torch" + import_stmt = "import torch" modified_code = f"{import_stmt}\n{original_code}" spec = importlib.util.spec_from_loader(module_name, loader=None) module = importlib.util.module_from_spec(spec) sys.modules[module_name] = module - compiled_code = compile(modified_code, filename=file, mode='exec') + compiled_code = compile(modified_code, filename=file, mode="exec") exec(compiled_code, module.__dict__) model_class = getattr(module, class_name, None) return model_class + def get_compiler(args): - assert args.compiler == 'default' - return torch.compile + assert args.compiler == "default" + return torch.compile + def get_synchronizer_func(args): - assert args.compiler == 'default' - return torch.cuda.synchronize + assert args.compiler == "default" + return torch.cuda.synchronize + def get_model(args): - model_class = load_class_from_file(f"{args.model_path}/model.py", class_name="GraphModule") + model_class = load_class_from_file( + f"{args.model_path}/model.py", class_name="GraphModule" + ) return model_class() + def get_input_dict(args): - inputs_params = utils.load_converted_from_text(f'{args.model_path}') + inputs_params = utils.load_converted_from_text(f"{args.model_path}") params = inputs_params["weight_info"] - return { - k: utils.replay_tensor(v) for k, v in params.items() - } + return {k: utils.replay_tensor(v) for k, v in params.items()} + @dataclass class DurationBox: - value: int - + value: int + + @contextmanager def naive_timer(duration_box, get_synchronizer_func): - get_synchronizer_func() - start = time.time() - yield - get_synchronizer_func() - end = time.time() - duration_box.value = end - start + get_synchronizer_func() + start = time.time() + yield + get_synchronizer_func() + end = time.time() + duration_box.value = end - start + def test_single_model(args): - compiler = get_compiler(args) - synchronizer_func = get_synchronizer_func(args) - input_dict = get_input_dict(args) - model = get_model(args) - compiled_model = compiler(model) - - # eager - eager_duration_box = DurationBox(-1) - with naive_timer(eager_duration_box, synchronizer_func): - expected_out = model(**input_dict) - - # warmup - for _ in range(args.warmup if args.warmup > 0 else 0): - compiled_model(**input_dict) - - # compiled - compiled_duration_box = DurationBox(-1) - with naive_timer(compiled_duration_box, synchronizer_func): - compiled_out = compiled_model(**input_dict) - - def print_cmp(key, func, **kwargs): - cmp_ret = func(expected_out, compiled_out, **kwargs) - print(f"{args.log_prompt} {key} model_path:{args.model_path} {cmp_ret}", file=sys.stderr) - - print_cmp("cmp.equal", get_cmp_equal) - print_cmp("cmp.all_close_atol8_rtol8", get_cmp_all_close, atol=1e-8, rtol=1e-8) - print_cmp("cmp.all_close_atol8_rtol5", get_cmp_all_close, atol=1e-8, rtol=1e-5) - print_cmp("cmp.all_close_atol5_rtol5", get_cmp_all_close, atol=1e-5, rtol=1e-5) - print_cmp("cmp.all_close_atol3_rtol2", get_cmp_all_close, atol=1e-3, rtol=1e-2) - print_cmp("cmp.all_close_atol2_rtol1", get_cmp_all_close, atol=1e-2, rtol=1e-1) - print_cmp("cmp.max_diff", get_cmp_max_diff) - print_cmp("cmp.mean_diff", get_cmp_mean_diff) - print_cmp("cmp.diff_count_atol8_rtol8", get_cmp_diff_count, atol=1e-8, rtol=1e-8) - print_cmp("cmp.diff_count_atol8_rtol5", get_cmp_diff_count, atol=1e-8, rtol=1e-5) - print_cmp("cmp.diff_count_atol5_rtol5", get_cmp_diff_count, atol=1e-5, rtol=1e-5) - print_cmp("cmp.diff_count_atol3_rtol2", get_cmp_diff_count, atol=1e-3, rtol=1e-2) - print_cmp("cmp.diff_count_atol2_rtol1", get_cmp_diff_count, atol=1e-2, rtol=1e-1) - - print(f"{args.log_prompt} duration model_path:{args.model_path} eager:{eager_duration_box.value} compiled:{compiled_duration_box.value}", file=sys.stderr) + compiler = get_compiler(args) + synchronizer_func = get_synchronizer_func(args) + input_dict = get_input_dict(args) + model = get_model(args) + compiled_model = compiler(model) + + # eager + eager_duration_box = DurationBox(-1) + with naive_timer(eager_duration_box, synchronizer_func): + expected_out = model(**input_dict) + + # warmup + for _ in range(args.warmup if args.warmup > 0 else 0): + compiled_model(**input_dict) + + # compiled + compiled_duration_box = DurationBox(-1) + with naive_timer(compiled_duration_box, synchronizer_func): + compiled_out = compiled_model(**input_dict) + + def print_cmp(key, func, **kwargs): + cmp_ret = func(expected_out, compiled_out, **kwargs) + print( + f"{args.log_prompt} {key} model_path:{args.model_path} {cmp_ret}", + file=sys.stderr, + ) + + print_cmp("cmp.equal", get_cmp_equal) + print_cmp("cmp.all_close_atol8_rtol8", get_cmp_all_close, atol=1e-8, rtol=1e-8) + print_cmp("cmp.all_close_atol8_rtol5", get_cmp_all_close, atol=1e-8, rtol=1e-5) + print_cmp("cmp.all_close_atol5_rtol5", get_cmp_all_close, atol=1e-5, rtol=1e-5) + print_cmp("cmp.all_close_atol3_rtol2", get_cmp_all_close, atol=1e-3, rtol=1e-2) + print_cmp("cmp.all_close_atol2_rtol1", get_cmp_all_close, atol=1e-2, rtol=1e-1) + print_cmp("cmp.max_diff", get_cmp_max_diff) + print_cmp("cmp.mean_diff", get_cmp_mean_diff) + print_cmp("cmp.diff_count_atol8_rtol8", get_cmp_diff_count, atol=1e-8, rtol=1e-8) + print_cmp("cmp.diff_count_atol8_rtol5", get_cmp_diff_count, atol=1e-8, rtol=1e-5) + print_cmp("cmp.diff_count_atol5_rtol5", get_cmp_diff_count, atol=1e-5, rtol=1e-5) + print_cmp("cmp.diff_count_atol3_rtol2", get_cmp_diff_count, atol=1e-3, rtol=1e-2) + print_cmp("cmp.diff_count_atol2_rtol1", get_cmp_diff_count, atol=1e-2, rtol=1e-1) + + print( + f"{args.log_prompt} duration model_path:{args.model_path} eager:{eager_duration_box.value} compiled:{compiled_duration_box.value}", + file=sys.stderr, + ) + def get_cmp_equal(expected_out, compiled_out): - return " ".join( - str(int(torch.equal(a, b))) - for a, b in zip(expected_out, compiled_out) - ) + return " ".join( + str(int(torch.equal(a, b))) for a, b in zip(expected_out, compiled_out) + ) + def get_cmp_all_close(expected_out, compiled_out, atol, rtol): - return " ".join( - str(int(torch.allclose(a, b, atol=atol, rtol=rtol))) - for a, b in zip(expected_out, compiled_out) - ) + return " ".join( + str(int(torch.allclose(a, b, atol=atol, rtol=rtol))) + for a, b in zip(expected_out, compiled_out) + ) + def get_cmp_max_diff(expected_out, compiled_out): - return " ".join( - str(torch.max(torch.abs(a - b)).item()) - for a, b in zip(expected_out, compiled_out) - ) + return " ".join( + str(torch.max(torch.abs(a - b)).item()) + for a, b in zip(expected_out, compiled_out) + ) + def get_cmp_mean_diff(expected_out, compiled_out): - return " ".join( - str(torch.mean(torch.abs(a - b)).item()) - for a, b in zip(expected_out, compiled_out) - ) + return " ".join( + str(torch.mean(torch.abs(a - b)).item()) + for a, b in zip(expected_out, compiled_out) + ) + def get_cmp_diff_count(expected_out, compiled_out, atol, rtol): - return " ".join( - str(torch.sum(~torch.isclose(a, b, atol=atol, rtol=rtol)).item()) - for a, b in zip(expected_out, compiled_out) - ) + return " ".join( + str(torch.sum(~torch.isclose(a, b, atol=atol, rtol=rtol)).item()) + for a, b in zip(expected_out, compiled_out) + ) + def test_multi_models(args): - for model_path in get_recursively_model_path(args.model_path): - cmd = "".join([ - sys.executable, - "-m graph_net.torch.test_compiler", - f"--model-path {model_path}", - f"--compiler {args.compiler}", - f"--warmup {args.warmup}", - f"--log-prompt {args.log_prompt}", - ]) - cmd_ret = os.system(cmd) - assert cmd_ret == 0, f"{cmd_ret=}, {cmd=}" + for model_path in get_recursively_model_path(args.model_path): + cmd = "".join( + [ + sys.executable, + "-m graph_net.torch.test_compiler", + f"--model-path {model_path}", + f"--compiler {args.compiler}", + f"--warmup {args.warmup}", + f"--log-prompt {args.log_prompt}", + ] + ) + cmd_ret = os.system(cmd) + assert cmd_ret == 0, f"{cmd_ret=}, {cmd=}" + def get_recursively_model_path(root_dir): - for sub_dir in get_immediate_subdirectory_paths(root_dir): - if is_single_model_dir(sub_dir): - yield sub_dir - else: - yield from get_recursively_model_path(sub_dir) + for sub_dir in get_immediate_subdirectory_paths(root_dir): + if is_single_model_dir(sub_dir): + yield sub_dir + else: + yield from get_recursively_model_path(sub_dir) + def get_immediate_subdirectory_paths(parent_dir): return [ - sub_dir - for name in os.listdir(parent_dir) - for sub_dir in [os.path.join(parent_dir, name)] - if os.path.isdir(sub_dir) - ] + sub_dir + for name in os.listdir(parent_dir) + for sub_dir in [os.path.join(parent_dir, name)] + if os.path.isdir(sub_dir) + ] + def is_single_model_dir(model_dir): - return os.path.isfile(f"{model_dir}/graph_net.json") + return os.path.isfile(f"{model_dir}/graph_net.json") + def main(args): - assert os.path.isdir(args.model_path) - if is_single_model_dir(args.model_path): - test_single_model(args) - else: - test_multi_models(args) - - -if __name__ == '__main__': + assert os.path.isdir(args.model_path) + if is_single_model_dir(args.model_path): + test_single_model(args) + else: + test_multi_models(args) + + +if __name__ == "__main__": parser = argparse.ArgumentParser(description="Test compiler performance.") - parser.add_argument("--model-path", type=str, required=True, - help="Path to model file(s), each subdirectory containing graph_net.json will be regarded as a model") - parser.add_argument("--compiler", type=str, required=False, default='default', - help="Path to customized compiler python file") - parser.add_argument("--warmup", type=int, required=False, default=5, help="Number of warmup steps") - parser.add_argument("--log-prompt", type=str, required=False, default="graph-net-test-compiler-log", help="Log prompt for performance log filtering.") + parser.add_argument( + "--model-path", + type=str, + required=True, + help="Path to model file(s), each subdirectory containing graph_net.json will be regarded as a model", + ) + parser.add_argument( + "--compiler", + type=str, + required=False, + default="default", + help="Path to customized compiler python file", + ) + parser.add_argument( + "--warmup", type=int, required=False, default=5, help="Number of warmup steps" + ) + parser.add_argument( + "--log-prompt", + type=str, + required=False, + default="graph-net-test-compiler-log", + help="Log prompt for performance log filtering.", + ) args = parser.parse_args() main(args=args) diff --git a/graph_net/torch/utils.py b/graph_net/torch/utils.py index 7d951dffc6..370ab8f1f1 100644 --- a/graph_net/torch/utils.py +++ b/graph_net/torch/utils.py @@ -9,16 +9,19 @@ import importlib import inspect + def apply_templates(forward_code: str) -> str: tab = " " forward_code = f"\n{tab}".join(forward_code.split("\n")) return f"import torch\n\nclass GraphModule(torch.nn.Module):\n{tab}{forward_code}" + def get_limited_precision_float_str(value): if not isinstance(value, float): return value return f"{value:.3f}" + def convert_state_and_inputs_impl(state_dict, example_inputs): def tensor_info(tensor): is_float = tensor.dtype.is_floating_point @@ -39,7 +42,11 @@ def process_tensor(tensor): info = tensor_info(tensor) if tensor.dtype in [torch.int8, torch.int16, torch.int32, torch.int64]: if tensor.numel() < 1024: - return {"type": "small_int_tensor", "data": tensor.clone(), "info": info} + return { + "type": "small_int_tensor", + "data": tensor.clone(), + "info": info, + } else: return {"type": "big_int_tensor", "data": tensor.clone(), "info": info} elif tensor.numel() < 1024: @@ -64,12 +71,8 @@ def handle_named_tensors(tensor): else: data_type = "big_int_tensor" info = tensor_info(tensor) - return { - "info": info, - "data": data_value, - "type": data_type - } - + return {"info": info, "data": data_value, "type": data_type} + processed_weights = { key: handle_named_tensors(tensor) for key, tensor in state_dict.items() } @@ -78,28 +81,31 @@ def handle_named_tensors(tensor): return { "input_info": processed_inputs, "weight_info": processed_weights, - "dynamic_shapes": None + "dynamic_shapes": None, } + def convert_state_and_inputs(state_dict, example_inputs): return convert_state_and_inputs_impl(state_dict, example_inputs) + def save_constraints_text(converted, file_path): lines = [] if converted["dynamic_shapes"] is not None: raise NotImplementedError("Handling constraints is not implemented yet.") - with open(file_path, 'w') as f: + with open(file_path, "w") as f: f.write("\n".join(lines)) + def save_converted_to_text(converted, file_path): def format_data(data): if data is None: return "None" elif isinstance(data, torch.Tensor): if data.dtype.is_floating_point: - return "[{}]".format(", ".join(f'{x:.6f}' for x in data.tolist())) + return "[{}]".format(", ".join(f"{x:.6f}" for x in data.tolist())) else: - return "[{}]".format(", ".join(f'{x}' for x in data.tolist())) + return "[{}]".format(", ".join(f"{x}" for x in data.tolist())) else: return repr(data) @@ -109,14 +115,16 @@ def process_tensor_info(tensor_info, name_prefix="example_input"): if tensor_info["type"] in ["small_tensor", "small_int_tensor"]: data_list = tensor_info["data"].flatten() elif tensor_info["type"] == "big_int_tensor": - data_list = f'pt-filename:xxx-key' + data_list = f"pt-filename:xxx-key" else: pass else: - if tensor_info["type"] == "small_int_tensor": + if tensor_info["type"] == "small_int_tensor": data_list = tensor_info["data"].flatten() - if tensor_info["type"] == "big_int_tensor": - raise ValueError("Unexpected cases: there are weights in big tensor of int type ") + if tensor_info["type"] == "big_int_tensor": + raise ValueError( + "Unexpected cases: there are weights in big tensor of int type " + ) info = tensor_info.get("info", {}) dtype = info.get("dtype", "torch.float") shape = info.get("shape", []) @@ -128,12 +136,12 @@ def process_tensor_info(tensor_info, name_prefix="example_input"): (f"class {uid}:"), (f"\tname = \"{tensor_info.get('name', '')}\""), (f"\tshape = {shape}"), - (f"\tdtype = \"{dtype}\""), - (f"\tdevice = \"{device}\""), + (f'\tdtype = "{dtype}"'), + (f'\tdevice = "{device}"'), (f"\tmean = {get_limited_precision_float_str(mean)}"), (f"\tstd = {get_limited_precision_float_str(std)}"), (f"\tdata = {format_data(data_list)}"), - ("") + (""), ] input_infos = converted["input_info"] @@ -145,44 +153,52 @@ def process_tensor_info(tensor_info, name_prefix="example_input"): input_info["name"] = f"input_{idx}" input_lines.extend(process_tensor_info(input_info, name_prefix="Program_input")) - with open(f"{file_path}/input_meta.py", 'w') as f: + with open(f"{file_path}/input_meta.py", "w") as f: f.write("\n".join(input_lines)) weight_lines = [] for name, weight_info in converted["weight_info"].items(): weight_info["name"] = name - weight_lines.extend(process_tensor_info(weight_info, name_prefix="Program_weight")) + weight_lines.extend( + process_tensor_info(weight_info, name_prefix="Program_weight") + ) - with open(f"{file_path}/weight_meta.py", 'w') as f: + with open(f"{file_path}/weight_meta.py", "w") as f: f.write("\n".join(weight_lines)) + def load_converted_from_text(file_path): input_info = list(convert_meta_classes_to_tensors(f"{file_path}/input_meta.py")) weight_info = { - data['name']: data + data["name"]: data for data in convert_meta_classes_to_tensors(f"{file_path}/weight_meta.py") } return { "input_info": input_info, "weight_info": weight_info, - "dynamic_shapes": None + "dynamic_shapes": None, } + def convert_meta_classes_to_tensors(file_path): for name, cls in _get_classes(file_path): attrs = { - k: v for k, v in cls.__dict__.items() if not k.startswith('__') and not callable(v) + k: v + for k, v in cls.__dict__.items() + if not k.startswith("__") and not callable(v) } data_value = None - data_type = getattr(torch, attrs.get("dtype", "torch.float").split('.')[-1]) + data_type = getattr(torch, attrs.get("dtype", "torch.float").split(".")[-1]) if attrs.get("data") is not None: if isinstance(attrs.get("data"), str): raise ValueError("Unimplemented") else: - data_value = torch.tensor(attrs["data"], dtype=data_type).reshape(attrs.get("shape"), []) + data_value = torch.tensor(attrs["data"], dtype=data_type).reshape( + attrs.get("shape"), [] + ) yield { "info": { "shape": attrs.get("shape", []), @@ -192,18 +208,21 @@ def convert_meta_classes_to_tensors(file_path): "std": attrs.get("std", 1.0), }, "data": data_value, - "name": attrs.get("name") + "name": attrs.get("name"), } + def _get_classes(file_path): spec = importlib.util.spec_from_file_location("unnamed", file_path) unnamed = importlib.util.module_from_spec(spec) spec.loader.exec_module(unnamed) yield from inspect.getmembers(unnamed, inspect.isclass) + def extract_dynamic_shapes(example_inputs): pass + def replay_tensor(info): device = info["info"]["device"] diff --git a/graph_net/torch/validate.py b/graph_net/torch/validate.py index 7da67ccf07..ba99bd540f 100644 --- a/graph_net/torch/validate.py +++ b/graph_net/torch/validate.py @@ -1,17 +1,18 @@ import argparse -import os +import os import tempfile import sys import contextlib import graph_net + @contextlib.contextmanager def temp_workspace(): with tempfile.TemporaryDirectory() as tmp_dir_name: - old = os.environ.get('GRAPH_NET_EXTRACT_WORKSPACE') - os.environ['GRAPH_NET_EXTRACT_WORKSPACE'] = tmp_dir_name + old = os.environ.get("GRAPH_NET_EXTRACT_WORKSPACE") + os.environ["GRAPH_NET_EXTRACT_WORKSPACE"] = tmp_dir_name yield tmp_dir_name - os.environ['GRAPH_NET_EXTRACT_WORKSPACE'] = old + os.environ["GRAPH_NET_EXTRACT_WORKSPACE"] = old def main(args): @@ -19,30 +20,39 @@ def main(args): with temp_workspace() as tmp_dir_name: extract_name = "temp" print("Check extractability ...") - cmd = f'{sys.executable} -m graph_net.torch.single_device_runner --model-path {model_path} --enable-extract True --extract-name {extract_name} --dump-graph-hash-key True' + cmd = f"{sys.executable} -m graph_net.torch.single_device_runner --model-path {model_path} --enable-extract True --extract-name {extract_name} --dump-graph-hash-key True" cmd_ret = os.system(cmd) - assert cmd_ret == 0, f'{cmd_ret=}, {cmd=}' - cmd = f'{sys.executable} -m graph_net.torch.single_device_runner --model-path {tmp_dir_name}/{extract_name}' + assert cmd_ret == 0, f"{cmd_ret=}, {cmd=}" + cmd = f"{sys.executable} -m graph_net.torch.single_device_runner --model-path {tmp_dir_name}/{extract_name}" cmd_ret = os.system(cmd) - assert cmd_ret == 0, f'{cmd_ret=}, {cmd=}' + assert cmd_ret == 0, f"{cmd_ret=}, {cmd=}" if not args.no_check_redundancy: print("Check redundancy ...") - graph_net_samples_path = graph_net.torch.samples_util.get_default_samples_directory() + graph_net_samples_path = ( + graph_net.torch.samples_util.get_default_samples_directory() + ) cmd = f"{sys.executable} -m graph_net.torch.check_redundant_incrementally --model-path {args.model_path} --graph-net-samples-path {graph_net_samples_path}" cmd_ret = os.system(cmd) rm_cmd = f"{sys.executable} -m graph_net.torch.remove_redundant_incrementally --model-path {args.model_path} --graph-net-samples-path {graph_net_samples_path}" - assert cmd_ret == 0, f"\nPlease use the following command to remove redundant model directories:\n\n{rm_cmd}\n" + assert ( + cmd_ret == 0 + ), f"\nPlease use the following command to remove redundant model directories:\n\n{rm_cmd}\n" - print(f'Validation success, {model_path=}') + print(f"Validation success, {model_path=}") -if __name__ == '__main__': +if __name__ == "__main__": parser = argparse.ArgumentParser(description="load and run model") - parser.add_argument("--model-path", type=str, required=True, - help="Path to folder e.g '../../samples/torch/resnet18'") - parser.add_argument("--no-check-redundancy", action='store_true', - help="whether check model graph redundancy") + parser.add_argument( + "--model-path", + type=str, + required=True, + help="Path to folder e.g '../../samples/torch/resnet18'", + ) + parser.add_argument( + "--no-check-redundancy", + action="store_true", + help="whether check model graph redundancy", + ) args = parser.parse_args() main(args=args) - - diff --git a/graph_net/unpack.py b/graph_net/unpack.py index 2faa6127d6..12e8b0b2b1 100644 --- a/graph_net/unpack.py +++ b/graph_net/unpack.py @@ -13,7 +13,10 @@ def unpack_archive(src_path: str, dest_dir: str): """ # Verify source ZIP exists if not os.path.isfile(src_path): - print(f"Error: archive '{src_path}' does not exist or is not a file", file=sys.stderr) + print( + f"Error: archive '{src_path}' does not exist or is not a file", + file=sys.stderr, + ) sys.exit(1) # Create destination directory if needed @@ -21,12 +24,14 @@ def unpack_archive(src_path: str, dest_dir: str): try: os.makedirs(dest_dir, exist_ok=True) except Exception as e: - print(f"Error: failed to create directory '{dest_dir}': {e}", file=sys.stderr) + print( + f"Error: failed to create directory '{dest_dir}': {e}", file=sys.stderr + ) sys.exit(1) # Extract all contents try: - with zipfile.ZipFile(src_path, 'r') as zf: + with zipfile.ZipFile(src_path, "r") as zf: zf.extractall(dest_dir) except zipfile.BadZipFile: print(f"Error: '{src_path}' is not a valid ZIP archive", file=sys.stderr) @@ -40,18 +45,16 @@ def unpack_archive(src_path: str, dest_dir: str): def main(): parser = argparse.ArgumentParser( - prog='python -m graph_net.unpack', - description='Unpack a ZIP archive into a specified directory' + prog="python -m graph_net.unpack", + description="Unpack a ZIP archive into a specified directory", ) parser.add_argument( - '--src', - required=True, - help='Path to the ZIP archive to unpack' + "--src", required=True, help="Path to the ZIP archive to unpack" ) parser.add_argument( - '--dst', + "--dst", required=True, - help='Destination directory where files will be unpacked' + help="Destination directory where files will be unpacked", ) args = parser.parse_args() @@ -59,5 +62,5 @@ def main(): unpack_archive(args.src, args.dst) -if __name__ == '__main__': +if __name__ == "__main__": main() From 52cc34dc36f224d316a8851f055c8238c8dbc3fc Mon Sep 17 00:00:00 2001 From: Xinqi Li Date: Thu, 31 Jul 2025 08:32:22 +0000 Subject: [PATCH 4/7] reformat bert_model_test.py and utils.py --- graph_net/test/bert_model_test.py | 1 - graph_net/torch/utils.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/graph_net/test/bert_model_test.py b/graph_net/test/bert_model_test.py index ab818c647f..7aab7115ee 100644 --- a/graph_net/test/bert_model_test.py +++ b/graph_net/test/bert_model_test.py @@ -15,7 +15,6 @@ def create_model(): if __name__ == "__main__": - tokenizer = AutoTokenizer.from_pretrained(get_model_name()) text = "Hello world" diff --git a/graph_net/torch/utils.py b/graph_net/torch/utils.py index 370ab8f1f1..8419e5d959 100644 --- a/graph_net/torch/utils.py +++ b/graph_net/torch/utils.py @@ -168,7 +168,6 @@ def process_tensor_info(tensor_info, name_prefix="example_input"): def load_converted_from_text(file_path): - input_info = list(convert_meta_classes_to_tensors(f"{file_path}/input_meta.py")) weight_info = { @@ -224,7 +223,6 @@ def extract_dynamic_shapes(example_inputs): def replay_tensor(info): - device = info["info"]["device"] dtype = info["info"]["dtype"] shape = info["info"]["shape"] From d8c6213b78240150cb93f581505030cc3f7a57c8 Mon Sep 17 00:00:00 2001 From: Xinqi Li Date: Thu, 31 Jul 2025 08:46:15 +0000 Subject: [PATCH 5/7] minor fix --- graph_net/torch/validate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graph_net/torch/validate.py b/graph_net/torch/validate.py index ba99bd540f..61469845a9 100644 --- a/graph_net/torch/validate.py +++ b/graph_net/torch/validate.py @@ -20,7 +20,7 @@ def main(args): with temp_workspace() as tmp_dir_name: extract_name = "temp" print("Check extractability ...") - cmd = f"{sys.executable} -m graph_net.torch.single_device_runner --model-path {model_path} --enable-extract True --extract-name {extract_name} --dump-graph-hash-key True" + cmd = f"{sys.executable} -m graph_net.torch.single_device_runner --model-path {model_path} --enable-extract True --extract-name {extract_name} --dump-graph-hash-key" cmd_ret = os.system(cmd) assert cmd_ret == 0, f"{cmd_ret=}, {cmd=}" cmd = f"{sys.executable} -m graph_net.torch.single_device_runner --model-path {tmp_dir_name}/{extract_name}" From 6bd1370354a6c0fe7392f485c847ce6d765c3a8c Mon Sep 17 00:00:00 2001 From: Xinqi Li Date: Mon, 4 Aug 2025 06:15:30 +0000 Subject: [PATCH 6/7] fix failed check by comparing directories after os.path.realpath() --- graph_net/torch/check_redundant_incrementally.py | 7 ++++++- graph_net/torch/remove_redundant_incrementally.py | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/graph_net/torch/check_redundant_incrementally.py b/graph_net/torch/check_redundant_incrementally.py index 747a710d4d..e784b536a4 100644 --- a/graph_net/torch/check_redundant_incrementally.py +++ b/graph_net/torch/check_redundant_incrementally.py @@ -15,8 +15,13 @@ def get_recursively_model_pathes(root_dir): + for sub_dir in _get_recursively_model_pathes(root_dir): + yield os.path.realpath(sub_dir) + + +def _get_recursively_model_pathes(root_dir): if is_single_model_dir(root_dir): - yield root_dir + yield os.path.realpath(root_dir) return for sub_dir in get_immediate_subdirectory_paths(root_dir): if is_single_model_dir(sub_dir): diff --git a/graph_net/torch/remove_redundant_incrementally.py b/graph_net/torch/remove_redundant_incrementally.py index 81a6b3d902..df046b5044 100644 --- a/graph_net/torch/remove_redundant_incrementally.py +++ b/graph_net/torch/remove_redundant_incrementally.py @@ -16,6 +16,11 @@ def get_recursively_model_pathes(root_dir): + for sub_dir in _get_recursively_model_pathes(root_dir): + yield os.path.realpath(sub_dir) + + +def _get_recursively_model_pathes(root_dir): if is_single_model_dir(root_dir): yield root_dir return From 165ae4b851cce4309ccd08932ab721c5053fd2e1 Mon Sep 17 00:00:00 2001 From: Xinqi Li Date: Mon, 4 Aug 2025 07:09:56 +0000 Subject: [PATCH 7/7] fix bugs in check_validate.sh --- graph_net/torch/check_redundant_incrementally.py | 2 +- graph_net/torch/validate.py | 11 ++++++++++- tools/ci/check_validate.sh | 4 ++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/graph_net/torch/check_redundant_incrementally.py b/graph_net/torch/check_redundant_incrementally.py index 2a1c41c827..0c0f705340 100644 --- a/graph_net/torch/check_redundant_incrementally.py +++ b/graph_net/torch/check_redundant_incrementally.py @@ -63,7 +63,7 @@ def main(args): graph_hash = open(current_model_graph_hash_path).read() assert ( graph_hash not in graph_hash2graph_net_model_path - ), f"Redundant models detected. old-model-path:{current_model_graph_hash_path}, new-model-path:{graph_hash2graph_net_model_path[graph_hash]}." + ), f"Redundant models detected. old-model-path:{graph_hash2graph_net_model_path[graph_hash]}, new-model-path:{current_model_graph_hash_path}." if __name__ == "__main__": diff --git a/graph_net/torch/validate.py b/graph_net/torch/validate.py index 61469845a9..0e1c63e2e9 100644 --- a/graph_net/torch/validate.py +++ b/graph_net/torch/validate.py @@ -29,7 +29,9 @@ def main(args): if not args.no_check_redundancy: print("Check redundancy ...") graph_net_samples_path = ( - graph_net.torch.samples_util.get_default_samples_directory() + (graph_net.torch.samples_util.get_default_samples_directory()) + if args.graph_net_samples_path is None + else args.graph_net_samples_path ) cmd = f"{sys.executable} -m graph_net.torch.check_redundant_incrementally --model-path {args.model_path} --graph-net-samples-path {graph_net_samples_path}" cmd_ret = os.system(cmd) @@ -49,6 +51,13 @@ def main(args): required=True, help="Path to folder e.g '../../samples/torch/resnet18'", ) + parser.add_argument( + "--graph-net-samples-path", + type=str, + required=False, + default=None, + help="Path to GraphNet samples folder. e.g '../../samples'", + ) parser.add_argument( "--no-check-redundancy", action="store_true", diff --git a/tools/ci/check_validate.sh b/tools/ci/check_validate.sh index 89bdc33a10..32681858c6 100644 --- a/tools/ci/check_validate.sh +++ b/tools/ci/check_validate.sh @@ -44,7 +44,7 @@ function check_validation() { fail_name=() for model_path in ${MODIFIED_MODEL_PATHS[@]} do - python -m graph_net.torch.validate --model-path ${GRAPH_NET_EXTRACT_WORKSPACE}/${model_path} >&2 + python -m graph_net.torch.validate --model-path ${GRAPH_NET_EXTRACT_WORKSPACE}/${model_path} --graph-net-samples-path ${GRAPH_NET_EXTRACT_WORKSPACE}/samples >&2 [ $? -ne 0 ] && fail_name[${#fail_name[@]}]="${model_path}" done if [ ${#fail_name[@]} -ne 0 ] @@ -76,4 +76,4 @@ function main() { LOG "[INFO] check_validation run success and no error!" } -main \ No newline at end of file +main