blob: a4c3594cdebda3e48f7de086824523fc5b75a832 [file] [log] [blame]
alexanderf4e2c472021-05-14 13:14:21 +01001#!/usr/bin/env python3
Isabella Gottardief2b9dd2022-02-16 14:24:03 +00002# Copyright (c) 2021-2022 Arm Limited. All rights reserved.
Isabella Gottardi2181d0a2021-04-07 09:27:38 +01003# SPDX-License-Identifier: Apache-2.0
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
Isabella Gottardief2b9dd2022-02-16 14:24:03 +000016import errno
Isabella Gottardi2181d0a2021-04-07 09:27:38 +010017import fnmatch
Isabella Gottardief2b9dd2022-02-16 14:24:03 +000018import json
Isabella Gottardi2181d0a2021-04-07 09:27:38 +010019import logging
Isabella Gottardief2b9dd2022-02-16 14:24:03 +000020import os
21import re
22import shutil
23import subprocess
Isabella Gottardi2181d0a2021-04-07 09:27:38 +010024import sys
Isabella Gottardief2b9dd2022-02-16 14:24:03 +000025import urllib.request
26from argparse import ArgumentParser
27from argparse import ArgumentTypeError
Kshitij Sisodia3be26232021-10-29 12:29:06 +010028from collections import namedtuple
Isabella Gottardief2b9dd2022-02-16 14:24:03 +000029from urllib.error import URLError
Isabella Gottardi6c2ea452022-03-11 13:25:08 +000030
Kshitij Sisodia6a2ac462022-03-01 17:36:06 +000031from scripts.py.check_update_resources_downloaded import get_md5sum_for_file
Kshitij Sisodia3be26232021-10-29 12:29:06 +010032
Isabella Gottardi2181d0a2021-04-07 09:27:38 +010033
Isabella Gottardief2b9dd2022-02-16 14:24:03 +000034json_uc_res = [
35 {
36 "use_case_name": "ad",
37 "url_prefix": [
38 "https://github.com/ARM-software/ML-zoo/raw/7c32b097f7d94aae2cd0b98a8ed5a3ba81e66b18/models/anomaly_detection/micronet_medium/tflite_int8/"
39 ],
40 "resources": [
41 {
42 "name": "ad_medium_int8.tflite",
43 "url": "{url_prefix:0}ad_medium_int8.tflite",
44 },
45 {"name": "ifm0.npy", "url": "{url_prefix:0}testing_input/input/0.npy"},
46 {"name": "ofm0.npy", "url": "{url_prefix:0}testing_output/Identity/0.npy"},
47 ],
48 },
Isabella Gottardi2181d0a2021-04-07 09:27:38 +010049 {
50 "use_case_name": "asr",
Isabella Gottardief2b9dd2022-02-16 14:24:03 +000051 "url_prefix": [
52 "https://github.com/ARM-software/ML-zoo/raw/1a92aa08c0de49a7304e0a7f3f59df6f4fd33ac8/models/speech_recognition/wav2letter/tflite_pruned_int8/"
53 ],
54 "resources": [
55 {
56 "name": "wav2letter_pruned_int8.tflite",
57 "url": "{url_prefix:0}wav2letter_pruned_int8.tflite",
58 },
59 {
60 "name": "ifm0.npy",
61 "url": "{url_prefix:0}testing_input/input_2_int8/0.npy",
62 },
63 {
64 "name": "ofm0.npy",
65 "url": "{url_prefix:0}testing_output/Identity_int8/0.npy",
66 },
67 ],
Isabella Gottardi2181d0a2021-04-07 09:27:38 +010068 },
69 {
70 "use_case_name": "img_class",
Isabella Gottardief2b9dd2022-02-16 14:24:03 +000071 "url_prefix": [
72 "https://github.com/ARM-software/ML-zoo/raw/e0aa361b03c738047b9147d1a50e3f2dcb13dbcb/models/image_classification/mobilenet_v2_1.0_224/tflite_int8/"
73 ],
74 "resources": [
75 {
76 "name": "mobilenet_v2_1.0_224_INT8.tflite",
77 "url": "{url_prefix:0}mobilenet_v2_1.0_224_INT8.tflite",
78 },
79 {
80 "name": "ifm0.npy",
81 "url": "{url_prefix:0}testing_input/tfl.quantize/0.npy",
82 },
83 {
84 "name": "ofm0.npy",
85 "url": "{url_prefix:0}testing_output/MobilenetV2/Predictions/Reshape_11/0.npy",
86 },
87 ],
Isabella Gottardi2181d0a2021-04-07 09:27:38 +010088 },
89 {
Michael Levit06fcf752022-01-12 11:53:46 +020090 "use_case_name": "object_detection",
Isabella Gottardief2b9dd2022-02-16 14:24:03 +000091 "url_prefix": [
92 "https://github.com/emza-vs/ModelZoo/blob/v1.0/object_detection/"
93 ],
94 "resources": [
95 {
96 "name": "yolo-fastest_192_face_v4.tflite",
97 "url": "{url_prefix:0}yolo-fastest_192_face_v4.tflite?raw=true",
98 }
99 ],
Michael Levit06fcf752022-01-12 11:53:46 +0200100 },
101 {
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100102 "use_case_name": "kws",
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000103 "url_prefix": [
104 "https://github.com/ARM-software/ML-zoo/raw/9f506fe52b39df545f0e6c5ff9223f671bc5ae00/models/keyword_spotting/micronet_medium/tflite_int8/"
105 ],
106 "resources": [
107 {"name": "ifm0.npy", "url": "{url_prefix:0}testing_input/input/0.npy"},
108 {"name": "ofm0.npy", "url": "{url_prefix:0}testing_output/Identity/0.npy"},
109 {
110 "name": "kws_micronet_m.tflite",
111 "url": "{url_prefix:0}kws_micronet_m.tflite",
112 },
113 ],
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100114 },
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000115 {
Éanna Ó Catháin8f958872021-09-15 09:32:30 +0100116 "use_case_name": "vww",
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000117 "url_prefix": [
118 "https://github.com/ARM-software/ML-zoo/raw/7dd3b16bb84007daf88be8648983c07f3eb21140/models/visual_wake_words/micronet_vww4/tflite_int8/"
119 ],
120 "resources": [
121 {
122 "name": "vww4_128_128_INT8.tflite",
123 "url": "{url_prefix:0}vww4_128_128_INT8.tflite",
124 },
125 {"name": "ifm0.npy", "url": "{url_prefix:0}testing_input/input/0.npy"},
126 {"name": "ofm0.npy", "url": "{url_prefix:0}testing_output/Identity/0.npy"},
127 ],
Éanna Ó Catháin8f958872021-09-15 09:32:30 +0100128 },
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100129 {
130 "use_case_name": "kws_asr",
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000131 "url_prefix": [
132 "https://github.com/ARM-software/ML-zoo/raw/1a92aa08c0de49a7304e0a7f3f59df6f4fd33ac8/models/speech_recognition/wav2letter/tflite_pruned_int8/",
133 "https://github.com/ARM-software/ML-zoo/raw/9f506fe52b39df545f0e6c5ff9223f671bc5ae00/models/keyword_spotting/micronet_medium/tflite_int8/",
134 ],
135 "resources": [
136 {
137 "name": "wav2letter_pruned_int8.tflite",
138 "url": "{url_prefix:0}wav2letter_pruned_int8.tflite",
139 },
140 {
141 "sub_folder": "asr",
142 "name": "ifm0.npy",
143 "url": "{url_prefix:0}testing_input/input_2_int8/0.npy",
144 },
145 {
146 "sub_folder": "asr",
147 "name": "ofm0.npy",
148 "url": "{url_prefix:0}testing_output/Identity_int8/0.npy",
149 },
150 {
151 "sub_folder": "kws",
152 "name": "ifm0.npy",
153 "url": "{url_prefix:1}testing_input/input/0.npy",
154 },
155 {
156 "sub_folder": "kws",
157 "name": "ofm0.npy",
158 "url": "{url_prefix:1}testing_output/Identity/0.npy",
159 },
160 {
161 "name": "kws_micronet_m.tflite",
162 "url": "{url_prefix:1}kws_micronet_m.tflite",
163 },
164 ],
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100165 },
166 {
Richard Burton00553462021-11-10 16:27:14 +0000167 "use_case_name": "noise_reduction",
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000168 "url_prefix": [
169 "https://github.com/ARM-software/ML-zoo/raw/a061600058097a2785d6f1f7785e5a2d2a142955/models/noise_suppression/RNNoise/tflite_int8/"
170 ],
171 "resources": [
172 {"name": "rnnoise_INT8.tflite", "url": "{url_prefix:0}rnnoise_INT8.tflite"},
173 {
174 "name": "ifm0.npy",
175 "url": "{url_prefix:0}testing_input/main_input_int8/0.npy",
176 },
177 {
178 "name": "ifm1.npy",
179 "url": "{url_prefix:0}testing_input/vad_gru_prev_state_int8/0.npy",
180 },
181 {
182 "name": "ifm2.npy",
183 "url": "{url_prefix:0}testing_input/noise_gru_prev_state_int8/0.npy",
184 },
185 {
186 "name": "ifm3.npy",
187 "url": "{url_prefix:0}testing_input/denoise_gru_prev_state_int8/0.npy",
188 },
189 {
190 "name": "ofm0.npy",
191 "url": "{url_prefix:0}testing_output/Identity_int8/0.npy",
192 },
193 {
194 "name": "ofm1.npy",
195 "url": "{url_prefix:0}testing_output/Identity_1_int8/0.npy",
196 },
197 {
198 "name": "ofm2.npy",
199 "url": "{url_prefix:0}testing_output/Identity_2_int8/0.npy",
200 },
201 {
202 "name": "ofm3.npy",
203 "url": "{url_prefix:0}testing_output/Identity_3_int8/0.npy",
204 },
205 {
206 "name": "ofm4.npy",
207 "url": "{url_prefix:0}testing_output/Identity_4_int8/0.npy",
208 },
209 ],
Richard Burton00553462021-11-10 16:27:14 +0000210 },
211 {
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100212 "use_case_name": "inference_runner",
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000213 "url_prefix": [
214 "https://github.com/ARM-software/ML-zoo/raw/68b5fbc77ed28e67b2efc915997ea4477c1d9d5b/models/keyword_spotting/dnn_small/tflite_int8/"
215 ],
216 "resources": [
217 {
218 "name": "dnn_s_quantized.tflite",
219 "url": "{url_prefix:0}dnn_s_quantized.tflite",
220 }
221 ],
222 },
223]
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100224
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100225# Valid NPU configurations:
226valid_npu_config_names = [
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000227 "ethos-u55-32",
228 "ethos-u55-64",
229 "ethos-u55-128",
230 "ethos-u55-256",
231 "ethos-u65-256",
232 "ethos-u65-512",
233]
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100234
235# Default NPU configurations (these are always run when the models are optimised)
236default_npu_config_names = [valid_npu_config_names[2], valid_npu_config_names[4]]
237
238# NPU config named tuple
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000239NPUConfig = namedtuple(
240 "NPUConfig",
241 [
242 "config_name",
243 "memory_mode",
244 "system_config",
245 "ethos_u_npu_id",
246 "ethos_u_config_id",
247 "arena_cache_size",
248 ],
249)
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100250
Kshitij Sisodia661959c2021-11-24 10:39:52 +0000251# The internal SRAM size for Corstone-300 implementation on MPS3 specified by AN552
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000252mps3_max_sram_sz = 2 * 1024 * 1024 # 2 MiB (2 banks of 1 MiB each)
Liam Barryb52b5852021-11-15 11:41:40 +0000253
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100254
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000255def call_command(command: str, verbose: bool = True) -> str:
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100256 """
257 Helpers function that call subprocess and return the output.
258
259 Parameters:
260 ----------
261 command (string): Specifies the command to run.
262 """
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000263 if verbose:
264 logging.info(command)
265 proc = subprocess.run(
266 command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True
267 )
alexander50a06502021-05-12 19:06:02 +0100268 log = proc.stdout.decode("utf-8")
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000269 if proc.returncode == 0 and verbose:
alexander50a06502021-05-12 19:06:02 +0100270 logging.info(log)
271 else:
272 logging.error(log)
273 proc.check_returncode()
274 return log
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100275
276
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000277def get_default_npu_config_from_name(
278 config_name: str, arena_cache_size: int = 0
279) -> NPUConfig:
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100280 """
281 Gets the file suffix for the tflite file from the
282 `accelerator_config` string.
283
284 Parameters:
285 ----------
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000286 config_name (str): Ethos-U NPU configuration from valid_npu_config_names
287
288 arena_cache_size (int): Specifies arena cache size in bytes. If a value
289 greater than 0 is provided, this will be taken
290 as the cache size. If 0, the default values, as per
291 the NPU config requirements, are used.
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100292
293 Returns:
294 -------
295 NPUConfig: An NPU config named tuple populated with defaults for the given
296 config name
297 """
298 if config_name not in valid_npu_config_names:
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000299 raise ValueError(
300 f"""
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100301 Invalid Ethos-U NPU configuration.
302 Select one from {valid_npu_config_names}.
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000303 """
304 )
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100305
306 strings_ids = ["ethos-u55-", "ethos-u65-"]
307 processor_ids = ["U55", "U65"]
308 prefix_ids = ["H", "Y"]
309 memory_modes = ["Shared_Sram", "Dedicated_Sram"]
310 system_configs = ["Ethos_U55_High_End_Embedded", "Ethos_U65_High_End"]
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000311 memory_modes_arena = {
312 # For shared SRAM memory mode, we use the MPS3 SRAM size by default
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000313 "Shared_Sram": mps3_max_sram_sz if arena_cache_size <= 0 else arena_cache_size,
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000314 # For dedicated SRAM memory mode, we do no override the arena size. This is expected to
315 # be defined in the vela configuration file instead.
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000316 "Dedicated_Sram": None if arena_cache_size <= 0 else arena_cache_size,
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000317 }
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100318
319 for i in range(len(strings_ids)):
320 if config_name.startswith(strings_ids[i]):
321 npu_config_id = config_name.replace(strings_ids[i], prefix_ids[i])
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000322 return NPUConfig(
323 config_name=config_name,
324 memory_mode=memory_modes[i],
325 system_config=system_configs[i],
326 ethos_u_npu_id=processor_ids[i],
327 ethos_u_config_id=npu_config_id,
328 arena_cache_size=memory_modes_arena[memory_modes[i]],
329 )
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100330
331 return None
332
333
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000334def remove_tree_dir(dir_path):
335 try:
336 # Remove the full directory
337 shutil.rmtree(dir_path)
338 # Re-create an empty one
339 os.mkdir(dir_path)
340 except Exception as e:
341 logging.error(f"Failed to delete {dir_path}.")
342
343
344def set_up_resources(
345 run_vela_on_models: bool = False,
346 additional_npu_config_names: list = (),
347 arena_cache_size: int = 0,
348 check_clean_folder: bool = False,
Isabella Gottardi6c2ea452022-03-11 13:25:08 +0000349 additional_requirements_file: str = "",
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000350):
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100351 """
352 Helpers function that retrieve the output from a command.
353
354 Parameters:
355 ----------
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000356 run_vela_on_models (bool): Specifies if run vela on downloaded models.
357 additional_npu_config_names(list): list of strings of Ethos-U NPU configs.
358 arena_cache_size (int): Specifies arena cache size in bytes. If a value
359 greater than 0 is provided, this will be taken
360 as the cache size. If 0, the default values, as per
361 the NPU config requirements, are used.
Kshitij Sisodia6a2ac462022-03-01 17:36:06 +0000362 check_clean_folder (bool): Indicates whether the resources folder needs to
363 be checked for updates and cleaned.
Kshitij Sisodiac22e80e2022-03-14 09:26:48 +0000364 additional_requirements_file (str): Path to a requirements.txt file if
365 additional packages need to be
366 installed.
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100367 """
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000368 # Paths
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100369 current_file_dir = os.path.dirname(os.path.abspath(__file__))
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000370 download_dir = os.path.abspath(
371 os.path.join(current_file_dir, "resources_downloaded")
372 )
373 metadata_file_path = os.path.join(
374 download_dir, "resources_downloaded_metadata.json"
375 )
376
377 metadata_dict = dict()
Richard Burtonb4123fd2022-03-04 09:19:09 +0000378 vela_version = "3.3.0"
Isabella Gottardi6c2ea452022-03-11 13:25:08 +0000379 py3_major_version_minimum = 3 # Python > 3.8 is required
380 py3_minor_version_minimum = 8
381
382 # Is Python minimum requirement matched?
383 py3_version = sys.version_info
384 if (
385 py3_version.major < py3_major_version_minimum
386 or py3_version.minor < py3_minor_version_minimum
387 ):
388 raise Exception(
389 "ERROR: Python3.8+ is required, please see the documentation on how to update it."
390 )
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000391
Kshitij Sisodia6a2ac462022-03-01 17:36:06 +0000392 setup_script_hash_verified = False
393 setup_script_hash = get_md5sum_for_file(os.path.abspath(__file__))
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100394
395 try:
396 # 1.1 Does the download dir exist?
397 os.mkdir(download_dir)
398 except OSError as e:
399 if e.errno == errno.EEXIST:
400 logging.info("'resources_downloaded' directory exists.")
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000401 # Check and clean?
402 if check_clean_folder and os.path.isfile(metadata_file_path):
403 with open(metadata_file_path) as (metadata_file):
404 metadata_dict = json.load(metadata_file)
405 vela_in_metadata = metadata_dict["ethosu_vela_version"]
406 if vela_in_metadata != vela_version:
407 # Check if all the resources needs to be removed and regenerated.
408 # This can happen when the Vela version has changed.
409 logging.info(
410 f"Vela version in metadata is {vela_in_metadata}, current {vela_version}. Removing the resources and re-download them."
411 )
412 remove_tree_dir(download_dir)
413 metadata_dict = dict()
414 else:
Kshitij Sisodia6a2ac462022-03-01 17:36:06 +0000415 # Check if the set_up_default_resorces.py has changed from last setup
416 setup_script_hash_verified = (
Isabella Gottardi6c2ea452022-03-11 13:25:08 +0000417 metadata_dict.get("set_up_script_md5sum")
418 == setup_script_hash
419 )
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100420 else:
421 raise
422
423 # 1.2 Does the virtual environment exist?
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000424 env_python = str(
425 os.path.abspath(os.path.join(download_dir, "env", "bin", "python3"))
426 )
427 env_activate = str(
428 os.path.abspath(os.path.join(download_dir, "env", "bin", "activate"))
429 )
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100430 if not os.path.isdir(os.path.join(download_dir, "env")):
431 os.chdir(download_dir)
432 # Create the virtual environment
433 command = "python3 -m venv env"
434 call_command(command)
435 commands = ["pip install --upgrade pip", "pip install --upgrade setuptools"]
436 for c in commands:
437 command = f"{env_python} -m {c}"
438 call_command(command)
439 os.chdir(current_file_dir)
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000440
Kshitij Sisodiac22e80e2022-03-14 09:26:48 +0000441 # 1.3 Make sure to have all the requirements
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000442 requirements = [f"ethos-u-vela=={vela_version}"]
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100443 command = f"{env_python} -m pip freeze"
444 packages = call_command(command)
445 for req in requirements:
446 if req not in packages:
447 command = f"{env_python} -m pip install {req}"
448 call_command(command)
449
Kshitij Sisodiac22e80e2022-03-14 09:26:48 +0000450 # 1.4 Install additional requirements, if a valid file has been provided
451 if additional_requirements_file and os.path.isfile(additional_requirements_file):
452 command = f"{env_python} -m pip install -r {additional_requirements_file}"
453 call_command(command)
454
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100455 # 2. Download models
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000456 logging.info("Downloading resources.")
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100457 for uc in json_uc_res:
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000458 use_case_name = uc["use_case_name"]
459 res_url_prefix = uc["url_prefix"]
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100460 try:
461 # Does the usecase_name download dir exist?
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000462 os.mkdir(os.path.join(download_dir, use_case_name))
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100463 except OSError as e:
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000464 if e.errno == errno.EEXIST:
465 # The usecase_name download dir exist
Kshitij Sisodia6a2ac462022-03-01 17:36:06 +0000466 if check_clean_folder and not setup_script_hash_verified:
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000467 for idx, metadata_uc_url_prefix in enumerate(
468 [
469 f
470 for f in metadata_dict["resources_info"]
471 if f["use_case_name"] == use_case_name
472 ][0]["url_prefix"]
473 ):
474 if metadata_uc_url_prefix != res_url_prefix[idx]:
475 logging.info(f"Removing {use_case_name} resources.")
476 remove_tree_dir(os.path.join(download_dir, use_case_name))
477 break
478 elif e.errno != errno.EEXIST:
479 logging.error(f"Error creating {use_case_name} directory.")
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100480 raise
481
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000482 reg_expr_str = r"{url_prefix:(.*\d)}"
483 reg_expr_pattern = re.compile(reg_expr_str)
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100484 for res in uc["resources"]:
485 res_name = res["name"]
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000486 url_prefix_idx = int(reg_expr_pattern.search(res["url"]).group(1))
487 res_url = res_url_prefix[url_prefix_idx] + re.sub(
488 reg_expr_str, "", res["url"]
489 )
490
491 sub_folder = ""
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100492 if "sub_folder" in res:
493 try:
494 # Does the usecase_name/sub_folder download dir exist?
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000495 os.mkdir(
496 os.path.join(download_dir, use_case_name, res["sub_folder"])
497 )
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100498 except OSError as e:
499 if e.errno != errno.EEXIST:
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000500 logging.error(
501 f"Error creating {use_case_name} / {res['sub_folder']} directory."
502 )
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100503 raise
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000504 sub_folder = res["sub_folder"]
505
506 res_dst = os.path.join(download_dir, use_case_name, sub_folder, res_name)
alexander3ef1fd42021-05-24 18:56:32 +0100507
508 if os.path.isfile(res_dst):
509 logging.info(f"File {res_dst} exists, skipping download.")
510 else:
511 try:
512 g = urllib.request.urlopen(res_url)
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000513 with open(res_dst, "b+w") as f:
alexander3ef1fd42021-05-24 18:56:32 +0100514 f.write(g.read())
515 logging.info(f"- Downloaded {res_url} to {res_dst}.")
516 except URLError:
517 logging.error(f"URLError while downloading {res_url}.")
518 raise
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100519
520 # 3. Run vela on models in resources_downloaded
521 # New models will have same name with '_vela' appended.
522 # For example:
Kshitij Sisodia76a15802021-12-24 11:05:11 +0000523 # original model: kws_micronet_m.tflite
524 # after vela model: kws_micronet_m_vela_H128.tflite
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100525 #
526 # Note: To avoid to run vela twice on the same model, it's supposed that
527 # downloaded model names don't contain the 'vela' word.
528 if run_vela_on_models is True:
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000529 config_file = os.path.join(
530 current_file_dir, "scripts", "vela", "default_vela.ini"
531 )
532 models = [
533 os.path.join(dirpath, f)
534 for dirpath, dirnames, files in os.walk(download_dir)
535 for f in fnmatch.filter(files, "*.tflite")
536 if "vela" not in f
537 ]
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100538
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100539 # Consolidate all config names while discarding duplicates:
540 config_names = list(set(default_npu_config_names + additional_npu_config_names))
541
542 # Get npu config tuple for each config name in a list:
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000543 npu_configs = [
544 get_default_npu_config_from_name(name, arena_cache_size)
545 for name in config_names
546 ]
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100547
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000548 logging.info(f"All models will be optimised for these configs:")
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100549 for config in npu_configs:
550 logging.info(config)
551
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000552 optimisation_skipped = False
553
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100554 for model in models:
555 output_dir = os.path.dirname(model)
alexander3ef1fd42021-05-24 18:56:32 +0100556 # model name after compiling with vela is an initial model name + _vela suffix
557 vela_optimised_model_path = str(model).replace(".tflite", "_vela.tflite")
alexander3ef1fd42021-05-24 18:56:32 +0100558
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100559 for config in npu_configs:
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000560 vela_command_arena_cache_size = ""
561
562 if config.arena_cache_size:
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000563 vela_command_arena_cache_size = (
564 f"--arena-cache-size={config.arena_cache_size}"
565 )
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000566
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000567 vela_command = (
568 f". {env_activate} && vela {model} "
569 + f"--accelerator-config={config.config_name} "
570 + "--optimise Performance "
571 + f"--config {config_file} "
572 + f"--memory-mode={config.memory_mode} "
573 + f"--system-config={config.system_config} "
574 + f"--output-dir={output_dir} "
575 + f"{vela_command_arena_cache_size}"
576 )
alexander3ef1fd42021-05-24 18:56:32 +0100577
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100578 # we want the name to include the configuration suffix. For example: vela_H128,
579 # vela_Y512 etc.
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000580 new_suffix = "_vela_" + config.ethos_u_config_id + ".tflite"
581 new_vela_optimised_model_path = vela_optimised_model_path.replace(
582 "_vela.tflite", new_suffix
583 )
Isabella Gottardi118f73e2021-09-16 17:54:35 +0100584
585 if os.path.isfile(new_vela_optimised_model_path):
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000586 logging.info(
587 f"File {new_vela_optimised_model_path} exists, skipping optimisation."
588 )
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000589 optimisation_skipped = True
Isabella Gottardi118f73e2021-09-16 17:54:35 +0100590 continue
591
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100592 call_command(vela_command)
Isabella Gottardi118f73e2021-09-16 17:54:35 +0100593
594 # rename default vela model
595 os.rename(vela_optimised_model_path, new_vela_optimised_model_path)
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000596 logging.info(
597 f"Renaming {vela_optimised_model_path} to {new_vela_optimised_model_path}."
598 )
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100599
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000600 # If any optimisation was skipped, show how to regenerate:
601 if optimisation_skipped:
602 logging.warning("One or more optimisations were skipped.")
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000603 logging.warning(
604 f"To optimise all the models, please remove the directory {download_dir}."
605 )
606
607 # 4. Collect and write metadata
608 logging.info("Collecting and write metadata.")
609 metadata_dict["ethosu_vela_version"] = vela_version
Kshitij Sisodia6a2ac462022-03-01 17:36:06 +0000610 metadata_dict["set_up_script_md5sum"] = setup_script_hash.strip("\n")
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000611 metadata_dict["resources_info"] = json_uc_res
612
613 with open(metadata_file_path, "w") as metadata_file:
614 json.dump(metadata_dict, metadata_file, indent=4)
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000615
616
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000617if __name__ == "__main__":
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100618 parser = ArgumentParser()
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000619 parser.add_argument(
620 "--skip-vela",
621 help="Do not run Vela optimizer on downloaded models.",
622 action="store_true",
623 )
624 parser.add_argument(
625 "--additional-ethos-u-config-name",
626 help=f"""Additional (non-default) configurations for Vela:
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100627 {valid_npu_config_names}""",
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000628 default=[],
629 action="append",
630 )
631 parser.add_argument(
632 "--arena-cache-size",
633 help="Arena cache size in bytes (if overriding the defaults)",
634 type=int,
635 default=0,
636 )
637 parser.add_argument(
638 "--clean",
639 help="Clean the disctory and optimize the downloaded resources",
640 action="store_true",
641 )
Kshitij Sisodiac22e80e2022-03-14 09:26:48 +0000642 parser.add_argument(
643 "--requirements-file",
644 help="Path to requirements.txt file to install additional packages",
645 type=str,
Isabella Gottardi6c2ea452022-03-11 13:25:08 +0000646 default=os.path.join(
647 os.path.dirname(os.path.abspath(__file__)),
648 "scripts",
649 "py",
650 "requirements.txt",
651 ),
Kshitij Sisodiac22e80e2022-03-14 09:26:48 +0000652 )
653
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100654 args = parser.parse_args()
Kshitij Sisodiab9e9c892021-05-27 13:57:35 +0100655
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000656 if args.arena_cache_size < 0:
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000657 raise ArgumentTypeError("Arena cache size cannot not be less than 0")
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000658
Kshitij Sisodiac22e80e2022-03-14 09:26:48 +0000659 if not os.path.isfile(args.requirements_file):
660 raise ArgumentTypeError(f"Invalid requirements file: {args.requirements_file}")
661
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000662 logging.basicConfig(filename="log_build_default.log", level=logging.DEBUG)
Kshitij Sisodiab9e9c892021-05-27 13:57:35 +0100663 logging.getLogger().addHandler(logging.StreamHandler(sys.stdout))
664
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000665 set_up_resources(
666 not args.skip_vela,
667 args.additional_ethos_u_config_name,
668 args.arena_cache_size,
669 args.clean,
Isabella Gottardi6c2ea452022-03-11 13:25:08 +0000670 args.requirements_file,
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000671 )