Skip to content

Commit 135e52f

Browse files
authored
Neural coder regular bug fix (#1175)
1 parent b5a9078 commit 135e52f

File tree

9 files changed

+206
-43
lines changed

9 files changed

+206
-43
lines changed

neural_coder/backends/pytorch_inc_static_quant_fx.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
transformation:
1616
location:
17-
- ["insert_below_dataloader_definition_line", "insert_below_model_definition_line", "insert_below_input_definition_line"]
17+
- ["insert_below_dataloader_definition_line", "insert_below_model_definition_line"]
1818
content:
1919
- |-
2020
[+] if "GraphModule" not in str(type(MODEL_NAME)):

neural_coder/backends/pytorch_inc_static_quant_ipex.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
transformation:
1616
location:
17-
- ["insert_below_dataloader_definition_line", "insert_below_model_definition_line", "insert_below_input_definition_line"]
17+
- ["insert_below_dataloader_definition_line", "insert_below_model_definition_line"]
1818
content:
1919
- |-
2020
[+] if "GraphModule" not in str(type(MODEL_NAME)):

neural_coder/coders/pytorch/batch_size.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ def transform(self):
2727
new_line = self.modify(line)
2828
self.result.append(new_line)
2929
else:
30-
if line == '' and self.result[-1] == '':
31-
continue
3230
self.result.append(line)
3331
for index, line in enumerate(self.result):
3432
if index != len(self.result)-1:

neural_coder/coders/pytorch/harness.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ def register_transformation(self):
269269
if is_eval_func and "[coder-enabled]" not in line:
270270
if eval_func_type == "non-forward":
271271
pass # do something
272-
272+
inference_line = line
273273
inference_line_indent_level = get_line_indent_level(line)
274274

275275
if "indent_inference_line" in loc:
@@ -312,7 +312,20 @@ def register_transformation(self):
312312
if "insert_above_inference_line" in loc:
313313
idx_offset = 0
314314
elif "insert_below_inference_line" in loc:
315-
idx_offset = 1
315+
if ")" in line: # e.g. model = Net(xxx)
316+
idx_offset = 1
317+
else: # e.g. model = Net(xxx, \n xxx, \n xxx)
318+
do_search = True
319+
i_search = 1
320+
while do_search:
321+
following_line = lines[line_idx + i_search]
322+
if ")" in following_line:
323+
do_search = False
324+
i_search += 1
325+
inference_line = \
326+
inference_line + "\n" + \
327+
" " * (get_line_indent_level(line) + 4) + following_line
328+
idx_offset = i_search
316329

317330
if "insert_above_inference_line" in loc or "insert_below_inference_line" in loc:
318331
bk_trans_content_this = bk_trans_content[bk_trans_location.index(loc)]
@@ -348,7 +361,7 @@ def register_transformation(self):
348361
lines_to_insert = lines_to_insert.replace("ACCURACY_MODE",
349362
str(globals.eval_accuracy))
350363
lines_to_insert = lines_to_insert.replace("INFERENCE_LINE",
351-
line.strip())
364+
inference_line.strip())
352365

353366
### register
354367

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Copyright (c) 2022 Intel Corporation
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from ... import globals
16+
from ...utils.line_operation import (
17+
get_line_indent_level,
18+
is_eval_func_model_name,
19+
get_line_left_hand_side,
20+
single_line_comment_or_empty_line_detection
21+
)
22+
23+
import logging
24+
25+
logging.basicConfig(level=globals.logging_level,
26+
format='%(asctime)s %(levelname)s %(message)s',
27+
datefmt='%a, %d %b %Y %H:%M:%S +0000')
28+
logger = logging.getLogger(__name__)
29+
30+
31+
class ReclaimInferenceTransformersTrainer(object):
32+
def __init__(self, list_model_def_instance):
33+
self.list_model_def_instance = list_model_def_instance
34+
35+
def print_info(self):
36+
for i in self.list_model_def_instance:
37+
logger.debug(f"i.print_info(): {i.print_info()}")
38+
39+
# collect file transformation info and register (store) in globals
40+
# (i.e. which file to add which lines at which location)
41+
def register_transformation(self):
42+
file_path = globals.list_code_path[0]
43+
lines = open(file_path, 'r').read().split('\n')
44+
line_idx = 0
45+
46+
for i in range(len(lines)):
47+
line = lines[i]
48+
49+
if "# Evaluation" in line:
50+
indent_level = get_line_indent_level(line)
51+
trans_insert_location = i
52+
lines_to_insert = ""
53+
lines_to_insert += " " * indent_level + "eval_dataloader = trainer.get_eval_dataloader()" + "\n"
54+
lines_to_insert += " " * indent_level + "import torch" + "\n"
55+
lines_to_insert += " " * indent_level + "for step, inputs in enumerate(eval_dataloader):" + "\n"
56+
lines_to_insert += " " * indent_level + " with torch.no_grad():" + "\n"
57+
lines_to_insert += " " * indent_level + " model(**inputs)"
58+
59+
if file_path not in globals.list_trans_insert_modified_file:
60+
globals.list_trans_insert_modified_file.append(file_path)
61+
globals.list_trans_insert_location_idxs.append([trans_insert_location])
62+
globals.list_trans_insert_number_insert_lines.append([lines_to_insert.count("\n") + 1])
63+
globals.list_trans_insert_lines_to_insert.append([lines_to_insert])
64+
else:
65+
idx = globals.list_trans_insert_modified_file.index(file_path)
66+
globals.list_trans_insert_location_idxs[idx].append(trans_insert_location)
67+
globals.list_trans_insert_number_insert_lines[idx].append(lines_to_insert.count("\n") + 1)
68+
globals.list_trans_insert_lines_to_insert[idx].append(lines_to_insert)
69+
70+
line_idx += 1
71+
72+
logger.debug(f"globals.list_trans_insert_modified_file: {globals.list_trans_insert_modified_file}")
73+
logger.debug(f"globals.list_trans_insert_location_idxs: {globals.list_trans_insert_location_idxs}")
74+
logger.debug(f"globals.list_trans_insert_number_insert_lines: {globals.list_trans_insert_number_insert_lines}")
75+
logger.debug(f"globals.list_trans_insert_lines_to_insert: {globals.list_trans_insert_lines_to_insert}")

neural_coder/docs/AutoQuant.md

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,7 @@ HuggingFace [Transformers](https://github.com/huggingface/transformers) models:
2424
from neural_coder import auto_quant
2525
auto_quant(
2626
code="https://github.com/huggingface/transformers/blob/v4.21-release/examples/pytorch/text-classification/run_glue.py",
27-
args="--model_name_or_path albert-base-v2 \
28-
--task_name sst2 \
29-
--do_eval \
30-
--output_dir result \
31-
--overwrite_output_dir",
27+
args="--model_name_or_path albert-base-v2 --task_name sst2 --do_eval --output_dir result",
3228
)
3329
```
3430

neural_coder/globals.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
# target batch size for feature of changing PyTorch batch size
2121
target_batch_size = 1
2222

23+
# mark for successful batch size change
24+
batch_size_changed = False
25+
2326
# number of benchmark iteration for feature of PyTorch benchmark
2427
num_benchmark_iteration = 30
2528

neural_coder/interface.py

Lines changed: 108 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def enable(
2828
code,
2929
features,
3030
target_batch_size=1, # effective for feature "pytorch_change_batch_size"
31-
num_benchmark_iteration=30, # effective for feature "pytorch_benchmark"
31+
num_benchmark_iteration=10, # effective for feature "pytorch_benchmark"
3232
eval_accuracy=False,
3333
generate_patch=True,
3434
overwrite=False,
@@ -178,6 +178,23 @@ def enable(
178178

179179
transformed_list_code_path = []
180180

181+
## Determine Code Domain
182+
# reset globals
183+
globals.reset_globals()
184+
185+
from .utils import handle_user_input
186+
globals.list_code_path, num_user_code_path = handle_user_input.get_all_code_path(code)
187+
188+
from .coders.autoinc import domain
189+
code_domain = domain.determine_domain(globals.list_code_path[0])
190+
if code_domain == "transformers_trainer":
191+
if "pytorch_benchmark" in features:
192+
features = ["pytorch_reclaim_inference_transformers_trainer"] + features
193+
# for BS
194+
args += " --per_device_eval_batch_size " + str(target_batch_size)
195+
globals.batch_size_changed = True
196+
197+
## Feature Transformation
181198
for idx_feature, feature in enumerate(features):
182199

183200
# reset globals
@@ -223,10 +240,14 @@ def enable(
223240
from .coders.pytorch.dummy_dataloader import DummyDataLoader
224241
opt = DummyDataLoader(globals.list_model_def_instance)
225242
opt.register_transformation()
226-
elif feature == "pytorch_reclaim_inputs": # is not in harness scope, but needs call graph and type inference
243+
elif feature == "pytorch_reclaim_inputs":
227244
from .coders.pytorch.reclaim_inputs import ReclaimInputs
228245
opt = ReclaimInputs(globals.list_model_def_instance)
229246
opt.register_transformation()
247+
elif feature == "pytorch_reclaim_inference_transformers_trainer":
248+
from .coders.pytorch.reclaim_inference_transformers_trainer import ReclaimInferenceTransformersTrainer
249+
opt = ReclaimInferenceTransformersTrainer(globals.list_model_def_instance)
250+
opt.register_transformation()
230251
elif feature in [
231252
"pytorch_inc_dynamic_quant",
232253
"pytorch_inc_static_quant_fx",
@@ -257,6 +278,8 @@ def enable(
257278
for i in range(len(list_transformed_code)):
258279
# Batch Size
259280
if "pytorch_change_batch_size" in features:
281+
if "batch_size" in list_transformed_code[0]: # entry code has "batch_size"
282+
globals.batch_size_changed = True
260283
from .coders.pytorch.batch_size import BatchSizeCoder
261284
globals.target_batch_size = str(target_batch_size)
262285
list_transformed_code[i] = BatchSizeCoder(list_transformed_code[i]).transform()
@@ -321,9 +344,6 @@ def enable(
321344
### Output of Enabling
322345
globals.list_code_path, num_user_code_path = handle_user_input.get_all_code_path(code)
323346

324-
if save_patch_path == "":
325-
save_patch_path = ws_path
326-
327347
if generate_patch:
328348
whole_patch_user_code = ""
329349
for path in globals.list_code_path[0:num_user_code_path]:
@@ -335,10 +355,12 @@ def enable(
335355
this_patch, _ = sp_gen_patch.communicate()
336356
this_patch = str(this_patch)[2:-1]
337357
whole_patch_user_code += this_patch
338-
open(save_patch_path + "neural_coder_patch" + patch_suffix, "w").write(
358+
if save_patch_path == "":
359+
save_patch_path = ws_path + "neural_coder_patch"
360+
open(save_patch_path + patch_suffix, "w").write(
339361
whole_patch_user_code.replace(r'\n', '\n').replace(r'\t', '\t').replace(r"\'", "\'"))
340362
abs_patch_path = os.path.abspath(
341-
save_patch_path + "neural_coder_patch" + patch_suffix)
363+
save_patch_path + patch_suffix)
342364
logger.info(f"The patch is saved to: [{abs_patch_path}]")
343365

344366
if overwrite:
@@ -358,10 +380,12 @@ def enable(
358380
this_patch, _ = sp_gen_patch.communicate()
359381
this_patch = str(this_patch)[2:-1]
360382
whole_patch_import_modules += this_patch
361-
open(save_patch_path + "neural_coder_patch_import_modules" + patch_suffix, "w").write(
383+
if save_patch_path == "":
384+
save_patch_path = ws_path + "neural_coder_patch_import_modules"
385+
open(save_patch_path + patch_suffix, "w").write(
362386
whole_patch_import_modules.replace(r'\n', '\n').replace(r'\t', '\t').replace(r"\'", "\'"))
363387
abs_patch_path = os.path.abspath(
364-
save_patch_path + "neural_coder_patch_import_modules" + patch_suffix)
388+
save_patch_path + patch_suffix)
365389
logger.info(
366390
f"The patch for imported modules is saved to: [{abs_patch_path}]")
367391

@@ -580,7 +604,10 @@ def bench(
580604
IPS[-1] = IPS[-2]
581605

582606
try:
583-
FPS = round(sum(IPS) / len(IPS) * ninstances * bench_batch_size, 3)
607+
if globals.batch_size_changed: # only times BS if BS has been modified, otherwise times 1
608+
FPS = round(sum(IPS) / len(IPS) * ninstances * bench_batch_size, 3)
609+
else:
610+
FPS = round(sum(IPS) / len(IPS) * ninstances * 1, 3)
584611
except:
585612
FPS = 0
586613
try:
@@ -824,20 +851,43 @@ def remove_if_have(list, element):
824851
return list
825852

826853
features = remove_if_have(features, "pytorch_benchmark")
827-
features = remove_if_have(
828-
features, "pytorch_change_batch_size")
854+
features = remove_if_have(features, "pytorch_change_batch_size")
829855
features = remove_if_have(features, "pytorch_cuda_to_cpu")
830856

831-
if not eval_accuracy:
832-
logger.info(
833-
f"Benchmark result (performance) of optimization set [{features}]"
834-
f" is [{bench_performance[0]}] (FPS)")
857+
if auto_quant:
858+
# convert feature name to display name for better user experience
859+
if features == ['pytorch_inc_dynamic_quant']:
860+
features_display = "Intel INT8 (Dynamic)"
861+
elif features == ['pytorch_inc_static_quant_fx']:
862+
features_display = "Intel INT8 (Static)"
863+
elif features == ['pytorch_inc_static_quant_ipex']:
864+
features_display = "Intel INT8 (IPEX)"
865+
elif features == ['pytorch_inc_bf16']:
866+
features_display = "Intel BF16"
867+
elif features == []:
868+
features_display = "The Original Model"
869+
870+
if not eval_accuracy:
871+
logger.info(
872+
f"Benchmark result (performance) of {features_display}"
873+
f" is {bench_performance[0]} (FPS)")
874+
else:
875+
logger.info(
876+
f"Benchmark result (performance) of {features_display}"
877+
f" is {bench_performance[0]} (FPS)")
878+
logger.info(
879+
f"Benchmark result (accuracy) of {features_display} is {bench_acc[5]}")
835880
else:
836-
logger.info(
837-
f"Benchmark result (performance) of optimization set [{features}]"
838-
f" is [{bench_performance[0]}] (FPS)")
839-
logger.info(
840-
f"Benchmark result (accuracy) of optimization set [{features}] is [{bench_acc[5]}]")
881+
if not eval_accuracy:
882+
logger.info(
883+
f"Benchmark result (performance) of optimization set [{features}]"
884+
f" is [{bench_performance[0]}] (FPS)")
885+
else:
886+
logger.info(
887+
f"Benchmark result (performance) of optimization set [{features}]"
888+
f" is [{bench_performance[0]}] (FPS)")
889+
logger.info(
890+
f"Benchmark result (accuracy) of optimization set [{features}] is [{bench_acc[5]}]")
841891

842892
d = {} # initialize dict
843893
d["features"] = features
@@ -857,8 +907,7 @@ def remove_if_have(list, element):
857907

858908
# print result
859909
if not eval_accuracy:
860-
logger.info(
861-
f"Superbench result of sweeping [{sweep_objective}] printed below with sorted FPS: ")
910+
print(f"Superbench result of sweeping [{sweep_objective}] printed below with sorted FPS: ")
862911
print("{:<20} {:<20} {:<120}".format(
863912
'Numactl Mode', 'Performance (FPS)', 'Features Applied'))
864913

@@ -878,8 +927,7 @@ def remove_if_have(list, element):
878927
)
879928
)
880929
else:
881-
logger.info(
882-
f"Superbench result of sweeping [{sweep_objective}] printed below with sorted FPS: ")
930+
print(f"Superbench result of sweeping [{sweep_objective}] printed below with sorted FPS: ")
883931
print("{:<20} {:<20} {:<20} {:<120}".format(
884932
'Numactl Mode', 'Performance (FPS)', 'Accuracy', 'Features Applied'))
885933

@@ -921,12 +969,42 @@ def remove_if_have(list, element):
921969
original_model_performance = list_FPS[i]
922970
break
923971

924-
logger.info(f"The best optimization set for your model is: {list_optimization_set_top3[0]}")
925-
logger.info(
926-
f"You can get up to: "
927-
f"{round(list_performance_top3[0] / original_model_performance, 1)}"
928-
f" X performance boost with the suggested optimization set."
972+
if auto_quant:
973+
# convert feature name to display name for better user experience
974+
if list_optimization_set_top3[0] == ['pytorch_inc_dynamic_quant']:
975+
best_optimization_display = "Intel INT8 (Dynamic)"
976+
elif list_optimization_set_top3[0] == ['pytorch_inc_static_quant_fx']:
977+
best_optimization_display = "Intel INT8 (Static)"
978+
elif list_optimization_set_top3[0] == ['pytorch_inc_static_quant_ipex']:
979+
best_optimization_display = "Intel INT8 (IPEX)"
980+
elif list_optimization_set_top3[0] == ['pytorch_inc_bf16']:
981+
best_optimization_display = "Intel BF16"
982+
elif list_optimization_set_top3[0] == []:
983+
best_optimization_display = "The Original Model"
984+
985+
logger.info(f"The best optimization set for your model is {best_optimization_display}")
986+
logger.info(
987+
f"You can get up to "
988+
f"{round(list_performance_top3[0] / original_model_performance, 1)}"
989+
f" X performance boost."
990+
)
991+
else:
992+
logger.info(f"The best optimization set for your model is: {list_optimization_set_top3[0]}")
993+
logger.info(
994+
f"You can get up to "
995+
f"{round(list_performance_top3[0] / original_model_performance, 1)}"
996+
f" X performance boost."
997+
)
998+
999+
# generate patch for the best optimization
1000+
features_to_generate = list_optimization_set_top3[0]
1001+
features_to_generate.append("pytorch_cuda_to_cpu")
1002+
enable(
1003+
code=code,
1004+
features=features_to_generate,
1005+
save_patch_path="intel_optimization",
9291006
)
1007+
logger.info('The optimization patch was saved to "intel_optimziation.diff"')
9301008

9311009
return list_optimization_set_top3, list_performance_top3, original_model_ranking, original_model_performance
9321010

0 commit comments

Comments
 (0)