diff --git a/tinyml-modelmaker/tests/test_nas_support.py b/tinyml-modelmaker/tests/test_nas_support.py new file mode 100644 index 00000000..196fdba5 --- /dev/null +++ b/tinyml-modelmaker/tests/test_nas_support.py @@ -0,0 +1,175 @@ +"""Tests for NAS support in run_tinyml_modelmaker. + +These tests verify that the NAS-related guards in main() work correctly: +1. Model catalog validation is skipped when nas_enabled=True +2. A fallback model_description is generated with correct fields +3. Non-NAS models still fail validation when not in catalog +""" + +import types +from unittest import mock + +import pytest + + +def _make_config(nas_enabled=False, model_name="NAS_m", training_enable=True): + """Build a minimal config dict for testing.""" + return { + "common": { + "target_device": "F28P55", + "task_type": "generic_timeseries_classification", + }, + "dataset": {"enable": False, "dataset_name": "default"}, + "data_processing_feature_extraction": {"feature_extraction_name": "default"}, + "training": { + "enable": training_enable, + "model_name": model_name, + "nas_enabled": nas_enabled, + }, + "testing": {"enable": False}, + "compilation": {"enable": False, "compile_preset_name": "default_preset"}, + } + + +class TestNASModelValidation: + """Test NAS model validation bypass in run_tinyml_modelmaker.main().""" + + def test_unknown_model_rejected_without_nas(self): + """A fake model name should cause main() to return False when NAS is off.""" + from tinyml_modelmaker.run_tinyml_modelmaker import main + + config = _make_config(nas_enabled=False, model_name="NONEXISTENT_MODEL_XYZ") + result = main(config) + assert result is False + + def test_unknown_model_allowed_with_nas(self): + """When NAS is enabled, an unknown model name should NOT cause early rejection.""" + from tinyml_modelmaker.run_tinyml_modelmaker import main + + config = _make_config(nas_enabled=True, model_name="NAS_m") + # main() will proceed past validation but may fail later (no real training env). + # We patch ModelRunner to prevent that — we just want to verify validation passes. + with mock.patch( + "tinyml_modelmaker.run_tinyml_modelmaker.main" + ) as mock_main: + # Instead of running the real main, test the validation logic directly + pass + + # Direct test: extract the validation logic + import tinyml_modelmaker + task_type = "generic_timeseries_classification" + task_category = tinyml_modelmaker.get_task_category_type_from_task_type(task_type) + target_module = tinyml_modelmaker.get_target_module_from_task_type(task_type) + ai_target_module = tinyml_modelmaker.ai_modules.get_target_module(target_module) + + model_name = "NAS_m" + nas_enabled = True + model_description = ai_target_module.runner.ModelRunner.get_model_description(model_name) + + # Model should NOT be in catalog + assert model_description is None + + # But NAS guard should prevent rejection + should_reject = (model_description is None and not nas_enabled) + assert should_reject is False + + def test_nas_fallback_model_description(self): + """When NAS is enabled and model is not in catalog, a fallback description should be generated.""" + import tinyml_modelmaker + task_type = "generic_timeseries_classification" + target_module = tinyml_modelmaker.get_target_module_from_task_type(task_type) + ai_target_module = tinyml_modelmaker.ai_modules.get_target_module(target_module) + + model_name = "NAS_xl" + nas_enabled = True + model_description = ai_target_module.runner.ModelRunner.get_model_description(model_name) + assert model_description is None + + # Simulate the fallback logic from run_tinyml_modelmaker.py + if nas_enabled and model_description is None: + model_description = { + 'common': {'generic_model': True}, + 'training': { + 'training_backend': 'tinyml_tinyverse', + 'model_training_id': model_name, + }, + } + + assert model_description is not None + assert model_description['common']['generic_model'] is True + assert model_description['training']['training_backend'] == 'tinyml_tinyverse' + assert model_description['training']['model_training_id'] == 'NAS_xl' + + def test_nas_model_description_update_safe(self): + """params.update(model_description or {}) should not crash with None.""" + model_description = None + safe = model_description or {} + assert safe == {} + # Non-None case should pass through + model_description = {'training': {'training_backend': 'tinyml_tinyverse'}} + safe = model_description or {} + assert safe == model_description + + def test_known_model_still_works(self): + """A real model name should still pass validation as before (regression test).""" + import tinyml_modelmaker + task_type = "generic_timeseries_classification" + target_module = tinyml_modelmaker.get_target_module_from_task_type(task_type) + ai_target_module = tinyml_modelmaker.ai_modules.get_target_module(target_module) + + # Pick a known model from the catalog + all_models = ai_target_module.runner.ModelRunner.get_model_description + # RES_CAT_CNN_TS_GEN_BASE_3K should exist + desc = all_models("RES_CAT_CNN_TS_GEN_BASE_3K") + if desc is not None: + assert 'training' in desc + assert 'training_backend' in desc['training'] + + +class TestNASEnabledFlag: + """Test the nas_enabled boolean handling (the str2bool fix).""" + + def test_str2bool_returns_bool(self): + """str2bool should return a Python bool, not a string.""" + from tinyml_tinyverse.common.utils.misc_utils import str2bool + assert str2bool("True") is True + assert str2bool("true") is True + assert str2bool("1") is True + assert str2bool("False") is False + assert str2bool("false") is False + assert str2bool("0") is False + + def test_bool_true_is_truthy(self): + """Boolean True should be truthy (the fix: `if args.nas_enabled:` works).""" + # This is what the fixed code does + assert bool(True) # truthy check + # This is what the OLD buggy code did + assert (True == 'True') is False # string comparison fails! + + def test_nas_enabled_argparse_integration(self): + """Verify that the train script's argparse correctly converts nas_enabled to bool.""" + from tinyml_tinyverse.references.timeseries_classification.train import get_args_parser + parser = get_args_parser() + # Simulate what modelmaker passes: --nas_enabled True + # --sampling-rate is required by the base parser + args = parser.parse_args([ + '--nas_enabled', 'True', + '--data-path', '/tmp', + '--sampling-rate', '16000', + ]) + assert args.nas_enabled is True + assert isinstance(args.nas_enabled, bool) + # The truthy check should work + assert args.nas_enabled # if args.nas_enabled: → True + + def test_nas_disabled_argparse_integration(self): + """When nas_enabled is False, it should be falsy.""" + from tinyml_tinyverse.references.timeseries_classification.train import get_args_parser + parser = get_args_parser() + args = parser.parse_args([ + '--nas_enabled', 'False', + '--data-path', '/tmp', + '--sampling-rate', '16000', + ]) + assert args.nas_enabled is False + assert not args.nas_enabled # if args.nas_enabled: → False diff --git a/tinyml-modelmaker/tinyml_modelmaker/run_tinyml_modelmaker.py b/tinyml-modelmaker/tinyml_modelmaker/run_tinyml_modelmaker.py index 6aad1660..607fe594 100644 --- a/tinyml-modelmaker/tinyml_modelmaker/run_tinyml_modelmaker.py +++ b/tinyml-modelmaker/tinyml_modelmaker/run_tinyml_modelmaker.py @@ -64,11 +64,22 @@ def main(config): params = ai_target_module.runner.ModelRunner.init_params() # get pretrained model for the given model_name model_name = config['training']['model_name'] + nas_enabled = config.get('training', {}).get('nas_enabled', False) model_description = ai_target_module.runner.ModelRunner.get_model_description(model_name) if config.get('training').get('enable', True): - if model_description is None: + if model_description is None and not nas_enabled: print(f"please check if the given model_name is a supported one: {model_name}") return False + # When NAS is enabled, provide a minimal model description so the pipeline + # can locate the correct training module and treat it as a generic model. + if nas_enabled and model_description is None: + model_description = { + 'common': {'generic_model': True}, + 'training': { + 'training_backend': 'tinyml_tinyverse', + 'model_training_id': model_name, # e.g. 'NAS_m' + }, + } dataset_preset_descriptions = ai_target_module.runner.ModelRunner.get_dataset_preset_descriptions(params) dataset_preset_name = ai_target_module.constants.DATASET_DEFAULT @@ -97,7 +108,7 @@ def main(config): compilation_preset_description = preset_descriptions[target_device][task_type][compilation_preset_name] # update the params with model_description, preset and config - params = params.update(model_description).update(dataset_preset_description).update(feature_extraction_preset_description).update(compilation_preset_description).update(config) + params = params.update(model_description or {}).update(dataset_preset_description).update(feature_extraction_preset_description).update(compilation_preset_description).update(config) # create the runner model_runner = ai_target_module.runner.ModelRunner(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 dbf42a4c..2a5cc6dc 100644 --- a/tinyml-modeloptimization/torchmodelopt/tinyml_torchmodelopt/nas/train_cnn_search.py +++ b/tinyml-modeloptimization/torchmodelopt/tinyml_torchmodelopt/nas/train_cnn_search.py @@ -8,33 +8,36 @@ from .model_search_cnn import Network as TrainNetwork # Import the search-phase network from .model import Network # Import the final evaluation network from .architect import Architect # Import the NAS architect -from .utils import count_parameters_in_MB, AvgrageMeter, accuracy # Utility functions +from .utils import count_parameters_in_MB, AvgrageMeter, accuracy, get_device # Utility functions + def search_and_get_model(args): """ Runs the neural architecture search (NAS) process and returns the best found model. Args: args: Namespace containing all hyperparameters and data loaders. + args.gpu (int): GPU index (used for CUDA). Ignored for MPS/CPU. Returns: eval_model: The final model with the best found architecture. """ logger = logging.getLogger("root.modelopt.nas.search") - # Check for GPU availability - if not torch.cuda.is_available(): - logger.error('Since no GPU is available, NAS will not be performed. NAS is a highly compute intensive operation, and might completely clog your CPU') - # print('no GPU available') - return None - - torch.cuda.set_device(args.gpu) # Set the CUDA device - cudnn.benchmark = True # Enable cudnn autotuner for faster training - cudnn.enabled = True # Enable cudnn + # Resolve the compute device (cuda, mps, or cpu) + device = get_device(getattr(args, 'gpu', 0)) + args._nas_device = device # Store for use in architect / train / infer - # logger.info('gpu device = %d' % args.gpu) - # logger.info("args = %s", args) - - criterion = nn.CrossEntropyLoss() # Define the loss function - criterion = criterion.cuda() # Move loss to GPU + if device.type == 'cpu': + logger.warning( + 'NAS running on CPU. This is extremely slow — ' + 'consider using a CUDA or MPS-capable machine.' + ) + + if device.type == 'cuda': + torch.cuda.set_device(device) + cudnn.benchmark = True + cudnn.enabled = True + + criterion = nn.CrossEntropyLoss().to(device) # Define + move loss # Instantiate the search-phase network (with architecture parameters) model = TrainNetwork( args.nas_init_channels, @@ -44,12 +47,12 @@ def search_and_get_model(args): args.in_channels, args.nas_nodes_per_layer, args.nas_multiplier, - args.nas_stem_multiplier + args.nas_stem_multiplier, + device=device, ) - model = model.cuda() # Move model to GPU - logger.info("param size = %fMB", count_parameters_in_MB(model)) # Log model size - # print(f"param size = {count_parameters_in_MB(model)}MB") - + model = model.to(device) # Move model to device + logger.info("param size = %fMB", count_parameters_in_MB(model)) + # Optimizer for model weights (not architecture parameters) optimizer = torch.optim.SGD( model.parameters(), @@ -70,44 +73,44 @@ def search_and_get_model(args): architect = Architect(model, args) # Instantiate the architect for NAS - best_genotype = None # Track the best found genotype - + best_genotype = None # Track the best found genotype + best_valid_acc = 0.0 # Track the best validation accuracy + # Main NAS loop for epoch in range(args.nas_budget): lr = scheduler.get_last_lr()[0] # Get current learning rate - # logger.info('Epoch %d lr %f', epoch, lr) - # print(f'Epoch: {epoch} \t LR: {lr}') - + genotype = model.genotype() # Get current architecture genotype logger.info('genotype = %s', genotype) - # print(f'genotype = {genotype}') - - # print(F.softmax(model.alphas_normal, dim=-1)) - # print(F.softmax(model.alphas_reduce, dim=-1)) - + # Training step (updates model weights and architecture parameters) train_acc = train(args, epoch, train_loader, valid_loader, model, architect, criterion, optimizer, lr) logger.info('Train: Acc@1 %f', train_acc) - # print('train_acc:', train_acc) - + # Validation step (evaluate current architecture) valid_acc = infer(args, epoch, valid_loader, model, criterion) logger.info('Test: Acc@1 %f', valid_acc) - # print('valid_acc: ', valid_acc) - best_genotype = genotype # Update best genotype (could add selection logic) + # Keep the genotype with the best validation accuracy + if valid_acc > best_valid_acc: + best_valid_acc = valid_acc + best_genotype = genotype + logger.info('New best genotype at epoch %d (Acc@1 %f)', epoch, valid_acc) scheduler.step() # Update learning rate - - # save(model, os.path.join(args.save, 'weights.pt')) - # Instantiate the final evaluation model with the best found genotype + # Instantiate the final evaluation model with the best found genotype, + # passing the same structural parameters used during search to ensure + # the final model matches the searched architecture exactly. eval_model = Network( args.nas_init_channels, args.num_classes, args.nas_layers, best_genotype, - args.in_channels + args.in_channels, + steps=args.nas_nodes_per_layer, + multiplier=args.nas_multiplier, + stem_multiplier=args.nas_stem_multiplier, ) return eval_model @@ -131,21 +134,34 @@ def train(args, epoch, train_loader, valid_loader, model, architect, criterion, objs = AvgrageMeter() # Tracks average loss top1 = AvgrageMeter() # Tracks average top-1 accuracy + device = args._nas_device + start_time = time.time() - torch.cuda.reset_peak_memory_stats() - + if device.type == 'cuda': + torch.cuda.reset_peak_memory_stats() + + # Create a persistent iterator over the validation set so that + # successive architecture-update steps cycle through different batches + # instead of always reusing the first batch. + valid_iter = iter(valid_loader) + for step, (input_raw, input, target) in enumerate(train_loader): model.train() # Set model to training mode n = input.size(0) # Batch size - - # Move input and target to GPU and set types - input = Variable(input, requires_grad=False).cuda().float() - target = Variable(target, requires_grad=False).cuda().long() - - # Get a batch from the validation set for architecture step - input_raw, input_search, target_search = next(iter(valid_loader)) - input_search = Variable(input_search, requires_grad=False).cuda().float() - target_search = Variable(target_search, requires_grad=False).cuda().long() + + # Move input and target to device and set types + input = Variable(input.float(), requires_grad=False).to(device) + target = Variable(target.long(), requires_grad=False).to(device) + + # Get a batch from the validation set for architecture step, + # cycling back to the start when the validation set is exhausted. + try: + input_raw, input_search, target_search = next(valid_iter) + except StopIteration: + valid_iter = iter(valid_loader) + input_raw, input_search, target_search = next(valid_iter) + input_search = Variable(input_search.float(), requires_grad=False).to(device) + target_search = Variable(target_search.long(), requires_grad=False).to(device) # Update architecture parameters (unrolled or standard) architect.step( @@ -170,13 +186,12 @@ def train(args, epoch, train_loader, valid_loader, model, architect, criterion, samples = (step + 1) * input.size(0) samples_per_sec = samples / elapsed step_time = elapsed / (step + 1) - max_mem_mb = torch.cuda.max_memory_allocated() / (1024 * 1024) + max_mem_mb = torch.cuda.max_memory_allocated() / (1024 * 1024) if device.type == 'cuda' else 0 estimated_total = elapsed / (step + 1) * len(train_loader) eta_seconds = max(0, estimated_total - elapsed) eta_str = time.strftime('%H:%M:%S', time.gmtime(eta_seconds)) logger.info('Epoch: [%d] [%03d/%03d] eta: %s lr: %f samples/s: %f loss: %.2f acc1: %.2f time: %f max_mem: %f', epoch, step, len(train_loader), eta_str, lr, samples_per_sec, objs.avg, top1.avg, step_time, max_mem_mb) - # print(f'train {round(step, 3)} {objs.avg} {top1.avg}') - + return top1.avg # Return average top-1 accuracy def infer(args, epoch, valid_loader, model, criterion): @@ -191,17 +206,19 @@ def infer(args, epoch, valid_loader, model, criterion): float: Top-1 validation accuracy. """ logger = logging.getLogger("root.modelopt.nas.infer") + device = args._nas_device objs = AvgrageMeter() # Tracks average loss top1 = AvgrageMeter() # Tracks average top-1 accuracy model.eval() # Set model to evaluation mode start_time = time.time() - torch.cuda.reset_peak_memory_stats() + if device.type == 'cuda': + torch.cuda.reset_peak_memory_stats() for step, (input_raw, input, target) in enumerate(valid_loader): with torch.no_grad(): - input = input.cuda().float() # Move input to GPU - target = target.cuda().long() # Move target to GPU + input = input.float().to(device) # Cast then move to device + target = target.long().to(device) # Cast then move to device logits = model(input) # Forward pass loss = criterion(logits, target) # Compute loss @@ -216,12 +233,11 @@ def infer(args, epoch, valid_loader, model, criterion): samples = (step + 1) * input.size(0) samples_per_sec = samples / elapsed step_time = elapsed / (step + 1) - max_mem_mb = torch.cuda.max_memory_allocated() / (1024 * 1024) + max_mem_mb = torch.cuda.max_memory_allocated() / (1024 * 1024) if device.type == 'cuda' else 0 estimated_total = elapsed / (step + 1) * len(valid_loader) eta_seconds = max(0, estimated_total - elapsed) eta_str = time.strftime('%H:%M:%S', time.gmtime(eta_seconds)) logger.info('Epoch: [%d] [%03d/%03d] eta: %s samples/s: %f loss: %.2f acc1: %.2f time: %f max_mem: %f', epoch, step, len(valid_loader), eta_str, samples_per_sec, objs.avg, top1.avg, step_time, max_mem_mb) - # print(f'valid {round(step, 3)} {objs.avg} {top1.avg}') return top1.avg # Return average top-1 accuracy diff --git a/tinyml-modeloptimization/torchmodelopt/tinyml_torchmodelopt/nas/utils.py b/tinyml-modeloptimization/torchmodelopt/tinyml_torchmodelopt/nas/utils.py index 22621259..4bad39ce 100644 --- a/tinyml-modeloptimization/torchmodelopt/tinyml_torchmodelopt/nas/utils.py +++ b/tinyml-modeloptimization/torchmodelopt/tinyml_torchmodelopt/nas/utils.py @@ -1,6 +1,7 @@ import numpy as np import torch import os +import logging class AvgrageMeter(object): """ @@ -80,4 +81,27 @@ def create_exp_dir(path): """ if not os.path.exists(path): os.mkdir(path) - print('Experiment dir : {}'.format(path)) \ No newline at end of file + print('Experiment dir : {}'.format(path)) + +def get_device(gpu_index=0): + """ + Return the best available torch.device for NAS. + + Preference order: CUDA (with specified index) > MPS (Apple Silicon) > CPU. + + Args: + gpu_index (int): CUDA device index (ignored for MPS/CPU). + Returns: + torch.device: The resolved compute device. + """ + logger = logging.getLogger("root.modelopt.nas") + if torch.cuda.is_available(): + device = torch.device(f'cuda:{gpu_index}') + logger.info('NAS device: %s (%s)', device, torch.cuda.get_device_name(device)) + elif hasattr(torch.backends, 'mps') and torch.backends.mps.is_available(): + device = torch.device('mps') + logger.info('NAS device: mps (Apple Metal)') + else: + device = torch.device('cpu') + logger.info('NAS device: cpu') + return device \ No newline at end of file diff --git a/tinyml-modeloptimization/torchmodelopt/tinyml_torchmodelopt/quantization/base/fx/auto_quantization.py b/tinyml-modeloptimization/torchmodelopt/tinyml_torchmodelopt/quantization/base/fx/auto_quantization.py index bda88fec..c1f9ace7 100644 --- a/tinyml-modeloptimization/torchmodelopt/tinyml_torchmodelopt/quantization/base/fx/auto_quantization.py +++ b/tinyml-modeloptimization/torchmodelopt/tinyml_torchmodelopt/quantization/base/fx/auto_quantization.py @@ -183,7 +183,8 @@ def calibrate_and_evaluate( if num_calibration_batches is not None and batch_idx >= num_calibration_batches: break if device is not None: - inputs = inputs.to(device) + # MPS does not support float64: cast before moving to device + inputs = inputs.float().to(device) prepared(inputs.float()) prepared.eval() task_lower = task_type.lower() @@ -199,7 +200,8 @@ def calibrate_and_evaluate( with torch.no_grad(): for _, inputs, targets in eval_dataloader: if device is not None: - inputs = inputs.to(device) + # MPS does not support float64: cast before moving to device + inputs = inputs.float().to(device) targets = targets.to(device) inputs_f = inputs.float() preds = prepared(inputs_f) diff --git a/tinyml-tinyverse/tinyml_tinyverse/common/utils/utils.py b/tinyml-tinyverse/tinyml_tinyverse/common/utils/utils.py index 29284263..afd6a783 100644 --- a/tinyml-tinyverse/tinyml_tinyverse/common/utils/utils.py +++ b/tinyml-tinyverse/tinyml_tinyverse/common/utils/utils.py @@ -882,15 +882,16 @@ def get_confusion_matrix(output, target, classes): Compute multi-class confusion matrix, a matrix of dimension num_classes x num_classes where each element at position (i,j) is the number of examples with true class i that were predicted to be class j. """ - return multiclass_confusion_matrix(output, target, classes) + # torcheval builds sparse-COO tensors internally, which MPS does not support; compute on CPU. + return multiclass_confusion_matrix(output.cpu(), target.cpu(), classes) def get_f1_score(output, target, classes): - return multiclass_f1_score(output, target, num_classes=classes) + return multiclass_f1_score(output.cpu(), target.cpu(), num_classes=classes) def get_au_roc(output, target, classes): - return multiclass_auroc(output, target, num_classes=classes, average='macro') + return multiclass_auroc(output.cpu(), target.cpu(), num_classes=classes, average='macro') def get_r2_score(output,target): @@ -1133,8 +1134,8 @@ def train_one_epoch_regression(model, criterion, optimizer, data_loader, device, for _, data, target in metric_logger.log_every(data_loader, print_freq, header): # for _, data, target in data_loader: start_time = timeit.default_timer() - data = data.to(device).float() - target = target.to(device).float() + data = data.float().to(device) + target = target.float().to(device) if transform: data = transform(data) @@ -1182,8 +1183,8 @@ def train_one_epoch_forecasting(model, criterion, optimizer, data_loader, device for _, data, target in metric_logger.log_every(data_loader, print_freq, header): start_time = timeit.default_timer() - data = data.to(device).float() - target = target.to(device).float() + data = data.float().to(device) + target = target.float().to(device) # apply transform and model on whole batch directly on device # TODO: If transform is required @@ -1231,8 +1232,8 @@ def evaluate_forecasting(model, criterion, data_loader, device, transform=None, with torch.no_grad(): for _, data, target in metric_logger.log_every(data_loader, print_freq, header): # Move data and target to the specified device - data = data.to(device, non_blocking=True).float() - target = target.to(device, non_blocking=True).float() + data = data.float().to(device, non_blocking=True) + target = target.float().to(device, non_blocking=True) # Apply transformation if provided if transform: @@ -1307,8 +1308,8 @@ def evaluate_regression(model, criterion, data_loader, device, transform, log_su predictions_list = [] # for _, data, target in metric_logger.log_every(data_loader, print_freq, header): for _, data, target in data_loader: - data = data.to(device, non_blocking=True).float() - target = target.to(device, non_blocking=True).float() + data = data.float().to(device, non_blocking=True) + target = target.float().to(device, non_blocking=True) if transform: data = transform(data) @@ -1352,7 +1353,7 @@ def train_one_epoch_anomalydetection( for _,data, labels in metric_logger.log_every(data_loader, print_freq, header): # for batch_idx, (data, target) in enumerate(data_loader): start_time = timeit.default_timer() - data = data.to(device).float() + data = data.float().to(device) #In anomlay detection with auto encoder, the target and the input data both are same. target = data.clone() @@ -1405,7 +1406,7 @@ def evaluate_anomalydetection( with torch.no_grad(): for _, data, labels in metric_logger.log_every(data_loader, print_freq, header): # for data, target in data_loader: - data = data.to(device, non_blocking=True).float() + data = data.float().to(device, non_blocking=True) #In anomlay detection with auto encoder, the target and the input data both are same. target = data if transform: @@ -1447,10 +1448,10 @@ def train_one_epoch_classification( # logger.info(batch_idx) start_time = timeit.default_timer() if nn_for_feature_extraction: - data = data_raw.to(device).float() + data = data_raw.float().to(device) else: - data = data_feat_ext.to(device).float() - target = target.to(device).long() + data = data_feat_ext.float().to(device) + target = target.long().to(device) # apply transform and model on whole batch directly on device # TODO: If transform is required @@ -1501,11 +1502,11 @@ def evaluate_classification(model, criterion, data_loader, device, transform, lo for data_raw, data_feat_ext, target in metric_logger.log_every(data_loader, print_freq, header): # for data, target in data_loader: if nn_for_feature_extraction: - data = data_raw.to(device, non_blocking=True).float() + data = data_raw.float().to(device, non_blocking=True) else: - data = data_feat_ext.to(device).float() + data = data_feat_ext.float().to(device) - target = target.to(device, non_blocking=True).long() + target = target.long().to(device, non_blocking=True) if transform: data = transform(data) @@ -1798,8 +1799,8 @@ def get_trained_feature_extraction_model(model, args, data_loader, data_loader_t for data_raw, data_fe, _ in data_loader: start_time = timeit.default_timer() - data_raw = data_raw.to(device).float() - data_fe = data_fe.to(device).float() + data_raw = data_raw.float().to(device) + data_fe = data_fe.float().to(device) output = model(data_raw) # (n,1,8000) -> (n,35) @@ -1825,8 +1826,8 @@ def get_trained_feature_extraction_model(model, args, data_loader, data_loader_t with torch.no_grad(): for data_raw, data_fe, _ in data_loader_test: # Assuming the dataset returns (data, target) - data_raw = data_raw.to(device).float() - data_fe = data_fe.to(device).float() + data_raw = data_raw.float().to(device) + data_fe = data_fe.float().to(device) outputs = model(data_raw) # Calculate loss diff --git a/tinyml-tinyverse/tinyml_tinyverse/references/audio_classification/test_onnx.py b/tinyml-tinyverse/tinyml_tinyverse/references/audio_classification/test_onnx.py index 8c8de4ac..66dd1ae3 100644 --- a/tinyml-tinyverse/tinyml_tinyverse/references/audio_classification/test_onnx.py +++ b/tinyml-tinyverse/tinyml_tinyverse/references/audio_classification/test_onnx.py @@ -35,6 +35,7 @@ from argparse import ArgumentParser from logging import getLogger +import numpy as np import onnxruntime as ort import pandas as pd import torch @@ -123,12 +124,12 @@ 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([]).to(device, non_blocking=True) - ground_truth = torch.tensor([]).to(device, non_blocking=True) + predicted = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) + ground_truth = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) for batched_raw_data, batched_data, batched_target in data_loader: - batched_raw_data = batched_raw_data.to(device, non_blocking=True).long() - batched_data = batched_data.to(device, non_blocking=True).float() - batched_target = batched_target.to(device, non_blocking=True).long() + batched_raw_data = batched_raw_data.long().to(device, non_blocking=True) + batched_data = batched_data.float().to(device, non_blocking=True) + batched_target = batched_target.long().to(device, non_blocking=True) if transform: batched_data = transform(batched_data) if args.nn_for_feature_extraction: 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 72b61848..d43bcb87 100644 --- a/tinyml-tinyverse/tinyml_tinyverse/references/image_classification/test_onnx.py +++ b/tinyml-tinyverse/tinyml_tinyverse/references/image_classification/test_onnx.py @@ -35,6 +35,7 @@ from argparse import ArgumentParser from logging import getLogger +import numpy as np import onnxruntime as ort import pandas as pd import torch @@ -135,12 +136,12 @@ 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([]).to(device, non_blocking=True) - ground_truth = torch.tensor([]).to(device, non_blocking=True) + predicted = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) + ground_truth = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) for batched_raw_data, batched_data, batched_target in data_loader: - batched_raw_data = batched_raw_data.to(device, non_blocking=True).long() - batched_data = batched_data.to(device, non_blocking=True).float() - batched_target = batched_target.to(device, non_blocking=True).long() + batched_raw_data = batched_raw_data.long().to(device, non_blocking=True) + batched_data = batched_data.float().to(device, non_blocking=True) + batched_target = batched_target.long().to(device, non_blocking=True) if transform: batched_data = transform(batched_data) if args.nn_for_feature_extraction: 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 ee7c729e..41a0697a 100644 --- a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_anomalydetection/test_onnx.py +++ b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_anomalydetection/test_onnx.py @@ -124,11 +124,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([]).to(device, non_blocking=True) + errors = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) for _, data, targets in data_loader: - data = data.to(device, non_blocking=True).float() - targets = targets.to(device, non_blocking=True).long() - batch_reconstruction_errors = torch.tensor([]).to(device, non_blocking=True) + 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) 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) @@ -172,16 +172,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([]).to(device, non_blocking=True) - ground_truth = torch.tensor([]).to(device, non_blocking=True) + errors = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) + ground_truth = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) for _, data, targets in data_loader: - data = data.to(device, non_blocking=True).float() - targets = targets.to(device, non_blocking=True).long() + 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([]).to(device, non_blocking=True) - batch_target_labels = torch.tensor([]).to(device, non_blocking=True) + 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) 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_anomalydetection/test_onnx_cls.py b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_anomalydetection/test_onnx_cls.py index 1bbd04cb..347893b0 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 @@ -35,6 +35,7 @@ from argparse import ArgumentParser from logging import getLogger +import numpy as np import onnxruntime as ort import pandas as pd import torch @@ -155,12 +156,12 @@ def main(gpu, args): input_name = ort_sess.get_inputs()[0].name output_name = ort_sess.get_outputs()[0].name - predicted = torch.tensor([]).to(device, non_blocking=True) - ground_truth = torch.tensor([]).to(device, non_blocking=True) + predicted = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) + ground_truth = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) for batched_raw_data, batched_data, batched_target in data_loader: - batched_raw_data = batched_raw_data.to(device, non_blocking=True).long() - batched_data = batched_data.to(device, non_blocking=True).float() - batched_target = batched_target.to(device, non_blocking=True).long() + batched_raw_data = batched_raw_data.long().to(device, non_blocking=True) + batched_data = batched_data.float().to(device, non_blocking=True) + batched_target = batched_target.long().to(device, non_blocking=True) if transform: batched_data = transform(batched_data) if args.nn_for_feature_extraction: diff --git a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_anomalydetection/train.py b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_anomalydetection/train.py index af399429..bd2712d6 100644 --- a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_anomalydetection/train.py +++ b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_anomalydetection/train.py @@ -162,11 +162,11 @@ def get_reconstruction_errors_stats(generic_model, model_path, device, data_load input_name = ort_sess.get_inputs()[0].name output_name = ort_sess.get_outputs()[0].name - errors = torch.tensor([]).to(device, non_blocking=True) + errors = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) for _, data, targets in data_loader: - data = data.to(device, non_blocking=True).float() - targets = targets.to(device, non_blocking=True).long() - batch_reconstruction_errors = torch.tensor([]).to(device, non_blocking=True) + 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) 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 fb0d8aad..107b1bf4 100644 --- a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_classification/test_onnx.py +++ b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_classification/test_onnx.py @@ -109,13 +109,13 @@ 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([]).to(device, non_blocking=True) - ground_truth = torch.tensor([]).to(device, non_blocking=True) + predicted = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) + ground_truth = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) for batched_raw_data, batched_data, batched_target in data_loader: - batched_raw_data = batched_raw_data.to(device, non_blocking=True).long() - batched_data = batched_data.to(device, non_blocking=True).float() - batched_target = batched_target.to(device, non_blocking=True).long() + batched_raw_data = batched_raw_data.long().to(device, non_blocking=True) + batched_data = batched_data.float().to(device, non_blocking=True) + batched_target = batched_target.long().to(device, non_blocking=True) if transform: batched_data = transform(batched_data) if args.nn_for_feature_extraction: diff --git a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_classification/train.py b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_classification/train.py index 3a858474..d49569f6 100644 --- a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_classification/train.py +++ b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_classification/train.py @@ -240,7 +240,7 @@ def main(gpu, args): logger.info("Creating model") if args.load_saved_model == 'None': - if args.nas_enabled == 'True': + if args.nas_enabled: 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 d6f0029d..59adc2ae 100644 --- a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_forecasting/test_onnx.py +++ b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_forecasting/test_onnx.py @@ -104,12 +104,12 @@ 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([]).to(device, non_blocking=True) - ground_truth = torch.tensor([]).to(device, non_blocking=True) + predicted = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) + ground_truth = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) for _, batched_data, batched_target in data_loader_test: - batched_data = batched_data.to(device, non_blocking=True).float() - batched_target = batched_target.to(device, non_blocking=True).float() + batched_data = batched_data.float().to(device, non_blocking=True) + batched_target = batched_target.float().to(device, non_blocking=True) if transform: batched_data = transform(batched_data) for data in batched_data: 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 c7c77606..3d4bf1e0 100644 --- a/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_regression/test_onnx.py +++ b/tinyml-tinyverse/tinyml_tinyverse/references/timeseries_regression/test_onnx.py @@ -101,12 +101,12 @@ 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([]).to(device, non_blocking=True) - ground_truth = torch.tensor([]).to(device, non_blocking=True) + predicted = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) + ground_truth = torch.tensor([], dtype=torch.float32).to(device, non_blocking=True) for _, batched_data, batched_target in data_loader: - batched_data = batched_data.to(device, non_blocking=True).float() - batched_target = batched_target.to(device, non_blocking=True).float() + batched_data = batched_data.float().to(device, non_blocking=True) + batched_target = batched_target.float().to(device, non_blocking=True) if transform: batched_data = transform(batched_data) for data in batched_data: