Skip to content

Commit dab24ec

Browse files
authored
Merge branch 'main' into gh/SS-JIA/229/orig
2 parents 167c063 + b5567be commit dab24ec

File tree

9 files changed

+233
-34
lines changed

9 files changed

+233
-34
lines changed

.github/scripts/label_utils.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@
2222

2323
LABEL_ERR_MSG_TITLE = "This PR needs a `release notes:` label"
2424
LABEL_ERR_MSG = f"""# {LABEL_ERR_MSG_TITLE}
25-
If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with `release notes:`.
26-
27-
If not, please add the `release notes: none` label.
25+
If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with `release notes:`. This helps us keep track and include your important work in the next release notes.
2826
2927
To add a label, you can comment to pytorchbot, for example
3028
`@pytorchbot label "release notes: none"`

.github/scripts/trymerge.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,7 @@
5959
patterns_to_regex,
6060
retries_decorator,
6161
)
62-
from label_utils import (
63-
gh_add_labels,
64-
gh_remove_label,
65-
has_required_labels,
66-
LABEL_ERR_MSG,
67-
)
62+
from label_utils import gh_add_labels, gh_remove_label
6863
from trymerge_explainer import get_revert_message, TryMergeExplainer
6964

7065
# labels
@@ -2116,9 +2111,6 @@ def merge(
21162111
# Check for approvals
21172112
find_matching_merge_rule(pr, repo, skip_mandatory_checks=True)
21182113

2119-
if not has_required_labels(pr):
2120-
raise RuntimeError(LABEL_ERR_MSG.lstrip(" #"))
2121-
21222114
if ignore_current:
21232115
checks = pr.get_checkrun_conclusions()
21242116
_, failing, _ = categorize_checks(

.github/workflows/check-labels.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,4 @@ jobs:
5151
PR_NUM: ${{ github.event.number || github.event.inputs.pr_number }}
5252
run: |
5353
set -ex
54-
python3 .github/scripts/check_labels.py --exit-non-zero "${PR_NUM}"
54+
python3 .github/scripts/check_labels.py "${PR_NUM}"

backends/cadence/aot/replace_ops.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2065,11 +2065,10 @@ def call_operator(
20652065
return super().call_operator(op, args, kwargs, meta)
20662066

20672067

2068-
@register_cadence_pass(CadencePassAttribute(opt_level=2))
2069-
class ReplaceGeluWithApproximateGeluPass(ExportPass):
2068+
@register_cadence_pass(CadencePassAttribute(opt_level=0))
2069+
class ReplaceAtenApproxGeluWithApproxGeluPass(ExportPass):
20702070
"""
2071-
Replace the gelu op with an approximate gelu op. The approximate gelu op
2072-
is more efficient on DSP backends.
2071+
Replace the aten gelu op with an approximate arg with an approximate gelu op.
20732072
"""
20742073

20752074
def call_operator(
@@ -2079,6 +2078,9 @@ def call_operator(
20792078
kwargs: Dict[str, Argument],
20802079
meta: NodeMetadata,
20812080
) -> ProxyValue:
2081+
if "approximate" not in kwargs:
2082+
return super().call_operator(op, args, kwargs, meta)
2083+
20822084
if op not in {
20832085
exir_ops.edge.aten.gelu.default,
20842086
}:
@@ -2414,7 +2416,7 @@ class CadenceReplaceOpsInGraph:
24142416
ReplaceSingleElementTensorArgumentsFromFullOpWithScalarPass,
24152417
ReplaceAtenAvgPoolWithJarvisAvgPoolPass,
24162418
ReplaceWhereWithFullArgsWithWhereScalar,
2417-
ReplaceGeluWithApproximateGeluPass,
2419+
ReplaceAtenApproxGeluWithApproxGeluPass,
24182420
ReplaceSplitWithSlicePass,
24192421
ReplacePowWithMulPass,
24202422
]

backends/cadence/aot/tests/test_replace_ops_passes.py

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@
2626
ForceChannelLastForConvPass,
2727
MakeSliceAndCatDimOutermostPass,
2828
ReplaceAddMMWithLinearPass,
29+
ReplaceAtenApproxGeluWithApproxGeluPass,
2930
ReplaceAtenConvolutionWithJarvisConvolutionPass,
3031
ReplaceConstantPadNdWithSlicePass,
3132
ReplaceConvolutionOptionalArgsWithConcreteArgsPass,
3233
ReplaceConvWithIm2RowAndLinear,
3334
ReplaceEmptyTensorsWithFullPass,
3435
ReplaceFunctionallyEquivalentOpTargets,
35-
ReplaceGeluWithApproximateGeluPass,
3636
ReplaceIm2RowWithViewPass,
3737
ReplaceLinearWithFullyConnectedOpPass,
3838
ReplaceMatmulWithTransposedMatmulPass,
@@ -1287,17 +1287,41 @@ def forward(self, cond: torch.Tensor):
12871287
1,
12881288
)
12891289

1290-
def test_replace_aten_gelu_with_approximate_gelu(self):
1291-
class Gelu(torch.nn.Module):
1292-
def forward(self, input):
1293-
return torch.nn.functional.gelu(input)
1290+
def test_no_replace_aten_gelu_with_approximate_gelu(self):
1291+
inputs = torch.randn(2, 1, 64)
1292+
1293+
gm = single_op_builder(
1294+
placeholders=(inputs,),
1295+
op=exir_ops.edge.aten.gelu.default,
1296+
args=(inputs,),
1297+
)
1298+
gm = ExportPass().call(gm).graph_module
1299+
1300+
p = ReplaceAtenApproxGeluWithApproxGeluPass()
1301+
graph_after_passes = p.call(gm).graph_module
12941302

1303+
# Assert that aten.gelu op was not decomposed, since it didn't have an approximate argument
1304+
self.assertEqual(
1305+
count_node(
1306+
graph_after_passes,
1307+
exir_ops.edge.aten.gelu.default,
1308+
),
1309+
1,
1310+
)
1311+
1312+
def test_replace_aten_approximate_gelu_with_approximate_gelu(self):
12951313
inputs = torch.randn(2, 1, 64)
12961314

1297-
graph_module = export_to_edge(Gelu(), (inputs,)).exported_program().graph_module
1315+
gm = single_op_builder(
1316+
placeholders=(inputs,),
1317+
op=exir_ops.edge.aten.gelu.default,
1318+
args=(inputs,),
1319+
kwargs={"approximate": "tanh"},
1320+
)
1321+
gm = ExportPass().call(gm).graph_module
12981322

1299-
p = ReplaceGeluWithApproximateGeluPass()
1300-
graph_after_passes = cast(PassResult, p(graph_module)).graph_module
1323+
p = ReplaceAtenApproxGeluWithApproxGeluPass()
1324+
graph_after_passes = p.call(gm).graph_module
13011325

13021326
# Assert that aten.gelu op was decomposed
13031327
self.assertEqual(

backends/qualcomm/tests/test_qnn_delegate.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3880,6 +3880,41 @@ def test_conv_former(self):
38803880
self.assertGreaterEqual(msg["top_1"], 60)
38813881
self.assertGreaterEqual(msg["top_5"], 80)
38823882

3883+
def test_deit(self):
3884+
if not self.required_envs([self.image_dataset]):
3885+
self.skipTest("missing required envs")
3886+
cmds = [
3887+
"python",
3888+
f"{self.executorch_root}/examples/qualcomm/oss_scripts/deit.py",
3889+
"--dataset",
3890+
self.image_dataset,
3891+
"--artifact",
3892+
self.artifact_dir,
3893+
"--build_folder",
3894+
self.build_folder,
3895+
"--device",
3896+
self.device,
3897+
"--model",
3898+
self.model,
3899+
"--ip",
3900+
self.ip,
3901+
"--port",
3902+
str(self.port),
3903+
]
3904+
if self.host:
3905+
cmds.extend(["--host", self.host])
3906+
3907+
p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL)
3908+
with Listener((self.ip, self.port)) as listener:
3909+
conn = listener.accept()
3910+
p.communicate()
3911+
msg = json.loads(conn.recv())
3912+
if "Error" in msg:
3913+
self.fail(msg["Error"])
3914+
else:
3915+
self.assertGreaterEqual(msg["top_1"], 75)
3916+
self.assertGreaterEqual(msg["top_5"], 90)
3917+
38833918
def test_dino_v2(self):
38843919
if not self.required_envs([self.image_dataset]):
38853920
self.skipTest("missing required envs")

examples/models/llama/README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ Llama 3 8B performance was measured on the Samsung Galaxy S22, S24, and OnePlus
164164
```
165165
# No quantization
166166
# Set these paths to point to the downloaded files
167-
LLAMA_CHECKPOINT=path/to/checkpoint.pth
167+
LLAMA_CHECKPOINT=path/to/consolidated.00.pth
168168
LLAMA_PARAMS=path/to/params.json
169169
170170
python -m examples.models.llama.export_llama \
@@ -186,7 +186,7 @@ For convenience, an [exported ExecuTorch bf16 model](https://huggingface.co/exec
186186
```
187187
# SpinQuant
188188
# Set these paths to point to the exported files
189-
LLAMA_QUANTIZED_CHECKPOINT=path/to/spinquant/checkpoint.pth
189+
LLAMA_QUANTIZED_CHECKPOINT=path/to/spinquant/consolidated.00.pth.pth
190190
LLAMA_PARAMS=path/to/spinquant/params.json
191191
192192
python -m examples.models.llama.export_llama \
@@ -215,7 +215,7 @@ For convenience, an [exported ExecuTorch SpinQuant model](https://huggingface.co
215215
```
216216
# QAT+LoRA
217217
# Set these paths to point to the exported files
218-
LLAMA_QUANTIZED_CHECKPOINT=path/to/qlora/checkpoint.pth
218+
LLAMA_QUANTIZED_CHECKPOINT=path/to/qlora/consolidated.00.pth.pth
219219
LLAMA_PARAMS=path/to/qlora/params.json
220220
221221
python -m examples.models.llama.export_llama \
@@ -248,7 +248,7 @@ You can export and run the original Llama 3 8B instruct model.
248248
2. Export model and generate `.pte` file
249249
```
250250
python -m examples.models.llama.export_llama \
251-
--checkpoint <consolidated.00.pth> \
251+
--checkpoint <consolidated.00.pth.pth> \
252252
-p <params.json> \
253253
-kv \
254254
--use_sdpa_with_kv_cache \
@@ -396,7 +396,7 @@ First export your model for lowbit quantization (step 2 above):
396396
397397
```
398398
# Set these paths to point to the downloaded files
399-
LLAMA_CHECKPOINT=path/to/checkpoint.pth
399+
LLAMA_CHECKPOINT=path/to/consolidated.00.pth.pth
400400
LLAMA_PARAMS=path/to/params.json
401401

402402
# Set low-bit quantization parameters
@@ -476,7 +476,7 @@ We use [LM Eval](https://github.com/EleutherAI/lm-evaluation-harness) to evaluat
476476
For base models, use the following example command to calculate its perplexity based on WikiText.
477477
```
478478
python -m examples.models.llama.eval_llama \
479-
-c <checkpoint.pth> \
479+
-c <consolidated.00.pth.pth> \
480480
-p <params.json> \
481481
-t <tokenizer.model/bin> \
482482
-kv \
@@ -489,7 +489,7 @@ python -m examples.models.llama.eval_llama \
489489
For instruct models, use the following example command to calculate its MMLU score.
490490
```
491491
python -m examples.models.llama.eval_llama \
492-
-c <checkpoint.pth> \
492+
-c <consolidated.00.pth.pth> \
493493
-p <params.json> \
494494
-t <tokenizer.model/bin> \
495495
-kv \

examples/models/qwen3/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,4 @@ cmake-out/examples/models/llama/llama_main
8888
To run the model on an example iOS or Android app, see the Llama README's [Step 5: Build Mobile apps](../llama/README.md#step-5-build-mobile-apps) section.
8989

9090
### FAQ
91-
For more help with exporting or running this model, feel free to ask in our [discord channel](https://lnkd.in/gWCM4ViK).
91+
For more help with exporting or running this model, feel free to ask in our [discord channel](https://discord.gg/UEjkY9Zs).
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# Copyright (c) Qualcomm Innovation Center, Inc.
2+
# All rights reserved
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
import getpass
8+
import json
9+
import os
10+
from multiprocessing.connection import Client
11+
12+
import numpy as np
13+
from executorch.backends.qualcomm._passes.qnn_pass_manager import (
14+
get_capture_program_passes,
15+
)
16+
from executorch.backends.qualcomm.quantizer.quantizer import QuantDtype
17+
from executorch.examples.qualcomm.utils import (
18+
build_executorch_binary,
19+
get_imagenet_dataset,
20+
make_output_dir,
21+
parse_skip_delegation_node,
22+
setup_common_args_and_variables,
23+
SimpleADB,
24+
topk_accuracy,
25+
)
26+
from transformers import AutoConfig, AutoModelForImageClassification
27+
28+
29+
def get_instance():
30+
module = (
31+
AutoModelForImageClassification.from_pretrained(
32+
"facebook/deit-base-distilled-patch16-224"
33+
)
34+
.eval()
35+
.to("cpu")
36+
)
37+
38+
return module
39+
40+
41+
def main(args):
42+
skip_node_id_set, skip_node_op_set = parse_skip_delegation_node(args)
43+
44+
os.makedirs(args.artifact, exist_ok=True)
45+
config = AutoConfig.from_pretrained("facebook/deit-base-distilled-patch16-224")
46+
data_num = 100
47+
height = config.image_size
48+
width = config.image_size
49+
inputs, targets, input_list = get_imagenet_dataset(
50+
dataset_path=f"{args.dataset}",
51+
data_size=data_num,
52+
image_shape=(height, width),
53+
crop_size=(height, width),
54+
)
55+
56+
# Get the Deit model.
57+
model = get_instance()
58+
pte_filename = "deit_qnn"
59+
60+
# lower to QNN
61+
passes_job = get_capture_program_passes()
62+
build_executorch_binary(
63+
model,
64+
inputs[0],
65+
args.model,
66+
f"{args.artifact}/{pte_filename}",
67+
dataset=inputs,
68+
skip_node_id_set=skip_node_id_set,
69+
skip_node_op_set=skip_node_op_set,
70+
quant_dtype=QuantDtype.use_8a8w,
71+
passes_job=passes_job,
72+
shared_buffer=args.shared_buffer,
73+
)
74+
75+
if args.compile_only:
76+
return
77+
78+
workspace = f"/data/local/tmp/{getpass.getuser()}/executorch/{pte_filename}"
79+
pte_path = f"{args.artifact}/{pte_filename}.pte"
80+
81+
adb = SimpleADB(
82+
qnn_sdk=os.getenv("QNN_SDK_ROOT"),
83+
build_path=f"{args.build_folder}",
84+
pte_path=pte_path,
85+
workspace=workspace,
86+
device_id=args.device,
87+
host_id=args.host,
88+
soc_model=args.model,
89+
)
90+
adb.push(inputs=inputs, input_list=input_list)
91+
adb.execute()
92+
93+
# collect output data
94+
output_data_folder = f"{args.artifact}/outputs"
95+
make_output_dir(output_data_folder)
96+
97+
adb.pull(output_path=args.artifact)
98+
99+
# top-k analysis
100+
predictions = []
101+
for i in range(data_num):
102+
predictions.append(
103+
np.fromfile(
104+
os.path.join(output_data_folder, f"output_{i}_0.raw"), dtype=np.float32
105+
)
106+
)
107+
108+
k_val = [1, 5]
109+
topk = [topk_accuracy(predictions, targets, k).item() for k in k_val]
110+
if args.ip and args.port != -1:
111+
with Client((args.ip, args.port)) as conn:
112+
conn.send(json.dumps({f"top_{k}": topk[i] for i, k in enumerate(k_val)}))
113+
else:
114+
for i, k in enumerate(k_val):
115+
print(f"top_{k}->{topk[i]}%")
116+
117+
118+
if __name__ == "__main__":
119+
parser = setup_common_args_and_variables()
120+
parser.add_argument(
121+
"-a",
122+
"--artifact",
123+
help="path for storing generated artifacts and output by this example. Default ./deit_qnn",
124+
default="./deit_qnn",
125+
type=str,
126+
)
127+
128+
parser.add_argument(
129+
"-d",
130+
"--dataset",
131+
help=(
132+
"path to the validation folder of ImageNet dataset. "
133+
"e.g. --dataset imagenet-mini/val "
134+
"for https://www.kaggle.com/datasets/ifigotin/imagenetmini-1000)"
135+
),
136+
type=str,
137+
required=True,
138+
)
139+
140+
args = parser.parse_args()
141+
try:
142+
main(args)
143+
except Exception as e:
144+
if args.ip and args.port != -1:
145+
with Client((args.ip, args.port)) as conn:
146+
conn.send(json.dumps({"Error": str(e)}))
147+
else:
148+
raise Exception(e)

0 commit comments

Comments
 (0)