Skip to content

SamsungLabs/nb-asr

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NASBench-ASR

Code for the "NAS-Bench-ASR: Reproducible Neural Architecture Search for Speech Recognition" paper published at ICLR'21: https://openreview.net/forum?id=CU0APx9LMaL

Content:

Installing

You can type pip install <nbasr_folder> to install our tool (optionally with the -e argument for in-place installation) and its dependencies. We recommend using pipenv (or some other virtual environment management tool) to avoid problems with TF/Pytorch versions.

Note: please let us know if you run into any problems related to missing dependencies

Dataset format

We split out dataset into multiple pickle files containing information about the models from the search space in different settings. Each dataset contains two parts: a header and the actual data. Those are serialized using pickle one after another, so when reading a similar sequence needs to be used:

with open('nb-asr-e40-1234.pickle', 'rb') as f:
    header = pickle.load(f)
    data = pickle.load(f)

The header contains usual metainformation about the dataset file, including things like: search space used when generating the dataset, column names, dataset type and version. The following chunk of data is a python list with raw values - the order of values follows the order of column names in the header.

We distinguish three main types of datasets:

  • training datasets, which contain information about accuracy of models during/after training
  • benchmarking datasets, which contain information about on-device performance of models
  • static information datasets, which constain static information about models, such as number of parameters

Please see the following section to see a brief summary of how to use the dataset conveniently.

Using the dataset

All relevant files can be downloded from the releases page in this repo.

We provide a Dataset class as the top-level interface to querying the datasets (although the user is free to read the pickle files on their own). The Dataset class is primarily used to deal with training datasets but it has an option to also piggy-back benchmarking and static datasets for convenience. If needed the user can also use BenchmarkingDataset and StaticInfoDataset classes to access only benchmarking and/or static information about models without loading the data realted to training.

Assuming all NB-ASR files are in the same directory and no special behaviour is needed, the user can also use a high-level nasbench_asr.from_folder function which automatically searches for files in the given directory and creates a Dataset object from them.

The rest of the section presents some typical use cases.

Creating the dataset:

>>> import nasbench_asr as nbasr
>>> d = nbasr.from_folder('~/data/nasbench_asr/', include_static_info=True)

Querying all information, returned as dict or list:

>>> d.full_info([[1,0], [1,0,0], [1,0,0,0]], include_static_info=True)
{'val_per': [0.47851497, 0.32516438, 0.27674836, 0.25589427, 0.24639702, 0.23125456, 0.22919573, 0.228598, 0.22308561, 0.21856944, 0.22109318, 0.2183702, 0.21451816, 0.21498306, 0.21458457, 0.21239291, 0.21431892, 0.21418609, 0.21584645, 0.21584645, 0.21578003, 0.21704191, 0.21664342, 0.21843661, 0.2188351, 0.22003055, 0.22109318, 0.22149166, 0.23816165, 0.23643488, 0.22886366, 0.22082752, 0.2207611, 0.22142525, 0.22169091, 0.22056186, 0.22149166, 0.22182374, 0.22142525, 0.22202298], 'test_per': 0.242688849568367, 'arch_vec': [(1, 0), (1, 0, 0), (1, 0, 0, 0)], 'model_hash': '36855332a5778e0df5114305bc3ce238', 'seed': 1235, 'gtx-1080ti-fp32': {'latency': 0.04320073127746582}, 'jetson-nano-fp32': {'latency': 0.5421140193939209}, 'info': {'params': 26338848}}
>>> d.full_info([[1,0], [1,0,0], [1, 0,0,0]], include_static_info=True, return_dict=False)
['36855332a5778e0df5114305bc3ce238', [0.4840938, 0.31912068, 0.27867436, 0.25908217, 0.24433818, 0.23291492, 0.22713688, 0.22979344, 0.22288637, 0.22036262, 0.22056186, 0.21637776, 0.21823737, 0.21637776, 0.21272498, 0.21245933, 0.21318989, 0.21458457, 0.21591286, 0.2169755, 0.21797171, 0.21863586, 0.22036262, 0.22129242, 0.22129242, 0.2216245, 0.23152022, 0.24480309, 0.23450887, 0.22554293, 0.22268713, 0.221226, 0.22175732, 0.2216245, 0.22202298, 0.22182374, 0.22149166, 0.22222222, 0.22242147, 0.22228864], 0.23728343844413757, [(1, 0), (1, 0, 0), (1, 0, 0, 0)], 1236, [0.04320073127746582], [0.5421140193939209], [26338848]]

Removing static information:

>>> d.full_info([[1,0], [1,0,0], [1, 0,0,0]], include_static_info=False)
{'val_per': [0.47851497, 0.32516438, 0.27674836, 0.25589427, 0.24639702, 0.23125456, 0.22919573, 0.228598, 0.22308561, 0.21856944, 0.22109318, 0.2183702, 0.21451816, 0.21498306, 0.21458457, 0.21239291, 0.21431892, 0.21418609, 0.21584645, 0.21584645, 0.21578003, 0.21704191, 0.21664342, 0.21843661, 0.2188351, 0.22003055, 0.22109318, 0.22149166, 0.23816165, 0.23643488, 0.22886366, 0.22082752, 0.2207611, 0.22142525, 0.22169091, 0.22056186, 0.22149166, 0.22182374, 0.22142525, 0.22202298], 'test_per': 0.242688849568367, 'arch_vec': [(1, 0), (1, 0, 0), (1, 0, 0, 0)], 'model_hash': '36855332a5778e0df5114305bc3ce238', 'seed': 1235, 'gtx-1080ti-fp32': {'latency': 0.04320073127746582}, 'jetson-nano-fp32': {'latency': 0.5421140193939209}}

Asking for a particular device performance only:

>>> d.full_info([[1,0], [1,0,0], [1, 0,0,0]], devices='jetson-nano-fp32')
{'val_per': [0.4840938, 0.31912068, 0.27867436, 0.25908217, 0.24433818, 0.23291492, 0.22713688, 0.22979344, 0.22288637, 0.22036262, 0.22056186, 0.21637776, 0.21823737, 0.21637776, 0.21272498, 0.21245933, 0.21318989, 0.21458457, 0.21591286, 0.2169755, 0.21797171, 0.21863586, 0.22036262, 0.22129242, 0.22129242, 0.2216245, 0.23152022, 0.24480309, 0.23450887, 0.22554293, 0.22268713, 0.221226, 0.22175732, 0.2216245, 0.22202298, 0.22182374, 0.22149166, 0.22222222, 0.22242147, 0.22228864], 'test_per': 0.23728343844413757, 'arch_vec': [(1, 0), (1, 0, 0), (1, 0, 0, 0)], 'model_hash': '36855332a5778e0df5114305bc3ce238', 'seed': 1236, 'jetson-nano-fp32': {'latency': 0.5421140193939209}, 'info': {'params': 26338848}}

Do not include any benchmarking results:

>>> d.full_info([[1,0], [1,0,0], [1, 0,0,0]], devices=False, include_static_info=False)
{'val_per': [0.48522282, 0.32031614, 0.28338978, 0.25430033, 0.24128312, 0.23942353, 0.22547652, 0.22733612, 0.22527727, 0.22109318, 0.21670984, 0.21929999, 0.21551438, 0.21458457, 0.21226008, 0.21305706, 0.2137876, 0.21352194, 0.2127914, 0.21491665, 0.21597928, 0.21777247, 0.21996413, 0.2249452, 0.2412167, 0.23484094, 0.23152022, 0.22281995, 0.21890152, 0.21870227, 0.21896791, 0.21896791, 0.21810454, 0.21863586, 0.21923357, 0.21896791, 0.21923357, 0.2198313, 0.21996413, 0.22056186], 'test_per': 0.23395703732967377, 'arch_vec': [(1, 0), (1, 0, 0), (1, 0, 0, 0)], 'model_hash': '36855332a5778e0df5114305bc3ce238', 'seed': 1234}

Querying test accuracy:

>>> d.test_acc([[1,0], [1,0,0], [1,0,0,0]])
0.242688849568367

Querying validation accuracy:

>>> d.val_acc([[1,0], [1,0,0], [1,0,0,0]])
0.21245933
>>> d.val_acc([[1,0], [1,0,0], [1,0,0,0]], best=False)
0.22056186
>>> d.val_acc([[1,0], [1,0,0], [1,0,0,0]], epoch=8)
0.22547652
>>> d.val_acc([[1,0], [1,0,0], [1,0,0,0]], epoch=8, best=False)
0.22979344

Querying latency alone:

>>> d.latency([[1,0], [1,0,0], [1,0,0,0]], devices='gtx-1080ti-fp32')
[[0.04320073127746582]]
>>> d.latency([[1,0], [1,0,0], [1,0,0,0]], devices='gtx-1080ti-fp32', return_dict=True)
{'gtx-1080ti-fp32': {'latency': 0.04320073127746582}}
>>> d.bench_info.latency([[1,0], [1,0,0], [1,0,0,0]], devices='gtx-1080ti-fp32', return_dict=True)
{'gtx-1080ti-fp32': {'latency': 0.04320073127746582}}

Asking for missing information will result in an error:

>>> d = nbasr.from_folder('~/data/nasbench_asr/', include_static_info=False)
>>> d.full_info([[1,0], [1,0,0], [1, 0,0,0]], include_static_info=True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/SERILOCAL/l.dudziak/dev/asr/pytorch-asr/nasbench_asr/dataset.py", line 345, in full_info
    return self._query(model_hash, seed, devices, include_static_info, return_dict)
  File "/home/SERILOCAL/l.dudziak/dev/asr/pytorch-asr/nasbench_asr/dataset.py", line 304, in _query
    raise ValueError('No static information attached')
ValueError: No static information attached

Default values will always include data only if available:

>>> d = nbasr.from_folder('~/data/nasbench_asr/', max_epochs=5, devices=False)
>>> d.full_info([[1,0], [1,0,0], [1, 0,0,0]])
{'val_per': [0.4846915, 0.3614266, 0.32323837, 0.31241283, 0.3053065], 'test_per': 0.3227997124195099, 'arch_vec': [(1, 0), (1, 0, 0), (1, 0, 0, 0)], 'model_hash': '36855332a5778e0df5114305bc3ce238', 'seed': 1234}
>>> d = nbasr.from_folder('~/data/nasbench_asr/', max_epochs=5, devices=None)
>>> d.full_info([[1,0], [1,0,0], [1, 0,0,0]])
{'val_per': [0.4846915, 0.3614266, 0.32323837, 0.31241283, 0.3053065], 'test_per': 0.3227997124195099, 'arch_vec': [(1, 0), (1, 0, 0), (1, 0, 0, 0)], 'model_hash': '36855332a5778e0df5114305bc3ce238', 'seed': 1234, 'gtx-1080ti-fp32': {'latency': 0.04320073127746582}, 'jetson-nano-fp32': {'latency': 0.5421140193939209}}

nasbench_asr.dataset.from_folder silently fails to include requested data if it doesn't exist, if this is undesired please consider using Dataset directly.

>>> d = nbasr.from_folder('~/data/nasbench_asr/', max_epochs=5, devices='non-existing-device')
>>> d.full_info([[1,0], [1,0,0], [1, 0,0,0]])
{'val_per': [0.4846915, 0.3614266, 0.32323837, 0.31241283, 0.3053065], 'test_per': 0.3227997124195099, 'arch_vec': [(1, 0), (1, 0, 0), (1, 0, 0, 0)], 'model_hash': '36855332a5778e0df5114305bc3ce238', 'seed': 1234}
>>> d.full_info([[1,0], [1,0,0], [1, 0,0,0]], devices='non-existing-device')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/SERILOCAL/l.dudziak/dev/asr/pytorch-asr/nasbench_asr/dataset.py", line 346, in full_info
    return self._query(model_hash, seed, devices, include_static_info, return_dict)
  File "/home/SERILOCAL/l.dudziak/dev/asr/pytorch-asr/nasbench_asr/dataset.py", line 295, in _query
    raise ValueError('No benchmarking information attached')
ValueError: No benchmarking information attached

Creating and training models

Alongside the dataset, we provide code to create models and training environment to train them in a reproducible way. We anticipate that this might be especially useful for people working on differentiable NAS.

We support two training backends - Tensorflow or PyTorch. However, please bear in mind that only the TF backend is meant to reproduce results from the dataset as we were using TF during our experiments. PyTorch implementation is provided as a courtesy and comes with no guarantees about achievable results

The following is a short summary of top-level function exposed by the package, most of those functions take an extra argument called backend which can be used to explicitly specify which implementation should be called. If unused, the functions will try to use the default backend - which can be either set explicitly or it can be deduced. The default backend is deduced based on available packages and prefers to use TF, falling back to PyTorch if TF is unavailable.

  • set_default_backend(name) sets the default backend
  • get_backend_name() gets the name of the backend in use (unless overwritten by a function-specific argument)
  • set_seed(seed) sets random seed(s) to the specific value
  • prepare_devices(devices) prepared the specified GPUs for training (only relevant for TF backend, this e.g. turns on dynamic memory growth)
  • get_model(arch) return an implementation of a model with the specified architecture (arch should come from the search space, see e.g. nasbench_asr.search_space.get_all_architectures)
  • get_dataloader(timti_root, batch_size) returns a tuple of 5 values, in order: encoder object (used to encode phonemes), iterable yielding training examples, iterable yielding validation examples, iterable yielding testing examples, backend-specific data
  • get_loss() returns a callable objective, the signature is (logits, logits_size, targets, targets_size)
  • get_trainer(dataloaders, gpus, save_dir, verbose) returns a trainer class which can be used to train models.

For more information on about to use those functions, please see for example train.py which can be used to run trainings of models, using those functions. You can also take a look at the Trainer abstract class defined in nasbench_asr/training/__init__.py. Briefly speaking, a sequence of functions like the following should do the trick:

   set_default_backend(args.backend)
   set_seed(args.seed)
   prepare_devices(args.gpus)

   print(f'Using backend: {get_backend_name()}')
   print(f'    Model vec: {args.model}')
   print(f'    Training for {args.epochs} epochs')
   print(f'    Batch size: {args.batch_size}')
   print(f'    Learning rate: {args.lr}')
   print(f'    Dropout: {args.dropout}')
   print(f'    GPUs: {args.gpus}')

   results_folder = pathlib.Path(args.exp_folder) / args.backend

   first_gpu = None
   if args.gpus:
       first_gpu = args.gpus[0]

   dataloaders = get_dataloaders(args.data, batch_size=args.batch_size)
   model = get_model(args.model, use_rnn=args.rnn, dropout_rate=args.dropout, gpu=first_gpu)
   trainer = get_trainer(dataloaders, gpus=args.gpus, save_dir=results_folder, verbose=True)
   trainer.train(model, epochs=args.epochs, lr=args.lr, reset=args.reset, model_name=args.exp_name)

Citation

Please consider citing NB-ASR if you find our work useful!

@inproceedings{
mehrotra2021nasbenchasr,
title={{\{}NAS{\}}-Bench-{\{}ASR{\}}: Reproducible Neural Architecture Search for Speech Recognition},
author={Abhinav Mehrotra and Alberto Gil C. P. Ramos and Sourav Bhattacharya and {\L}ukasz Dudziak and Ravichander Vipperla and Thomas Chau and Mohamed S Abdelfattah and Samin Ishtiaq and Nicholas Donald Lane},
booktitle={International Conference on Learning Representations (ICLR)},
year={2021}
}