blob: 6c65ee1ea72f231e62cb9418740e4f4dd0cdd305 [file] [log] [blame]
alexanderf4e2c472021-05-14 13:14:21 +01001#!/usr/bin/env python3
Isabella Gottardi2181d0a2021-04-07 09:27:38 +01002
3# Copyright (c) 2021 Arm Limited. All rights reserved.
4# SPDX-License-Identifier: Apache-2.0
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17
18import os, errno
19import urllib.request
20import subprocess
21import fnmatch
22import logging
23import sys
24
25from argparse import ArgumentParser
26from urllib.error import URLError
Kshitij Sisodia3be26232021-10-29 12:29:06 +010027from collections import namedtuple
28
Isabella Gottardi2181d0a2021-04-07 09:27:38 +010029
30json_uc_res = [{
31 "use_case_name": "ad",
32 "resources": [{"name": "ad_medium_int8.tflite",
33 "url": "https://github.com/ARM-software/ML-zoo/raw/7c32b097f7d94aae2cd0b98a8ed5a3ba81e66b18/models/anomaly_detection/micronet_medium/tflite_int8/ad_medium_int8.tflite"},
34 {"name": "ifm0.npy",
35 "url": "https://github.com/ARM-software/ML-zoo/raw/7c32b097f7d94aae2cd0b98a8ed5a3ba81e66b18/models/anomaly_detection/micronet_medium/tflite_int8/testing_input/input/0.npy"},
36 {"name": "ofm0.npy",
37 "url": "https://github.com/ARM-software/ML-zoo/raw/7c32b097f7d94aae2cd0b98a8ed5a3ba81e66b18/models/anomaly_detection/micronet_medium/tflite_int8/testing_output/Identity/0.npy"}]
38},
39 {
40 "use_case_name": "asr",
Kshitij Sisodiae12ac832021-05-20 11:18:53 +010041 "resources": [{"name": "wav2letter_pruned_int8.tflite",
42 "url": "https://github.com/ARM-software/ML-zoo/raw/1a92aa08c0de49a7304e0a7f3f59df6f4fd33ac8/models/speech_recognition/wav2letter/tflite_pruned_int8/wav2letter_pruned_int8.tflite"},
Isabella Gottardi2181d0a2021-04-07 09:27:38 +010043 {"name": "ifm0.npy",
Kshitij Sisodiae12ac832021-05-20 11:18:53 +010044 "url": "https://github.com/ARM-software/ML-zoo/raw/1a92aa08c0de49a7304e0a7f3f59df6f4fd33ac8/models/speech_recognition/wav2letter/tflite_pruned_int8/testing_input/input_2_int8/0.npy"},
Isabella Gottardi2181d0a2021-04-07 09:27:38 +010045 {"name": "ofm0.npy",
Kshitij Sisodiae12ac832021-05-20 11:18:53 +010046 "url": "https://github.com/ARM-software/ML-zoo/raw/1a92aa08c0de49a7304e0a7f3f59df6f4fd33ac8/models/speech_recognition/wav2letter/tflite_pruned_int8/testing_output/Identity_int8/0.npy"}]
Isabella Gottardi2181d0a2021-04-07 09:27:38 +010047 },
48 {
49 "use_case_name": "img_class",
Richard Burton0d110592021-08-12 17:26:30 +010050 "resources": [{"name": "mobilenet_v2_1.0_224_INT8.tflite",
51 "url": "https://github.com/ARM-software/ML-zoo/raw/e0aa361b03c738047b9147d1a50e3f2dcb13dbcb/models/image_classification/mobilenet_v2_1.0_224/tflite_int8/mobilenet_v2_1.0_224_INT8.tflite"},
Isabella Gottardi2181d0a2021-04-07 09:27:38 +010052 {"name": "ifm0.npy",
Richard Burton0d110592021-08-12 17:26:30 +010053 "url": "https://github.com/ARM-software/ML-zoo/raw/e0aa361b03c738047b9147d1a50e3f2dcb13dbcb/models/image_classification/mobilenet_v2_1.0_224/tflite_int8/testing_input/tfl.quantize/0.npy"},
Isabella Gottardi2181d0a2021-04-07 09:27:38 +010054 {"name": "ofm0.npy",
Richard Burton0d110592021-08-12 17:26:30 +010055 "url": "https://github.com/ARM-software/ML-zoo/raw/e0aa361b03c738047b9147d1a50e3f2dcb13dbcb/models/image_classification/mobilenet_v2_1.0_224/tflite_int8/testing_output/MobilenetV2/Predictions/Reshape_11/0.npy"}]
Isabella Gottardi2181d0a2021-04-07 09:27:38 +010056 },
57 {
58 "use_case_name": "kws",
59 "resources": [{"name": "ds_cnn_clustered_int8.tflite",
60 "url": "https://github.com/ARM-software/ML-zoo/raw/68b5fbc77ed28e67b2efc915997ea4477c1d9d5b/models/keyword_spotting/ds_cnn_large/tflite_clustered_int8/ds_cnn_clustered_int8.tflite"},
61 {"name": "ifm0.npy",
62 "url": "https://github.com/ARM-software/ML-zoo/raw/68b5fbc77ed28e67b2efc915997ea4477c1d9d5b/models/keyword_spotting/ds_cnn_large/tflite_clustered_int8/testing_input/input_2/0.npy"},
63 {"name": "ofm0.npy",
64 "url": "https://github.com/ARM-software/ML-zoo/raw/68b5fbc77ed28e67b2efc915997ea4477c1d9d5b/models/keyword_spotting/ds_cnn_large/tflite_clustered_int8/testing_output/Identity/0.npy"}]
65 },
Éanna Ó Catháin8f958872021-09-15 09:32:30 +010066 {
67 "use_case_name": "vww",
68 "resources": [{"name": "vww4_128_128_INT8.tflite",
69 "url": "https://github.com/ARM-software/ML-zoo/raw/7dd3b16bb84007daf88be8648983c07f3eb21140/models/visual_wake_words/micronet_vww4/tflite_int8/vww4_128_128_INT8.tflite"},
70 {"name": "ifm0.npy",
71 "url": "https://github.com/ARM-software/ML-zoo/raw/7dd3b16bb84007daf88be8648983c07f3eb21140/models/visual_wake_words/micronet_vww4/tflite_int8/testing_input/input/0.npy"},
72 {"name": "ofm0.npy",
73 "url": "https://github.com/ARM-software/ML-zoo/raw/7dd3b16bb84007daf88be8648983c07f3eb21140/models/visual_wake_words/micronet_vww4/tflite_int8/testing_output/Identity/0.npy"}]
74 },
Isabella Gottardi2181d0a2021-04-07 09:27:38 +010075 {
76 "use_case_name": "kws_asr",
Kshitij Sisodiae12ac832021-05-20 11:18:53 +010077 "resources": [{"name": "wav2letter_pruned_int8.tflite",
78 "url": "https://github.com/ARM-software/ML-zoo/raw/1a92aa08c0de49a7304e0a7f3f59df6f4fd33ac8/models/speech_recognition/wav2letter/tflite_pruned_int8/wav2letter_pruned_int8.tflite"},
Isabella Gottardi2181d0a2021-04-07 09:27:38 +010079 {"sub_folder": "asr", "name": "ifm0.npy",
Kshitij Sisodiae12ac832021-05-20 11:18:53 +010080 "url": "https://github.com/ARM-software/ML-zoo/raw/1a92aa08c0de49a7304e0a7f3f59df6f4fd33ac8/models/speech_recognition/wav2letter/tflite_pruned_int8/testing_input/input_2_int8/0.npy"},
Isabella Gottardi2181d0a2021-04-07 09:27:38 +010081 {"sub_folder": "asr", "name": "ofm0.npy",
Kshitij Sisodiae12ac832021-05-20 11:18:53 +010082 "url": "https://github.com/ARM-software/ML-zoo/raw/1a92aa08c0de49a7304e0a7f3f59df6f4fd33ac8/models/speech_recognition/wav2letter/tflite_pruned_int8/testing_output/Identity_int8/0.npy"},
Isabella Gottardi2181d0a2021-04-07 09:27:38 +010083 {"name": "ds_cnn_clustered_int8.tflite",
84 "url": "https://github.com/ARM-software/ML-zoo/raw/68b5fbc77ed28e67b2efc915997ea4477c1d9d5b/models/keyword_spotting/ds_cnn_large/tflite_clustered_int8/ds_cnn_clustered_int8.tflite"},
85 {"sub_folder": "kws", "name": "ifm0.npy",
86 "url": "https://github.com/ARM-software/ML-zoo/raw/68b5fbc77ed28e67b2efc915997ea4477c1d9d5b/models/keyword_spotting/ds_cnn_large/tflite_clustered_int8/testing_input/input_2/0.npy"},
87 {"sub_folder": "kws", "name": "ofm0.npy",
88 "url": "https://github.com/ARM-software/ML-zoo/raw/68b5fbc77ed28e67b2efc915997ea4477c1d9d5b/models/keyword_spotting/ds_cnn_large/tflite_clustered_int8/testing_output/Identity/0.npy"}]
89 },
90 {
91 "use_case_name": "inference_runner",
92 "resources": [{"name": "dnn_s_quantized.tflite",
93 "url": "https://github.com/ARM-software/ML-zoo/raw/68b5fbc77ed28e67b2efc915997ea4477c1d9d5b/models/keyword_spotting/dnn_small/tflite_int8/dnn_s_quantized.tflite"}
94 ]
95 },]
96
Kshitij Sisodia3be26232021-10-29 12:29:06 +010097# Valid NPU configurations:
98valid_npu_config_names = [
99 'ethos-u55-32', 'ethos-u55-64',
100 'ethos-u55-128', 'ethos-u55-256',
101 'ethos-u65-256','ethos-u65-512']
102
103# Default NPU configurations (these are always run when the models are optimised)
104default_npu_config_names = [valid_npu_config_names[2], valid_npu_config_names[4]]
105
106# NPU config named tuple
107NPUConfig = namedtuple('NPUConfig',['config_name',
108 'memory_mode',
109 'system_config',
110 'ethos_u_npu_id',
111 'ethos_u_config_id'])
112
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100113
114def call_command(command: str) -> str:
115 """
116 Helpers function that call subprocess and return the output.
117
118 Parameters:
119 ----------
120 command (string): Specifies the command to run.
121 """
122 logging.info(command)
alexander50a06502021-05-12 19:06:02 +0100123 proc = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
124 log = proc.stdout.decode("utf-8")
125 if proc.returncode == 0:
126 logging.info(log)
127 else:
128 logging.error(log)
129 proc.check_returncode()
130 return log
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100131
132
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100133def get_default_npu_config_from_name(config_name: str) -> NPUConfig:
134 """
135 Gets the file suffix for the tflite file from the
136 `accelerator_config` string.
137
138 Parameters:
139 ----------
140 config_name (str): Ethos-U NPU configuration from valid_npu_config_names
141
142 Returns:
143 -------
144 NPUConfig: An NPU config named tuple populated with defaults for the given
145 config name
146 """
147 if config_name not in valid_npu_config_names:
148 raise ValueError(f"""
149 Invalid Ethos-U NPU configuration.
150 Select one from {valid_npu_config_names}.
151 """)
152
153 strings_ids = ["ethos-u55-", "ethos-u65-"]
154 processor_ids = ["U55", "U65"]
155 prefix_ids = ["H", "Y"]
156 memory_modes = ["Shared_Sram", "Dedicated_Sram"]
157 system_configs = ["Ethos_U55_High_End_Embedded", "Ethos_U65_High_End"]
158
159 for i in range(len(strings_ids)):
160 if config_name.startswith(strings_ids[i]):
161 npu_config_id = config_name.replace(strings_ids[i], prefix_ids[i])
162 return NPUConfig(config_name=config_name,
163 memory_mode=memory_modes[i],
164 system_config=system_configs[i],
165 ethos_u_npu_id=processor_ids[i],
166 ethos_u_config_id=npu_config_id)
167
168 return None
169
170
171def set_up_resources(run_vela_on_models=False, additional_npu_config_names=[]):
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100172 """
173 Helpers function that retrieve the output from a command.
174
175 Parameters:
176 ----------
177 run_vela_on_models (bool): Specifies if run vela on downloaded models.
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100178 additional_npu_config_names(list): list of strings of Ethos-U NPU configs.
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100179 """
180 current_file_dir = os.path.dirname(os.path.abspath(__file__))
181 download_dir = os.path.abspath(os.path.join(current_file_dir, "resources_downloaded"))
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100182
183 try:
184 # 1.1 Does the download dir exist?
185 os.mkdir(download_dir)
186 except OSError as e:
187 if e.errno == errno.EEXIST:
188 logging.info("'resources_downloaded' directory exists.")
189 else:
190 raise
191
192 # 1.2 Does the virtual environment exist?
193 env_python = str(os.path.abspath(os.path.join(download_dir, "env", "bin", "python3")))
194 env_activate = str(os.path.abspath(os.path.join(download_dir, "env", "bin", "activate")))
195 if not os.path.isdir(os.path.join(download_dir, "env")):
196 os.chdir(download_dir)
197 # Create the virtual environment
198 command = "python3 -m venv env"
199 call_command(command)
200 commands = ["pip install --upgrade pip", "pip install --upgrade setuptools"]
201 for c in commands:
202 command = f"{env_python} -m {c}"
203 call_command(command)
204 os.chdir(current_file_dir)
205 # 1.3 Make sure to have all the requirement
Nina Drozdf6753c92021-09-07 09:41:28 +0100206 requirements = ["ethos-u-vela==3.1.0"]
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100207 command = f"{env_python} -m pip freeze"
208 packages = call_command(command)
209 for req in requirements:
210 if req not in packages:
211 command = f"{env_python} -m pip install {req}"
212 call_command(command)
213
214 # 2. Download models
215 for uc in json_uc_res:
216 try:
217 # Does the usecase_name download dir exist?
218 os.mkdir(os.path.join(download_dir, uc["use_case_name"]))
219 except OSError as e:
220 if e.errno != errno.EEXIST:
221 logging.error(f"Error creating {uc['use_case_name']} directory.")
222 raise
223
224 for res in uc["resources"]:
225 res_name = res["name"]
226 res_url = res["url"]
227 if "sub_folder" in res:
228 try:
229 # Does the usecase_name/sub_folder download dir exist?
230 os.mkdir(os.path.join(download_dir, uc["use_case_name"], res["sub_folder"]))
231 except OSError as e:
232 if e.errno != errno.EEXIST:
233 logging.error(f"Error creating {uc['use_case_name']} / {res['sub_folder']} directory.")
234 raise
235 res_dst = os.path.join(download_dir,
236 uc["use_case_name"],
237 res["sub_folder"],
238 res_name)
239 else:
240 res_dst = os.path.join(download_dir,
241 uc["use_case_name"],
242 res_name)
alexander3ef1fd42021-05-24 18:56:32 +0100243
244 if os.path.isfile(res_dst):
245 logging.info(f"File {res_dst} exists, skipping download.")
246 else:
247 try:
248 g = urllib.request.urlopen(res_url)
249 with open(res_dst, 'b+w') as f:
250 f.write(g.read())
251 logging.info(f"- Downloaded {res_url} to {res_dst}.")
252 except URLError:
253 logging.error(f"URLError while downloading {res_url}.")
254 raise
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100255
256 # 3. Run vela on models in resources_downloaded
257 # New models will have same name with '_vela' appended.
258 # For example:
259 # original model: ds_cnn_clustered_int8.tflite
alexander50a06502021-05-12 19:06:02 +0100260 # after vela model: ds_cnn_clustered_int8_vela_H128.tflite
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100261 #
262 # Note: To avoid to run vela twice on the same model, it's supposed that
263 # downloaded model names don't contain the 'vela' word.
264 if run_vela_on_models is True:
265 config_file = os.path.join(current_file_dir, "scripts", "vela", "default_vela.ini")
266 models = [os.path.join(dirpath, f)
267 for dirpath, dirnames, files in os.walk(download_dir)
268 for f in fnmatch.filter(files, '*.tflite') if "vela" not in f]
269
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100270 # Consolidate all config names while discarding duplicates:
271 config_names = list(set(default_npu_config_names + additional_npu_config_names))
272
273 # Get npu config tuple for each config name in a list:
274 npu_configs = [get_default_npu_config_from_name(name) for name in config_names]
275
276 logging.info(f'All models will be optimised for these configs:')
277 for config in npu_configs:
278 logging.info(config)
279
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100280 for model in models:
281 output_dir = os.path.dirname(model)
alexander3ef1fd42021-05-24 18:56:32 +0100282 # model name after compiling with vela is an initial model name + _vela suffix
283 vela_optimised_model_path = str(model).replace(".tflite", "_vela.tflite")
alexander3ef1fd42021-05-24 18:56:32 +0100284
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100285 for config in npu_configs:
286 vela_command = (f". {env_activate} && vela {model} " +
287 f"--accelerator-config={config.config_name} " +
alexanderd475f092021-06-24 15:36:49 +0100288 "--optimise Performance " +
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100289 f"--config {config_file} " +
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100290 f"--memory-mode={config.memory_mode} " +
291 f"--system-config={config.system_config} " +
292 f"--output-dir={output_dir}")
alexander3ef1fd42021-05-24 18:56:32 +0100293
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100294 # we want the name to include the configuration suffix. For example: vela_H128,
295 # vela_Y512 etc.
296 new_suffix = "_vela_" + config.ethos_u_config_id + '.tflite'
297 new_vela_optimised_model_path = (
298 vela_optimised_model_path.replace("_vela.tflite", new_suffix))
Isabella Gottardi118f73e2021-09-16 17:54:35 +0100299
300 if os.path.isfile(new_vela_optimised_model_path):
301 logging.info(f"File {new_vela_optimised_model_path} exists, skipping optimisation.")
302 continue
303
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100304 call_command(vela_command)
Isabella Gottardi118f73e2021-09-16 17:54:35 +0100305
306 # rename default vela model
307 os.rename(vela_optimised_model_path, new_vela_optimised_model_path)
308 logging.info(f"Renaming {vela_optimised_model_path} to {new_vela_optimised_model_path}.")
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100309
310
311if __name__ == '__main__':
312 parser = ArgumentParser()
313 parser.add_argument("--skip-vela",
314 help="Do not run Vela optimizer on downloaded models.",
315 action="store_true")
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100316 parser.add_argument("--additional-ethos-u-config-name",
317 help=f"""Additional (non-default) configurations for Vela:
318 {valid_npu_config_names}""",
319 default=[], action="append")
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100320 args = parser.parse_args()
Kshitij Sisodiab9e9c892021-05-27 13:57:35 +0100321
322 logging.basicConfig(filename='log_build_default.log', level=logging.DEBUG)
323 logging.getLogger().addHandler(logging.StreamHandler(sys.stdout))
324
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100325 set_up_resources(not args.skip_vela, args.additional_ethos_u_config_name)