blob: 48e6a67ead0050f4b231a0a235abd82ab15be966 [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
Kshitij Sisodia3be26232021-10-29 12:29:06 +010030
Isabella Gottardi2181d0a2021-04-07 09:27:38 +010031
Isabella Gottardief2b9dd2022-02-16 14:24:03 +000032json_uc_res = [
33 {
34 "use_case_name": "ad",
35 "url_prefix": [
36 "https://github.com/ARM-software/ML-zoo/raw/7c32b097f7d94aae2cd0b98a8ed5a3ba81e66b18/models/anomaly_detection/micronet_medium/tflite_int8/"
37 ],
38 "resources": [
39 {
40 "name": "ad_medium_int8.tflite",
41 "url": "{url_prefix:0}ad_medium_int8.tflite",
42 },
43 {"name": "ifm0.npy", "url": "{url_prefix:0}testing_input/input/0.npy"},
44 {"name": "ofm0.npy", "url": "{url_prefix:0}testing_output/Identity/0.npy"},
45 ],
46 },
Isabella Gottardi2181d0a2021-04-07 09:27:38 +010047 {
48 "use_case_name": "asr",
Isabella Gottardief2b9dd2022-02-16 14:24:03 +000049 "url_prefix": [
50 "https://github.com/ARM-software/ML-zoo/raw/1a92aa08c0de49a7304e0a7f3f59df6f4fd33ac8/models/speech_recognition/wav2letter/tflite_pruned_int8/"
51 ],
52 "resources": [
53 {
54 "name": "wav2letter_pruned_int8.tflite",
55 "url": "{url_prefix:0}wav2letter_pruned_int8.tflite",
56 },
57 {
58 "name": "ifm0.npy",
59 "url": "{url_prefix:0}testing_input/input_2_int8/0.npy",
60 },
61 {
62 "name": "ofm0.npy",
63 "url": "{url_prefix:0}testing_output/Identity_int8/0.npy",
64 },
65 ],
Isabella Gottardi2181d0a2021-04-07 09:27:38 +010066 },
67 {
68 "use_case_name": "img_class",
Isabella Gottardief2b9dd2022-02-16 14:24:03 +000069 "url_prefix": [
70 "https://github.com/ARM-software/ML-zoo/raw/e0aa361b03c738047b9147d1a50e3f2dcb13dbcb/models/image_classification/mobilenet_v2_1.0_224/tflite_int8/"
71 ],
72 "resources": [
73 {
74 "name": "mobilenet_v2_1.0_224_INT8.tflite",
75 "url": "{url_prefix:0}mobilenet_v2_1.0_224_INT8.tflite",
76 },
77 {
78 "name": "ifm0.npy",
79 "url": "{url_prefix:0}testing_input/tfl.quantize/0.npy",
80 },
81 {
82 "name": "ofm0.npy",
83 "url": "{url_prefix:0}testing_output/MobilenetV2/Predictions/Reshape_11/0.npy",
84 },
85 ],
Isabella Gottardi2181d0a2021-04-07 09:27:38 +010086 },
87 {
Michael Levit06fcf752022-01-12 11:53:46 +020088 "use_case_name": "object_detection",
Isabella Gottardief2b9dd2022-02-16 14:24:03 +000089 "url_prefix": [
90 "https://github.com/emza-vs/ModelZoo/blob/v1.0/object_detection/"
91 ],
92 "resources": [
93 {
94 "name": "yolo-fastest_192_face_v4.tflite",
95 "url": "{url_prefix:0}yolo-fastest_192_face_v4.tflite?raw=true",
96 }
97 ],
Michael Levit06fcf752022-01-12 11:53:46 +020098 },
99 {
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100100 "use_case_name": "kws",
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000101 "url_prefix": [
102 "https://github.com/ARM-software/ML-zoo/raw/9f506fe52b39df545f0e6c5ff9223f671bc5ae00/models/keyword_spotting/micronet_medium/tflite_int8/"
103 ],
104 "resources": [
105 {"name": "ifm0.npy", "url": "{url_prefix:0}testing_input/input/0.npy"},
106 {"name": "ofm0.npy", "url": "{url_prefix:0}testing_output/Identity/0.npy"},
107 {
108 "name": "kws_micronet_m.tflite",
109 "url": "{url_prefix:0}kws_micronet_m.tflite",
110 },
111 ],
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100112 },
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000113 {
Éanna Ó Catháin8f958872021-09-15 09:32:30 +0100114 "use_case_name": "vww",
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000115 "url_prefix": [
116 "https://github.com/ARM-software/ML-zoo/raw/7dd3b16bb84007daf88be8648983c07f3eb21140/models/visual_wake_words/micronet_vww4/tflite_int8/"
117 ],
118 "resources": [
119 {
120 "name": "vww4_128_128_INT8.tflite",
121 "url": "{url_prefix:0}vww4_128_128_INT8.tflite",
122 },
123 {"name": "ifm0.npy", "url": "{url_prefix:0}testing_input/input/0.npy"},
124 {"name": "ofm0.npy", "url": "{url_prefix:0}testing_output/Identity/0.npy"},
125 ],
Éanna Ó Catháin8f958872021-09-15 09:32:30 +0100126 },
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100127 {
128 "use_case_name": "kws_asr",
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000129 "url_prefix": [
130 "https://github.com/ARM-software/ML-zoo/raw/1a92aa08c0de49a7304e0a7f3f59df6f4fd33ac8/models/speech_recognition/wav2letter/tflite_pruned_int8/",
131 "https://github.com/ARM-software/ML-zoo/raw/9f506fe52b39df545f0e6c5ff9223f671bc5ae00/models/keyword_spotting/micronet_medium/tflite_int8/",
132 ],
133 "resources": [
134 {
135 "name": "wav2letter_pruned_int8.tflite",
136 "url": "{url_prefix:0}wav2letter_pruned_int8.tflite",
137 },
138 {
139 "sub_folder": "asr",
140 "name": "ifm0.npy",
141 "url": "{url_prefix:0}testing_input/input_2_int8/0.npy",
142 },
143 {
144 "sub_folder": "asr",
145 "name": "ofm0.npy",
146 "url": "{url_prefix:0}testing_output/Identity_int8/0.npy",
147 },
148 {
149 "sub_folder": "kws",
150 "name": "ifm0.npy",
151 "url": "{url_prefix:1}testing_input/input/0.npy",
152 },
153 {
154 "sub_folder": "kws",
155 "name": "ofm0.npy",
156 "url": "{url_prefix:1}testing_output/Identity/0.npy",
157 },
158 {
159 "name": "kws_micronet_m.tflite",
160 "url": "{url_prefix:1}kws_micronet_m.tflite",
161 },
162 ],
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100163 },
164 {
Richard Burton00553462021-11-10 16:27:14 +0000165 "use_case_name": "noise_reduction",
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000166 "url_prefix": [
167 "https://github.com/ARM-software/ML-zoo/raw/a061600058097a2785d6f1f7785e5a2d2a142955/models/noise_suppression/RNNoise/tflite_int8/"
168 ],
169 "resources": [
170 {"name": "rnnoise_INT8.tflite", "url": "{url_prefix:0}rnnoise_INT8.tflite"},
171 {
172 "name": "ifm0.npy",
173 "url": "{url_prefix:0}testing_input/main_input_int8/0.npy",
174 },
175 {
176 "name": "ifm1.npy",
177 "url": "{url_prefix:0}testing_input/vad_gru_prev_state_int8/0.npy",
178 },
179 {
180 "name": "ifm2.npy",
181 "url": "{url_prefix:0}testing_input/noise_gru_prev_state_int8/0.npy",
182 },
183 {
184 "name": "ifm3.npy",
185 "url": "{url_prefix:0}testing_input/denoise_gru_prev_state_int8/0.npy",
186 },
187 {
188 "name": "ofm0.npy",
189 "url": "{url_prefix:0}testing_output/Identity_int8/0.npy",
190 },
191 {
192 "name": "ofm1.npy",
193 "url": "{url_prefix:0}testing_output/Identity_1_int8/0.npy",
194 },
195 {
196 "name": "ofm2.npy",
197 "url": "{url_prefix:0}testing_output/Identity_2_int8/0.npy",
198 },
199 {
200 "name": "ofm3.npy",
201 "url": "{url_prefix:0}testing_output/Identity_3_int8/0.npy",
202 },
203 {
204 "name": "ofm4.npy",
205 "url": "{url_prefix:0}testing_output/Identity_4_int8/0.npy",
206 },
207 ],
Richard Burton00553462021-11-10 16:27:14 +0000208 },
209 {
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100210 "use_case_name": "inference_runner",
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000211 "url_prefix": [
212 "https://github.com/ARM-software/ML-zoo/raw/68b5fbc77ed28e67b2efc915997ea4477c1d9d5b/models/keyword_spotting/dnn_small/tflite_int8/"
213 ],
214 "resources": [
215 {
216 "name": "dnn_s_quantized.tflite",
217 "url": "{url_prefix:0}dnn_s_quantized.tflite",
218 }
219 ],
220 },
221]
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100222
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100223# Valid NPU configurations:
224valid_npu_config_names = [
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000225 "ethos-u55-32",
226 "ethos-u55-64",
227 "ethos-u55-128",
228 "ethos-u55-256",
229 "ethos-u65-256",
230 "ethos-u65-512",
231]
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100232
233# Default NPU configurations (these are always run when the models are optimised)
234default_npu_config_names = [valid_npu_config_names[2], valid_npu_config_names[4]]
235
236# NPU config named tuple
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000237NPUConfig = namedtuple(
238 "NPUConfig",
239 [
240 "config_name",
241 "memory_mode",
242 "system_config",
243 "ethos_u_npu_id",
244 "ethos_u_config_id",
245 "arena_cache_size",
246 ],
247)
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100248
Kshitij Sisodia661959c2021-11-24 10:39:52 +0000249# The internal SRAM size for Corstone-300 implementation on MPS3 specified by AN552
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000250mps3_max_sram_sz = 2 * 1024 * 1024 # 2 MiB (2 banks of 1 MiB each)
Liam Barryb52b5852021-11-15 11:41:40 +0000251
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100252
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000253def call_command(command: str, verbose: bool = True) -> str:
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100254 """
255 Helpers function that call subprocess and return the output.
256
257 Parameters:
258 ----------
259 command (string): Specifies the command to run.
260 """
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000261 if verbose:
262 logging.info(command)
263 proc = subprocess.run(
264 command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True
265 )
alexander50a06502021-05-12 19:06:02 +0100266 log = proc.stdout.decode("utf-8")
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000267 if proc.returncode == 0 and verbose:
alexander50a06502021-05-12 19:06:02 +0100268 logging.info(log)
269 else:
270 logging.error(log)
271 proc.check_returncode()
272 return log
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100273
274
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000275def get_default_npu_config_from_name(
276 config_name: str, arena_cache_size: int = 0
277) -> NPUConfig:
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100278 """
279 Gets the file suffix for the tflite file from the
280 `accelerator_config` string.
281
282 Parameters:
283 ----------
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000284 config_name (str): Ethos-U NPU configuration from valid_npu_config_names
285
286 arena_cache_size (int): Specifies arena cache size in bytes. If a value
287 greater than 0 is provided, this will be taken
288 as the cache size. If 0, the default values, as per
289 the NPU config requirements, are used.
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100290
291 Returns:
292 -------
293 NPUConfig: An NPU config named tuple populated with defaults for the given
294 config name
295 """
296 if config_name not in valid_npu_config_names:
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000297 raise ValueError(
298 f"""
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100299 Invalid Ethos-U NPU configuration.
300 Select one from {valid_npu_config_names}.
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000301 """
302 )
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100303
304 strings_ids = ["ethos-u55-", "ethos-u65-"]
305 processor_ids = ["U55", "U65"]
306 prefix_ids = ["H", "Y"]
307 memory_modes = ["Shared_Sram", "Dedicated_Sram"]
308 system_configs = ["Ethos_U55_High_End_Embedded", "Ethos_U65_High_End"]
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000309 memory_modes_arena = {
310 # For shared SRAM memory mode, we use the MPS3 SRAM size by default
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000311 "Shared_Sram": mps3_max_sram_sz if arena_cache_size <= 0 else arena_cache_size,
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000312 # For dedicated SRAM memory mode, we do no override the arena size. This is expected to
313 # be defined in the vela configuration file instead.
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000314 "Dedicated_Sram": None if arena_cache_size <= 0 else arena_cache_size,
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000315 }
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100316
317 for i in range(len(strings_ids)):
318 if config_name.startswith(strings_ids[i]):
319 npu_config_id = config_name.replace(strings_ids[i], prefix_ids[i])
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000320 return NPUConfig(
321 config_name=config_name,
322 memory_mode=memory_modes[i],
323 system_config=system_configs[i],
324 ethos_u_npu_id=processor_ids[i],
325 ethos_u_config_id=npu_config_id,
326 arena_cache_size=memory_modes_arena[memory_modes[i]],
327 )
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100328
329 return None
330
331
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000332def remove_tree_dir(dir_path):
333 try:
334 # Remove the full directory
335 shutil.rmtree(dir_path)
336 # Re-create an empty one
337 os.mkdir(dir_path)
338 except Exception as e:
339 logging.error(f"Failed to delete {dir_path}.")
340
341
342def set_up_resources(
343 run_vela_on_models: bool = False,
344 additional_npu_config_names: list = (),
345 arena_cache_size: int = 0,
346 check_clean_folder: bool = False,
347):
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100348 """
349 Helpers function that retrieve the output from a command.
350
351 Parameters:
352 ----------
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000353 run_vela_on_models (bool): Specifies if run vela on downloaded models.
354 additional_npu_config_names(list): list of strings of Ethos-U NPU configs.
355 arena_cache_size (int): Specifies arena cache size in bytes. If a value
356 greater than 0 is provided, this will be taken
357 as the cache size. If 0, the default values, as per
358 the NPU config requirements, are used.
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100359 """
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000360 # Paths
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100361 current_file_dir = os.path.dirname(os.path.abspath(__file__))
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000362 download_dir = os.path.abspath(
363 os.path.join(current_file_dir, "resources_downloaded")
364 )
365 metadata_file_path = os.path.join(
366 download_dir, "resources_downloaded_metadata.json"
367 )
368
369 metadata_dict = dict()
370 vela_version = "3.2.0"
371
372 # Check if the current directory is a git repository
373 is_git_repo = os.path.exists(os.path.join(current_file_dir, ".git"))
374 git_commit_hash = ""
375 setup_script_hash_changed = False
376 if is_git_repo:
377 # If the current directory is a git script then extract the set_up_default_resorces.py hash
378 command = f"git log -1 --pretty=tformat:%H {os.path.abspath(__file__)}"
379 git_commit_hash = call_command(command, False)
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100380
381 try:
382 # 1.1 Does the download dir exist?
383 os.mkdir(download_dir)
384 except OSError as e:
385 if e.errno == errno.EEXIST:
386 logging.info("'resources_downloaded' directory exists.")
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000387 # Check and clean?
388 if check_clean_folder and os.path.isfile(metadata_file_path):
389 with open(metadata_file_path) as (metadata_file):
390 metadata_dict = json.load(metadata_file)
391 vela_in_metadata = metadata_dict["ethosu_vela_version"]
392 if vela_in_metadata != vela_version:
393 # Check if all the resources needs to be removed and regenerated.
394 # This can happen when the Vela version has changed.
395 logging.info(
396 f"Vela version in metadata is {vela_in_metadata}, current {vela_version}. Removing the resources and re-download them."
397 )
398 remove_tree_dir(download_dir)
399 metadata_dict = dict()
400 else:
401 # Check if the set_up_default_resorces.py has changed from last setup, only if this is a git repo
402 if is_git_repo:
403 setup_script_hash_changed = not (
404 metadata_dict["set_up_script_hash"] == git_commit_hash
405 )
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100406 else:
407 raise
408
409 # 1.2 Does the virtual environment exist?
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000410 env_python = str(
411 os.path.abspath(os.path.join(download_dir, "env", "bin", "python3"))
412 )
413 env_activate = str(
414 os.path.abspath(os.path.join(download_dir, "env", "bin", "activate"))
415 )
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100416 if not os.path.isdir(os.path.join(download_dir, "env")):
417 os.chdir(download_dir)
418 # Create the virtual environment
419 command = "python3 -m venv env"
420 call_command(command)
421 commands = ["pip install --upgrade pip", "pip install --upgrade setuptools"]
422 for c in commands:
423 command = f"{env_python} -m {c}"
424 call_command(command)
425 os.chdir(current_file_dir)
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000426
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100427 # 1.3 Make sure to have all the requirement
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000428 requirements = [f"ethos-u-vela=={vela_version}"]
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100429 command = f"{env_python} -m pip freeze"
430 packages = call_command(command)
431 for req in requirements:
432 if req not in packages:
433 command = f"{env_python} -m pip install {req}"
434 call_command(command)
435
436 # 2. Download models
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000437 logging.info("Downloading resources.")
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100438 for uc in json_uc_res:
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000439 use_case_name = uc["use_case_name"]
440 res_url_prefix = uc["url_prefix"]
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100441 try:
442 # Does the usecase_name download dir exist?
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000443 os.mkdir(os.path.join(download_dir, use_case_name))
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100444 except OSError as e:
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000445 if e.errno == errno.EEXIST:
446 # The usecase_name download dir exist
447 if setup_script_hash_changed:
448 for idx, metadata_uc_url_prefix in enumerate(
449 [
450 f
451 for f in metadata_dict["resources_info"]
452 if f["use_case_name"] == use_case_name
453 ][0]["url_prefix"]
454 ):
455 if metadata_uc_url_prefix != res_url_prefix[idx]:
456 logging.info(f"Removing {use_case_name} resources.")
457 remove_tree_dir(os.path.join(download_dir, use_case_name))
458 break
459 elif e.errno != errno.EEXIST:
460 logging.error(f"Error creating {use_case_name} directory.")
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100461 raise
462
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000463 reg_expr_str = r"{url_prefix:(.*\d)}"
464 reg_expr_pattern = re.compile(reg_expr_str)
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100465 for res in uc["resources"]:
466 res_name = res["name"]
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000467 url_prefix_idx = int(reg_expr_pattern.search(res["url"]).group(1))
468 res_url = res_url_prefix[url_prefix_idx] + re.sub(
469 reg_expr_str, "", res["url"]
470 )
471
472 sub_folder = ""
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100473 if "sub_folder" in res:
474 try:
475 # Does the usecase_name/sub_folder download dir exist?
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000476 os.mkdir(
477 os.path.join(download_dir, use_case_name, res["sub_folder"])
478 )
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100479 except OSError as e:
480 if e.errno != errno.EEXIST:
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000481 logging.error(
482 f"Error creating {use_case_name} / {res['sub_folder']} directory."
483 )
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100484 raise
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000485 sub_folder = res["sub_folder"]
486
487 res_dst = os.path.join(download_dir, use_case_name, sub_folder, res_name)
alexander3ef1fd42021-05-24 18:56:32 +0100488
489 if os.path.isfile(res_dst):
490 logging.info(f"File {res_dst} exists, skipping download.")
491 else:
492 try:
493 g = urllib.request.urlopen(res_url)
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000494 with open(res_dst, "b+w") as f:
alexander3ef1fd42021-05-24 18:56:32 +0100495 f.write(g.read())
496 logging.info(f"- Downloaded {res_url} to {res_dst}.")
497 except URLError:
498 logging.error(f"URLError while downloading {res_url}.")
499 raise
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100500
501 # 3. Run vela on models in resources_downloaded
502 # New models will have same name with '_vela' appended.
503 # For example:
Kshitij Sisodia76a15802021-12-24 11:05:11 +0000504 # original model: kws_micronet_m.tflite
505 # after vela model: kws_micronet_m_vela_H128.tflite
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100506 #
507 # Note: To avoid to run vela twice on the same model, it's supposed that
508 # downloaded model names don't contain the 'vela' word.
509 if run_vela_on_models is True:
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000510 config_file = os.path.join(
511 current_file_dir, "scripts", "vela", "default_vela.ini"
512 )
513 models = [
514 os.path.join(dirpath, f)
515 for dirpath, dirnames, files in os.walk(download_dir)
516 for f in fnmatch.filter(files, "*.tflite")
517 if "vela" not in f
518 ]
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100519
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100520 # Consolidate all config names while discarding duplicates:
521 config_names = list(set(default_npu_config_names + additional_npu_config_names))
522
523 # Get npu config tuple for each config name in a list:
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000524 npu_configs = [
525 get_default_npu_config_from_name(name, arena_cache_size)
526 for name in config_names
527 ]
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100528
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000529 logging.info(f"All models will be optimised for these configs:")
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100530 for config in npu_configs:
531 logging.info(config)
532
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000533 optimisation_skipped = False
534
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100535 for model in models:
536 output_dir = os.path.dirname(model)
alexander3ef1fd42021-05-24 18:56:32 +0100537 # model name after compiling with vela is an initial model name + _vela suffix
538 vela_optimised_model_path = str(model).replace(".tflite", "_vela.tflite")
alexander3ef1fd42021-05-24 18:56:32 +0100539
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100540 for config in npu_configs:
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000541 vela_command_arena_cache_size = ""
542
543 if config.arena_cache_size:
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000544 vela_command_arena_cache_size = (
545 f"--arena-cache-size={config.arena_cache_size}"
546 )
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000547
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000548 vela_command = (
549 f". {env_activate} && vela {model} "
550 + f"--accelerator-config={config.config_name} "
551 + "--optimise Performance "
552 + f"--config {config_file} "
553 + f"--memory-mode={config.memory_mode} "
554 + f"--system-config={config.system_config} "
555 + f"--output-dir={output_dir} "
556 + f"{vela_command_arena_cache_size}"
557 )
alexander3ef1fd42021-05-24 18:56:32 +0100558
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100559 # we want the name to include the configuration suffix. For example: vela_H128,
560 # vela_Y512 etc.
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000561 new_suffix = "_vela_" + config.ethos_u_config_id + ".tflite"
562 new_vela_optimised_model_path = vela_optimised_model_path.replace(
563 "_vela.tflite", new_suffix
564 )
Isabella Gottardi118f73e2021-09-16 17:54:35 +0100565
566 if os.path.isfile(new_vela_optimised_model_path):
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000567 logging.info(
568 f"File {new_vela_optimised_model_path} exists, skipping optimisation."
569 )
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000570 optimisation_skipped = True
Isabella Gottardi118f73e2021-09-16 17:54:35 +0100571 continue
572
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100573 call_command(vela_command)
Isabella Gottardi118f73e2021-09-16 17:54:35 +0100574
575 # rename default vela model
576 os.rename(vela_optimised_model_path, new_vela_optimised_model_path)
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000577 logging.info(
578 f"Renaming {vela_optimised_model_path} to {new_vela_optimised_model_path}."
579 )
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100580
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000581 # If any optimisation was skipped, show how to regenerate:
582 if optimisation_skipped:
583 logging.warning("One or more optimisations were skipped.")
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000584 logging.warning(
585 f"To optimise all the models, please remove the directory {download_dir}."
586 )
587
588 # 4. Collect and write metadata
589 logging.info("Collecting and write metadata.")
590 metadata_dict["ethosu_vela_version"] = vela_version
591 metadata_dict["set_up_script_hash"] = git_commit_hash.strip("\n")
592 metadata_dict["resources_info"] = json_uc_res
593
594 with open(metadata_file_path, "w") as metadata_file:
595 json.dump(metadata_dict, metadata_file, indent=4)
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000596
597
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000598if __name__ == "__main__":
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100599 parser = ArgumentParser()
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000600 parser.add_argument(
601 "--skip-vela",
602 help="Do not run Vela optimizer on downloaded models.",
603 action="store_true",
604 )
605 parser.add_argument(
606 "--additional-ethos-u-config-name",
607 help=f"""Additional (non-default) configurations for Vela:
Kshitij Sisodia3be26232021-10-29 12:29:06 +0100608 {valid_npu_config_names}""",
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000609 default=[],
610 action="append",
611 )
612 parser.add_argument(
613 "--arena-cache-size",
614 help="Arena cache size in bytes (if overriding the defaults)",
615 type=int,
616 default=0,
617 )
618 parser.add_argument(
619 "--clean",
620 help="Clean the disctory and optimize the downloaded resources",
621 action="store_true",
622 )
Isabella Gottardi2181d0a2021-04-07 09:27:38 +0100623 args = parser.parse_args()
Kshitij Sisodiab9e9c892021-05-27 13:57:35 +0100624
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000625 if args.arena_cache_size < 0:
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000626 raise ArgumentTypeError("Arena cache size cannot not be less than 0")
Isabella Gottardi3acaaee2021-11-30 12:33:27 +0000627
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000628 logging.basicConfig(filename="log_build_default.log", level=logging.DEBUG)
Kshitij Sisodiab9e9c892021-05-27 13:57:35 +0100629 logging.getLogger().addHandler(logging.StreamHandler(sys.stdout))
630
Isabella Gottardief2b9dd2022-02-16 14:24:03 +0000631 set_up_resources(
632 not args.skip_vela,
633 args.additional_ethos_u_config_name,
634 args.arena_cache_size,
635 args.clean,
636 )