diff --git a/.github/workflows/test-modelmaker.yml b/.github/workflows/test-modelmaker.yml index 1a8f8c36..7233d670 100644 --- a/.github/workflows/test-modelmaker.yml +++ b/.github/workflows/test-modelmaker.yml @@ -36,8 +36,24 @@ jobs: with: python-version: '3.10' + - name: Install ti_mcu_nnc (Linux) + if: runner.os == 'Linux' + run: | + pip install "ti_mcu_nnc @ https://software-dl.ti.com/mctools/esd/tvm/mcu/ti_mcu_nnc-2.1.2-cp310-cp310-linux_x86_64.whl" || true + + - name: Install ti_mcu_nnc (Windows) + if: runner.os == 'Windows' + run: | + pip install "ti_mcu_nnc @ https://software-dl.ti.com/mctools/esd/tvm/mcu/ti_mcu_nnc-2.1.2-cp310-cp310-win_amd64.whl" || true + + - name: Install ti_mcu_nnc (macOS) + if: runner.os == 'macOS' + run: | + pip install "ti_mcu_nnc @ https://software-dl.ti.com/mctools/esd/tvm/mcu/ti_mcu_nnc-2.1.2-cp310-cp310-macosx_14_0_arm64.whl" || true + - name: Install dependencies run: | + set -e python -m pip install --upgrade pip setuptools wheel pip install -e tinyml-modelzoo pip install -e tinyml-tinyverse @@ -46,16 +62,6 @@ jobs: pip install defusedxml==0.7.1 "numpy==2.2.6" "PyYAML==6.0.3" "tqdm==4.67.1" "requests==2.32.5" "torch==2.7.1" pip install pytest - - name: Install ti_mcu_nnc (Linux only) - if: runner.os == 'Linux' - run: | - pip install "ti_mcu_nnc @ https://software-dl.ti.com/mctools/esd/tvm/mcu/ti_mcu_nnc-2.1.1-cp310-cp310-linux_x86_64.whl" || true - - - name: Install ti_mcu_nnc (Windows only) - if: runner.os == 'Windows' - run: | - pip install "ti_mcu_nnc @ https://software-dl.ti.com/mctools/esd/tvm/mcu/ti_mcu_nnc-2.1.1-cp310-cp310-win_amd64.whl" || true - - name: Tier 1 — Component Tests working-directory: tinyml-modelmaker run: python -m pytest tests/test_model_registry.py tests/test_config_validation.py -v --tb=short diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index e9838e62..4b155c9e 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -89,7 +89,7 @@ tinyml-modelmaker ---> tinyml-tinyverse | Method | Command | |---|---| -| CLI | `python tinyml_modelmaker/run_tinyml_modelmaker.py config.yaml` | +| CLI | `cd tinyml-modelmaker && python tinyml_modelmaker/run_tinyml_modelmaker.py config.yaml` | | Shell | `run_tinyml_modelmaker.sh config.yaml` | | Python API | `import tinyml_modelmaker; tinyml_modelmaker.get_set_go(config)` | | GUI | Edge AI Studio Model Composer (uses `tinyml-mlbackend` Docker wrapper) | @@ -522,12 +522,12 @@ graph TB | # | Category | Issue | Severity | Effort | |---|---|---|---|---| -| 1 | Testing | No test suite | Critical | High | +| 1 | Testing | No test suite (protocol conformance tests added; unit/integration/CI gaps remain) | Medium | High | | 2 | Architecture | Monolithic descriptions file | Critical | Medium | | 3 | Architecture | Overloaded constructor | High | Low | | 4 | Architecture | Tight cross-repo coupling | High | High | | 5 | Architecture | Duplicated task-type code | Medium | Medium | -| 6 | Architecture | No ABCs/Protocols | Medium | Low | +| 6 | Architecture | No ABCs/Protocols (Protocols added for modelmaker; tinyverse/torchmodelopt remain) | Low | Low | | 7 | Code Quality | Magic strings | Medium | Low | | 8 | Code Quality | Commented-out code | Low | Low | | 9 | Code Quality | Inconsistent error handling | High | Medium | diff --git a/tinyml-agent-skills/plugins/tinyml-agent-skills/skills/tinyml-workflow-agent/scripts/dataset_format_tools.py b/tinyml-agent-skills/plugins/tinyml-agent-skills/skills/tinyml-workflow-agent/scripts/dataset_format_tools.py index 8cd97cb8..5f04ec29 100644 --- a/tinyml-agent-skills/plugins/tinyml-agent-skills/skills/tinyml-workflow-agent/scripts/dataset_format_tools.py +++ b/tinyml-agent-skills/plugins/tinyml-agent-skills/skills/tinyml-workflow-agent/scripts/dataset_format_tools.py @@ -461,7 +461,7 @@ def _check_regression(path: str, is_zip: bool) -> Tuple[List[str], List[str], bo return errors, warnings, True, "" errors.append( - f"ZIP missing top-level 'files/' directory. " + "ZIP missing top-level 'files/' directory. " "Regression/forecasting data must be in a 'files/' subdirectory." ) return errors, warnings, False, "Create a 'files/' directory at ZIP top level and place all CSV files inside it." diff --git a/tinyml-agent-skills/plugins/tinyml-agent-skills/skills/tinyml-workflow-agent/scripts/device_deployment.py b/tinyml-agent-skills/plugins/tinyml-agent-skills/skills/tinyml-workflow-agent/scripts/device_deployment.py index 7fc37638..b5335ac3 100644 --- a/tinyml-agent-skills/plugins/tinyml-agent-skills/skills/tinyml-workflow-agent/scripts/device_deployment.py +++ b/tinyml-agent-skills/plugins/tinyml-agent-skills/skills/tinyml-workflow-agent/scripts/device_deployment.py @@ -194,7 +194,7 @@ def check_sdk_installation( "errors": [ f"{sdk_name} not found. Searched: {searched}", f"Download from: {info['download_url']}", - f"After installing, pass sdk_path='' to create_ccs_project.", + "After installing, pass sdk_path='' to create_ccs_project.", ], } diff --git a/tinyml-agent-skills/plugins/tinyml-agent-skills/skills/tinyml-workflow-agent/scripts/runner.py b/tinyml-agent-skills/plugins/tinyml-agent-skills/skills/tinyml-workflow-agent/scripts/runner.py index 811b20ac..5a2df9f3 100644 --- a/tinyml-agent-skills/plugins/tinyml-agent-skills/skills/tinyml-workflow-agent/scripts/runner.py +++ b/tinyml-agent-skills/plugins/tinyml-agent-skills/skills/tinyml-workflow-agent/scripts/runner.py @@ -181,7 +181,7 @@ def main(): "function": func_name, "required_params": required, "provided_params": list(kwargs.keys()), - "hint": f"Add the missing parameter(s) to your JSON args object.", + "hint": "Add the missing parameter(s) to your JSON args object.", })) sys.exit(1) diff --git a/tinyml-agent-skills/plugins/tinyml-agent-skills/skills/tinyml-workflow-agent/scripts/update_manager.py b/tinyml-agent-skills/plugins/tinyml-agent-skills/skills/tinyml-workflow-agent/scripts/update_manager.py index c89a1d90..6b55bbb5 100644 --- a/tinyml-agent-skills/plugins/tinyml-agent-skills/skills/tinyml-workflow-agent/scripts/update_manager.py +++ b/tinyml-agent-skills/plugins/tinyml-agent-skills/skills/tinyml-workflow-agent/scripts/update_manager.py @@ -32,7 +32,7 @@ def _read_env_vars() -> Dict: print(f"[setup] ERROR reading .env from {ENV_FILE}: {e}", file=__import__('sys').stderr) else: print(f"[setup] ERROR: .env NOT FOUND at {ENV_FILE}", file=__import__('sys').stderr) - print(f"[setup] This file must be created by /tinyml-agent-skills:setup first", file=__import__('sys').stderr) + print("[setup] This file must be created by /tinyml-agent-skills:setup first", file=__import__('sys').stderr) return vars_dict diff --git a/tinyml-modelmaker/test_all_configs.py b/tinyml-modelmaker/test_all_configs.py index dfd4717f..d2237a8d 100755 --- a/tinyml-modelmaker/test_all_configs.py +++ b/tinyml-modelmaker/test_all_configs.py @@ -145,24 +145,24 @@ def save_failure_log(config_path, result, timestamp): log_file = LOGS_DIR / f"{timestamp}_{config_name}_FAILED.log" with open(log_file, 'w') as f: - f.write(f"=" * 80 + "\n") - f.write(f"FAILED CONFIG TEST\n") - f.write(f"=" * 80 + "\n\n") + f.write("=" * 80 + "\n") + f.write("FAILED CONFIG TEST\n") + f.write("=" * 80 + "\n\n") f.write(f"Config: {config_path}\n") f.write(f"Timestamp: {timestamp}\n") f.write(f"Duration: {result['duration']:.2f}s\n") f.write(f"Return Code: {result['return_code']}\n") f.write(f"Timeout: {result['timeout_exceeded']}\n") f.write(f"Error Detected: {result.get('error_detected', False)}\n") - f.write(f"\n" + "=" * 80 + "\n") - f.write(f"STDOUT:\n") - f.write(f"=" * 80 + "\n") + f.write("\n" + "=" * 80 + "\n") + f.write("STDOUT:\n") + f.write("=" * 80 + "\n") f.write(result['stdout']) - f.write(f"\n\n" + "=" * 80 + "\n") - f.write(f"STDERR:\n") - f.write(f"=" * 80 + "\n") + f.write("\n\n" + "=" * 80 + "\n") + f.write("STDERR:\n") + f.write("=" * 80 + "\n") f.write(result['stderr']) - f.write(f"\n") + f.write("\n") return log_file @@ -205,7 +205,7 @@ def main(): start_timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") print("=" * 80) - print(f"ModelMaker Config Test Suite") + print("ModelMaker Config Test Suite") print("=" * 80) print(f"Timeout: {args.timeout}s") print(f"Total Configs: {len(all_configs)}") @@ -265,7 +265,7 @@ def main(): # Stop on first failure if flag is set if args.stop_on_error and not result['success']: - print(f"\n✗ Stopping on first failure (--stop-on-error flag set).") + print("\n✗ Stopping on first failure (--stop-on-error flag set).") break # Summary @@ -304,7 +304,7 @@ def main(): f.write(f"Passed: {passed}\n") f.write(f"Failed: {failed}\n") f.write(f"Total Time: {format_duration(total_duration)}\n") - f.write(f"\n" + "=" * 80 + "\n") + f.write("\n" + "=" * 80 + "\n") f.write("DETAILED RESULTS\n") f.write("=" * 80 + "\n\n") diff --git a/tinyml-modelmaker/tests/test_protocols.py b/tinyml-modelmaker/tests/test_protocols.py index 64652d67..4ec07607 100644 --- a/tinyml-modelmaker/tests/test_protocols.py +++ b/tinyml-modelmaker/tests/test_protocols.py @@ -2,15 +2,8 @@ Uses ``@runtime_checkable`` isinstance checks to verify that every component implementation provides the methods required by its corresponding protocol. - -Requires TVM (compilation backend) which is not available on all platforms. """ -import pytest - -# TVM is required transitively via tinyml_benchmark → compilation.py -tvm = pytest.importorskip("tvm", reason="TVM not available on this platform") - from tinyml_modelmaker.ai_modules.protocols import ( Compiler, DatasetHandler, diff --git a/tinyml-modelmaker/tinyml_modelmaker/ai_modules/audio/constants.py b/tinyml-modelmaker/tinyml_modelmaker/ai_modules/audio/constants.py index 3434d1d6..0c6af03a 100644 --- a/tinyml-modelmaker/tinyml_modelmaker/ai_modules/audio/constants.py +++ b/tinyml-modelmaker/tinyml_modelmaker/ai_modules/audio/constants.py @@ -576,8 +576,8 @@ def get_default_data_dir_for_task(task_category): CROSS_COMPILER_OPTIONS_C28 = (f"--abi=eabi -O3 --opt_for_speed=5 --c99 -v28 -ml -mt --gen_func_subsections --float_support={{FLOAT_SUPPORT}} -I{C2000_CGT_INCLUDE} -I.") CROSS_COMPILER_OPTIONS_F29H85 = (f"-O3 -ffast-math -I{C29_CGT_INCLUDE} -I.") -CROSS_COMPILER_OPTIONS_MSPM0 = (f"-Os -mcpu=cortex-m0plus -march=thumbv6m -mtune=cortex-m0plus -mthumb -mfloat-abi=soft -I. -Wno-return-type") -CROSS_COMPILER_OPTIONS_MSPM33C = (f"-O3 -mcpu=cortex-m33 -march=thumbv6m -mfpu=fpv5-sp-d16 -DARM_CPU_INTRINSICS_EXIST -mlittle-endian -mfloat-abi=hard -I. -Wno-return-type") +CROSS_COMPILER_OPTIONS_MSPM0 = ("-Os -mcpu=cortex-m0plus -march=thumbv6m -mtune=cortex-m0plus -mthumb -mfloat-abi=soft -I. -Wno-return-type") +CROSS_COMPILER_OPTIONS_MSPM33C = ("-O3 -mcpu=cortex-m33 -march=thumbv6m -mfpu=fpv5-sp-d16 -DARM_CPU_INTRINSICS_EXIST -mlittle-endian -mfloat-abi=hard -I. -Wno-return-type") CROSS_COMPILER_OPTIONS_F280013 = CROSS_COMPILER_OPTIONS_C28.format(FLOAT_SUPPORT='fpu32', DEVICE_NAME=TARGET_DEVICE_F280013.lower() + 'x') CROSS_COMPILER_OPTIONS_F280015 = CROSS_COMPILER_OPTIONS_C28.format(FLOAT_SUPPORT='fpu32', DEVICE_NAME=TARGET_DEVICE_F280015.lower() + 'x') diff --git a/tinyml-modelmaker/tinyml_modelmaker/ai_modules/audio/runner.py b/tinyml-modelmaker/tinyml_modelmaker/ai_modules/audio/runner.py index 85b1e5c6..caa990b3 100644 --- a/tinyml-modelmaker/tinyml_modelmaker/ai_modules/audio/runner.py +++ b/tinyml-modelmaker/tinyml_modelmaker/ai_modules/audio/runner.py @@ -251,7 +251,7 @@ def run(self): self.model_compilation.clear() exit_flag = self.model_compilation.run() if exit_flag: - print(f'Compilation failed') + print('Compilation failed') with open(self.params.compilation.log_file_path, 'a') as lfp: lfp.write('FAILURE: ModelMaker - Compilation failed.') return self.params diff --git a/tinyml-modelmaker/tinyml_modelmaker/ai_modules/audio/training/tinyml_tinyverse/audio_base.py b/tinyml-modelmaker/tinyml_modelmaker/ai_modules/audio/training/tinyml_tinyverse/audio_base.py index e07c5155..e8cd74f5 100644 --- a/tinyml-modelmaker/tinyml_modelmaker/ai_modules/audio/training/tinyml_tinyverse/audio_base.py +++ b/tinyml-modelmaker/tinyml_modelmaker/ai_modules/audio/training/tinyml_tinyverse/audio_base.py @@ -286,6 +286,7 @@ def _get_device(self): if self.params.training.num_gpus > 0: if torch.backends.mps.is_available(): device = 'mps' + os.environ.setdefault('PYTORCH_ENABLE_MPS_FALLBACK', '1') else: device = 'cuda' diff --git a/tinyml-modelmaker/tinyml_modelmaker/ai_modules/timeseries/constants.py b/tinyml-modelmaker/tinyml_modelmaker/ai_modules/timeseries/constants.py index 2e8f9411..5660b4a2 100644 --- a/tinyml-modelmaker/tinyml_modelmaker/ai_modules/timeseries/constants.py +++ b/tinyml-modelmaker/tinyml_modelmaker/ai_modules/timeseries/constants.py @@ -166,7 +166,10 @@ def get_default_data_dir_for_task(task_category): elif task_category in [TASK_CATEGORY_TS_REGRESSION, TASK_CATEGORY_TS_FORECASTING]: return DATA_DIR_FILES else: - return DATA_DIR_CLASSES # Safe fallback + raise ValueError( + f"Unsupported task_category '{task_category}'. " + f"Expected one of: {[TASK_CATEGORY_TS_CLASSIFICATION, TASK_CATEGORY_TS_ANOMALYDETECTION, TASK_CATEGORY_TS_REGRESSION, TASK_CATEGORY_TS_FORECASTING]}" + ) # target_device @@ -1438,18 +1441,18 @@ def get_default_data_dir_for_task(task_category): CROSS_COMPILER_OPTIONS_C28 = f"--abi=eabi -O3 --opt_for_speed=5 --c99 -v28 -ml -mt --gen_func_subsections --float_support={{FLOAT_SUPPORT}} -I{C2000_CGT_INCLUDE} -I." CROSS_COMPILER_OPTIONS_C29 = f"-O3 -ffast-math -I{C29_CGT_INCLUDE} -I." -CROSS_COMPILER_OPTIONS_MSPM0 = f"-Os -mcpu=cortex-m0plus -march=thumbv6m -mtune=cortex-m0plus -mthumb -mfloat-abi=soft -I. -Wno-return-type" -CROSS_COMPILER_OPTIONS_AM263 = f"-DARM_CPU_INTRINSICS_EXIST -mcpu=cortex-r5 -mfloat-abi=hard -mfpu=vfpv3-d16 -mlittle-endian -O3 -I. -Wno-return-type" -CROSS_COMPILER_OPTIONS_AM263P = f"-DARM_CPU_INTRINSICS_EXIST -mcpu=cortex-r5 -mfloat-abi=hard -mfpu=vfpv3-d16 -mlittle-endian -O3 -I. -Wno-return-type" -CROSS_COMPILER_OPTIONS_AM261 = f"-DARM_CPU_INTRINSICS_EXIST -mcpu=cortex-r5 -mfloat-abi=hard -mfpu=vfpv3-d16 -mlittle-endian -O3 -I. -Wno-return-type" -CROSS_COMPILER_OPTIONS_MSPM33C = f"-DARM_CPU_INTRINSICS_EXIST -mcpu=cortex-m33 -mfloat-abi=hard -mfpu=fpv5-sp-d16 -mlittle-endian -O3 -I. -Wno-return-type -march=thumbv8.1-m.main+cdecp0" -CROSS_COMPILER_OPTIONS_AM13E2 = f"-DARM_CPU_INTRINSICS_EXIST -mcpu=cortex-m33 -mfloat-abi=hard -mfpu=fpv5-sp-d16 -mlittle-endian -O3 -I. -Wno-return-type -march=thumbv8.1-m.main+cdecp0" -CROSS_COMPILER_OPTIONS_CC1352 = f"-DARM_CPU_INTRINSICS_EXIST -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -O3 -Wl,-u,_c_int00 -Wno-return-type -march=armv7e-m -mthumb" -CROSS_COMPILER_OPTIONS_CC1312 = f"-DARM_CPU_INTRINSICS_EXIST -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -O3 -Wl,-u,_c_int00 -Wno-return-type -march=armv7e-m -mthumb" -CROSS_COMPILER_OPTIONS_CC1354 = f"-DARM_CPU_INTRINSICS_EXIST -mcpu=cortex-m33 -mfloat-abi=hard -mfpu=fpv5-sp-d16 -mlittle-endian -O3 -I. -Wno-return-type -march=armv8-m.main" -CROSS_COMPILER_OPTIONS_CC1314 = f"-DARM_CPU_INTRINSICS_EXIST -mcpu=cortex-m33 -mfloat-abi=hard -mfpu=fpv5-sp-d16 -mlittle-endian -O3 -I. -Wno-return-type -march=armv8-m.main" -CROSS_COMPILER_OPTIONS_CC2755 = f"-DARM_CPU_INTRINSICS_EXIST -mcpu=cortex-m33 -mfloat-abi=hard -mfpu=fpv5-sp-d16 -mlittle-endian -O3 -I. -Wno-return-type -march=thumbv8.1-m.main+cdecp0 -Wno-incompatible-pointer-types" -CROSS_COMPILER_OPTIONS_CC35X1 = f"-DARM_CPU_INTRINSICS_EXIST -mcpu=cortex-m33 -mfloat-abi=hard -mfpu=fpv5-sp-d16 -mlittle-endian -O3 -I. -Wno-return-type -march=thumbv8.1-m.main+cdecp0 -Wno-incompatible-pointer-types" +CROSS_COMPILER_OPTIONS_MSPM0 = "-Os -mcpu=cortex-m0plus -march=thumbv6m -mtune=cortex-m0plus -mthumb -mfloat-abi=soft -I. -Wno-return-type" +CROSS_COMPILER_OPTIONS_AM263 = "-DARM_CPU_INTRINSICS_EXIST -mcpu=cortex-r5 -mfloat-abi=hard -mfpu=vfpv3-d16 -mlittle-endian -O3 -I. -Wno-return-type" +CROSS_COMPILER_OPTIONS_AM263P = "-DARM_CPU_INTRINSICS_EXIST -mcpu=cortex-r5 -mfloat-abi=hard -mfpu=vfpv3-d16 -mlittle-endian -O3 -I. -Wno-return-type" +CROSS_COMPILER_OPTIONS_AM261 = "-DARM_CPU_INTRINSICS_EXIST -mcpu=cortex-r5 -mfloat-abi=hard -mfpu=vfpv3-d16 -mlittle-endian -O3 -I. -Wno-return-type" +CROSS_COMPILER_OPTIONS_MSPM33C = "-DARM_CPU_INTRINSICS_EXIST -mcpu=cortex-m33 -mfloat-abi=hard -mfpu=fpv5-sp-d16 -mlittle-endian -O3 -I. -Wno-return-type -march=thumbv8.1-m.main+cdecp0" +CROSS_COMPILER_OPTIONS_AM13E2 = "-DARM_CPU_INTRINSICS_EXIST -mcpu=cortex-m33 -mfloat-abi=hard -mfpu=fpv5-sp-d16 -mlittle-endian -O3 -I. -Wno-return-type -march=thumbv8.1-m.main+cdecp0" +CROSS_COMPILER_OPTIONS_CC1352 = "-DARM_CPU_INTRINSICS_EXIST -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -O3 -Wl,-u,_c_int00 -Wno-return-type -march=armv7e-m -mthumb" +CROSS_COMPILER_OPTIONS_CC1312 = "-DARM_CPU_INTRINSICS_EXIST -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -O3 -Wl,-u,_c_int00 -Wno-return-type -march=armv7e-m -mthumb" +CROSS_COMPILER_OPTIONS_CC1354 = "-DARM_CPU_INTRINSICS_EXIST -mcpu=cortex-m33 -mfloat-abi=hard -mfpu=fpv5-sp-d16 -mlittle-endian -O3 -I. -Wno-return-type -march=armv8-m.main" +CROSS_COMPILER_OPTIONS_CC1314 = "-DARM_CPU_INTRINSICS_EXIST -mcpu=cortex-m33 -mfloat-abi=hard -mfpu=fpv5-sp-d16 -mlittle-endian -O3 -I. -Wno-return-type -march=armv8-m.main" +CROSS_COMPILER_OPTIONS_CC2755 = "-DARM_CPU_INTRINSICS_EXIST -mcpu=cortex-m33 -mfloat-abi=hard -mfpu=fpv5-sp-d16 -mlittle-endian -O3 -I. -Wno-return-type -march=thumbv8.1-m.main+cdecp0 -Wno-incompatible-pointer-types" +CROSS_COMPILER_OPTIONS_CC35X1 = "-DARM_CPU_INTRINSICS_EXIST -mcpu=cortex-m33 -mfloat-abi=hard -mfpu=fpv5-sp-d16 -mlittle-endian -O3 -I. -Wno-return-type -march=thumbv8.1-m.main+cdecp0 -Wno-incompatible-pointer-types" CROSS_COMPILER_OPTIONS_F280013 = CROSS_COMPILER_OPTIONS_C28.format(FLOAT_SUPPORT='fpu32', DEVICE_NAME=TARGET_DEVICE_F280013.lower() + 'x') diff --git a/tinyml-modelmaker/tinyml_modelmaker/ai_modules/timeseries/training/tinyml_tinyverse/timeseries_base.py b/tinyml-modelmaker/tinyml_modelmaker/ai_modules/timeseries/training/tinyml_tinyverse/timeseries_base.py index 7563020c..75ccb71c 100644 --- a/tinyml-modelmaker/tinyml_modelmaker/ai_modules/timeseries/training/tinyml_tinyverse/timeseries_base.py +++ b/tinyml-modelmaker/tinyml_modelmaker/ai_modules/timeseries/training/tinyml_tinyverse/timeseries_base.py @@ -683,6 +683,10 @@ def _get_device(self): if self.params.training.num_gpus > 0: if torch.backends.mps.is_available(): device = 'mps' + # MPS doesn't implement every op; unknown ops fall back to CPU + # when this flag is set. Must be in os.environ before the first + # MPS-dispatched kernel runs. + os.environ.setdefault('PYTORCH_ENABLE_MPS_FALLBACK', '1') else: device = 'cuda' return device, distributed diff --git a/tinyml-modelmaker/tinyml_modelmaker/ai_modules/vision/constants.py b/tinyml-modelmaker/tinyml_modelmaker/ai_modules/vision/constants.py index 73c3ff61..c228a39e 100644 --- a/tinyml-modelmaker/tinyml_modelmaker/ai_modules/vision/constants.py +++ b/tinyml-modelmaker/tinyml_modelmaker/ai_modules/vision/constants.py @@ -600,8 +600,8 @@ def get_default_data_dir_for_task(task_category): CROSS_COMPILER_OPTIONS_C28 = (f"--abi=eabi -O3 --opt_for_speed=5 --c99 -v28 -ml -mt --gen_func_subsections --float_support={{FLOAT_SUPPORT}} -I{C2000_CGT_INCLUDE} -I.") CROSS_COMPILER_OPTIONS_F29H85 = (f"-O3 -ffast-math -I{C29_CGT_INCLUDE} -I.") -CROSS_COMPILER_OPTIONS_MSPM0 = (f"-Os -mcpu=cortex-m0plus -march=thumbv6m -mtune=cortex-m0plus -mthumb -mfloat-abi=soft -I. -Wno-return-type") -CROSS_COMPILER_OPTIONS_MSPM33C = (f"-O3 -mcpu=cortex-m33 -march=thumbv6m -mfpu=fpv5-sp-d16 -DARM_CPU_INTRINSICS_EXIST -mlittle-endian -mfloat-abi=hard -I. -Wno-return-type") +CROSS_COMPILER_OPTIONS_MSPM0 = ("-Os -mcpu=cortex-m0plus -march=thumbv6m -mtune=cortex-m0plus -mthumb -mfloat-abi=soft -I. -Wno-return-type") +CROSS_COMPILER_OPTIONS_MSPM33C = ("-O3 -mcpu=cortex-m33 -march=thumbv6m -mfpu=fpv5-sp-d16 -DARM_CPU_INTRINSICS_EXIST -mlittle-endian -mfloat-abi=hard -I. -Wno-return-type") CROSS_COMPILER_OPTIONS_F280013 = CROSS_COMPILER_OPTIONS_C28.format(FLOAT_SUPPORT='fpu32', DEVICE_NAME=TARGET_DEVICE_F280013.lower() + 'x') CROSS_COMPILER_OPTIONS_F280015 = CROSS_COMPILER_OPTIONS_C28.format(FLOAT_SUPPORT='fpu32', DEVICE_NAME=TARGET_DEVICE_F280015.lower() + 'x') diff --git a/tinyml-modelmaker/tinyml_modelmaker/ai_modules/vision/training/tinyml_tinyverse/image_base.py b/tinyml-modelmaker/tinyml_modelmaker/ai_modules/vision/training/tinyml_tinyverse/image_base.py index a120a0fc..a6d3d462 100644 --- a/tinyml-modelmaker/tinyml_modelmaker/ai_modules/vision/training/tinyml_tinyverse/image_base.py +++ b/tinyml-modelmaker/tinyml_modelmaker/ai_modules/vision/training/tinyml_tinyverse/image_base.py @@ -295,6 +295,7 @@ def _get_device(self): if self.params.training.num_gpus > 0: if torch.backends.mps.is_available(): device = 'mps' + os.environ.setdefault('PYTORCH_ENABLE_MPS_FALLBACK', '1') else: device = 'cuda' diff --git a/tinyml-modelmaker/tinyml_modelmaker/utils/misc_utils.py b/tinyml-modelmaker/tinyml_modelmaker/utils/misc_utils.py index 9e039f47..c3f73f37 100644 --- a/tinyml-modelmaker/tinyml_modelmaker/utils/misc_utils.py +++ b/tinyml-modelmaker/tinyml_modelmaker/utils/misc_utils.py @@ -154,14 +154,14 @@ def resolve_paths(params, target_devices_all): params.compilation.compilation_path = absolute_path(params.compilation.compile_output_path) params.compilation.model_packaged_path = os.path.join( params.compilation.compile_output_path, - '_'.join(os.path.split(params.common.run_name)) + '_'.join(filter(None, os.path.split(params.common.run_name))) + f'_{params.common.target_device}.zip') else: params.compilation.compilation_path = absolute_path( os.path.join(params.common.project_run_path, 'compilation')) params.compilation.model_packaged_path = os.path.join( params.compilation.compilation_path, - '_'.join(os.path.split(params.common.run_name)) + '_'.join(filter(None, os.path.split(params.common.run_name))) + f'_{params.common.target_device}.zip') return params diff --git a/tinyml-modeloptimization/torchmodelopt/tinyml_torchmodelopt/nas/train_cnn_search.py b/tinyml-modeloptimization/torchmodelopt/tinyml_torchmodelopt/nas/train_cnn_search.py index 3c975c93..cdce0380 100644 --- a/tinyml-modeloptimization/torchmodelopt/tinyml_torchmodelopt/nas/train_cnn_search.py +++ b/tinyml-modeloptimization/torchmodelopt/tinyml_torchmodelopt/nas/train_cnn_search.py @@ -220,9 +220,8 @@ def infer(args, epoch, valid_loader, model, criterion): with torch.no_grad(): input = input.to(device).float() # Move input to device target = target.to(device).long() # Move target to device - - logits = model(input) # Forward pass - loss = criterion(logits, target) # Compute loss + logits = model(input) # Forward pass + loss = criterion(logits, target) # Compute loss prec1, prec5 = accuracy(logits, target, topk=(1, 1)) # Compute accuracy n = input.size(0) diff --git a/tinyml-modeloptimization/torchmodelopt/tinyml_torchmodelopt/quantization/base/fx/quant_base.py b/tinyml-modeloptimization/torchmodelopt/tinyml_torchmodelopt/quantization/base/fx/quant_base.py index 8fa181bd..04f7fffa 100644 --- a/tinyml-modeloptimization/torchmodelopt/tinyml_torchmodelopt/quantization/base/fx/quant_base.py +++ b/tinyml-modeloptimization/torchmodelopt/tinyml_torchmodelopt/quantization/base/fx/quant_base.py @@ -482,20 +482,38 @@ def _export_int_model(self, model, example_inputs, filename, opset_version, def _simplify_onnx_model(self, filename, skipped_optimizers): """Simplify exported ONNX model. + On macOS ARM64, onnxsim's C++ destructor causes a SIGSEGV during Python + interpreter shutdown. We isolate it in a subprocess so a crash there cannot + propagate to the main process — packaging and compilation continue normally. + Args: filename: ONNX model filename skipped_optimizers: Optimizers to skip """ - try: - import onnx - from onnxsim import simplify - - onnx_model = onnx.load(filename) - onnx_model, check = simplify(onnx_model, skipped_optimizers=skipped_optimizers) - onnx.save(onnx_model, filename) - except Exception as e: - print(f"Warning: ONNX model simplification failed " - f"(possibly due to multi-process issues): {e}") + import sys + import platform + import subprocess as _sp + + script = ( + 'import onnx, sys; from onnxsim import simplify as _s; ' + f'm,_ = _s(onnx.load(sys.argv[1]), skipped_optimizers={repr(skipped_optimizers)}); ' + 'onnx.save(m, sys.argv[1])' + ) + if sys.platform == 'darwin' and platform.machine() == 'arm64': + result = _sp.run([sys.executable, '-c', script, filename], capture_output=True) + if result.returncode not in (0, -11): # -11 = SIGSEGV; both mean simplification ran + print(f"Warning: ONNX model simplification failed: {result.stderr.decode()}") + else: + try: + import onnx + from onnxsim import simplify + + onnx_model = onnx.load(filename) + onnx_model, check = simplify(onnx_model, skipped_optimizers=skipped_optimizers) + onnx.save(onnx_model, filename) + except Exception as e: + print(f"Warning: ONNX model simplification failed " + f"(possibly due to multi-process issues): {e}") # ======================================================================== # PTQ-Specific Utilities diff --git a/tinyml-modelzoo/test_all_configs.py b/tinyml-modelzoo/test_all_configs.py index 95494a6a..7a4b67f4 100644 --- a/tinyml-modelzoo/test_all_configs.py +++ b/tinyml-modelzoo/test_all_configs.py @@ -149,24 +149,24 @@ def save_failure_log(config_path, result, timestamp): log_file = LOGS_DIR / f"{timestamp}_{config_name}_FAILED.log" with open(log_file, 'w') as f: - f.write(f"=" * 80 + "\n") - f.write(f"FAILED CONFIG TEST\n") - f.write(f"=" * 80 + "\n\n") + f.write("=" * 80 + "\n") + f.write("FAILED CONFIG TEST\n") + f.write("=" * 80 + "\n\n") f.write(f"Config: {config_path}\n") f.write(f"Timestamp: {timestamp}\n") f.write(f"Duration: {result['duration']:.2f}s\n") f.write(f"Return Code: {result['return_code']}\n") f.write(f"Timeout: {result['timeout_exceeded']}\n") f.write(f"Error Detected: {result.get('error_detected', False)}\n") - f.write(f"\n" + "=" * 80 + "\n") - f.write(f"STDOUT:\n") - f.write(f"=" * 80 + "\n") + f.write("\n" + "=" * 80 + "\n") + f.write("STDOUT:\n") + f.write("=" * 80 + "\n") f.write(result['stdout']) - f.write(f"\n\n" + "=" * 80 + "\n") - f.write(f"STDERR:\n") - f.write(f"=" * 80 + "\n") + f.write("\n\n" + "=" * 80 + "\n") + f.write("STDERR:\n") + f.write("=" * 80 + "\n") f.write(result['stderr']) - f.write(f"\n") + f.write("\n") return log_file @@ -231,7 +231,7 @@ def main(): args = parser.parse_args() print("=" * 80) - print(f"Tiny ML ModelZoo Test Suite") + print("Tiny ML ModelZoo Test Suite") print("=" * 80) print() @@ -266,7 +266,7 @@ def main(): start_timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") print("=" * 80) - print(f"Training Test Suite") + print("Training Test Suite") print("=" * 80) print(f"Timeout: {args.timeout}s") print(f"Total Configs: {len(all_configs)}") @@ -318,7 +318,7 @@ def main(): }) if args.stop_on_error and not result['success']: - print(f"\n✗ Stopping on first failure (--stop-on-error flag set).") + print("\n✗ Stopping on first failure (--stop-on-error flag set).") break # Summary @@ -356,7 +356,7 @@ def main(): f.write(f"Passed: {passed}\n") f.write(f"Failed: {failed}\n") f.write(f"Total Time: {format_duration(total_duration)}\n") - f.write(f"\n" + "=" * 80 + "\n") + f.write("\n" + "=" * 80 + "\n") f.write("DETAILED RESULTS\n") f.write("=" * 80 + "\n\n") diff --git a/tinyml-tinyverse/tinyml_tinyverse/common/utils/gof_utils.py b/tinyml-tinyverse/tinyml_tinyverse/common/utils/gof_utils.py index 18d44889..4da9dd2c 100644 --- a/tinyml-tinyverse/tinyml_tinyverse/common/utils/gof_utils.py +++ b/tinyml-tinyverse/tinyml_tinyverse/common/utils/gof_utils.py @@ -284,17 +284,17 @@ def goodness_of_fit_test(frame_size, classes_dir, output_dir,class_names): ("Disclaimer: Not all eight plots need to have separable clusters. Each plot represents a different method to analyze a time-series classification dataset. If any of the plots show separable clusters, it’s a strong sign that the dataset is suitable for classification.", ""), - (f"* Cluster purity matters more than shape or number of clusters", - f"If a plot clearly shows the expected number of clusters corresponding to the true number of classes, users should focus on that plot for classification insights. Even if a class splits into multiple clusters in some plots, as long as each cluster maintains high purity (ie, almost all points within a cluster belong to the same class) and is separable from other classes, it is still classifiable and should be fine. The shape of clusters does not always matter—clusters may appear as annular or ring like structures. If they are separable, they are still valid."), + ("* Cluster purity matters more than shape or number of clusters", + "If a plot clearly shows the expected number of clusters corresponding to the true number of classes, users should focus on that plot for classification insights. Even if a class splits into multiple clusters in some plots, as long as each cluster maintains high purity (ie, almost all points within a cluster belong to the same class) and is separable from other classes, it is still classifiable and should be fine. The shape of clusters does not always matter—clusters may appear as annular or ring like structures. If they are separable, they are still valid."), - (f"* Overlapping classes indicate potential issues", - f"If two or more classes consistently overlap in most plots, it might indicate either a dataset issue (e.g., mislabeled data, inadequate features or noise in the data) or a natural similarity between those classes, leading to a higher risk of misclassification."), + ("* Overlapping classes indicate potential issues", + "If two or more classes consistently overlap in most plots, it might indicate either a dataset issue (e.g., mislabeled data, inadequate features or noise in the data) or a natural similarity between those classes, leading to a higher risk of misclassification."), - (f"* Possible reasons why a class splits into multiple clusters", - f"One possible reason is different sampling frequencies within the dataset (e.g., points sampled at 10Hz, 20Hz, and 30Hz may result in three separate clusters per class). However, this is just one possibility—multiple clusters could arise due to other factors like different environment setups or data collection inconsistencies."), + ("* Possible reasons why a class splits into multiple clusters", + "One possible reason is different sampling frequencies within the dataset (e.g., points sampled at 10Hz, 20Hz, and 30Hz may result in three separate clusters per class). However, this is just one possibility—multiple clusters could arise due to other factors like different environment setups or data collection inconsistencies."), - (f"* Outliers and Noise", - f"Small, scattered points appearing outside main clusters might be outliers or noise in the data.") + ("* Outliers and Noise", + "Small, scattered points appearing outside main clusters might be outliers or noise in the data.") ] # y coordinate diff --git a/tinyml-tinyverse/tinyml_tinyverse/common/utils/ondevice_training.py b/tinyml-tinyverse/tinyml_tinyverse/common/utils/ondevice_training.py index 2fdf43d7..695eb54f 100644 --- a/tinyml-tinyverse/tinyml_tinyverse/common/utils/ondevice_training.py +++ b/tinyml-tinyverse/tinyml_tinyverse/common/utils/ondevice_training.py @@ -685,7 +685,7 @@ def extract_frozen_subgraph(graph, split_tensor): frozen_graph.toposort() frozen_graph.cleanup() - logger.info(f" Frozen model created:") + logger.info(" Frozen model created:") logger.info(f" Frozen model Inputs: {[inp.name for inp in frozen_graph.inputs]}") logger.info(f" Frozen model Outputs: {[out.name for out in frozen_graph.outputs]}") @@ -1050,7 +1050,7 @@ def flatten_weights(layers): all_weights_array = np.array(all_weights, dtype=np.float32) - logger.info(f" Flattening complete:") + logger.info(" Flattening complete:") logger.info(f" Total parameters: {len(all_weights_array)}") logger.info(f" Offsets: {offsets}") logger.info(f" Layer weight map: {layer_weight_map}") diff --git a/tinyml-tinyverse/tinyml_tinyverse/common/utils/utils.py b/tinyml-tinyverse/tinyml_tinyverse/common/utils/utils.py index fdde2c23..a8fb82fa 100644 --- a/tinyml-tinyverse/tinyml_tinyverse/common/utils/utils.py +++ b/tinyml-tinyverse/tinyml_tinyverse/common/utils/utils.py @@ -472,7 +472,7 @@ def plot_regression(ground_truth, predictions, output_dir, phase=''): # Add labels, title, and legend ax.set_xlabel('Index', fontsize=12) ax.set_ylabel('Target', fontsize=12) - ax.set_title(f'Regression Scatter Plot', fontsize=14) + ax.set_title('Regression Scatter Plot', fontsize=14) ax.legend() ax.grid(alpha=0.3) @@ -1547,11 +1547,11 @@ def evaluate_classification(model, criterion, data_loader, device, transform, lo else: output = model(data) - target_list.append(target) - predictions_list.append(output) + target_list.append(target.cpu()) + predictions_list.append(output.cpu()) - loss = criterion(output.squeeze(-1), target) - acc1 = accuracy(output.squeeze(-1), target, topk=(1,)) + loss = criterion(output.squeeze(), target) + acc1 = accuracy(output.squeeze(), target, topk=(1,)) batch_size = data.shape[0] metric_logger.update(loss=loss.item()) @@ -1564,13 +1564,13 @@ def evaluate_classification(model, criterion, data_loader, device, transform, lo predictions_array = torch.cat(predictions_list) # Compute all metrics at epoch-end instead of per-batch - logger.info(f'{header} Acc@1 {accuracy(predictions_array.squeeze(-1), target_array, topk=(1,))[0]:.3f}') - f1 = get_f1_score(predictions_array.squeeze(-1), target_array, num_classes) + logger.info(f'{header} Acc@1 {accuracy(predictions_array.squeeze(), target_array, topk=(1,))[0]:.3f}') + f1 = get_f1_score(predictions_array.squeeze(), target_array, num_classes) logger.info(f'{header} F1-Score {f1:.3f}') - auc = get_au_roc(predictions_array, target_array, num_classes) + auc = get_au_roc(predictions_array.squeeze(), target_array, num_classes) logger.info("AU-ROC Score: {:.3f}".format(auc)) confusion_matrix_total = get_confusion_matrix( - predictions_array.cpu(), target_array.type(dtype=torch.int64).cpu(), num_classes).numpy() + predictions_array.squeeze(), target_array.type(dtype=torch.int64), num_classes).numpy() logger.info('Confusion Matrix:\n {}'.format(tabulate(pd.DataFrame(confusion_matrix_total, columns=[f"Predicted as: {x}" for x in range(num_classes)], index=[f"Ground Truth: {x}" for x in range(num_classes)]), headers="keys", tablefmt='grid'))) diff --git a/tinyml-tinyverse/tinyml_tinyverse/references/audio_classification/train.py b/tinyml-tinyverse/tinyml_tinyverse/references/audio_classification/train.py index dda1ad2f..d7d0ca31 100644 --- a/tinyml-tinyverse/tinyml_tinyverse/references/audio_classification/train.py +++ b/tinyml-tinyverse/tinyml_tinyverse/references/audio_classification/train.py @@ -261,7 +261,7 @@ def main(gpu, args): gof_utils.goodness_of_fit_test(frame_size=int(args.frame_size), classes_dir=args.data_path, output_dir=args.output_dir, class_names=dataset.classes) else: - logger.warning(f"Goodness of Fit plots will not be generated because frame_size was not given in the YAML file.") + logger.warning("Goodness of Fit plots will not be generated because frame_size was not given in the YAML file.") except Exception as e: logger.warning(f"Feature Extraction plots will not be generated because: {e}") diff --git a/tinyml-tinyverse/tinyml_tinyverse/references/image_classification/test_onnx.py b/tinyml-tinyverse/tinyml_tinyverse/references/image_classification/test_onnx.py index d592ff11..0d9aa775 100644 --- a/tinyml-tinyverse/tinyml_tinyverse/references/image_classification/test_onnx.py +++ b/tinyml-tinyverse/tinyml_tinyverse/references/image_classification/test_onnx.py @@ -138,8 +138,8 @@ def main(gpu, args): logger.info(f"Loading ONNX model: {args.model_path}") ort_sess, input_name, output_name = load_onnx_model(args.model_path, args.generic_model) - predicted = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) - ground_truth = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) + predicted = torch.tensor([]).to(device, non_blocking=True) + ground_truth = torch.tensor([]).to(device, non_blocking=True) for batched_raw_data, batched_data, batched_target in data_loader: batched_raw_data = batched_raw_data.long().to(device, non_blocking=True) batched_data = batched_data.float().to(device, non_blocking=True) diff --git a/tinyml-tinyverse/tinyml_tinyverse/references/image_classification/train.py b/tinyml-tinyverse/tinyml_tinyverse/references/image_classification/train.py index 55148bf5..4a7799dd 100644 --- a/tinyml-tinyverse/tinyml_tinyverse/references/image_classification/train.py +++ b/tinyml-tinyverse/tinyml_tinyverse/references/image_classification/train.py @@ -278,7 +278,7 @@ def main(gpu, args): gof_utils.goodness_of_fit_test(frame_size=int(args.frame_size), classes_dir=args.data_path, output_dir=args.output_dir, class_names=dataset.classes) else: - logger.warning(f"Goodness of Fit plots will not be generated because frame_size was not given in the YAML file.") + logger.warning("Goodness of Fit plots will not be generated because frame_size was not given in the YAML file.") except Exception as e: logger.warning(f"Feature Extraction plots will not be generated because: {e}") @@ -298,7 +298,7 @@ def main(gpu, args): logger.info("Creating model") if args.load_saved_model == 'None': - if args.nas_enabled: + if args.nas_enabled == 'True': if args.quantization: model = torch.load(os.path.join(os.path.dirname(args.output_dir), os.path.join('base', 'nas_model.pt')), weights_only=False) else: @@ -426,7 +426,7 @@ def main(gpu, args): logger.info(f"Best Epoch: {best['epoch']}") logger.info(f"Acc@1 {best['accuracy']:.3f}") logger.info(f"F1-Score {best['f1']:.3f}") - logger.info(f"AUC ROC Score {best['f1']:.3f}") + logger.info(f"AUC ROC Score {best['auc']:.3f}") logger.info("") logger.info('Confusion Matrix:\n {}'.format(tabulate(pd.DataFrame(best['conf_matrix'], columns=[f"Predicted as: {x}" for x in dataset.inverse_label_map.values()], diff --git a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_anomalydetection/test_onnx.py b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_anomalydetection/test_onnx.py index 07ca8a33..26d382e0 100644 --- a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_anomalydetection/test_onnx.py +++ b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_anomalydetection/test_onnx.py @@ -126,11 +126,11 @@ def get_reconstruction_errors_stats(args): logger.info(f"Loading ONNX model: {args.model_path}") ort_sess, input_name, output_name = load_onnx_model(args.model_path, args.generic_model) - errors = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) + errors = torch.tensor([]).to(device, non_blocking=True) for _, data, targets in data_loader: data = data.float().to(device, non_blocking=True) targets = targets.long().to(device, non_blocking=True) - batch_reconstruction_errors = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) + batch_reconstruction_errors = torch.tensor([]).to(device, non_blocking=True) for input, target_label in zip(data, targets): input = input.unsqueeze(0).cpu().numpy() output = torch.tensor(ort_sess.run([output_name], {input_name: input})[0]).to(device) @@ -177,16 +177,16 @@ def main(gpu, args): logger.info(f"Loading ONNX model: {args.model_path}") ort_sess, input_name, output_name = load_onnx_model(args.model_path, args.generic_model) - errors = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) - ground_truth = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) + errors = torch.tensor([]).to(device, non_blocking=True) + ground_truth = torch.tensor([]).to(device, non_blocking=True) for _, data, targets in data_loader: data = data.float().to(device, non_blocking=True) targets = targets.long().to(device, non_blocking=True) if transform: data = transform(data) - batch_reconstruction_errors = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) - batch_target_labels = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) + batch_reconstruction_errors = torch.tensor([]).to(device, non_blocking=True) + batch_target_labels = torch.tensor([]).to(device, non_blocking=True) for input, target_label in zip(data, targets): input = input.unsqueeze(0).cpu().numpy() output = torch.tensor(ort_sess.run([output_name], {input_name: input})[0]).to(device) @@ -211,7 +211,7 @@ def main(gpu, args): normal_test_std = np.std(normal_errors) # Results - logger.info(f"Reconstruction Error Statistics:") + logger.info("Reconstruction Error Statistics:") logger.info(f"Normal training data - Mean: {normal_train_mean:.6f}, Std: {normal_train_std:.6f}") logger.info(f"Anomaly test data - Mean: {anomaly_test_mean:.6f}, Std: {anomaly_test_std:.6f}") logger.info(f"Normal test data - Mean: {normal_test_mean:.6f}, Std: {normal_test_std:.6f}") diff --git a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_anomalydetection/test_onnx_cls.py b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_anomalydetection/test_onnx_cls.py index 07125287..11e58db2 100644 --- a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_anomalydetection/test_onnx_cls.py +++ b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_anomalydetection/test_onnx_cls.py @@ -158,8 +158,8 @@ def main(gpu, args): input_name = ort_sess.get_inputs()[0].name output_name = ort_sess.get_outputs()[0].name - predicted = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) - ground_truth = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) + predicted = torch.tensor([]).to(device, non_blocking=True) + ground_truth = torch.tensor([]).to(device, non_blocking=True) for batched_raw_data, batched_data, batched_target in data_loader: batched_raw_data = batched_raw_data.long().to(device, non_blocking=True) batched_data = batched_data.float().to(device, non_blocking=True) diff --git a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_anomalydetection/train.py b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_anomalydetection/train.py index 1d38f38e..ed64a8ed 100644 --- a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_anomalydetection/train.py +++ b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_anomalydetection/train.py @@ -170,7 +170,7 @@ def get_reconstruction_errors_stats(generic_model, model_path, device, data_load for _, data, targets in data_loader: data = data.float().to(device, non_blocking=True) targets = targets.long().to(device, non_blocking=True) - batch_reconstruction_errors = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) + batch_reconstruction_errors = torch.tensor([]).to(device, non_blocking=True) for input, target_label in zip(data, targets): input = input.unsqueeze(0).cpu().numpy() output = torch.tensor(ort_sess.run([output_name], {input_name: input})[0]).to(device) diff --git a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_classification/test_onnx.py b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_classification/test_onnx.py index 17655001..6d616849 100644 --- a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_classification/test_onnx.py +++ b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_classification/test_onnx.py @@ -111,8 +111,8 @@ def main(gpu, args): logger.info(f"Loading ONNX model: {args.model_path}") ort_sess, input_name, output_name = load_onnx_model(args.model_path, args.generic_model) - predicted = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) - ground_truth = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) + predicted = torch.tensor([]).to(device, non_blocking=True) + ground_truth = torch.tensor([]).to(device, non_blocking=True) for batched_raw_data, batched_data, batched_target in data_loader: batched_raw_data = batched_raw_data.long().to(device, non_blocking=True) diff --git a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_classification/train.py b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_classification/train.py index b4b245b4..e4233a4a 100644 --- a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_classification/train.py +++ b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_classification/train.py @@ -227,7 +227,7 @@ def main(gpu, args): gof_utils.goodness_of_fit_test(frame_size=int(args.frame_size), classes_dir=args.data_path, output_dir=args.output_dir, class_names=dataset.classes) else: - logger.warning(f"Goodness of Fit plots will not be generated because frame_size was not given in the YAML file.") + logger.warning("Goodness of Fit plots will not be generated because frame_size was not given in the YAML file.") except Exception as e: logger.warning(f"Feature Extraction plots will not be generated because: {e}") @@ -245,7 +245,7 @@ def main(gpu, args): logger.info("Creating model") if args.load_saved_model == 'None': - if args.nas_enabled: + if args.nas_enabled == 'True': if args.quantization: model = torch.load(os.path.join(os.path.dirname(args.output_dir), os.path.join('base', 'nas_model.pt')), weights_only=False) else: diff --git a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_forecasting/test_onnx.py b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_forecasting/test_onnx.py index afb85f40..7e4942b6 100644 --- a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_forecasting/test_onnx.py +++ b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_forecasting/test_onnx.py @@ -106,8 +106,8 @@ def main(gpu, args): logger.info(f"Loading ONNX model: {args.model_path}") ort_sess, input_name, output_name = load_onnx_model(args.model_path, args.generic_model) - predicted = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) - ground_truth = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) + predicted = torch.tensor([]).to(device, non_blocking=True) + ground_truth = torch.tensor([]).to(device, non_blocking=True) for _, batched_data, batched_target in data_loader_test: batched_data = batched_data.float().to(device, non_blocking=True) diff --git a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_forecasting/train.py b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_forecasting/train.py index 6913d7e3..d7172be1 100644 --- a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_forecasting/train.py +++ b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_forecasting/train.py @@ -73,6 +73,9 @@ save_checkpoint, handle_export_only, move_model_to_device, + compile_model_if_enabled, + get_amp_context, + get_grad_scaler, export_trained_model, compile_model_if_enabled, get_amp_context, diff --git a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_regression/test_onnx.py b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_regression/test_onnx.py index a1caf110..76d1fab2 100644 --- a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_regression/test_onnx.py +++ b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_regression/test_onnx.py @@ -103,8 +103,8 @@ def main(gpu, args): logger.info(f"Loading ONNX model: {args.model_path}") ort_sess, input_name, output_name = load_onnx_model(args.model_path, args.generic_model) - predicted = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) - ground_truth = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) + predicted = torch.tensor([]).to(device, non_blocking=True) + ground_truth = torch.tensor([]).to(device, non_blocking=True) for _, batched_data, batched_target in data_loader: batched_data = batched_data.float().to(device, non_blocking=True)