From b8d19e4531f1955b0c256f0887494d8eb96f42ff Mon Sep 17 00:00:00 2001 From: colorjam Date: Thu, 26 Mar 2020 13:45:24 +0800 Subject: [PATCH 01/12] fix activation collection and add gradient pruners (#2187) --- docs/en_US/Compressor/Pruner.md | 44 ++++- docs/img/importance_estimation_sum.png | Bin 0 -> 42532 bytes .../pynni/nni/compression/torch/__init__.py | 1 + .../torch/activation_rank_filter_pruners.py | 42 +++-- .../pynni/nni/compression/torch/compressor.py | 4 +- .../torch/gradient_rank_filter_pruners.py | 160 ++++++++++++++++++ .../torch/weight_rank_filter_pruners.py | 6 +- src/sdk/pynni/tests/test_compressor.py | 46 +++++ 8 files changed, 281 insertions(+), 22 deletions(-) create mode 100644 docs/img/importance_estimation_sum.png create mode 100644 src/sdk/pynni/nni/compression/torch/gradient_rank_filter_pruners.py diff --git a/docs/en_US/Compressor/Pruner.md b/docs/en_US/Compressor/Pruner.md index cb31b99b10..54927d1d59 100644 --- a/docs/en_US/Compressor/Pruner.md +++ b/docs/en_US/Compressor/Pruner.md @@ -13,6 +13,8 @@ Index of supported pruning algorithms * [Filter Pruners with Activation Rank](#activationrankfilterpruner) * [APoZ Rank Pruner](#activationapozrankfilterpruner) * [Activation Mean Rank Pruner](#activationmeanrankfilterpruner) +* [Filter Pruners with Gradient Rank](#gradientrankfilterpruner) + * [Taylor FO On Weight Pruner](#taylorfoweightfilterpruner) ## Level Pruner @@ -281,7 +283,7 @@ pruner.compress() - **op_types:** Only Conv1d and Conv2d is supported in L2Filter Pruner ## ActivationRankFilterPruner -ActivationRankFilterPruner is a series of pruners which prune the filters with the smallest importance criterion calculated from the output activations of convolution layers to achieve a preset level of network sparsity +ActivationRankFilterPruner is a series of pruners which prune the filters with the smallest importance criterion calculated from the output activations of convolution layers to achieve a preset level of network sparsity. ### ActivationAPoZRankFilterPruner @@ -341,4 +343,42 @@ You can view example for more information #### User configuration for ActivationMeanRankFilterPruner - **sparsity:** How much percentage of convolutional filters are to be pruned. -- **op_types:** Only Conv2d is supported in ActivationMeanRankFilterPruner +- **op_types:** Only Conv2d is supported in ActivationMeanRankFilterPruner. + + +## GradientRankFilterPruner + +GradientRankFilterPruner is a series of pruners which prune the filters with the smallest importance criterion calculated from the gradients of convolution layers to achieve a preset level of network sparsity. + +### TaylorFOWeightFilterPruner + +We implemented it as a one-shot pruner, it prunes convolutional layers based on the first order taylor expansion on weights. The estimated importance of filters is defined as the paper [Importance Estimation for Neural Network Pruning](http://jankautz.com/publications/Importance4NNPruning_CVPR19.pdf). Other pruning criteria mentioned in this paper will be supported in future release. + +> + +![](../../img/importance_estimation_sum.png) + +#### Usage + +PyTorch code + +```python +from nni.compression.torch import TaylorFOWeightFilterPruner +config_list = [{ + 'sparsity': 0.5, + 'op_types': ['Conv2d'] +}] +pruner = TaylorFOWeightFilterPruner(model, config_list, optimizer) +pruner.compress() +``` + +You can view example for more information + +#### User configuration for GradientWeightSumFilterPruner + +- **sparsity:** How much percentage of convolutional filters are to be pruned. +- **op_types:** Currently only Conv2d is supported in TaylorFOWeightFilterPruner. + + + +  \ No newline at end of file diff --git a/docs/img/importance_estimation_sum.png b/docs/img/importance_estimation_sum.png new file mode 100644 index 0000000000000000000000000000000000000000..d2f25e813acf32d9c88232dbe00e74f3e15adc22 GIT binary patch literal 42532 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 self.statistics_batch_num: wrapper.if_calculated = True + if self.hook_id in self._fwd_hook_handles: + self.remove_activation_collector(self.hook_id) + return mask @@ -148,7 +162,7 @@ def __init__(self, model, config_list, optimizer=None, activation='relu', statis def get_mask(self, base_mask, activations, num_prune): """ Calculate the mask of given layer. - Filters with the smallest APoZ(average percentage of zeros) of output activations are masked. + Filters with the largest APoZ(average percentage of zeros) of output activations are masked. Parameters ---------- diff --git a/src/sdk/pynni/nni/compression/torch/compressor.py b/src/sdk/pynni/nni/compression/torch/compressor.py index 14bdef6bbc..55d527f7f4 100644 --- a/src/sdk/pynni/nni/compression/torch/compressor.py +++ b/src/sdk/pynni/nni/compression/torch/compressor.py @@ -314,8 +314,8 @@ def compress(self): return self.bound_model def update_mask(self): - for wrapper in self.get_modules_wrapper(): - masks = self.calc_mask(wrapper) + for wrapper_idx, wrapper in enumerate(self.get_modules_wrapper()): + masks = self.calc_mask(wrapper, wrapper_idx=wrapper_idx) if masks is not None: for k in masks: assert hasattr(wrapper, k), "there is no attribute '%s' in wrapper" % k diff --git a/src/sdk/pynni/nni/compression/torch/gradient_rank_filter_pruners.py b/src/sdk/pynni/nni/compression/torch/gradient_rank_filter_pruners.py new file mode 100644 index 0000000000..f8007b19da --- /dev/null +++ b/src/sdk/pynni/nni/compression/torch/gradient_rank_filter_pruners.py @@ -0,0 +1,160 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +import logging +import torch +from .compressor import Pruner + +__all__ = ['TaylorFOWeightFilterPruner'] + +logger = logging.getLogger('torch gradient rank filter pruners') + +class GradientRankFilterPruner(Pruner): + """ + A structured pruning base class that prunes the filters with the smallest + importance criterion in convolution layers (using gradient values) + to achieve a preset level of network sparsity. + """ + + def __init__(self, model, config_list, optimizer, statistics_batch_num=1): + """ + Parameters + ---------- + model : torch.nn.module + Model to be pruned + config_list : list + support key for each list item: + - sparsity: percentage of convolutional filters to be pruned. + optimizer: torch.optim.Optimizer + Optimizer used to train model + statistics_batch_num : int + Num of batches for calculating contribution + """ + + super().__init__(model, config_list, optimizer) + self.set_wrappers_attribute("if_calculated", False) + self.set_wrappers_attribute("contribution", None) + self.statistics_batch_num = statistics_batch_num + self.iterations = 0 + self.old_step = self.optimizer.step + self.patch_optimizer(self.calc_contributions) + + def calc_contributions(self): + raise NotImplementedError('{} calc_contributions is not implemented'.format(self.__class__.__name__)) + + def get_mask(self, base_mask, contribution, num_prune): + raise NotImplementedError('{} get_mask is not implemented'.format(self.__class__.__name__)) + + def calc_mask(self, wrapper, **kwargs): + """ + Calculate the mask of given layer. + Filters with the smallest importance criterion which is calculated from the activation are masked. + + Parameters + ---------- + wrapper : Module + the layer to instrument the compression operation + + Returns + ------- + dict + dictionary for storing masks + """ + + weight = wrapper.module.weight.data + op_type = wrapper.type + config = wrapper.config + assert 0 <= config.get('sparsity') < 1, "sparsity must in the range [0, 1)" + assert op_type in config.get('op_types') + + if wrapper.if_calculated: + return None + + mask_weight = torch.ones(weight.size()).type_as(weight).detach() + if hasattr(wrapper.module, 'bias') and wrapper.module.bias is not None: + mask_bias = torch.ones(wrapper.module.bias.size()).type_as(wrapper.module.bias).detach() + else: + mask_bias = None + mask = {'weight_mask': mask_weight, 'bias_mask': mask_bias} + try: + filters = weight.size(0) + num_prune = int(filters * config.get('sparsity')) + if filters < 2 or num_prune < 1 or self.iterations < self.statistics_batch_num: + return mask + + mask = self.get_mask(mask, wrapper.contribution, num_prune) + finally: + if self.iterations >= self.statistics_batch_num: + wrapper.if_calculated = True + + return mask + + +class TaylorFOWeightFilterPruner(GradientRankFilterPruner): + """ + A structured pruning algorithm that prunes the filters with the smallest + importance approximations based on the first order taylor expansion on the weight. + Molchanov, Pavlo and Mallya, Arun and Tyree, Stephen and Frosio, Iuri and Kautz, Jan, + "Importance Estimation for Neural Network Pruning", CVPR 2019. + http://jankautz.com/publications/Importance4NNPruning_CVPR19.pdf + """ + + def __init__(self, model, config_list, optimizer, statistics_batch_num=1): + """ + Parameters + ---------- + model : torch.nn.module + Model to be pruned + config_list : list + support key for each list item: + - sparsity: percentage of convolutional filters to be pruned. + optimizer: torch.optim.Optimizer + Optimizer used to train model + statistics_batch_num : int + Num of batches for activation statistics + """ + super().__init__(model, config_list, optimizer, statistics_batch_num) + + def get_mask(self, base_mask, contribution, num_prune): + """ + Calculate the mask of given layer. + Filters with the smallest importance approximations are masked. + + Parameters + ---------- + base_mask : dict + The basic mask with the same shape of weight, all item in the basic mask is 1. + contribution : torch.Tensor + Layer's importance approximations + num_prune : int + Num of filters to prune + + Returns + ------- + dict + dictionary for storing masks + """ + prune_indices = torch.argsort(contribution)[:num_prune] + for idx in prune_indices: + base_mask['weight_mask'][idx] = 0. + if base_mask['bias_mask'] is not None: + base_mask['bias_mask'][idx] = 0. + return base_mask + + def calc_contributions(self): + """ + Calculate the estimated importance of filters as a sum of individual contribution + based on the first order taylor expansion. + """ + + if self.iterations >= self.statistics_batch_num: + return + for wrapper in self.get_modules_wrapper(): + filters = wrapper.module.weight.size(0) + contribution = (wrapper.module.weight*wrapper.module.weight.grad).data.pow(2).view(filters, -1).sum(dim=1) + if wrapper.contribution is None: + wrapper.contribution = contribution + else: + wrapper.contribution += contribution + + self.iterations += 1 diff --git a/src/sdk/pynni/nni/compression/torch/weight_rank_filter_pruners.py b/src/sdk/pynni/nni/compression/torch/weight_rank_filter_pruners.py index 7e1177e304..66f796fad8 100644 --- a/src/sdk/pynni/nni/compression/torch/weight_rank_filter_pruners.py +++ b/src/sdk/pynni/nni/compression/torch/weight_rank_filter_pruners.py @@ -60,10 +60,8 @@ def calc_mask(self, wrapper, **kwargs): Filters with the smallest importance criterion of the kernel weights are masked. Parameters ---------- - layer : LayerInfo - the layer to instrument the compression operation - config : dict - layer's pruning config + wrapper : Module + the module to instrument the compression operation Returns ------- dict diff --git a/src/sdk/pynni/tests/test_compressor.py b/src/sdk/pynni/tests/test_compressor.py index bf9daeaad5..640cac3d0e 100644 --- a/src/sdk/pynni/tests/test_compressor.py +++ b/src/sdk/pynni/tests/test_compressor.py @@ -228,6 +228,52 @@ def test_torch_slim_pruner(self): assert all(mask1['bias_mask'].numpy() == np.array([0., 0., 0., 1., 1.])) assert all(mask2['bias_mask'].numpy() == np.array([0., 0., 0., 1., 1.])) + def test_torch_taylorFOweight_pruner(self): + """ + Filters with the minimum importance approxiamtion based on the first order + taylor expansion on the weights (w*grad)**2 are pruned in this paper: + Importance Estimation for Neural Network Pruning, + http://jankautz.com/publications/Importance4NNPruning_CVPR19.pdf + + So if sparsity of conv1 is 0.2, the expected masks should mask out filter 0, this can be verified through: + `all(torch.sum(mask1['weight_mask'], (1, 2, 3)).numpy() == np.array([0., 25., 25., 25., 25.]))` + + If sparsity of conv2 is 0.6, the expected masks should mask out filter 4,5,6,7,8,9 this can be verified through: + `all(torch.sum(mask2['weight_mask'], (1, 2, 3)).numpy() == np.array([125., 125., 125., 125., 0., 0., 0., 0., 0., 0., ]))` + """ + + w1 = np.array([np.zeros((1, 5, 5)), np.ones((1, 5, 5)), np.ones((1, 5, 5)) * 2, + np.ones((1, 5, 5)) * 3, np.ones((1, 5, 5)) * 4]) + w2 = np.array([[[[i + 1] * 5] * 5] * 5 for i in range(10)[::-1]]) + + grad1 = np.array([np.ones((1, 5, 5)) * -1, np.ones((1, 5, 5)) * 1, np.ones((1, 5, 5)) * -1, + np.ones((1, 5, 5)) * 1, np.ones((1, 5, 5)) * -1]) + + grad2 = np.array([[[[(-1)**i] * 5] * 5] * 5 for i in range(10)]) + + config_list = [{'sparsity': 0.2, 'op_types': ['Conv2d'], 'op_names': ['conv1']}, + {'sparsity': 0.6, 'op_types': ['Conv2d'], 'op_names': ['conv2']}] + + model = TorchModel() + optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.5) + pruner = torch_compressor.TaylorFOWeightFilterPruner(model, config_list, optimizer, statistics_batch_num=1) + + x = torch.rand((1, 1, 28, 28), requires_grad=True) + model.conv1.module.weight.data = torch.tensor(w1).float() + model.conv2.module.weight.data = torch.tensor(w2).float() + + y = model(x) + y.backward(torch.ones_like(y)) + + model.conv1.module.weight.grad.data = torch.tensor(grad1).float() + model.conv2.module.weight.grad.data = torch.tensor(grad2).float() + optimizer.step() + + mask1 = pruner.calc_mask(model.conv1) + mask2 = pruner.calc_mask(model.conv2) + assert all(torch.sum(mask1['weight_mask'], (1, 2, 3)).numpy() == np.array([0., 25., 25., 25., 25.])) + assert all(torch.sum(mask2['weight_mask'], (1, 2, 3)).numpy() == np.array([125., 125., 125., 125., 0., 0., 0., 0., 0., 0., ])) + def test_torch_QAT_quantizer(self): model = TorchModel() config_list = [{ From 39cd591bdb81c763e0f87192f9f4e635d7791ee7 Mon Sep 17 00:00:00 2001 From: chicm-ms <38930155+chicm-ms@users.noreply.github.com> Date: Fri, 27 Mar 2020 10:34:05 +0800 Subject: [PATCH 02/12] Fix integration tests (#2237) --- examples/model_compress/model_prune_torch.py | 6 ++-- examples/model_compress/model_speedup.py | 2 +- examples/trials/mnist-pytorch/mnist.py | 31 ++++++++------------ test/config/examples/mnist-pytorch.yml | 4 +-- test/config/integration_tests.yml | 19 ++++++------ test/config/pr_tests.yml | 19 ++++++------ test/nni_test/nnitest/validators.py | 4 +-- test/scripts/model_compression.sh | 24 +++++---------- 8 files changed, 47 insertions(+), 62 deletions(-) diff --git a/examples/model_compress/model_prune_torch.py b/examples/model_compress/model_prune_torch.py index 5402fec9de..5f79ba104a 100644 --- a/examples/model_compress/model_prune_torch.py +++ b/examples/model_compress/model_prune_torch.py @@ -53,13 +53,13 @@ 'op_types': ['Conv2d'] }] }, - 'l1': { + 'l1filter': { 'dataset_name': 'cifar10', 'model_name': 'vgg16', 'pruner_class': L1FilterPruner, 'config_list': [{ 'sparsity': 0.5, - 'op_types': ['default'], + 'op_types': ['Conv2d'], 'op_names': ['feature.0', 'feature.24', 'feature.27', 'feature.30', 'feature.34', 'feature.37'] }] }, @@ -69,7 +69,7 @@ 'pruner_class': ActivationMeanRankFilterPruner, 'config_list': [{ 'sparsity': 0.5, - 'op_types': ['default'], + 'op_types': ['Conv2d'], 'op_names': ['feature.0', 'feature.24', 'feature.27', 'feature.30', 'feature.34', 'feature.37'] }] }, diff --git a/examples/model_compress/model_speedup.py b/examples/model_compress/model_speedup.py index f6ada91d96..2214fc137b 100644 --- a/examples/model_compress/model_speedup.py +++ b/examples/model_compress/model_speedup.py @@ -25,7 +25,7 @@ 'model_name': 'vgg16', 'device': 'cuda', 'input_shape': [64, 3, 32, 32], - 'masks_file': './checkpoints/mask_vgg16_cifar10_l1.pth' + 'masks_file': './checkpoints/mask_vgg16_cifar10_l1filter.pth' }, 'fpgm': { 'model_name': 'naive', diff --git a/examples/trials/mnist-pytorch/mnist.py b/examples/trials/mnist-pytorch/mnist.py index 4ced9c9c7e..ec9641af00 100644 --- a/examples/trials/mnist-pytorch/mnist.py +++ b/examples/trials/mnist-pytorch/mnist.py @@ -15,14 +15,6 @@ import torch.optim as optim from torchvision import datasets, transforms -# Temporary patch this example until the MNIST dataset download issue get resolved -# https://github.com/pytorch/vision/issues/1938 -import urllib - -opener = urllib.request.build_opener() -opener.addheaders = [('User-agent', 'Mozilla/5.0')] -urllib.request.install_opener(opener) - logger = logging.getLogger('mnist_AutoML') @@ -48,6 +40,8 @@ def forward(self, x): def train(args, model, device, train_loader, optimizer, epoch): model.train() for batch_idx, (data, target) in enumerate(train_loader): + if (args['batch_num'] is not None) and batch_idx >= args['batch_num']: + break data, target = data.to(device), target.to(device) optimizer.zero_grad() output = model(data) @@ -119,16 +113,15 @@ def main(args): train(args, model, device, train_loader, optimizer, epoch) test_acc = test(args, model, device, test_loader) - if epoch < args['epochs']: - # report intermediate result - nni.report_intermediate_result(test_acc) - logger.debug('test accuracy %g', test_acc) - logger.debug('Pipe send intermediate result done.') - else: - # report final result - nni.report_final_result(test_acc) - logger.debug('Final result is %g', test_acc) - logger.debug('Send final result done.') + # report intermediate result + nni.report_intermediate_result(test_acc) + logger.debug('test accuracy %g', test_acc) + logger.debug('Pipe send intermediate result done.') + + # report final result + nni.report_final_result(test_acc) + logger.debug('Final result is %g', test_acc) + logger.debug('Send final result done.') def get_params(): @@ -138,6 +131,7 @@ def get_params(): default='/tmp/pytorch/mnist/input_data', help="data directory") parser.add_argument('--batch_size', type=int, default=64, metavar='N', help='input batch size for training (default: 64)') + parser.add_argument("--batch_num", type=int, default=None) parser.add_argument("--hidden_size", type=int, default=512, metavar='N', help='hidden layer size (default: 512)') parser.add_argument('--lr', type=float, default=0.01, metavar='LR', @@ -165,6 +159,7 @@ def get_params(): logger.debug(tuner_params) params = vars(get_params()) params.update(tuner_params) + print(params) main(params) except Exception as exception: logger.exception(exception) diff --git a/test/config/examples/mnist-pytorch.yml b/test/config/examples/mnist-pytorch.yml index 6aab3fc80f..c62f0579d4 100644 --- a/test/config/examples/mnist-pytorch.yml +++ b/test/config/examples/mnist-pytorch.yml @@ -1,7 +1,7 @@ authorName: nni experimentName: default_test maxExecDuration: 15m -maxTrialNum: 4 +maxTrialNum: 2 trialConcurrency: 2 searchSpacePath: ./mnist_pytorch_search_space.json @@ -13,7 +13,7 @@ assessor: optimize_mode: maximize trial: codeDir: ../../../examples/trials/mnist-pytorch - command: python3 mnist.py --epochs 1 + command: python3 mnist.py --epochs 1 --batch_num 10 gpuNum: 0 useAnnotation: false diff --git a/test/config/integration_tests.yml b/test/config/integration_tests.yml index bd52a69596..61bfc3a796 100644 --- a/test/config/integration_tests.yml +++ b/test/config/integration_tests.yml @@ -77,16 +77,15 @@ testCases: kwargs: expected_result_file: expected_metrics.json -# to be enabled -#- name: metrics-dict -# configFile: test/config/metrics_test/config_dict_metrics.yml -# config: -# maxTrialNum: 1 -# trialConcurrency: 1 -# validator: -# class: MetricsValidator -# kwargs: -# expected_result_file: expected_metrics_dict.json +- name: metrics-dict + configFile: test/config/metrics_test/config_dict_metrics.yml + config: + maxTrialNum: 1 + trialConcurrency: 1 + validator: + class: MetricsValidator + kwargs: + expected_result_file: expected_metrics_dict.json - name: nnicli configFile: test/config/examples/sklearn-regression.yml diff --git a/test/config/pr_tests.yml b/test/config/pr_tests.yml index 365b038d0b..75be8bbc01 100644 --- a/test/config/pr_tests.yml +++ b/test/config/pr_tests.yml @@ -31,16 +31,15 @@ testCases: kwargs: expected_result_file: expected_metrics.json -# to be enabled -#- name: metrics-dict -# configFile: test/config/metrics_test/config_dict_metrics.yml -# config: -# maxTrialNum: 1 -# trialConcurrency: 1 -# validator: -# class: MetricsValidator -# kwargs: -# expected_result_file: expected_metrics_dict.json +- name: metrics-dict + configFile: test/config/metrics_test/config_dict_metrics.yml + config: + maxTrialNum: 1 + trialConcurrency: 1 + validator: + class: MetricsValidator + kwargs: + expected_result_file: expected_metrics_dict.json - name: nnicli configFile: test/config/examples/sklearn-regression.yml diff --git a/test/nni_test/nnitest/validators.py b/test/nni_test/nnitest/validators.py index 2fc43abe89..26faf0f423 100644 --- a/test/nni_test/nnitest/validators.py +++ b/test/nni_test/nnitest/validators.py @@ -35,8 +35,8 @@ def check_metrics(self, nni_source_dir, **kwargs): assert len(trial_final_result) == 1, 'there should be 1 final result' assert trial_final_result[0] == expected_metrics['final_result'] # encode dict/number into json string to compare them in set - assert set([json.dumps(x) for x in trial_intermediate_result]) \ - == set([json.dumps(x) for x in expected_metrics['intermediate_result']]) + assert set([json.dumps(x, sort_keys=True) for x in trial_intermediate_result]) \ + == set([json.dumps(x, sort_keys=True) for x in expected_metrics['intermediate_result']]) def get_metric_results(self, metrics): intermediate_result = {} diff --git a/test/scripts/model_compression.sh b/test/scripts/model_compression.sh index c3a7754fb5..f76a7064d6 100644 --- a/test/scripts/model_compression.sh +++ b/test/scripts/model_compression.sh @@ -6,22 +6,14 @@ echo "" echo "===========================Testing: pruning and speedup===========================" cd ${CWD}/../examples/model_compress -echo "testing slim pruning and speedup..." -python3 model_prune_torch.py --pruner_name slim --pretrain_epochs 1 --prune_epochs 1 -python3 model_speedup.py --example_name slim --model_checkpoint ./checkpoints/pruned_vgg19_cifar10_slim.pth \ - --masks_file ./checkpoints/mask_vgg19_cifar10_slim.pth - -echo "testing l1 pruning and speedup..." -python3 model_prune_torch.py --pruner_name l1 --pretrain_epochs 1 --prune_epochs 1 -python3 model_speedup.py --example_name l1filter --model_checkpoint ./checkpoints/pruned_vgg16_cifar10_l1.pth \ - --masks_file ./checkpoints/mask_vgg16_cifar10_l1.pth - -echo "testing apoz pruning and speedup..." -python3 model_prune_torch.py --pruner_name apoz --pretrain_epochs 1 --prune_epochs 1 -python3 model_speedup.py --example_name apoz --model_checkpoint ./checkpoints/pruned_vgg16_cifar10_apoz.pth \ - --masks_file ./checkpoints/mask_vgg16_cifar10_apoz.pth - -for name in level fpgm mean_activation +for name in fpgm slim l1filter apoz +do + echo "testing $name pruning and speedup..." + python3 model_prune_torch.py --pruner_name $name --pretrain_epochs 1 --prune_epochs 1 + python3 model_speedup.py --example_name $name +done + +for name in level mean_activation do echo "testing $name pruning..." python3 model_prune_torch.py --pruner_name $name --pretrain_epochs 1 --prune_epochs 1 From 9efce4fe901142b8637504e92f8206d1c1923325 Mon Sep 17 00:00:00 2001 From: SparkSnail Date: Fri, 27 Mar 2020 16:53:36 +0800 Subject: [PATCH 03/12] Support tensorboard 2.x command (#2242) --- tools/nni_cmd/common_utils.py | 8 ++++++++ tools/nni_cmd/tensorboard_utils.py | 5 +++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/tools/nni_cmd/common_utils.py b/tools/nni_cmd/common_utils.py index d7c57d40bc..56dfc7af29 100644 --- a/tools/nni_cmd/common_utils.py +++ b/tools/nni_cmd/common_utils.py @@ -76,6 +76,14 @@ def get_python_dir(sitepackages_path): else: return str(Path(sitepackages_path).parents[2]) +def check_tensorboard_version(): + try: + import tensorboard + return tensorboard.__version__ + except: + print_error('import tensorboard error!') + exit(1) + def get_nni_installation_path(): ''' Find nni lib from the following locations in order Return nni root directory if it exists diff --git a/tools/nni_cmd/tensorboard_utils.py b/tools/nni_cmd/tensorboard_utils.py index 60d589083a..5d3101605c 100644 --- a/tools/nni_cmd/tensorboard_utils.py +++ b/tools/nni_cmd/tensorboard_utils.py @@ -10,7 +10,7 @@ from .config_utils import Config, Experiments from .url_utils import trial_jobs_url, get_local_urls from .constants import COLOR_GREEN_FORMAT, REST_TIME_OUT -from .common_utils import print_normal, print_error, detect_process, detect_port +from .common_utils import print_normal, print_error, detect_process, detect_port, check_tensorboard_version from .nnictl_utils import check_experiment_id, check_experiment_id from .ssh_utils import create_ssh_sftp_client, copy_remote_directory_to_local @@ -77,7 +77,8 @@ def start_tensorboard_process(args, nni_config, path_list, temp_nni_path): exit(1) with open(os.path.join(temp_nni_path, 'tensorboard_stdout'), 'a+') as stdout_file, \ open(os.path.join(temp_nni_path, 'tensorboard_stderr'), 'a+') as stderr_file: - cmds = ['tensorboard', '--logdir', format_tensorboard_log_path(path_list), '--port', str(args.port)] + log_dir_cmd = '--logdir_spec' if check_tensorboard_version() >= '2.0' else '--logdir' + cmds = ['tensorboard', log_dir_cmd, format_tensorboard_log_path(path_list), '--port', str(args.port)] tensorboard_process = Popen(cmds, stdout=stdout_file, stderr=stderr_file) url_list = get_local_urls(args.port) print_normal(COLOR_GREEN_FORMAT % 'Start tensorboard success!\n' + 'Tensorboard urls: ' + ' '.join(url_list)) From 5c8cb25893de12c8c67f9c42fbfa446cf75e8e98 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Mar 2020 17:00:05 +0800 Subject: [PATCH 04/12] Bump acorn from 6.4.0 to 6.4.1 in /src/webui (#2203) Bumps [acorn](https://github.com/acornjs/acorn) from 6.4.0 to 6.4.1. - [Release notes](https://github.com/acornjs/acorn/releases) - [Commits](https://github.com/acornjs/acorn/compare/6.4.0...6.4.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/webui/yarn.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/webui/yarn.lock b/src/webui/yarn.lock index 32927c153d..89702e04a6 100644 --- a/src/webui/yarn.lock +++ b/src/webui/yarn.lock @@ -1518,8 +1518,8 @@ acorn-jsx@^5.0.0: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384" acorn@^6.0.5, acorn@^6.0.7: - version "6.4.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.0.tgz#b659d2ffbafa24baf5db1cdbb2c94a983ecd2784" + version "6.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" address@1.1.2, address@^1.0.1: version "1.1.2" From 6e6299084b5626b15bda09f882f30033baaa5ebc Mon Sep 17 00:00:00 2001 From: QuanluZhang Date: Fri, 27 Mar 2020 21:07:43 +0800 Subject: [PATCH 05/12] [BUG] finding leaf modules (#2241) --- .../compression/speedup/torch/compressor.py | 59 +++++++++---------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/src/sdk/pynni/nni/compression/speedup/torch/compressor.py b/src/sdk/pynni/nni/compression/speedup/torch/compressor.py index c594aa2ff8..363eb714da 100644 --- a/src/sdk/pynni/nni/compression/speedup/torch/compressor.py +++ b/src/sdk/pynni/nni/compression/speedup/torch/compressor.py @@ -229,42 +229,39 @@ def _extract_leaf_modules(self, graph): list a list of scope name of all the leaf modules """ - pieces = [] # each element is a dict + class SNode: + def __init__(self, name): + self.sname = name + self.childs = {} + + root = None for node in graph.nodes(): scope_name = node.scopeName() if scope_name == '': continue segs = scope_name.split('/') - segs_len = len(segs) - # increase the length of `pieces` if not enough - for _ in range(segs_len - len(pieces)): - pieces.append({}) - # process internal segments of the scope name - # 'L' means leaf segment - # 'I' means internal segment - # internal segment can replace leaf segment at the same position of `pieces` - for i, seg in enumerate(segs[:-1]): - seg_name_dict = pieces[i] - if seg in seg_name_dict: - if seg_name_dict[seg][0] == 'L': - seg_name_dict[seg] = ('I', node) - else: - seg_name_dict[seg] = ('I', node) - # process the leaf segment of the scope name - last_segs_dict = pieces[len(segs) - 1] - if not segs[-1] in last_segs_dict: - last_segs_dict[segs[-1]] = ('L', node) - # traverse `pieces` to obtain all the leaf modules which are labeled with 'L' - leaf_modules = [] - for piece in pieces: - for _, value in piece.items(): - if value[0] == 'L': - assert value[1].scopeName() not in leaf_modules - # if this is a leaf module, the last segment of its scope name - # must be in pattern `xxx[xxx]` - if value[1].scopeName()[-1] == ']': - leaf_modules.append(value[1].scopeName()) - return leaf_modules + if root is None: + root = SNode(segs[0]) + curr = root + for seg in segs[1:]: + if not seg in curr.childs: + curr.childs[seg] = SNode(seg) + curr = curr.childs[seg] + + leaf_nodes = [] + def traverse_tree(node, scope_name): + if scope_name == '': + sn = node.sname + else: + sn = scope_name + '/' + node.sname + if not node.childs: + if node.sname[-1] == ']': + leaf_nodes.append(sn) + else: + for key in node.childs: + traverse_tree(node.childs[key], sn) + traverse_tree(root, '') + return leaf_nodes def _build_graph(self): """ From 116fd9adaca1141aa1b189ca53faf98c23339a43 Mon Sep 17 00:00:00 2001 From: Yuge Zhang Date: Fri, 27 Mar 2020 21:59:55 +0800 Subject: [PATCH 06/12] Integration tests for NAS (#2231) * add nas it * fix issue with nnictl and test nas first * change python2 to python3 * move test order --- examples/nas/classic_nas/mnist.py | 2 +- examples/nas/darts/search.py | 3 ++- examples/nas/enas/search.py | 5 ++-- examples/nas/pdarts/search.py | 15 +++++++---- test/pipelines/pipelines-it-local.yml | 4 +++ test/scripts/nas.sh | 37 +++++++++++++++++++++++++++ 6 files changed, 57 insertions(+), 9 deletions(-) create mode 100644 test/scripts/nas.sh diff --git a/examples/nas/classic_nas/mnist.py b/examples/nas/classic_nas/mnist.py index 55c9601cd4..629e04ca28 100644 --- a/examples/nas/classic_nas/mnist.py +++ b/examples/nas/classic_nas/mnist.py @@ -149,7 +149,7 @@ def get_params(): # Training settings parser = argparse.ArgumentParser(description='PyTorch MNIST Example') parser.add_argument("--data_dir", type=str, - default='/tmp/tensorflow/mnist/input_data', help="data directory") + default='./data', help="data directory") parser.add_argument('--batch_size', type=int, default=64, metavar='N', help='input batch size for training (default: 64)') parser.add_argument("--hidden_size", type=int, default=512, metavar='N', diff --git a/examples/nas/darts/search.py b/examples/nas/darts/search.py index 9800c349b3..691e6c71a9 100644 --- a/examples/nas/darts/search.py +++ b/examples/nas/darts/search.py @@ -22,12 +22,13 @@ parser.add_argument("--batch-size", default=64, type=int) parser.add_argument("--log-frequency", default=10, type=int) parser.add_argument("--epochs", default=50, type=int) + parser.add_argument("--channels", default=16, type=int) parser.add_argument("--unrolled", default=False, action="store_true") args = parser.parse_args() dataset_train, dataset_valid = datasets.get_dataset("cifar10") - model = CNN(32, 3, 16, 10, args.layers) + model = CNN(32, 3, args.channels, 10, args.layers) criterion = nn.CrossEntropyLoss() optim = torch.optim.SGD(model.parameters(), 0.025, momentum=0.9, weight_decay=3.0E-4) diff --git a/examples/nas/enas/search.py b/examples/nas/enas/search.py index 38096a3fe6..44c870bb00 100644 --- a/examples/nas/enas/search.py +++ b/examples/nas/enas/search.py @@ -24,16 +24,17 @@ parser.add_argument("--batch-size", default=128, type=int) parser.add_argument("--log-frequency", default=10, type=int) parser.add_argument("--search-for", choices=["macro", "micro"], default="macro") + parser.add_argument("--epochs", default=None, type=int, help="Number of epochs (default: macro 310, micro 150)") args = parser.parse_args() dataset_train, dataset_valid = datasets.get_dataset("cifar10") if args.search_for == "macro": model = GeneralNetwork() - num_epochs = 310 + num_epochs = args.epochs or 310 mutator = None elif args.search_for == "micro": model = MicroNetwork(num_layers=6, out_channels=20, num_nodes=5, dropout_rate=0.1, use_aux_heads=True) - num_epochs = 150 + num_epochs = args.epochs or 150 mutator = enas.EnasMutator(model, tanh_constant=1.1, cell_exit_extra_step=True) else: raise AssertionError diff --git a/examples/nas/pdarts/search.py b/examples/nas/pdarts/search.py index 4e5fdfad98..9f3fea234f 100644 --- a/examples/nas/pdarts/search.py +++ b/examples/nas/pdarts/search.py @@ -25,23 +25,28 @@ if __name__ == "__main__": parser = ArgumentParser("pdarts") - parser.add_argument('--add_layers', action='append', - default=[0, 6, 12], help='add layers') - parser.add_argument('--dropped_ops', action='append', - default=[3, 2, 1], help='drop ops') + parser.add_argument('--add_layers', action='append', type=int, + help='add layers, default: [0, 6, 12]') + parser.add_argument('--dropped_ops', action='append', type=int, + help='drop ops, default: [3, 2, 1]') parser.add_argument("--nodes", default=4, type=int) parser.add_argument("--init_layers", default=5, type=int) + parser.add_argument("--channels", default=16, type=int) parser.add_argument("--batch-size", default=64, type=int) parser.add_argument("--log-frequency", default=1, type=int) parser.add_argument("--epochs", default=50, type=int) parser.add_argument("--unrolled", default=False, action="store_true") args = parser.parse_args() + if args.add_layers is None: + args.add_layers = [0, 6, 12] + if args.dropped_ops is None: + args.dropped_ops = [3, 2, 1] logger.info("loading data") dataset_train, dataset_valid = datasets.get_dataset("cifar10") def model_creator(layers): - model = CNN(32, 3, 16, 10, layers, n_nodes=args.nodes) + model = CNN(32, 3, args.channels, 10, layers, n_nodes=args.nodes) criterion = nn.CrossEntropyLoss() optim = torch.optim.SGD(model.parameters(), 0.025, momentum=0.9, weight_decay=3.0E-4) diff --git a/test/pipelines/pipelines-it-local.yml b/test/pipelines/pipelines-it-local.yml index 941dcaedb8..4606795274 100644 --- a/test/pipelines/pipelines-it-local.yml +++ b/test/pipelines/pipelines-it-local.yml @@ -26,6 +26,10 @@ jobs: cd test PATH=$HOME/.local/bin:$PATH python3 nni_test/nnitest/run_tests.py --config config/integration_tests.yml --ts local displayName: 'Integration test' + - script: | + cd test + PATH=$HOME/.local/bin:$PATH source scripts/nas.sh + displayName: 'NAS test' - script: | cd test source scripts/model_compression.sh diff --git a/test/scripts/nas.sh b/test/scripts/nas.sh new file mode 100644 index 0000000000..ca4d3fd1cc --- /dev/null +++ b/test/scripts/nas.sh @@ -0,0 +1,37 @@ +#!/bin/bash +set -e +CWD=${PWD} + +echo "" +echo "===========================Testing: NAS===========================" +EXAMPLE_DIR=${CWD}/../examples/nas + +echo "testing classic nas..." +cd $EXAMPLE_DIR/classic_nas +SEARCH_SPACE_JSON=nni_auto_gen_search_space.json +if [ -f $SEARCH_SPACE_JSON ]; then + rm $SEARCH_SPACE_JSON +fi +nnictl ss_gen -t "python3 mnist.py" +if [ ! -f $SEARCH_SPACE_JSON ]; then + echo "Search space file not found!" + exit 1 +fi + +echo "testing darts..." +cd $EXAMPLE_DIR/darts +python3 search.py --epochs 1 --channels 2 --layers 4 +python3 retrain.py --arc-checkpoint ./checkpoints/epoch_0.json --layers 4 --epochs 1 + +echo "testing enas..." +cd $EXAMPLE_DIR/enas +python3 search.py --search-for macro --epochs 1 +python3 search.py --search-for micro --epochs 1 + +echo "testing naive..." +cd $EXAMPLE_DIR/naive +python3 train.py + +echo "testing pdarts..." +cd $EXAMPLE_DIR/pdarts +python3 search.py --epochs 1 --channels 4 --nodes 2 --log-frequency 10 --add_layers 0 --add_layers 1 --dropped_ops 3 --dropped_ops 3 From 7215fdd301949a198cfc719b80aa5b760028255d Mon Sep 17 00:00:00 2001 From: Chi Song <27178119+squirrelsc@users.noreply.github.com> Date: Sat, 28 Mar 2020 07:51:35 +0800 Subject: [PATCH 07/12] Chinese translation (#2074) --- README_zh_CN.md | 50 +++---- docs/zh_CN/AdvancedFeature/MultiPhase.md | 2 + docs/zh_CN/Assessor/BuiltinAssessor.md | 10 +- docs/zh_CN/Assessor/CurvefittingAssessor.md | 14 +- docs/zh_CN/Assessor/MedianstopAssessor.md | 2 +- docs/zh_CN/Compressor/Framework.md | 102 +++++++++++++++ docs/zh_CN/Compressor/ModelSpeedup.md | 96 ++++++++++++++ docs/zh_CN/Compressor/Overview.md | 12 +- docs/zh_CN/Compressor/Pruner.md | 39 ++++++ docs/zh_CN/Compressor/QuickStart.md | 4 +- docs/zh_CN/NAS/Advanced.md | 6 +- docs/zh_CN/NAS/CDARTS.md | 4 - docs/zh_CN/NAS/DARTS.md | 6 +- docs/zh_CN/NAS/ENAS.md | 4 - docs/zh_CN/NAS/NasGuide.md | 16 +-- docs/zh_CN/NAS/NasReference.md | 109 ++++++++++++++++ docs/zh_CN/NAS/Overview.md | 18 +-- docs/zh_CN/NAS/Proxylessnas.md | 2 +- docs/zh_CN/NAS/QuickStart.md | 8 +- docs/zh_CN/NAS/SPOS.md | 6 - docs/zh_CN/Overview.md | 24 ++-- docs/zh_CN/Release.md | 76 ++++++++--- docs/zh_CN/TrainingService/DLTSMode.md | 49 +++++++ .../TrainingService/RemoteMachineMode.md | 4 +- docs/zh_CN/TrialExample/Trials.md | 14 +- docs/zh_CN/Tuner/BatchTuner.md | 2 +- docs/zh_CN/Tuner/BohbAdvisor.md | 32 ++--- docs/zh_CN/Tuner/BuiltinTuner.md | 122 +++++++++--------- docs/zh_CN/Tuner/EvolutionTuner.md | 2 +- docs/zh_CN/Tuner/GPTuner.md | 2 +- docs/zh_CN/Tuner/HyperbandAdvisor.md | 10 +- docs/zh_CN/Tuner/HyperoptTuner.md | 8 +- docs/zh_CN/Tuner/MetisTuner.md | 10 +- docs/zh_CN/Tuner/NetworkmorphismTuner.md | 22 ++-- docs/zh_CN/Tuner/PPOTuner.md | 4 +- docs/zh_CN/Tutorial/HowToUseDocker.md | 68 +++++----- docs/zh_CN/Tutorial/InstallationLinux.md | 6 +- docs/zh_CN/Tutorial/InstallationWin.md | 10 +- docs/zh_CN/Tutorial/QuickStart.md | 28 ++-- docs/zh_CN/Tutorial/WebUI.md | 14 +- docs/zh_CN/autotune_ref.md | 80 ++++++++++++ docs/zh_CN/builtin_assessor.rst | 8 +- docs/zh_CN/conf.py | 2 +- docs/zh_CN/hpo_advanced.rst | 2 + docs/zh_CN/hyperparameter_tune.rst | 4 +- docs/zh_CN/model_compression.rst | 1 + docs/zh_CN/pruners.rst | 16 +++ docs/zh_CN/quantizers.rst | 11 ++ docs/zh_CN/sdk_reference.rst | 74 +---------- docs/zh_CN/training_services.rst | 1 + 50 files changed, 848 insertions(+), 368 deletions(-) create mode 100644 docs/zh_CN/Compressor/Framework.md create mode 100644 docs/zh_CN/Compressor/ModelSpeedup.md create mode 100644 docs/zh_CN/NAS/NasReference.md create mode 100644 docs/zh_CN/TrainingService/DLTSMode.md create mode 100644 docs/zh_CN/autotune_ref.md create mode 100644 docs/zh_CN/pruners.rst create mode 100644 docs/zh_CN/quantizers.rst diff --git a/README_zh_CN.md b/README_zh_CN.md index ec9f505b5b..b3d9ecff56 100644 --- a/README_zh_CN.md +++ b/README_zh_CN.md @@ -19,7 +19,7 @@ NNI 管理自动机器学习 (AutoML) 的 Experiment,**调度运行**由调优 * 想要更容易**实现或试验新的自动机器学习算法**的研究员或数据科学家,包括:超参调优算法,神经网络搜索算法以及模型压缩算法。 * 在机器学习平台中**支持自动机器学习**。 -### **NNI v1.3 已发布!  [](#nni-released-reminder)** +### **NNI v1.4 已发布!  [](#nni-released-reminder)** ## **NNI 功能一览** @@ -100,31 +100,33 @@ NNI 提供命令行工具以及友好的 WebUI 来管理训练的 Experiment。 启发式搜索 贝叶斯优化 +
  • GP Tuner
  • + 基于强化学习 神经网络架构搜索 - 模型压缩
      @@ -148,7 +150,7 @@ NNI 提供命令行工具以及友好的 WebUI 来管理训练的 Experiment。 提前终止算法 @@ -219,7 +221,7 @@ Linux 和 macOS 下 NNI 系统需求[参考这里](https://nni.readthedocs.io/zh * 如果遇到任何权限问题,可添加 `--user` 在用户目录中安装 NNI。 * 目前,Windows 上的 NNI 支持本机,远程和 OpenPAI 模式。 强烈推荐使用 Anaconda 或 Miniconda 在 Windows 上安装 NNI。 -* 如果遇到如 `Segmentation fault` 等错误参考[常见问题](docs/zh_CN/Tutorial/FAQ.md)。 Windows 上的 FAQ 参考[在 Windows 上使用 NNI](docs/zh_CN/Tutorial/NniOnWindows.md)。 +* 如果遇到如 `Segmentation fault` 等错误参考[常见问题](docs/zh_CN/Tutorial/FAQ.md)。 Windows 上的 FAQ 参考[在 Windows 上使用 NNI](docs/zh_CN/Tutorial/InstallationWin.md#faq)。 ### **验证安装** @@ -228,7 +230,7 @@ Linux 和 macOS 下 NNI 系统需求[参考这里](https://nni.readthedocs.io/zh * 通过克隆源代码下载示例。 ```bash - git clone -b v1.3 https://github.com/Microsoft/nni.git + git clone -b v1.4 https://github.com/Microsoft/nni.git ``` * 运行 MNIST 示例。 @@ -283,9 +285,9 @@ You can use these commands to get more information about the experiment ## **文档** -* 要了解 NNI,请阅读 [NNI 概述](https://nni.readthedocs.io/zh/latest/Overview.html)。 -* 要熟悉如何使用 NNI,请阅读[文档](https://nni.readthedocs.io/zh/latest/index.html)。 -* 要安装 NNI,请参阅[安装 NNI](docs/zh_CN/Tutorial/Installation.md)。 +* 要了解 NNI,请阅读 [NNI 概述](https://nni.readthedocs.io/zh/latest/Overview.html)。 +* 要熟悉如何使用 NNI,请阅读[文档](https://nni.readthedocs.io/zh/latest/index.html)。 +* 要安装并使用 NNI,参考[安装指南](https://nni.readthedocs.io/zh/latest/installation.html)。 ## **贡献** @@ -303,22 +305,20 @@ You can use these commands to get more information about the experiment * 如果有使用上的问题,可先查看[常见问题解答](https://github.com/microsoft/nni/blob/master/docs/zh_CN/Tutorial/FAQ.md)。如果没能解决问题,可通过 [Gitter](https://gitter.im/Microsoft/nni?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 联系 NNI 开发团队或在 GitHub 上 [报告问题](https://github.com/microsoft/nni/issues/new/choose)。 * [自定义 Tuner](docs/zh_CN/Tuner/CustomizeTuner.md) * [实现定制的训练平台](docs/zh_CN/TrainingService/HowToImplementTrainingService.md) -* [在 NNI 上实现新的 NAS Trainer](https://github.com/microsoft/nni/blob/master/docs/zh_CN/NAS/NasInterface.md#implement-a-new-nas-trainer-on-nni) +* [在 NNI 上实现新的 NAS Trainer](docs/zh_CN/NAS/Advanced.md) * [自定义 Advisor](docs/zh_CN/Tuner/CustomizeAdvisor.md) ## **其它代码库和参考** 经作者许可的一些 NNI 用法示例和相关文档。 -* ### **外部代码库** - +* ### **外部代码库** ### * 在 NNI 中运行 [ENAS](examples/tuners/enas_nni/README_zh_CN.md) - * 在 NNI 中运行 [神经网络架构结构搜索](examples/trials/nas_cifar10/README_zh_CN.md) - * [NNI 中的自动特征工程](examples/feature_engineering/auto-feature-engineering/README_zh_CN.md) + * 在 NNI 中运行 [神经网络架构结构搜索](examples/trials/nas_cifar10/README_zh_CN.md) + * [NNI 中的自动特征工程](examples/feature_engineering/auto-feature-engineering/README_zh_CN.md) * 使用 NNI 的 [矩阵分解超参调优](https://github.com/microsoft/recommenders/blob/master/notebooks/04_model_select_and_optimize/nni_surprise_svd.ipynb) * [scikit-nni](https://github.com/ksachdeva/scikit-nni) 使用 NNI 为 scikit-learn 开发的超参搜索。 -* ### **相关文章** - +* ### **相关文章** ### * [超参数优化的对比](docs/zh_CN/CommunitySharings/HpoComparision.md) * [神经网络结构搜索的对比](docs/zh_CN/CommunitySharings/NasComparision.md) * [并行化顺序算法:TPE](docs/zh_CN/CommunitySharings/ParallelizingTpeSearch.md) diff --git a/docs/zh_CN/AdvancedFeature/MultiPhase.md b/docs/zh_CN/AdvancedFeature/MultiPhase.md index 2d07709234..4b0a560180 100644 --- a/docs/zh_CN/AdvancedFeature/MultiPhase.md +++ b/docs/zh_CN/AdvancedFeature/MultiPhase.md @@ -1,3 +1,5 @@ +# 多阶段 + ## 多阶段 Experiment 通常,每个 Trial 任务只需要从 Tuner 获取一个配置(超参等),然后使用这个配置执行并报告结果,然后退出。 但有时,一个 Trial 任务可能需要从 Tuner 请求多次配置。 这是一个非常有用的功能。 例如: diff --git a/docs/zh_CN/Assessor/BuiltinAssessor.md b/docs/zh_CN/Assessor/BuiltinAssessor.md index 69b0a0aa58..0ee9d9367a 100644 --- a/docs/zh_CN/Assessor/BuiltinAssessor.md +++ b/docs/zh_CN/Assessor/BuiltinAssessor.md @@ -1,10 +1,10 @@ # 内置 Assessor -NNI 提供了先进的调优算法,使用上也很简单。 下面是内置 Assessor 的介绍: +NNI 提供了先进的评估算法,使用上也很简单。 下面是内置 Assessor 的介绍。 -注意:点击 **Assessor 的名称**可看到 Assessor 的安装需求,建议的场景以及示例。 算法的详细说明在每个 Assessor 建议场景的最后。 +注意:点击 **Assessor 的名称**可了解每个 Assessor 的安装需求,建议的场景以及示例。 在每个 Assessor 建议场景最后,还有算法的详细说明。 -当前支持的 Assessor: +当前支持以下 Assessor: | Assessor | 算法简介 | | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -27,7 +27,7 @@ NNI 提供了先进的调优算法,使用上也很简单。 下面是内置 As 适用于各种性能曲线,可用到各种场景中来加速优化过程。 [详细说明](./MedianstopAssessor.md) -**参数** +**classArgs 要求:** * **optimize_mode** (*maximize 或 minimize, 可选, 默认值为 maximize*) - 如果为 'maximize', Assessor 会在结果小于期望值时**终止** Trial。 如果为 'minimize',Assessor 会在结果大于期望值时**终止** Trial。 * **start_step** (*int, 可选, 默认值为 0*) - 只有收到 start_step 个中间结果后,才开始判断是否一个 Trial 应该被终止。 @@ -55,7 +55,7 @@ assessor: 适用于各种性能曲线,可用到各种场景中来加速优化过程。 更好的地方是,它能处理并评估性能类似的曲线。 [详细说明](./CurvefittingAssessor.md) -**参数** +**classArgs 要求:** * **epoch_num** (*int, **必需***) - epoch 的总数。 需要此数据来决定需要预测点的总数。 * **optimize_mode** (*maximize 或 minimize, 可选, 默认值为 maximize*) - 如果为 'maximize', Assessor 会在结果小于期望值时**终止** Trial。 如果为 'minimize',Assessor 会在结果大于期望值时**终止** Trial。 diff --git a/docs/zh_CN/Assessor/CurvefittingAssessor.md b/docs/zh_CN/Assessor/CurvefittingAssessor.md index b713fba2b1..b24512ad0b 100644 --- a/docs/zh_CN/Assessor/CurvefittingAssessor.md +++ b/docs/zh_CN/Assessor/CurvefittingAssessor.md @@ -2,9 +2,9 @@ ## 1. 介绍 -Curve Fitting Assessor 是一个 LPA (learning, predicting, assessing,即学习、预测、评估) 的算法。 如果预测的Trial X 在 step S 比性能最好的 Trial 要差,就会提前终止它。 +Curve Fitting Assessor 是一个 LPA (learning, predicting, assessing,即学习、预测、评估) 的算法。 如果预测的 Trial X 在 step S 比性能最好的 Trial 要差,就会提前终止它。 -此算法中,使用了 12 条曲线来拟合学习曲线,从[参考论文](http://aad.informatik.uni-freiburg.de/papers/15-IJCAI-Extrapolation_of_Learning_Curves.pdf)中选择了大量的参数曲线模型。 学习曲线的形状与先验知识是一致的:都是典型的递增的、饱和的函数。 +此算法中采用了 12 种曲线来拟合学习曲线。 这组参数曲线模型来自于[参考论文](http://aad.informatik.uni-freiburg.de/papers/15-IJCAI-Extrapolation_of_Learning_Curves.pdf)。 学习曲线的形状与先验知识是一致的:都是典型的递增的、饱和的函数。 ![](../../img/curvefitting_learning_curve.PNG) @@ -12,7 +12,7 @@ Curve Fitting Assessor 是一个 LPA (learning, predicting, assessing,即学 ![](../../img/curvefitting_f_comb.gif) -合并后的参数向量 +合并后的新参数向量 ![](../../img/curvefitting_expression_xi.gif) @@ -22,11 +22,11 @@ Curve Fitting Assessor 是一个 LPA (learning, predicting, assessing,即学 具体来说,该算法有学习、预测和评估三个阶段。 -* 步骤 1:学习。 从当前 Trial 的历史中学习,并从贝叶斯角度决定 \xi 。 首先,使用最小二乘法 (由 `fit_theta` 实现) 来节省时间。 获得参数后,过滤曲线并移除异常点(由 `filter_curve` 实现)。 最后,使用 MCMC 采样方法 (由 `mcmc_sampling` 实现) 来调整每个曲线的权重。 至此,确定了 \xi 中的所有参数。 +* 步骤 1:学习。 从当前 Trial 的历史中学习,并从贝叶斯角度决定 \xi 。 首先,使用由 `fit_theta` 实现的最小二乘法。 获得参数后,过滤曲线并移除异常点(由 `filter_curve` 实现)。 最后,使用 MCMC 采样方法。 由 `mcmc_sampling` 实现,来调整每条曲线的权重。 至此,确定了 \xi 中的所有参数。 -* 步骤 2:预测。 用 \xi 和混合模型公式,在目标位置(例如 epoch 的总数)来计算期望的最终结果精度(由 `f_comb` 实现)。 +* 步骤 2:预测。 用 \xi 和混合模型公式,由 `f_comb` 实现了,在目标位置(例如 epoch 的总数)来计算期望的最终结果精度。 -* 步骤 3:如果拟合结果没有收敛,预测结果会是 `None`,并返回 `AssessResult.Good`,待下次有了更多精确信息后再次预测。 此外,会通过 `predict()` 函数获得正数。如果该值大于 __历史最好结果__ * `THRESHOLD`(默认为 0.95),则返回 `AssessResult.Good`,否则返回 `AssessResult.Bad`。 +* 步骤 3:如果拟合结果不收敛,则预测值将为 `None`。 在这种情况下,会返回 `AssessResult.Good/code> 来请求进一步的精度和预测信息。 此外,将从 predict()` 函数获得正确值。 如果该值大于历史最好结果 * `THRESHOLD`(默认为 0.95),则返回 `AssessResult.Good`,否则返回 `AssessResult.Bad`。 下图显示了此算法在 MNIST Trial 历史数据上结果。其中绿点表示 Assessor 获得的数据,蓝点表示将来,但未知的数据,红色线条是 Curve fitting Assessor 的预测曲线。 @@ -61,7 +61,7 @@ Curve Fitting Assessor 是一个 LPA (learning, predicting, assessing,即学 ## 3. 文件结构 -Assessor 有大量的文件、函数和类。 这里只简单介绍最重要的文件: +Assessor 有大量的文件、函数和类。 在这里,会简要描述其中一部分。 * `curvefunctions.py` 包含了所有函数表达式和默认参数。 * `modelfactory.py` 包括学习和预测部分,并实现了相应的计算部分。 diff --git a/docs/zh_CN/Assessor/MedianstopAssessor.md b/docs/zh_CN/Assessor/MedianstopAssessor.md index 805a08dc96..e079eea64d 100644 --- a/docs/zh_CN/Assessor/MedianstopAssessor.md +++ b/docs/zh_CN/Assessor/MedianstopAssessor.md @@ -2,4 +2,4 @@ ## Median Stop -Medianstop 是一种简单的提前终止 Trial 的策略,可参考[论文](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/46180.pdf)。 如果 Trial X 在步骤 S 的最好目标值低于所有已完成 Trial 前 S 个步骤目标平均值的中位数,这个 Trial 就会被提前停止。 \ No newline at end of file +Medianstop 是一种简单的提前终止策略,可参考[论文](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/46180.pdf)。 如果 Trial X 在步骤 S 的最好目标值低于所有已完成 Trial 前 S 个步骤目标平均值的中位数,这个 Trial 就会被提前停止。 \ No newline at end of file diff --git a/docs/zh_CN/Compressor/Framework.md b/docs/zh_CN/Compressor/Framework.md new file mode 100644 index 0000000000..83696b124f --- /dev/null +++ b/docs/zh_CN/Compressor/Framework.md @@ -0,0 +1,102 @@ +# 设计文档 + +## 概述 +模型压缩框架有两个主要组件: `Pruner` 和 `module 的包装`。 + +### Pruner +`Pruner` 用于: +1. 提供 `cal_mask` 方法来计算权重和偏差的掩码(mask)。 +2. 根据配置,用 `module 的包装`来替换原始的 module。 +3. 修改优化器,来在 `step` 方法被调用时,调用 `cal_mask`。 + +### module 的包装 +`module 的包装` 包含: +1. 原始的 module +2. `cal_mask` 使用的一些缓存 +3. 新的 forward 方法,用于在运行原始的 forward 方法前应用掩码。 + +使用 `module 包装`的原因: +1. 计算掩码所需要的 `cal_mask` 方法需要一些缓存,这些缓存需要注册在 `module 包装`里,这样就不需要修改原始的 module。 +2. 新的 `forward` 方法用来在原始 `forward` 调用前,将掩码应用到权重上。 + +## 工作原理 +基本的 Pruner 用法: +```python +configure_list = [{ + 'sparsity': 0.7, + 'op_types': ['BatchNorm2d'], +}] + +optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9, weight_decay=1e-4) +pruner = SlimPruner(model, configure_list, optimizer) +model = pruner.compress() +``` + +Pruner 接收模型,配置和优化器作为参数。 在 `__init__` 方法中,优化器的 `step` 方法会被一个会调用 `cal_mask` 的新的 `step` 方法替换。 同样,所有 module 都会检查它们是否被配置为需要剪枝。如果 module 需要被剪枝,就会用 `module 包装`来替换它。 之后,会返回新的模型和优化器,并进行训练。 `compress` 方法会计算默认的掩码。 + +## 实现新的剪枝算法 +要实现新的剪枝算法,需要继承 `Pruner` 来实现新的类,并重载 `cal_mask` 方法。 `cal_mask` 会被 `optimizer.step` 方法调用。 `Pruner` 基类提供了上述的基本功能,如替换 module 和优化器。 + +基础的 Pruner 如下所示: +```python +class NewPruner(Pruner): + def __init__(self, model, config_list, optimizer) + super().__init__(model, config_list, optimizer) + # 进行初始化 + + def calc_mask(self, wrapper, **kwargs): + # 计算 weight_mask + wrapper.weight_mask = weight_mask +``` +### 设置包装的属性 +有时,`cal_mask` 需要保存一些状态数据,可以像 PyTorch 的 module 一样,使用 `set_wrappers_attribute` API 来注册属性。 这些缓存会注册到 `module 包装`中。 用户可以通过 `module 包装`来直接访问这些缓存。 + +```python +class NewPruner(Pruner): + def __init__(self, model, config_list, optimizer): + super().__init__(model, config_list, optimizer) + self.set_wrappers_attribute("if_calculated", False) + + def calc_mask(self, wrapper): + # 计算 weight_mask + if wrapper.if_calculated: + pass + else: + wrapper.if_calculated = True + # 更新掩码 +``` + +### 在 forward 时收集数据 +有时,需要在 forward 方法中收集数据,例如,需要激活的平均值。 这时,可以为 module 增加定制的收集方法。 + +```python +class ActivationRankFilterPruner(Pruner): + def __init__(self, model, config_list, optimizer, activation='relu', statistics_batch_num=1): + super().__init__(model, config_list, optimizer) + self.set_wrappers_attribute("if_calculated", False) + self.set_wrappers_attribute("collected_activation", []) + self.statistics_batch_num = statistics_batch_num + + def collector(module_, input_, output): + if len(module_.collected_activation) < self.statistics_batch_num: + module_.collected_activation.append(self.activation(output.detach().cpu())) + self.add_activation_collector(collector) + assert activation in ['relu', 'relu6'] + if activation == 'relu': + self.activation = torch.nn.functional.relu + elif activation == 'relu6': + self.activation = torch.nn.functional.relu6 + else: + self.activation = None +``` +收集函数会在每次 forward 方法运行时调用。 + +还可这样来移除收集方法: +```python +collector_id = self.add_activation_collector(collector) +# ... +self.remove_activation_collector(collector_id) +``` + +### 多 GPU 支持 +在多 GPU 训练中,缓存和参数会在每次 `forward` 方法被调用时,复制到多个 GPU 上。 如果缓存和参数要在 `forward` 更新,就需要通过`原地`更新来提高效率。 因为 `cal_mask` 会在 `optimizer.step` 方法中的调用,会在 `forward` 方法后才被调用,且只会发生在单 GPU 上,因此它天然的就支持多 GPU 的情况。 \ No newline at end of file diff --git a/docs/zh_CN/Compressor/ModelSpeedup.md b/docs/zh_CN/Compressor/ModelSpeedup.md new file mode 100644 index 0000000000..821dd91321 --- /dev/null +++ b/docs/zh_CN/Compressor/ModelSpeedup.md @@ -0,0 +1,96 @@ +# 加速掩码的模型 + +*此功能还处于预览版。* + +## 介绍 + +剪枝算法通常都用权重掩码来模拟实际的剪枝。 掩码可以用来检查某个剪枝(或稀疏)算法的模型性能,但还没有真正加速。 模型加速才是模型剪枝的最终目标。因此提供了此工具,来帮助基于用户提供的掩码(掩码来自于剪枝算法),将已有模型转换成小模型。 + +有两种剪枝算法。 一种是细粒度的剪枝,不改变权重形状,和输入输出的张量。 稀疏内核会被用来加速细粒度剪枝的层。 另一类是粗粒度的剪枝(例如,通道),通常,权重形状,输入输出张量会有所改变。 要加速这类剪枝算法,不需要使用系数内核,只需要用更小的层来替换。 由于开源社区中对稀疏内核的支持还比较有限,当前仅支持粗粒度剪枝,会在将来再支持细粒度的剪枝算法。 + +## 设计和实现 + +为了加速模型,被剪枝的层应该被替换掉,要么为粗粒度掩码使用较小的层,要么用稀疏内核来替换细粒度的掩码。 粗粒度掩码通常会改变权重的形状,或输入输出张量,因此,应该通过形状推断,来检查是否其它未被剪枝的层由于形状变化而需要改变形状。 因此,在设计中,主要有两个步骤:第一,做形状推理,找出所有应该替换的模块;第二,替换模块。 第一步需要模型的拓扑(即连接),我们使用了 `jit.trace` 来获取 PyTorch 的模型图。 + +对于每个模块,要准备四个函数,三个用于形状推理,一个用于模块替换。 三个形状推理函数是:给定权重形状推断输入/输出形状,给定输入形状推断权重/输出形状,给定输出形状推断权重/输入形状。 模块替换功能返回一个较小的新创建的模块。 + +## 用法 + +```python +from nni.compression.speedup.torch import ModelSpeedup +# model: 要加速的模型 +# dummy_input: 模型的示输入,传给 `jit.trace` +# masks_file: 剪枝算法创建的掩码文件 +m_speedup = ModelSpeedup(model, dummy_input.to(device), masks_file) +m_speedup.speedup_model() +dummy_input = dummy_input.to(device) +start = time.time() +out = model(dummy_input) +print('elapsed time: ', time.time() - start) +``` +完整示例参考[这里](https://github.com/microsoft/nni/tree/master/examples/model_compress/model_speedup.py) + +注意:当前实现仅用于 torch 1.3.1 和 torchvision 0.4.2 + +## 局限性 + +由于每个模块需要 4 个函数用于形状推理和模块替换,因此工作量较大,当前仅实现了示例所需的函数。 如果要加速自己的模型,但当前不支持,欢迎贡献。 + +对于 PyTorch,仅提供了替换模块,如果是在 `forward` 中的函数,当前不支持。 一种解决方案是将函数变为 PyTorch 模块。 + +## 示例的加速结果 + +实验代码可在[这里](https://github.com/microsoft/nni/tree/master/examples/model_compress/model_speedup.py)找到。 + +### slim Pruner 示例 + +在一块 V100 GPU 上, 输入张量:`torch.randn(64, 3, 32, 32)` + +| 次数 | 掩码时延 | 加速后的时延 | +| -- | ------- | -------- | +| 1 | 0.01197 | 0.005107 | +| 2 | 0.02019 | 0.008769 | +| 4 | 0.02733 | 0.014809 | +| 8 | 0.04310 | 0.027441 | +| 16 | 0.07731 | 0.05008 | +| 32 | 0.14464 | 0.10027 | + +### fpgm Pruner 示例 + +在 CPU 上, 输入张量:`torch.randn(64, 1, 28, 28)`, 方差较大 + +| 次数 | 掩码时延 | 加速后的时延 | +| --- | ------- | -------- | +| 1 | 0.01383 | 0.01839 | +| 2 | 0.01167 | 0.003558 | +| 4 | 0.01636 | 0.01088 | +| 40 | 0.14412 | 0.08268 | +| 40 | 1.29385 | 0.14408 | +| 40 | 0.41035 | 0.46162 | +| 400 | 6.29020 | 5.82143 | + +### l1filter Pruner 示例 + +在一块 V100 GPU 上, 输入张量:`torch.randn(64, 3, 32, 32)` + +| 次数 | 掩码时延 | 加速后的时延 | +| -- | ------- | -------- | +| 1 | 0.01026 | 0.003677 | +| 2 | 0.01657 | 0.008161 | +| 4 | 0.02458 | 0.020018 | +| 8 | 0.03498 | 0.025504 | +| 16 | 0.06757 | 0.047523 | +| 32 | 0.10487 | 0.086442 | + +### APoZ Pruner 示例 + +在一块 V100 GPU 上, 输入张量:`torch.randn(64, 3, 32, 32)` + +| 次数 | 掩码时延 | 加速后的时延 | +| -- | ------- | -------- | +| 1 | 0.01389 | 0.004208 | +| 2 | 0.01628 | 0.008310 | +| 4 | 0.02521 | 0.014008 | +| 8 | 0.03386 | 0.023923 | +| 16 | 0.06042 | 0.046183 | +| 32 | 0.12421 | 0.087113 | diff --git a/docs/zh_CN/Compressor/Overview.md b/docs/zh_CN/Compressor/Overview.md index ae8722d5f6..6e29cbe5bf 100644 --- a/docs/zh_CN/Compressor/Overview.md +++ b/docs/zh_CN/Compressor/Overview.md @@ -1,9 +1,9 @@ # 使用 NNI 进行模型压缩 随着更多层和节点大型神经网络的使用,降低其存储和计算成本变得至关重要,尤其是对于某些实时应用程序。 模型压缩可用于解决此问题。 -我们很高兴的宣布,基于 NNI 的模型压缩工具发布了试用版本。该版本仍处于试验阶段,根据用户反馈会进行改进。 诚挚邀请您使用、反馈,或有更多贡献。 +我们很高兴的宣布,基于 NNI 的模型压缩工具发布了。该版本仍处于试验阶段,会根据用户反馈进行改进。 诚挚邀请您使用、反馈,或有更多贡献。 -NNI 提供了易于使用的工具包来帮助用户设计并使用压缩算法。 当前支持基于 PyTorch 的统一接口。 只需要添加几行代码即可压缩模型。 NNI 中也内置了一些流程的模型压缩算法。 用户还可以通过 NNI 强大的自动调参功能来找到最好的压缩后的模型,详见[自动模型压缩](./AutoCompression.md)。 另外,用户还能使用 NNI 的接口,轻松定制新的压缩算法,详见[教程](#customize-new-compression-algorithms)。 +NNI 提供了易于使用的工具包来帮助用户设计并使用压缩算法。 当前支持基于 PyTorch 的统一接口。 只需要添加几行代码即可压缩模型。 NNI 中也内置了一些流程的模型压缩算法。 用户还可以通过 NNI 强大的自动调参功能来找到最好的压缩后的模型,详见[自动模型压缩](./AutoCompression.md)。 另外,用户还能使用 NNI 的接口,轻松定制新的压缩算法,详见[教程](#customize-new-compression-algorithms)。 关于模型压缩框架如何工作的详情可参考[这里](./Framework.md)。 模型压缩方面的综述可参考:[Recent Advances in Efficient Computation of Deep Convolutional Neural Networks](https://arxiv.org/pdf/1802.00939.pdf)。 @@ -332,9 +332,9 @@ class YourQuantizer(Quantizer): 如果不定制 `QuantGrad`,默认的 backward 为 Straight-Through Estimator。 _即将推出_... -## **参考和反馈** +## 参考和反馈 * 在 GitHub 中[提交此功能的 Bug](https://github.com/microsoft/nni/issues/new?template=bug-report.md); * 在 GitHub 中[提交新功能或改进请求](https://github.com/microsoft/nni/issues/new?template=enhancement.md); -* 了解 NNI 中[特征工程的更多信息](https://github.com/microsoft/nni/blob/master/docs/zh_CN/FeatureEngineering/Overview.md); -* 了解 NNI 中[ NAS 的更多信息](https://github.com/microsoft/nni/blob/master/docs/zh_CN/NAS/Overview.md); -* 了解如何[使用 NNI 进行超参数调优](https://github.com/microsoft/nni/blob/master/docs/zh_CN/Tuner/BuiltinTuner.md); +* 了解更多关于 [NNI 中的特征工程](../FeatureEngineering/Overview.md); +* 了解更多关于 [NNI 中的 NAS](../NAS/Overview.md); +* 了解更多关于 [NNI 中的超参调优](../Tuner/BuiltinTuner.md); diff --git a/docs/zh_CN/Compressor/Pruner.md b/docs/zh_CN/Compressor/Pruner.md index d564109149..a8610ce0a2 100644 --- a/docs/zh_CN/Compressor/Pruner.md +++ b/docs/zh_CN/Compressor/Pruner.md @@ -13,6 +13,8 @@ NNI Compressor 中的 Pruner * [具有激活等级的 Filter Pruners](#activationrankfilterpruner) * [APoZ Rank Pruner](#activationapozrankfilterpruner) * [Activation Mean Rank Pruner](#activationmeanrankfilterpruner) +* [具有梯度等级的 Filter Pruners](#gradientrankfilterpruner) + * [Taylor FO On Weight Pruner](#taylorfoweightfilterpruner) ## Level Pruner @@ -335,3 +337,40 @@ pruner.compress() - **sparsity:** 卷积过滤器要修剪的百分比。 - **op_types:** 在 ActivationMeanRankFilterPruner 中仅支持 Conv2d。 + + +## GradientRankFilterPruner + +GradientRankFilterPruner 是一系列的 Pruner,在卷积层梯度上,用最小的重要性标准修剪过滤器,来达到预设的网络稀疏度。 + +### TaylorFOWeightFilterPruner + +其实现为一次性 Pruner,会根据权重的一阶泰勒展开式来对卷积层进行剪枝。 过滤器的估计重要性在论文 [Importance Estimation for Neural Network Pruning](http://jankautz.com/publications/Importance4NNPruning_CVPR19.pdf) 中有定义。 本文中提到的其他修剪标准将在以后的版本中支持。 +> + +![](../../img/importance_estimation_sum.png) + +#### 用法 + +PyTorch 代码 + +```python +from nni.compression.torch import TaylorFOWeightFilterPruner +config_list = [{ + 'sparsity': 0.5, + 'op_types': ['Conv2d'] +}] +pruner = TaylorFOWeightFilterPruner(model, config_list, optimizer) +pruner.compress() +``` + +查看示例进一步了解 + +#### GradientWeightSumFilterPruner 的用户配置 + +- **sparsity:** 卷积过滤器要修剪的百分比。 +- **op_types:** 当前 TaylorFOWeightFilterPruner 中仅支持 Conv2d。 + + + +  \ No newline at end of file diff --git a/docs/zh_CN/Compressor/QuickStart.md b/docs/zh_CN/Compressor/QuickStart.md index 412de82a2a..2e417e1270 100644 --- a/docs/zh_CN/Compressor/QuickStart.md +++ b/docs/zh_CN/Compressor/QuickStart.md @@ -1,6 +1,6 @@ # 模型压缩快速入门 -NNI 为模型压缩提供了非常简单的 API。 压缩包括剪枝和量化算法。 它们的用法相同,这里通过 slim Pruner 来演示如何使用。 完整示例在[这里](https://github.com/microsoft/nni/blob/master/examples/model_compress/slim_torch_cifar10.py) +NNI 为模型压缩提供了非常简单的 API。 压缩包括剪枝和量化算法。 它们的用法相同,这里通过 slim Pruner 来演示如何使用。 ## 编写配置 @@ -34,6 +34,8 @@ model = pruner.compress() pruner.export_model(model_path='pruned_vgg19_cifar10.pth', mask_path='mask_vgg19_cifar10.pth') ``` +模型的完整示例代码在[这里](https://github.com/microsoft/nni/blob/master/examples/model_compress/model_prune_torch.py) + ## 加速模型 掩码实际上并不能加速模型。 要基于导出的掩码,来对模型加速,因此,NNI 提供了 API 来加速模型。 在模型上调用 `apply_compression_results` 后,模型会变得更小,推理延迟也会减小。 diff --git a/docs/zh_CN/NAS/Advanced.md b/docs/zh_CN/NAS/Advanced.md index af6a1939d5..204b331043 100644 --- a/docs/zh_CN/NAS/Advanced.md +++ b/docs/zh_CN/NAS/Advanced.md @@ -31,7 +31,7 @@ for _ in range(epochs): 最后,Mutator 会提供叫做 `mutator.export()` 的方法来将模型的架构参数作为 dict 导出。 注意,当前 dict 是从 Mutable 键值到选择张量的映射。 为了存储到 JSON,用户需要将张量显式的转换为 Python 的 list。 -同时,NNI 提供了工具,能更容易地实现 Trainer。 参考 [Trainer](./NasReference.md#trainers) 了解详情。 +同时,NNI 提供了工具,能更容易地实现 Trainer。 参考 [Trainer](./NasReference.md) 了解详情。 ## 实现新的 Mutator @@ -94,7 +94,7 @@ class RandomMutator(Mutator): ## 实现分布式 NAS Tuner -在学习编写 One-Shot NAS Tuner前,应先了解如何写出通用的 Tuner。 阅读[自定义 Tuner](../Tuner/CustomizeTuner.md) 的教程。 +在学习编写分布式 NAS Tuner前,应先了解如何写出通用的 Tuner。 阅读[自定义 Tuner](../Tuner/CustomizeTuner.md) 的教程。 当调用 "[nnictl ss_gen](../Tutorial/Nnictl.md)" 时,会生成下面这样的搜索空间文件: @@ -129,4 +129,4 @@ class RandomMutator(Mutator): } ``` -和普通超参优化 Tuner 类似,通过 `generate_parameters` 来发送。 参考 [SPOS](./SPOS.md) 示例代码。 \ No newline at end of file +和普通超参优化 Tuner 类似,通过 `generate_parameters` 来发送。 参考 [SPOS](./SPOS.md) 示例代码。 diff --git a/docs/zh_CN/NAS/CDARTS.md b/docs/zh_CN/NAS/CDARTS.md index b4347127e7..05da020bef 100644 --- a/docs/zh_CN/NAS/CDARTS.md +++ b/docs/zh_CN/NAS/CDARTS.md @@ -46,16 +46,12 @@ bash run_retrain_cifar.sh .. autoclass:: nni.nas.pytorch.cdarts.CdartsTrainer :members: - .. automethod:: __init__ - .. autoclass:: nni.nas.pytorch.cdarts.RegularizedDartsMutator :members: .. autoclass:: nni.nas.pytorch.cdarts.DartsDiscreteMutator :members: - .. automethod:: __init__ - .. autoclass:: nni.nas.pytorch.cdarts.RegularizedMutatorParallel :members: ``` diff --git a/docs/zh_CN/NAS/DARTS.md b/docs/zh_CN/NAS/DARTS.md index c092070dc4..7a9a9613b4 100644 --- a/docs/zh_CN/NAS/DARTS.md +++ b/docs/zh_CN/NAS/DARTS.md @@ -43,8 +43,10 @@ python3 retrain.py --arc-checkpoint ./checkpoints/epoch_49.json .. autoclass:: nni.nas.pytorch.darts.DartsTrainer :members: - .. automethod:: __init__ - .. autoclass:: nni.nas.pytorch.darts.DartsMutator :members: ``` + +## 局限性 + +* DARTS 不支持 DataParallel,若要支持 DistributedDataParallel,则需要定制。 diff --git a/docs/zh_CN/NAS/ENAS.md b/docs/zh_CN/NAS/ENAS.md index dcfa3ec060..abebdcde8e 100644 --- a/docs/zh_CN/NAS/ENAS.md +++ b/docs/zh_CN/NAS/ENAS.md @@ -37,10 +37,6 @@ python3 search.py -h .. autoclass:: nni.nas.pytorch.enas.EnasTrainer :members: - .. automethod:: __init__ - .. autoclass:: nni.nas.pytorch.enas.EnasMutator :members: - - .. automethod:: __init__ ``` diff --git a/docs/zh_CN/NAS/NasGuide.md b/docs/zh_CN/NAS/NasGuide.md index 61fa9230b6..350f884b01 100644 --- a/docs/zh_CN/NAS/NasGuide.md +++ b/docs/zh_CN/NAS/NasGuide.md @@ -70,17 +70,17 @@ Input Choice 可被视为可调用的模块,它接收张量数组,输出其 `LayerChoice` 和 `InputChoice` 都是 **Mutable**。 Mutable 表示 "可变化的"。 与传统深度学习层、模型都是固定的不同,使用 Mutable 的模块,是一组可能选择的模型。 -用户可为每个 Mutable 指定 **key**。 默认情况下,NNI 会分配全局唯一的,但如果需要共享 Choice(例如,两个 `LayerChoice` 有同样的候选操作,希望共享同样的 Choice。即,如果一个选择了第 i 个操作,第二个也要选择第 i 个操作),那么就应该给它们相同的 key。 key 标记了此 Choice,并会在存储的检查点中使用。 如果要增加导出架构的可读性,可为每个 Mutable 的 key 指派名称。 高级用法参考 [Mutable](./NasReference.md#mutables)。 +用户可为每个 Mutable 指定 **key**。 默认情况下,NNI 会分配全局唯一的,但如果需要共享 Choice(例如,两个 `LayerChoice` 有同样的候选操作,希望共享同样的 Choice。即,如果一个选择了第 i 个操作,第二个也要选择第 i 个操作),那么就应该给它们相同的 key。 key 标记了此 Choice,并会在存储的检查点中使用。 如果要增加导出架构的可读性,可为每个 Mutable 的 key 指派名称。 高级用法参考 [Mutable](./NasReference.md)。 ## 使用搜索算法 -搜索空间的探索方式和 Trial 生成方式不同,至少有两种不同的方法用来搜索。 一种是分布式运行 NAS,可从头枚举运行所有架构。或者利用更多高级功能,如 [SMASH](https://arxiv.org/abs/1708.05344), [ENAS](https://arxiv.org/abs/1802.03268), [DARTS](https://arxiv.org/abs/1808.05377), [FBNet](https://arxiv.org/abs/1812.03443), [ProxylessNAS](https://arxiv.org/abs/1812.00332), [SPOS](https://arxiv.org/abs/1904.00420), [Single-Path NAS](https://arxiv.org/abs/1904.02877), [Understanding One-shot](http://proceedings.mlr.press/v80/bender18a) 以及 [GDAS](https://arxiv.org/abs/1910.04465)。 由于很多不同架构搜索起来成本较高,另一类方法,即 One-Shot NAS,在搜索空间中,构建包含有所有候选网络的超网络,每一步中选择一个或几个子网络来训练。 +除了使用搜索空间外,还可以通过其他两种方式进行搜索。 一种是分布式运行 NAS,可从头枚举运行所有架构。或者利用更多高级功能,如 [SMASH](https://arxiv.org/abs/1708.05344), [ENAS](https://arxiv.org/abs/1802.03268), [DARTS](https://arxiv.org/abs/1808.05377), [FBNet](https://arxiv.org/abs/1812.03443), [ProxylessNAS](https://arxiv.org/abs/1812.00332), [SPOS](https://arxiv.org/abs/1904.00420), [Single-Path NAS](https://arxiv.org/abs/1904.02877), [Understanding One-shot](http://proceedings.mlr.press/v80/bender18a) 以及 [GDAS](https://arxiv.org/abs/1910.04465)。 由于很多不同架构搜索起来成本较高,另一类方法,即 One-Shot NAS,在搜索空间中,构建包含有所有候选网络的超网络,每一步中选择一个或几个子网络来训练。 -当前,NNI 支持数种 One-Shot 方法。 例如,`DartsTrainer` 使用 SGD 来交替训练架构和模型权重,`ENASTrainer` [使用 Controller 来训练模型](https://arxiv.org/abs/1802.03268)。 新的、更高效的 NAS Trainer 在研究界不断的涌现出来。 +当前,NNI 支持数种 One-Shot 方法。 例如,`DartsTrainer` 使用 SGD 来交替训练架构和模型权重,`ENASTrainer` [使用 Controller 来训练模型](https://arxiv.org/abs/1802.03268)。 新的、更高效的 NAS Trainer 在研究界不断的涌现出来,NNI 会在将来的版本中实现其中的一部分。 ### One-Shot NAS -每个 One-Shot NAS 都实现了 Trainer,可在每种算法说明中找到详细信息。 这是如何使用 `EnasTrainer` 的简单示例。 +每个 One-Shot NAS 算法都实现了 Trainer,可在每种算法说明中找到详细信息。 这是如何使用 `EnasTrainer` 的简单示例。 ```python # 此处与普通模型训练相同 @@ -118,7 +118,7 @@ trainer.export(file="model_dir/final_architecture.json") # 将最终架构导 用户可直接通过 `python3 train.py` 开始训练,不需要使用 `nnictl`。 训练完成后,可通过 `trainer.export()` 导出找到的最好的模型。 -通常,Trainer 会有些可定制的参数,例如,损失函数,指标函数,优化器,以及数据集。 这些功能可满足大部分需求,NNI 会尽力让内置 Trainer 能够处理更多的模型、任务和数据集。 但无法保证全面的支持。 例如,一些 Trainer 假设必须是分类任务;一些 Trainer 对 "Epoch" 的定义有所不同(例如,ENAS 的 epoch 表示一部分子步骤加上一些 Controller 的步骤);大多数 Trainer 不支持分布式训练,不会将模型通过 `DataParallel` 或 `DistributedDataParallel` 进行包装。 如果通过试用,想要在定制的应用中使用 Trainer,可能需要[自定义 Trainer](#extend-the-ability-of-one-shot-trainers)。 +通常,Trainer 会提供一些可以自定义的参数。 如,损失函数,指标函数,优化器以及数据集。 这些功能可满足大部分需求,NNI 会尽力让内置 Trainer 能够处理更多的模型、任务和数据集。 但无法保证全面的支持。 例如,一些 Trainer 假设必须是分类任务;一些 Trainer 对 "Epoch" 的定义有所不同(例如,ENAS 的 epoch 表示一部分子步骤加上一些 Controller 的步骤);大多数 Trainer 不支持分布式训练,不会将模型通过 `DataParallel` 或 `DistributedDataParallel` 进行包装。 如果通过试用,想要在定制的应用中使用 Trainer,可能需要[自定义 Trainer](#extend-the-ability-of-one-shot-trainers)。 ### 分布式 NAS @@ -136,7 +136,7 @@ acc = test(model) # 测试训练好的模型 nni.report_final_result(acc) # 报告所选架构的性能 ``` -搜索空间应生成,并发送给 Tuner。 通过 NNI NAS API,搜索空间嵌入在用户代码中,需要通过 "[nnictl ss_gen](../Tutorial/Nnictl.md)" 来生成搜索空间文件。 然后,将生成的搜索空间文件路径填入 `config.yml` 的 `searchSpacePath`。 `config.yml` 中的其它字段参考[教程](../Tutorial/QuickStart.md)。 +搜索空间应生成,并发送给 Tuner。 与 NNI NAS API 一样,搜索空间嵌入到了用户代码中。 用户可以使用 "[nnictl ss_gen](../Tutorial/Nnictl.md)" 以生成搜索空间文件。 然后,将生成的搜索空间文件路径填入 `config.yml` 的 `searchSpacePath`。 `config.yml` 中的其它字段参考[教程](../Tutorial/QuickStart.md)。 可使用 [NNI Tuner](../Tuner/BuiltinTuner.md) 来搜索。 目前,只有 PPO Tuner 支持 NAS 搜索空间。 @@ -162,6 +162,6 @@ JSON 文件是从 Mutable key 到 Choice 的表示。 例如: } ``` -应用后,模型会被固定,并准备好进行最终训练。 虽然它可能包含了更多的参数,但可作为单个模型来使用。 这各有利弊。 好的方面是,可以在搜索阶段直接读取来自超网络的检查点,并开始重新训练。 但是,这也造成模型有荣誉的参数,在计算模型所包含的参数数量时,可能会不准确。 更多深层次原因和解决方法可参考 [Trainer](./NasReference.md#retrain)。 +应用后,模型会被固定,并准备好进行最终训练。 虽然它可能包含了更多的参数,但可作为单个模型来使用。 这各有利弊。 好的方面是,可以在搜索阶段直接读取来自超网络的检查点,并开始重新训练。 但是,这也造成模型有冗余的参数,在计算模型所包含的参数数量时,可能会不准确。 更多深层次原因和解决方法可参考 [Trainer](./NasReference.md)。 -也可参考 [DARTS](./DARTS.md) 的重新训练代码。 \ No newline at end of file +也可参考 [DARTS](./DARTS.md) 的重新训练代码。 diff --git a/docs/zh_CN/NAS/NasReference.md b/docs/zh_CN/NAS/NasReference.md new file mode 100644 index 0000000000..15150c5ed2 --- /dev/null +++ b/docs/zh_CN/NAS/NasReference.md @@ -0,0 +1,109 @@ +# NAS 参考 + +```eval_rst +.. contents:: +``` + +## Mutable + +```eval_rst +.. autoclass:: nni.nas.pytorch.mutables.Mutable + :members: + +.. autoclass:: nni.nas.pytorch.mutables.LayerChoice + :members: + +.. autoclass:: nni.nas.pytorch.mutables.InputChoice + :members: + +.. autoclass:: nni.nas.pytorch.mutables.MutableScope + :members: +``` + +### 工具 + +```eval_rst +.. autofunction:: nni.nas.pytorch.utils.global_mutable_counting +``` + +## Mutator + +```eval_rst +.. autoclass:: nni.nas.pytorch.base_mutator.BaseMutator + :members: + +.. autoclass:: nni.nas.pytorch.mutator.Mutator + :members: +``` + +### Random Mutator + +```eval_rst +.. autoclass:: nni.nas.pytorch.random.RandomMutator + :members: +``` + +### 工具 + +```eval_rst +.. autoclass:: nni.nas.pytorch.utils.StructuredMutableTreeNode + :members: +``` + +## Trainer + +### Trainer + +```eval_rst +.. autoclass:: nni.nas.pytorch.base_trainer.BaseTrainer + :members: + +.. autoclass:: nni.nas.pytorch.trainer.Trainer + :members: +``` + +### 重新训练 + +```eval_rst +.. autofunction:: nni.nas.pytorch.fixed.apply_fixed_architecture + +.. autoclass:: nni.nas.pytorch.fixed.FixedArchitecture + :members: +``` + +### 分布式 NAS + +```eval_rst +.. autofunction:: nni.nas.pytorch.classic_nas.get_and_apply_next_architecture + +.. autoclass:: nni.nas.pytorch.classic_nas.mutator.ClassicMutator + :members: +``` + +### 回调 + +```eval_rst +.. autoclass:: nni.nas.pytorch.callbacks.Callback + :members: + +.. autoclass:: nni.nas.pytorch.callbacks.LRSchedulerCallback + :members: + +.. autoclass:: nni.nas.pytorch.callbacks.ArchitectureCheckpoint + :members: + +.. autoclass:: nni.nas.pytorch.callbacks.ModelCheckpoint + :members: +``` + +### 工具 + +```eval_rst +.. autoclass:: nni.nas.pytorch.utils.AverageMeterGroup + :members: + +.. autoclass:: nni.nas.pytorch.utils.AverageMeter + :members: + +.. autofunction:: nni.nas.pytorch.utils.to_device +``` diff --git a/docs/zh_CN/NAS/Overview.md b/docs/zh_CN/NAS/Overview.md index c96c1ea6da..8b7ded917a 100644 --- a/docs/zh_CN/NAS/Overview.md +++ b/docs/zh_CN/NAS/Overview.md @@ -1,16 +1,16 @@ # 神经网络结构搜索在 NNI 上的应用 -自动化的神经网络架构(NAS)搜索在寻找更好的模型方面发挥着越来越重要的作用。 最近的研究工作证明了自动化 NAS 的可行性,并发现了一些超越手动设计和调整的模型。 代表算法有 [NASNet](https://arxiv.org/abs/1707.07012),[ENAS](https://arxiv.org/abs/1802.03268),[DARTS](https://arxiv.org/abs/1806.09055),[Network Morphism](https://arxiv.org/abs/1806.10282),以及 [Evolution](https://arxiv.org/abs/1703.01041) 等。 新的算法还在不断涌现。 +自动化的神经网络架构(NAS)搜索在寻找更好的模型方面发挥着越来越重要的作用。 最近的研究工作证明了自动化 NAS 的可行性,并发现了一些超越手动设计和调整的模型。 代表算法有 [NASNet](https://arxiv.org/abs/1707.07012),[ENAS](https://arxiv.org/abs/1802.03268),[DARTS](https://arxiv.org/abs/1806.09055),[Network Morphism](https://arxiv.org/abs/1806.10282),以及 [Evolution](https://arxiv.org/abs/1703.01041) 等。 此外,新的创新不断涌现。 但是,要实现NAS算法需要花费大量的精力,并且很难在新算法中重用现有算法的代码。 为了促进 NAS 创新(例如,设计、实现新的 NAS 模型,并列比较不同的 NAS 模型),易于使用且灵活的编程接口非常重要。 以此为动力,NNI 的目标是提供统一的体系结构,以加速NAS上的创新,并将最新的算法更快地应用于现实世界中的问题上。 -通过统一的接口,有两种方法来使用神经网络架构搜索。 [一种](#supported-one-shot-nas-algorithms)称为 one-shot NAS,基于搜索空间构建了一个超级网络,并使用 one-shot 训练来生成性能良好的子模型。 [第二种](#支持的分布式-nas-算法)是传统的搜索方法,搜索空间中每个子模型作为独立的 Trial 运行,将性能结果发给 Tuner,由 Tuner 来生成新的子模型。 +通过统一的接口,有两种方法来使用神经网络架构搜索。 [一种](#supported-one-shot-nas-algorithms)称为 one-shot NAS,基于搜索空间构建了一个超级网络,并使用 one-shot 训练来生成性能良好的子模型。 [第二种](#支持的分布式-nas-算法)是传统的搜索方法,搜索空间中每个子模型作为独立的 Trial 运行。 将性能结果发给 Tuner,由 Tuner 来生成新的子模型。 ## 支持的 One-shot NAS 算法 -NNI 现在支持以下 NAS 算法,并且正在添加更多算法。 用户可以重现算法或在自己的数据集上使用它。 鼓励用户使用 [NNI API](#use-nni-api) 实现其它算法,以使更多人受益。 +NNI 目前支持下面列出的 NAS 算法,并且正在添加更多算法。 用户可以重现算法或在自己的数据集上使用它。 鼓励用户使用 [NNI API](#use-nni-api) 实现其它算法,以使更多人受益。 | 名称 | 算法简介 | | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -32,12 +32,12 @@ One-shot 算法**不需要 nnictl,可单独运行**。 只实现了 PyTorch ## 支持的分布式 NAS 算法 -| 名称 | 算法简介 | -| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [SPOS](SPOS.md) | 论文 [Single Path One-Shot Neural Architecture Search with Uniform Sampling](https://arxiv.org/abs/1904.00420) 构造了一个采用统一的路径采样方法来训练简化的超网络,并使用进化算法来提高搜索神经网络结构的效率。 | +| 名称 | 算法简介 | +| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [SPOS 的第二阶段](SPOS.md) | 论文 [Single Path One-Shot Neural Architecture Search with Uniform Sampling](https://arxiv.org/abs/1904.00420) 构造了一个采用统一的路径采样方法来训练简化的超网络,并使用进化算法来提高搜索神经网络结构的效率。 | -```eval_rst -.. 注意:SPOS 是一种两阶段算法,第一阶段是 one-shot,第二阶段是分布式的,利用第一阶段的结果作为检查点。 +```eval_rst +.. 注意:SPOS 是一种两阶段算法,第一阶段是 one-shot,第二阶段是分布式的,利用第一阶段的结果作为检查点。 ``` ## 使用 NNI API @@ -52,4 +52,4 @@ One-shot 算法**不需要 nnictl,可单独运行**。 只实现了 PyTorch ## 参考和反馈 * 在 GitHub 中[提交此功能的 Bug](https://github.com/microsoft/nni/issues/new?template=bug-report.md); -* 在 GitHub 中[提交新功能或改进请求](https://github.com/microsoft/nni/issues/new?template=enhancement.md)。 \ No newline at end of file +* 在 GitHub 中[提交新功能或改进请求](https://github.com/microsoft/nni/issues/new?template=enhancement.md)。 diff --git a/docs/zh_CN/NAS/Proxylessnas.md b/docs/zh_CN/NAS/Proxylessnas.md index c5ca64b05d..8456e6a19e 100644 --- a/docs/zh_CN/NAS/Proxylessnas.md +++ b/docs/zh_CN/NAS/Proxylessnas.md @@ -60,4 +60,4 @@ ProxylessNasMutator 还实现了可变量的前向逻辑 (即, LayerChoice)。 ## 重现结果 -进行中... +为了重现结果,首先运行了搜索过程。我们发现虽然需要跑许多 Epoch,但选择的架构会在头几个 Epoch 就收敛了。 这可能是由超参或实现造成的,正在分析中。 找到架构的测试精度为 top1: 72.31, top5: 90.26。 diff --git a/docs/zh_CN/NAS/QuickStart.md b/docs/zh_CN/NAS/QuickStart.md index 2097df999f..f8b5e56b3e 100644 --- a/docs/zh_CN/NAS/QuickStart.md +++ b/docs/zh_CN/NAS/QuickStart.md @@ -29,11 +29,11 @@ class Net(nn.Module): self.fc3 = nn.Linear(84, 10) ``` -有关 `LayerChoice` 和 `InputChoice` 的详细描述可参考[指南](NasGuide.md)。 +有关 `LayerChoice` 和 `InputChoice` 的详细描述可参考[ NAS 指南](NasGuide.md)。 ## 选择 NAS Trainer -实例化模型后,需要通过 NAS Trainer 来训练模型。 不同的 Trainer 会使用不同的方法来从指定的神经网络模块中搜索出最好的。 NNI 提供了流行的 NAS 训练方法,如 DARTS,ENAS。 以下以 `DartsTrainer` 为例。 在 Trainer 实例化后,调用`trainer.train()` 开始搜索。 +实例化模型后,需要通过 NAS Trainer 来训练模型。 不同的 Trainer 会使用不同的方法来从指定的神经网络模块中搜索出最好的。 NNI 提供了几种流行的 NAS 训练方法,如 DARTS,ENAS。 以下以 `DartsTrainer` 为例。 在 Trainer 实例化后,调用`trainer.train()` 开始搜索。 ```python trainer = DartsTrainer(net, @@ -54,11 +54,11 @@ trainer.train() ## NAS 可视化 -正在开发 NAS 的可视化,并将很快发布。 +正在研究 NAS 的可视化,并将很快发布此功能。 ## 重新训练导出的最佳模型 -重新训练找到(导出)的网络架构非常容易。 第一步,实例化上面定义的模型。 第二步,在模型上调用 `apply_fixed_architecture`。 然后,模型会变为找到(导出)的模型,可通过正常的训练方法来训练此模型。 +重新训练找到(导出)的网络架构非常容易。 第一步,实例化上面定义的模型。 第二步,在模型上调用 `apply_fixed_architecture`。 然后,此模型会作为找到的(导出的)模型。 之后,可以使用传统方法来训练此模型。 ```python model = Net() diff --git a/docs/zh_CN/NAS/SPOS.md b/docs/zh_CN/NAS/SPOS.md index 4251a6a9fe..3266442483 100644 --- a/docs/zh_CN/NAS/SPOS.md +++ b/docs/zh_CN/NAS/SPOS.md @@ -92,17 +92,11 @@ python scratch.py .. autoclass:: nni.nas.pytorch.spos.SPOSEvolution :members: - .. automethod:: __init__ - .. autoclass:: nni.nas.pytorch.spos.SPOSSupernetTrainer :members: - .. automethod:: __init__ - .. autoclass:: nni.nas.pytorch.spos.SPOSSupernetTrainingMutator :members: - - .. automethod:: __init__ ``` ## 已知的局限 diff --git a/docs/zh_CN/Overview.md b/docs/zh_CN/Overview.md index 72c3501e5d..9919227f0f 100644 --- a/docs/zh_CN/Overview.md +++ b/docs/zh_CN/Overview.md @@ -2,10 +2,10 @@ NNI (Neural Network Intelligence) 是一个工具包,可有效的帮助用户设计并调优机器学习模型的神经网络架构,复杂系统的参数(如超参)等。 NNI 的特性包括:易于使用,可扩展,灵活,高效。 -* **易于使用**:NNI 可通过 pip 安装。 只需要在代码中添加几行,就可以利用 NNI 来调优参数。 可使用命令行工具或 Web 界面来查看实验过程。 +* **易于使用**:NNI 可通过 pip 安装。 只需要在代码中添加几行,就可以利用 NNI 来调优参数。 可使用命令行工具或 Web 界面来查看 Experiment。 * **可扩展**:调优超参或网络结构通常需要大量的计算资源。NNI 在设计时就支持了多种不同的计算资源,如远程服务器组,训练平台(如:OpenPAI,Kubernetes),等等。 通过训练平台,可拥有同时运行数百个 Trial 的能力。 * **灵活**:除了内置的算法,NNI 中还可以轻松集成自定义的超参调优算法,神经网络架构搜索算法,提前终止算法等等。 还可以将 NNI 连接到更多的训练平台上,如云中的虚拟机集群,Kubernetes 服务等等。 此外,NNI 还可以连接到外部环境中的特殊应用和模型上。 -* **高效**:NNI 在系统及算法级别上不停的优化。 例如:通过 Trial 早期的反馈来加速调优过程。 +* **高效**:NNI 在系统及算法级别上不断地进行优化。 例如:通过早期的反馈来加速调优过程。 下图显示了 NNI 的体系结构。 @@ -15,17 +15,17 @@ NNI (Neural Network Intelligence) 是一个工具包,可有效的帮助用户 ## 主要概念 -* *Experiment(实验)*:实验是一次找到模型的最佳超参组合,或最好的神经网络架构的任务。 它由 Trial 和自动机器学习算法所组成。 +* *Experiment(实验)*: 表示一次任务,用来寻找模型的最佳超参组合,或最好的神经网络架构等。 它由 Trial 和自动机器学习算法所组成。 * *搜索空间*:是模型调优的范围。 例如,超参的取值范围。 -* *Configuration(配置)*:配置是来自搜索空间的一个参数实例,每个超参都会有一个特定的值。 +* *Configuration(配置)*:配置是来自搜索空间的实例,每个超参都会有特定的值。 -* *Trial*: Trial 是一次尝试,它会使用某组配置(例如,一组超参值,或者特定的神经网络架构)。 Trial 会基于提供的配置来运行。 +* *Trial*: 是一次独立的尝试,它会使用某组配置(例如,一组超参值,或者特定的神经网络架构)。 Trial 会基于提供的配置来运行。 -* *Tuner*: Tuner 是一个自动机器学习算法,会为下一个 Trial 生成新的配置。 新的 Trial 会使用这组配置来运行。 +* *Tuner(调优器)*: Tuner 个自动机器学习算法,会为下一个 Trial 生成新的配置。 新的 Trial 会使用这组配置来运行。 -* *Assessor*:Assessor 分析 Trial 的中间结果(例如,测试数据集上定期的精度),来确定 Trial 是否应该被提前终止。 +* *Assessor(评估器)*:分析 Trial 的中间结果(例如,定期评估数据集上的精度),来确定 Trial 是否应该被提前终止。 * *训练平台*:是 Trial 的执行环境。 根据 Experiment 的配置,可以是本机,远程服务器组,或其它大规模训练平台(如,OpenPAI,Kubernetes)。 @@ -53,25 +53,25 @@ NNI 还希望提供用于机器学习和深度学习的算法工具包,尤其 ### 超参调优 -这是 NNI 最核心、基本的功能,其中提供了许多流行的[自动调优算法](Tuner/BuiltinTuner.md) (即 Tuner) 以及 [提前终止算法](Assessor/BuiltinAssessor.md) (即 Assessor)。 可查看[快速入门](Tutorial/QuickStart.md)来调优模型或系统。 基本上通过以上三步,就能开始NNI Experiment。 +这是 NNI 最核心、基本的功能,其中提供了许多流行的[自动调优算法](Tuner/BuiltinTuner.md) (即 Tuner) 以及 [提前终止算法](Assessor/BuiltinAssessor.md) (即 Assessor)。 可查看[快速入门](Tutorial/QuickStart.md)来调优模型或系统。 基本上通过以上三步,就能开始 NNI Experiment。 ### 通用 NAS 框架 -此 NAS 框架可供用户轻松指定候选的神经体系结构,例如,可以为单个层指定多个候选操作(例如,可分离的 conv、扩张 conv),并指定可能的跳过连接。 NNI 将自动找到最佳候选。 另一方面,NAS 框架为其他类型的用户(如,NAS 算法研究人员)提供了简单的接口,以实现新的 NAS 算法。 详情及用法参考[这里](NAS/Overview.md)。 +此 NAS 框架可供用户轻松指定候选的神经体系结构,例如,可以为单个层指定多个候选操作(例如,可分离的 conv、扩张 conv),并指定可能的跳过连接。 NNI 将自动找到最佳候选。 另一方面,NAS 框架为其他类型的用户(如,NAS 算法研究人员)提供了简单的接口,以实现新的 NAS 算法。 NAS 详情及用法参考[这里](NAS/Overview.md)。 -NNI 通过 Trial SDK 支持多种 one-shot NAS 算法,如:ENAS、DARTS。 使用这些算法时,不需启动 NNI Experiment。 在 Trial 代码中加入算法,直接运行即可。 如果要调整算法中的超参数,或运行多个实例,可以使用 Tuner 并启动 NNI Experiment。 +NNI 通过 Trial SDK 支持多种 one-shot(一次性) NAS 算法,如:ENAS、DARTS。 使用这些算法时,不需启动 NNI Experiment。 在 Trial 代码中加入算法,直接运行即可。 如果要调整算法中的超参数,或运行多个实例,可以使用 Tuner 并启动 NNI Experiment。 除了 one-shot NAS 外,NAS 还能以 NNI 模式运行,其中每个候选的网络结构都作为独立 Trial 任务运行。 在此模式下,与超参调优类似,必须启动 NNI Experiment 并为 NAS 选择 Tuner。 ### 模型压缩 -NNI 上的模型压缩包括剪枝和量化算法。 这些算法通过 NNI Trial SDK 提供。 可以直接在 Trial 代码中使用,并在不启动 NNI Experiment 的情况下运行 Trial 代码。 详情及用法参考[这里](Compressor/Overview.md)。 +NNI 上的模型压缩包括剪枝和量化算法。 这些算法通过 NNI Trial SDK 提供。 可以直接在 Trial 代码中使用,并在不启动 NNI Experiment 的情况下运行 Trial 代码。 模型压缩的详细说明和算法可在[这里](Compressor/Overview.md)找到。 模型压缩中有不同的超参。 一种类型是在输入配置中的超参,例如,压缩算法的稀疏性、量化的位宽。 另一种类型是压缩算法的超参。 NNI 的超参调优可以自动找到最佳的压缩模型。 参考[简单示例](Compressor/AutoCompression.md)。 ### 自动特征工程 -自动特征工程,为下游任务找到最有效的特征。 详情及用法参考[这里](FeatureEngineering/Overview.md)。 通过 NNI Trial SDK 支持,不必创建 NNI Experiment。 只需在 Trial 代码中加入内置的自动特征工程算法,然后直接运行 Trial 代码。 +自动特征工程,为下游任务找到最有效的特征。 自动特征工程及其用法的详细说明可在[这里](FeatureEngineering/Overview.md)找到。 通过 NNI Trial SDK 支持,不必创建 NNI Experiment。 只需在 Trial 代码中加入内置的自动特征工程算法,然后直接运行 Trial 代码。 自动特征工程算法通常有一些超参。 如果要自动调整这些超参,可以利用 NNI 的超参数调优,即选择调优算法(即 Tuner)并启动 NNI Experiment。 diff --git a/docs/zh_CN/Release.md b/docs/zh_CN/Release.md index 1831171c82..ece38950bb 100644 --- a/docs/zh_CN/Release.md +++ b/docs/zh_CN/Release.md @@ -1,5 +1,51 @@ # 更改日志 +## 发布 1.4 - 2/19/2020 + +### 主要功能 + +#### 神经网络架构搜索 + +* 支持 [C-DARTS](https://github.com/microsoft/nni/blob/v1.4/docs/en_US/NAS/CDARTS.md) 算法,并增加对应[示例](https://github.com/microsoft/nni/tree/v1.4/examples/nas/cdarts)。 +* 初步支持 [ProxylessNAS](https://github.com/microsoft/nni/blob/v1.4/docs/en_US/NAS/Proxylessnas.md) 以及对应[示例](https://github.com/microsoft/nni/tree/v1.4/examples/nas/proxylessnas) +* 为 NAS 框架增加单元测试 + +#### 模型压缩 + +* 为压缩模型增加 DataParallel,并提供相应的 [示例](https://github.com/microsoft/nni/blob/v1.4/examples/model_compress/multi_gpu.py) +* 支持压缩模型的[加速](https://github.com/microsoft/nni/blob/v1.4/docs/en_US/Compressor/ModelSpeedup.md)(试用版) + +#### 训练平台 + +* 通过允许指定 OpenPAI 配置文件路径,来支持完整的 OpenPAI 配置 +* 为新的 OpenPAI 模式(又称,paiK8S)增加示例配置 YAML 文件 +* 支持删除远程模式下使用 sshkey 的 Experiment (感谢外部贡献者 @tyusr) + +#### Web 界面 + +* Web 界面重构:采用 fabric 框架 + +#### 其它 + +* 支持[在前台运行 NNI Experiment](https://github.com/microsoft/nni/blob/v1.4/docs/en_US/Tutorial/Nnictl.md#manage-an-experiment),即,`nnictl create/resume/view` 的 `--foreground` 参数 +* 支持取消 UNKNOWN 状态的 Trial。 +* 支持最大 50MB 的搜索空间文件 (感谢外部贡献者 @Sundrops) + +### 文档 + +* 改进 NNI readthedocs 的[目录索引结构](https://nni.readthedocs.io/en/latest/) +* 改进 [NAS 文档](https://github.com/microsoft/nni/blob/v1.4/docs/en_US/NAS/NasGuide.md) +* 改进[新的 OpenPAI 模式的文档](https://github.com/microsoft/nni/blob/v1.4/docs/en_US/TrainingService/PaiMode.md) +* 为 [NAS](https://github.com/microsoft/nni/blob/v1.4/docs/en_US/NAS/QuickStart.md) 和[模型压缩](https://github.com/microsoft/nni/blob/v1.4/docs/en_US/Compressor/QuickStart.md)增加入门指南 +* 改进支持 [EfficientNet](https://github.com/microsoft/nni/blob/v1.4/docs/en_US/TrialExample/EfficientNet.md) 的文档 + +### 修复的 Bug + +* 修复在指标数据和 JSON 格式中对 NaN 的支持 +* 修复搜索空间 `randint` 类型的 out-of-range Bug +* 修复模型压缩中导出 ONNX 模型时的错误张量设备的 Bug +* 修复新 OpenPAI 模式(又称,paiK8S)下,错误处理 nnimanagerIP 的 Bug + ## 发布 1.3 - 12/30/2019 ### 主要功能 @@ -12,10 +58,10 @@ * [知识蒸馏](https://github.com/microsoft/nni/blob/v1.3/docs/zh_CN/TrialExample/KDExample.md)算法和使用示例 * Pruners - * [L2Filter Pruner](https://github.com/microsoft/nni/blob/master/docs/zh_CN/Compressor/Pruner.md#l2filter-pruner) - * [ActivationAPoZRankFilterPruner](https://github.com/microsoft/nni/blob/master/docs/zh_CN/Compressor/Pruner.md#activationapozrankfilterpruner) - * [ActivationMeanRankFilterPruner](https://github.com/microsoft/nni/blob/master/docs/zh_CN/Compressor/Pruner.md#activationmeanrankfilterpruner) -* [BNN Quantizer](https://github.com/microsoft/nni/blob/v1.3/docs/zh_CN/Compressor/Quantizer.md#bnn-quantizer) + * [L2Filter Pruner](https://github.com/microsoft/nni/blob/v1.3/docs/en_US/Compressor/Pruner.md#3-l2filter-pruner) + * [ActivationAPoZRankFilterPruner](https://github.com/microsoft/nni/blob/v1.3/docs/en_US/Compressor/Pruner.md#1-activationapozrankfilterpruner) + * [ActivationMeanRankFilterPruner](https://github.com/microsoft/nni/blob/v1.3/docs/en_US/Compressor/Pruner.md#2-activationmeanrankfilterpruner) +* [BNN Quantizer](https://github.com/microsoft/nni/blob/v1.3/docs/en_US/Compressor/Quantizer.md#bnn-quantizer) #### 训练平台 @@ -43,12 +89,12 @@ ### 主要功能 -* [特征工程](https://github.com/microsoft/nni/blob/v1.2/docs/zh_CN/FeatureEngineering/Overview.md) +* [特征工程](https://github.com/microsoft/nni/blob/v1.2/docs/en_US/FeatureEngineering/Overview.md) - 新增特征工程接口 - 特征选择算法: [Gradient feature selector](https://github.com/microsoft/nni/blob/v1.2/docs/zh_CN/FeatureEngineering/GradientFeatureSelector.md) & [GBDT selector](https://github.com/microsoft/nni/blob/v1.2/docs/zh_CN/FeatureEngineering/GBDTSelector.md) - [特征工程示例](https://github.com/microsoft/nni/tree/v1.2/examples/feature_engineering) - 神经网络结构搜索在 NNI 上的应用 - - [新的 NAS 接口](https://github.com/microsoft/nni/blob/v1.2/docs/zh_CN/NAS/NasInterface.md) + - [新的 NAS 接口](https://github.com/microsoft/nni/blob/v1.2/docs/en_US/NAS/NasInterface.md) - NAS 算法: [ENAS](https://github.com/microsoft/nni/blob/v1.2/docs/zh_CN/NAS/Overview.md#enas), [DARTS](https://github.com/microsoft/nni/blob/v1.2/docs/zh_CN/NAS/Overview.md#darts), [P-DARTS](https://github.com/microsoft/nni/blob/v1.2/docs/zh_CN/NAS/Overview.md#p-darts) (PyTorch) - 经典模式下的 NAS(每次 Trial 独立运行) - 模型压缩 @@ -69,7 +115,7 @@ - 文档 - 改进了 NNI API 文档,增加了更多的 docstring。 -### Bug 修复 +### 修复的 Bug - 修复当失败的 Trial 没有指标时,表格的排序问题。 -Issue #1773 - 页面切换时,保留选择的(最大、最小)状态。 -PR#1710 @@ -81,7 +127,7 @@ ### 主要功能 * 新 Tuner: [PPO Tuner](https://github.com/microsoft/nni/blob/v1.1/docs/zh_CN/Tuner/PPOTuner.md) -* [查看已停止的 Experiment](https://github.com/microsoft/nni/blob/master/docs/zh_CN/Tutorial/Nnictl.md#view) +* [查看已停止的 Experiment](https://github.com/microsoft/nni/blob/v1.1/docs/en_US/Tutorial/Nnictl.md#view) * Tuner 可使用专门的 GPU 资源(参考[教程](https://github.com/microsoft/nni/blob/v1.1/docs/zh_CN/Tutorial/ExperimentConfig.md)中的 `gpuIndices` 了解详情) * 改进 WEB 界面 - Trial 详情页面可列出每个 Trial 的超参,以及开始结束时间(需要通过 "add column" 添加) @@ -137,7 +183,7 @@ + 添加配置示例 + [Web 界面描述改进](Tutorial/WebUI.md) -PR #1419 -### Bug 修复 +### 修复的 Bug * (Bug 修复)修复 0.9 版本中的链接 -Issue #1236 * (Bug 修复)自动完成脚本 @@ -158,7 +204,7 @@ ### 主要功能 -* 生成 NAS 编程接口 +* 通用 NAS 编程接口 * 为 NAS 接口添加 `enas-mode` 和 `oneshot-mode`:[PR #1201](https://github.com/microsoft/nni/pull/1201#issue-291094510) * [有 Matern 核的高斯 Tuner](Tuner/GPTuner.md) @@ -202,7 +248,7 @@ * 在已经运行非 NNI 任务的 GPU 上也能运行 Trial * 支持 Kubeflow v1beta2 操作符 * 支持 Kubeflow TFJob/PyTorchJob v1beta2 -* [通用 NAS 编程接口](https://github.com/microsoft/nni/blob/v0.8/docs/zh_CN/GeneralNasInterfaces.md) +* [通用 NAS 编程接口](https://github.com/microsoft/nni/blob/v0.8/docs/en_US/GeneralNasInterfaces.md) * 实现了 NAS 的编程接口,可通过 NNI Annotation 很容易的表达神经网络架构搜索空间 * 提供新命令 `nnictl trial codegen` 来调试 NAS 代码生成部分 * 提供 NAS 编程接口教程,NAS 在 MNIST 上的示例,用于 NAS 的可定制的随机 Tuner @@ -246,7 +292,7 @@ * 为 nnictl 提供更友好的错误消息 * 为 YAML 文件格式错误提供更有意义的错误信息 -### Bug 修复 +### 修复的 Bug * 运行 nnictl stop 的异步 Dispatcher 模式时,无法杀掉所有的 Python 线程 * nnictl --version 不能在 make dev-install 下使用 @@ -265,7 +311,7 @@ * 使日志集合功能可配置 * 为所有 Trial 增加中间结果的视图 -### Bug 修复 +### 修复的 Bug * [为 OpenPAI 增加 shmMB 配置](https://github.com/microsoft/nni/issues/842) * 修复在指标为 dict 时,无法显示任何结果的 Bug。 @@ -290,7 +336,7 @@ * 修复了在某些极端条件下,不能正确存储任务的取消状态。 * 修复在使用 SMAC Tuner 时,解析搜索空间的错误。 -* 修复 CIFAR-10 示例中的 broken pipe 问题。 +* 修复 CIFAR-10 样例中的 broken pipe 问题。 * 为本地训练和 NNI 管理器添加单元测试。 * 为远程服务器、OpenPAI 和 Kubeflow 训练平台在 Azure 中增加集成测试。 * 在 OpenPAI 客户端中支持 Pylon 路径。 @@ -438,7 +484,7 @@ ### 新示例 -* 公开的 NNI Docker 映像: +* 公共的 NNI Docker 映像: ```bash docker pull msranni/nni:latest diff --git a/docs/zh_CN/TrainingService/DLTSMode.md b/docs/zh_CN/TrainingService/DLTSMode.md new file mode 100644 index 0000000000..a33f2c86f8 --- /dev/null +++ b/docs/zh_CN/TrainingService/DLTSMode.md @@ -0,0 +1,49 @@ +**在 DLTS 上运行 Experiment** +=== +NNI 支持在 [DLTS](https://github.com/microsoft/DLWorkspace.git) 上运行 Experiment ,称之为 dlts 模式。 在开始使用 NNI dlts 模式之前,应该有访问 DLTS 仪表板的账号。 + +## 设置环境 + +步骤 1. 从 DLTS 仪表板中选择集群,关于仪表板地址,需咨询管理员。 + +![选择集群](../../img/dlts-step1.png) + +步骤 2. 准备 NNI 配置 YAML,如下所示: + +```yaml +# 将此字段设置为 "dlts" +trainingServicePlatform: dlts +authorName: your_name +experimentName: auto_mnist +trialConcurrency: 2 +maxExecDuration: 3h +maxTrialNum: 100 +searchSpacePath: search_space.json +useAnnotation: false +tuner: + builtinTunerName: TPE + classArgs: + optimize_mode: maximize +trial: + command: python3 mnist.py + codeDir: . + gpuNum: 1 + image: msranni/nni +# 访问 DLTS 的配置 +dltsConfig: + dashboard: # Ask administrator for the cluster dashboard URL +``` + +记得将群集仪表板地址填到最后一行。 + +步骤 3. 打开群集的工作目录,将 NNI 配置和相关代码放入目录中。 + +![复制配置](../../img/dlts-step3.png) + +步骤 4. 将 NNI 管理器任务提交到指定的群集。 + +![提交 Job](../../img/dlts-step4.png) + +步骤 5. 转到新创建的任务选项卡,单击端口 40000 的链接检查 Trial 的信息。 + +![查看 NNI Web 界面](../../img/dlts-step5.png) diff --git a/docs/zh_CN/TrainingService/RemoteMachineMode.md b/docs/zh_CN/TrainingService/RemoteMachineMode.md index e4b6917f84..643d4d6a69 100644 --- a/docs/zh_CN/TrainingService/RemoteMachineMode.md +++ b/docs/zh_CN/TrainingService/RemoteMachineMode.md @@ -4,9 +4,9 @@ NNI 可以通过 SSH 在多个远程计算机上运行同一个 Experiment,称 ## 远程计算机的要求 -* 仅支持 Linux 作为远程计算机,其[配置需求](../Tutorial/Installation.md)与 NNI 本机模式相同。 +* 仅支持 Linux 作为远程计算机,其[配置需求](../Tutorial/InstallationLinux.md)与 NNI 本机模式相同。 -* 根据[安装文章](../Tutorial/Installation.md),在每台计算机上安装 NNI。 +* 根据[安装文章](../Tutorial/InstallationLinux.md),在每台计算机上安装 NNI。 * 确保远程计算机满足 Trial 代码的环境要求。 如果默认环境不符合要求,可以将设置脚本添加到 NNI 配置的 `command` 字段。 diff --git a/docs/zh_CN/TrialExample/Trials.md b/docs/zh_CN/TrialExample/Trials.md index 510226c180..449f4329e2 100644 --- a/docs/zh_CN/TrialExample/Trials.md +++ b/docs/zh_CN/TrialExample/Trials.md @@ -45,7 +45,7 @@ RECEIVED_PARAMS = nni.get_next_parameter() nni.report_intermediate_result(metrics) ``` -`指标`可以是任意的 Python 对象。 如果使用了 NNI 内置的 Tuner/Assessor,`指标`只可以是两种类型:1) 数值类型,如 float、int, 2) dict 对象,其中必须由键名为 `default`,值为数值的项目。 `指标`会发送给 [Assessor](../Assessor/BuiltinAssessor.md)。 通常,`指标`是损失值或精度。 +`指标`可以是任意的 Python 对象。 如果使用了 NNI 内置的 Tuner/Assessor,`指标`只可以是两种类型:1) 数值类型,如 float、int, 2) dict 对象,其中必须由键名为 `default`,值为数值的项目。 `指标`会发送给 [Assessor](../Assessor/BuiltinAssessor.md)。 通常,`指标`包含了定期评估的损失值或精度。 * 返回配置的最终性能 @@ -53,11 +53,11 @@ nni.report_intermediate_result(metrics) nni.report_final_result(metrics) ``` -`指标`也可以是任意的 Python 对象。 如果使用了内置的 Tuner/Assessor,`指标`格式和 `report_intermediate_result` 中一样,这个数值表示模型的性能,如精度、损失值等。 `指标`会发送给 [Tuner](../Tuner/BuiltinTuner.md)。 +`指标`可以是任意的 Python 对象。 如果使用了内置的 Tuner/Assessor,`指标`格式和 `report_intermediate_result` 中一样,这个数值表示模型的性能,如精度、损失值等。 `指标`会发送给 [Tuner](../Tuner/BuiltinTuner.md)。 ### 第三步:启用 NNI API -要启用 NNI 的 API 模式,需要将 useAnnotation 设置为 *false*,并提供搜索空间文件的路径(即第一步中定义的文件): +要启用 NNI 的 API 模式,需要将 useAnnotation 设置为 *false*,并提供搜索空间文件的路径,即第一步中定义的文件: ```yaml useAnnotation: false @@ -72,10 +72,10 @@ searchSpacePath: /path/to/your/search_space.json ## NNI Annotation -另一种实现 Trial 的方法是使用 Python 注释来标记 NNI。 就像其它 Python Annotation,NNI 的 Annotation 和代码中的注释一样。 不需要在代码中做大量改动。 只需要添加一些 NNI Annotation,就能够: +另一种实现 Trial 的方法是使用 Python 注释来标记 NNI。 NN Annotation 很简单,类似于注释。 不必对现有代码进行结构更改。 只需要添加一些 NNI Annotation,就能够: * 标记需要调整的参数变量 -* 指定变量的搜索空间范围 +* 指定要在其中调整的变量的范围 * 标记哪个变量需要作为中间结果范围给 `Assessor` * 标记哪个变量需要作为最终结果(例如:模型精度)返回给 `Tuner`。 @@ -89,7 +89,7 @@ searchSpacePath: /path/to/your/search_space.json 2. 每执行 100 步返回 test\_acc 3. 最后返回 test\_acc 作为最终结果。 -新添加的代码都是注释,不会影响以前的执行逻辑。因此这些代码仍然能在没有安装 NNI 的环境中运行。 +值得注意的是,新添加的代码都是注释,不会影响以前的执行逻辑。因此这些代码仍然能在没有安装 NNI 的环境中运行。 ```diff with tf.Session() as sess: @@ -120,7 +120,7 @@ with tf.Session() as sess: **注意**: -* `@nni.variable` 会对它的下面一行进行修改,左边被赋值变量必须在 `@nni.variable` 的 `name` 参数中指定。 +* `@nni.variable` 会对它的下面一行进行修改,左边被赋值变量必须与 `@nni.variable` 的关键字 `name` 相同。 * `@nni.report_intermediate_result`/`@nni.report_final_result` 会将数据发送给 Assessor、Tuner。 Annotation 的语法和用法等,参考 [Annotation](../Tutorial/AnnotationSpec.md)。 diff --git a/docs/zh_CN/Tuner/BatchTuner.md b/docs/zh_CN/Tuner/BatchTuner.md index a7bc7a0fc0..dd61aefe6b 100644 --- a/docs/zh_CN/Tuner/BatchTuner.md +++ b/docs/zh_CN/Tuner/BatchTuner.md @@ -4,4 +4,4 @@ Batch Tuner 能让用户简单的提供几组配置(如,超参选项的组合)。 当所有配置都执行完后,Experiment 即结束。 Batch Tuner 的[搜索空间](../Tutorial/SearchSpaceSpec.md)只支持 `choice`。 -建议场景:如果 Experiment 配置已确定,可通过 choice 将它们罗列到搜索空间文件中运行即可。 \ No newline at end of file +建议场景:如果 Experiment 配置已确定,可通过 `choice` 将它们罗列到搜索空间文件中运行即可。 \ No newline at end of file diff --git a/docs/zh_CN/Tuner/BohbAdvisor.md b/docs/zh_CN/Tuner/BohbAdvisor.md index 46cc69750b..cfa089293e 100644 --- a/docs/zh_CN/Tuner/BohbAdvisor.md +++ b/docs/zh_CN/Tuner/BohbAdvisor.md @@ -10,7 +10,7 @@ BOHB 依赖 HB(Hyperband)来决定每次跑多少组参数和每组参数分 ### HB(Hyperband) -按照 Hyperband 的方式来选择每次跑的参数个数与分配多少资源(budget),并继续使用“连续减半(SuccessiveHalving)”策略,更多有关Hyperband算法的细节,请参考[NNI 中的 Hyperband](HyperbandAdvisor.md) 和 [Hyperband 的参考论文](https://arxiv.org/abs/1603.06560)。 下面的伪代码描述了这个过程。 +按照 Hyperband 的方式来选择资源(budget),并继续使用 "连续减半(SuccessiveHalving)" 策略。 更多细节,参考 [NNI 中的 Hyperband](HyperbandAdvisor.md) 和 [Hyperband 的参考论文](https://arxiv.org/abs/1603.06560)。 下面的伪代码描述了这个过程。 ![](../../img/bohb_1.png) @@ -36,13 +36,13 @@ BOHB 的 BO 与 TPE 非常相似, 它们的主要区别是: BOHB 中使用一个 以上这张图展示了 BOHB 的工作流程。 将每次训练的最大资源配置(max_budget)设为 9,最小资源配置设为(min_budget)1,逐次减半比例(eta)设为 3,其他的超参数为默认值。 那么在这个例子中,s_max 计算的值为 2, 所以会持续地进行 {s=2, s=1, s=0, s=2, s=1, s=0, ...} 的循环。 在“逐次减半”(SuccessiveHalving)算法的每一个阶段,即图中橙色框,都将选取表现最好的前 1/eta 个参数,并在赋予更多计算资源(budget)的情况下运行。不断重复“逐次减半” (SuccessiveHalving)过程,直到这个循环结束。 同时,收集这些试验的超参数组合,使用了计算资源(budget)和其表现(metrics),使用这些数据来建立一个以使用了多少计算资源(budget)为维度的多维核密度估计(KDE)模型。 这个多维的核密度估计(KDE)模型将用于指导下一个循环的参数选择。 -有关如何使用多维的KDE模型来指导参数选择的采样规程,用以下伪代码来描述。 +有关如何使用多维的 KDE 模型来指导参数选择的采样规程,用以下伪代码来描述。 ![](../../img/bohb_4.png) ## 3. 用法 -BOHB advisor 的使用依赖 [ConfigSpace](https://github.com/automl/ConfigSpace) 包,在第一次使用 BOHB 的时候,在命令行运行以下的指令来安装要求的 ConfigSpace 包。 +BOHB Advisor 需要 [ConfigSpace](https://github.com/automl/ConfigSpace) 包。 可以使用以下命令安装 ConfigSpace。 ```bash nnictl package install --name=SMAC @@ -66,27 +66,27 @@ advisor: min_bandwidth: 0.001 ``` -**需要的参数** +**classArgs 要求:** -* **optimize_mode** (*maximize 或 minimize, 可选项, 默认值为 maximize*) - 如果为 'maximize',表示 Tuner 的目标是将指标最大化。 如果为 'minimize',表示 Tuner 的目标是将指标最小化。 +* **optimize_mode** (*maximize 或 minimize, 可选项, 默认值为 maximize*) - 如果为 'maximize',表示 Tuner 会试着最大化指标。 如果为 'minimize',表示 Tuner 的目标是将指标最小化。 * **min_budget** (*整数, 可选项, 默认值为 1*) - 运行一个试验给予的最低计算资源(budget),这里的计算资源通常使用mini-batches 或者 epochs。 该参数必须为正数。 * **max_budget** (*整数, 可选项, 默认值为 3*) - 运行一个试验给予的最大计算资源(budget),这里的计算资源通常使用 mini-batches 或者 epochs。 该参数必须大于“min_budget”。 * **eta** (*整数, 可选项, 默认值为3*) - 在每次迭代中,执行完整的“连续减半”算法。 在这里,当一个使用相同计算资源的子集结束后,选择表现前 1/eta 好的参数,给予更高的优先级,进入下一轮比较(会获得更多计算资源)。 该参数必须大于等于 2。 -* **min_points_in_model**(*整数, 可选项, 默认值为None*): 建立核密度估计(KDE)要求的最小观察到的点。 默认值 None 表示 dim+1,当在该计算资源(budget)下试验过的参数已经大于等于`max{dim+1, min_points_in_model}` 时,BOHB 将会开始建立这个计算资源(budget)下对应的核密度估计(KDE)模型,然后用这个模型来指导参数的选取。 该参数必须为正数。(dim 指的是搜索空间中超参数的维度) -* **top_n_percent**(*整数, 可选项, 默认值为15*): 认为观察点为好点的百分数(在 1 到 99 之间,默认值为 15)。 区分表现好的点与坏的点是为了建立树形核密度估计模型。 比如,如果观察到了100个点的表现情况,同时把 top_n_percent 设置为 15,那么表现最好的 15个点将会用于创建表现好的点的分布 "l(x)",剩下的85个点将用于创建表现坏的点的分布 “g(x)”。 -* **num_samples** (*整数, 可选项, 默认值为64*): 用于优化 EI 值的采样个数(默认值为64)。 在这个例子中,将根据 l(x) 的分布采样“num_samples”(默认值为64)个点。若优化的目标为最大化指标,则会返回其中 l(x)/g(x) 的值最大的点作为下一个试验的参数。 否则,使用值最小的点。 +* **min_points_in_model**(*整数, 可选项, 默认值为None*): 建立核密度估计(KDE)要求的最小观察到的点。 默认值 None 表示 dim+1,当在该计算资源(budget)下试验过的参数已经大于等于`max{dim+1, min_points_in_model}` 时,BOHB 将会开始建立这个计算资源(budget)下对应的核密度估计(KDE)模型,然后用这个模型来指导参数的选取。 该参数必须为正数。 (dim 表示搜索空间中超参的数量) +* **top_n_percent**(*整数, 可选, 默认值为 15*): 认为观察点为好点的百分数 (在 1 到 99 之间)。 区分表现好的点与坏的点是为了建立树形核密度估计模型。 例如,如果有 100 个观察到的 Trial,top_n_percent 为 15,则前 15% 的点将用于构建好点模型 "l(x)"。 其余 85% 的点将用于构建坏点模型 "g(x)"。 +* **num_samples** (*整数, 可选项, 默认值为64*): 用于优化 EI 值的采样个数(默认值为64)。 在这种情况下,将对 "num_samples" 点进行采样,并比较 l(x)/g(x) 的结果。 然后,如果 optimize_mode 是 `maximize`,就会返回其中 l(x)/g(x) 值最大的点作为下一个配置参数。 否则,使用值最小的点。 * **random_fraction**(*浮点数, 可选项, 默认值为0.33*): 使用模型的先验(通常是均匀)来随机采样的比例。 -* **bandwidth_factor**(< 1>浮点数, 可选, 默认值为3.0 ): 为了鼓励多样性,把优化EI的点加宽,即把KDE中采样的点乘以这个因子,从而增加KDE中的带宽。 如果不熟悉 KDE,建议保留默认值。 -* **min_bandwidth**(< 1>float, 可选, 默认值 = 0.001 ): 为了保持多样性, 即使所有好的样本对其中一个参数具有相同的值,使用最小带宽 (默认值: 1e-3) 而不是零。 如果不熟悉 KDE,建议保留默认值。 +* **bandwidth_factor**(< 1>浮点数, 可选, 默认值为3.0 ): 为了鼓励多样性,把优化EI的点加宽,即把KDE中采样的点乘以这个因子,从而增加KDE中的带宽。 如果不熟悉 KDE,建议使用默认值。 +* **min_bandwidth**(< 1>float, 可选, 默认值 = 0.001 ): 为了保持多样性, 即使所有好的样本对其中一个参数具有相同的值,使用最小带宽 (默认值: 1e-3) 而不是零。 如果不熟悉 KDE,建议使用默认值。 -*目前 NNI 的浮点类型仅支持十进制表示,必须使用 0.333 来代替 1/3,0.001代替 1e-3。* +*请注意,浮点类型当前仅支持十进制表示。 必须使用 0.333 而不是 1/3 ,0.001 而不是 1e-3。* ## 4. 文件结构 -Advisor 有大量的文件、函数和类。 文件内容的简单介绍: +Advisor 有大量的文件、函数和类。 这里只简单介绍最重要的文件: -* `bohb_advisor.py` BOHB类的定义, 包括与dispatcher进行交互的部分,以及控制新试验的生成,计算资源以及试验结果的处理。 基本包含了HB(Hyperband)的实现部分。 -* `config_generator.py` 包含了BO(贝叶斯优化)算法的实验部分。 内置函数 *get_config* 使用基于贝叶斯优化生成一个新的参数组合, 内置函数 *new_result* 接受新的结果并使用这些结果来更新贝叶斯优化模型。 +* `bohb_advisor.py` BOHB 类的定义, 包括与 Dispatcher 进行交互的部分,以及控制新 Trial 的生成,计算资源以及结果的处理。 还包含了 HB(Hyperband)的实现部分。 +* `config_generator.py` 包含了 BO(贝叶斯优化)算法的实现。 内置函数 *get_config* 使用基于贝叶斯优化生成一个新的参数组合,内置函数 *new_result* 接受新的结果并使用这些结果来更新贝叶斯优化模型。 ## 5. 实验 @@ -94,8 +94,8 @@ Advisor 有大量的文件、函数和类。 文件内容的简单介绍: 源码地址: [examples/trials/mnist-advisor](https://github.com/Microsoft/nni/tree/master/examples/trials/) -使用BOHB这个调参算法,在CNN模型上跑MNIST数据集。 下面是实验结果: +使用 BOHB 调参算法,在 CNN 模型上跑 MNIST 数据集。 下面是实验结果: ![](../../img/bohb_5.png) -更多的实验结果可以在 [参考论文](https://arxiv.org/abs/1807.01774)中看到,们可以发现BOHB很好的利用了之前的试验结果,且在开发与探索中得到了一个很好的平衡。 \ No newline at end of file +更多实验结果可以在[参考文献](https://arxiv.org/abs/1807.01774)中找到。 可以看到,BOHB 充分利用了以往的成果,在探索和挖掘方面有很好的平衡。 \ No newline at end of file diff --git a/docs/zh_CN/Tuner/BuiltinTuner.md b/docs/zh_CN/Tuner/BuiltinTuner.md index 2de9b34741..d29a81f492 100644 --- a/docs/zh_CN/Tuner/BuiltinTuner.md +++ b/docs/zh_CN/Tuner/BuiltinTuner.md @@ -4,19 +4,19 @@ NNI 提供了先进的调优算法,使用上也很简单。 下面是内置 Tu 注意:点击 **Tuner 的名称**可看到 Tuner 的安装需求,建议的场景以及示例。 算法的详细说明在每个 Tuner 建议场景的最后。 [本文](../CommunitySharings/HpoComparision.md)对比了不同 Tuner 在几个问题下的不同效果。 -当前支持的 Tuner: +当前支持的算法: | Tuner(调参器) | 算法简介 | | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [**TPE**](#TPE) | Tree-structured Parzen Estimator (TPE) 是一种 sequential model-based optimization(SMBO,即基于序列模型优化)的方法。 SMBO 方法根据历史指标数据来按顺序构造模型,来估算超参的性能,随后基于此模型来选择新的超参。 [参考论文](https://papers.nips.cc/paper/4443-algorithms-for-hyper-parameter-optimization.pdf) | | [**Random Search(随机搜索)**](#Random) | 在超参优化时,随机搜索算法展示了其惊人的简单和效果。 建议当不清楚超参的先验分布时,采用随机搜索作为基准。 [参考论文](http://www.jmlr.org/papers/volume13/bergstra12a/bergstra12a.pdf) | | [**Anneal(退火算法)**](#Anneal) | 这种简单的退火算法从先前的采样开始,会越来越靠近发现的最佳点取样。 此算法是随机搜索的简单变体,利用了反应曲面的平滑性。 退火率不是自适应的。 | -| [**Naïve Evolution(进化算法)**](#Evolution) | Naïve Evolution(朴素进化算法)来自于 Large-Scale Evolution of Image Classifiers。 它会基于搜索空间随机生成一个种群。 在每一代中,会选择较好的结果,并对其下一代进行一些变异(例如,改动一个超参,增加或减少一层)。 Naïve Evolution 需要很多次 Trial 才能有效,但它也非常简单,也很容易扩展新功能。 [参考论文](https://arxiv.org/pdf/1703.01041.pdf) | +| [**Naïve Evolution(进化算法)**](#Evolution) | Naïve Evolution(朴素进化算法)来自于 Large-Scale Evolution of Image Classifiers。 它会基于搜索空间随机生成一个种群。 在每一代中,会选择较好的结果,并对其下一代进行一些变异(例如,改动一个超参,增加或减少一层)。 朴素进化算法需要很多次的 Trial 才能有效,但它也非常简单,也很容易扩展新功能。 [参考论文](https://arxiv.org/pdf/1703.01041.pdf) | | [**SMAC**](#SMAC) | SMAC 基于 Sequential Model-Based Optimization (SMBO,即序列的基于模型优化方法)。 它利用使用过的结果好的模型(高斯随机过程模型),并将随机森林引入到 SMBO 中,来处理分类参数。 SMAC 算法包装了 Github 的 SMAC3。 注意:SMAC 需要通过 `nnictl package` 命令来安装。 [参考论文,](https://www.cs.ubc.ca/~hutter/papers/10-TR-SMAC.pdf) [Github 代码库](https://github.com/automl/SMAC3) | | [**Batch Tuner(批处理 Tuner)**](#Batch) | Batch Tuner 能让用户简单的提供几组配置(如,超参选项的组合)。 当所有配置都执行完后,Experiment 即结束。 Batch Tuner 仅支持 choice 类型。 | | [**Grid Search(遍历搜索)**](#GridSearch) | Grid Search 会穷举定义在搜索空间文件中的所有超参组合。 遍历搜索可以使用的类型有 choice, quniform, randint。 | -| [**Hyperband**](#Hyperband) | Hyperband 试图用有限的资源来探索尽可能多的组合,并发现最好的结果。 它的基本思路是生成大量的配置,并使用少量的资源来找到有可能好的配置,然后继续训练找到其中更好的配置。 [参考论文](https://arxiv.org/pdf/1603.06560.pdf) | -| [**Network Morphism**](#NetworkMorphism) | Network Morphism 提供了深度学习模型的自动架构搜索功能。 每个子网络都继承于父网络的知识和形态,并变换网络的不同形态,包括深度,宽度,跨层连接(skip-connection)。 然后使用历史的架构和指标,来估计子网络的值。 然后会选择最有希望的模型进行训练。 [参考论文](https://arxiv.org/abs/1806.10282) | +| [**Hyperband**](#Hyperband) | Hyperband 试图用有限的资源来探索尽可能多的组合,并发现最好的结果。 基本思想是生成许多配置,并通过少量的 Trial 来运行一部分。 一半性能不好的配置会被抛弃,剩下的部分与新选择出的配置会进行下一步的训练。 数量的多少对资源约束非常敏感(例如,分配的搜索时间)。 [参考论文](https://arxiv.org/pdf/1603.06560.pdf) | +| [**Network Morphism**](#NetworkMorphism) | 网络模态(Network Morphism)提供自动搜索深度学习体系结构的功能。 它会继承父网络的知识,来生成变形的子网络。 包括深度、宽度、跳连接等变化。 然后使用历史的架构和指标,来估计子网络的值。 然后会选择最有希望的模型进行训练。 [参考论文](https://arxiv.org/abs/1806.10282) | | [**Metis Tuner**](#MetisTuner) | 大多数调参工具仅仅预测最优配置,而 Metis 的优势在于有两个输出:(a) 最优配置的当前预测结果, 以及 (b) 下一次 Trial 的建议。 它不进行随机取样。 大多数工具假设训练集没有噪声数据,但 Metis 会知道是否需要对某个超参重新采样。 [参考论文](https://www.microsoft.com/en-us/research/publication/metis-robustly-tuning-tail-latencies-cloud-systems/) | | [**BOHB**](#BOHB) | BOHB 是 Hyperband 算法的后续工作。 Hyperband 在生成新的配置时,没有利用已有的 Trial 结果,而本算法利用了 Trial 结果。 BOHB 中,HB 表示 Hyperband,BO 表示贝叶斯优化(Byesian Optimization)。 BOHB 会建立多个 TPE 模型,从而利用已完成的 Trial 生成新的配置。 [参考论文](https://arxiv.org/abs/1807.01774) | | [**GP Tuner**](#GPTuner) | Gaussian Process(高斯过程) Tuner 是序列化的基于模型优化(SMBO)的方法,并使用了高斯过程来替代。 [参考论文](https://papers.nips.cc/paper/4443-algorithms-for-hyper-parameter-optimization.pdf),[Github 库](https://github.com/fmfn/BayesianOptimization) | @@ -24,7 +24,7 @@ NNI 提供了先进的调优算法,使用上也很简单。 下面是内置 Tu ## 用法 -要使用 NNI 内置的 Tuner,需要在 `config.yml` 文件中添加 **builtinTunerName** 和 **classArgs**。 这一节会介绍推荐的场景、参数等详细用法以及示例。 +要使用 NNI 内置的 Tuner,需要在 `config.yml` 文件中添加 **builtinTunerName** 和 **classArgs**。 本部分中,将介绍每个 Tuner 的用法和建议场景、参数要求,并提供配置示例。 注意:参考示例中的格式来创建新的 `config.yml` 文件。 一些内置的 Tuner 还需要通过 `nnictl package` 命令先安装,如 SMAC。 @@ -38,13 +38,13 @@ NNI 提供了先进的调优算法,使用上也很简单。 下面是内置 Tu TPE 是一种黑盒优化方法,可以使用在各种场景中,通常情况下都能得到较好的结果。 特别是在计算资源有限,只能运行少量 Trial 的情况。 大量的实验表明,TPE 的性能远远优于随机搜索。 [详细说明](./HyperoptTuner.md) -**参数** +**classArgs 要求:** -* **optimize_mode** (*maximize 或 minimize, 可选项, 默认值为 maximize*) - 如果为 'maximize',表示 Tuner 的目标是将指标最大化。 如果为 'minimize',表示 Tuner 的目标是将指标最小化。 +* **optimize_mode** (*maximize 或 minimize, 可选项, 默认值为 maximize*) - 如果为 'maximize',表示 Tuner 会试着最大化指标。 如果为 'minimize',表示 Tuner 的目标是将指标最小化。 注意:为实现大规模并发 Trial,TPE 的并行性得到了优化。 有关优化原理或开启优化,参考 [TPE 文档](HyperoptTuner.md)。 -**示例** +**配置示例:** ```yaml # config.yml @@ -64,13 +64,13 @@ tuner: **建议场景** -在每个 Trial 运行时间不长(例如,能够非常快的完成,或者很快的被 Assessor 终止),并有充足计算资源的情况下。 或者需要均匀的探索搜索空间。 随机搜索可作为搜索算法的基准线。 [详细说明](./HyperoptTuner.md) +随机搜索,可用于每个 Trial 运行时间不长(例如,能够非常快的完成,或者很快的被 Assessor 终止),并有充足计算资源的情况下。 如果要均衡的探索搜索空间,它也很有用。 随机搜索可作为搜索算法的基准线。 [详细说明](./HyperoptTuner.md) -**参数** +**classArgs 要求:** -* **optimize_mode** (*maximize 或 minimize, 可选项, 默认值为 maximize*) - 如果为 'maximize',表示 Tuner 的目标是将指标最大化。 如果为 'minimize',表示 Tuner 的目标是将指标最小化。 +* **optimize_mode** (*maximize 或 minimize, 可选项, 默认值为 maximize*) - 如果为 'maximize',表示 Tuner 会试着最大化指标。 如果为 'minimize',表示 Tuner 的目标是将指标最小化。 -**示例** +**配置示例:** ```yaml # config.yml @@ -88,13 +88,13 @@ tuner: **建议场景** -当每个 Trial 的时间不长,并且有足够的计算资源时使用(与随机搜索基本相同)。 或者搜索空间的变量能从一些先验分布中采样。 [详细说明](./HyperoptTuner.md) +退火算法,用于每个 Trial 的时间不长,并且有足够的计算资源(与随机搜索基本相同)。 当搜索空间中的变量可以从某些先前的分布中采样时,它也很有用。 [详细说明](./HyperoptTuner.md) -**参数** +**classArgs 要求:** -* **optimize_mode** (*maximize 或 minimize, 可选项, 默认值为 maximize*) - 如果为 'maximize',表示 Tuner 的目标是将指标最大化。 如果为 'minimize',表示 Tuner 的目标是将指标最小化。 +* **optimize_mode** (*maximize 或 minimize, 可选项, 默认值为 maximize*) - 如果为 'maximize',表示 Tuner 会试着最大化指标。 如果为 'minimize',表示 Tuner 的目标是将指标最小化。 -**示例** +**配置示例:** ```yaml # config.yml @@ -114,15 +114,15 @@ tuner: **建议场景** -此算法对计算资源的需求相对较高。 需要非常大的初始种群,以免落入局部最优中。 如果 Trial 时间很短,或者使用了 Assessor,就非常适合此算法。 如果 Trial 代码支持权重迁移,即每次 Trial 会从上一轮继承已经收敛的权重,建议使用此算法。 这会大大提高训练速度。 [详细说明](./EvolutionTuner.md) +其计算资源要求相对较高。 特别是,它需要非常大的初始种群,以免落入局部最优中。 如果 Trial 时间很短,或者使用了 Assessor,就非常适合此算法。 如果 Trial 代码支持权重迁移,即每次 Trial 会从上一轮继承已经收敛的权重,建议使用此算法。 这会大大提高训练速度。 [详细说明](./EvolutionTuner.md) -**参数** +**classArgs 要求:** -* **optimize_mode** (*maximize 或 minimize, 可选项, 默认值为 maximize*) - 如果为 'maximize',表示 Tuner 的目标是将指标最大化。 如果为 'minimize',表示 Tuner 的目标是将指标最小化。 +* **optimize_mode** (*maximize 或 minimize, 可选项, 默认值为 maximize*) - 如果为 'maximize',表示 Tuner 会试着最大化指标。 如果为 'minimize',表示 Tuner 的目标是将指标最小化。 -* **population_size** (*int 类型(大于 0), 可选项, 默认值为 20*) - 表示遗传 Tuner 中的种群(Trial 数量)。 建议 `population_size` 比 `concurrency` 取值更大,这样用户能充分利用算法(至少要等于 `concurrency`,否则 Tuner 在生成第一代参数的时候就会失败)。 +* **population_size** (*int 类型 (需要大于 0), 可选项, 默认值为 20*) - 表示遗传 Tuner 中的初始种群(Trial 数量)。 建议 `population_size` 比 `concurrency` 取值更大,这样能充分利用算法(至少要等于 `concurrency`,否则 Tuner 在生成第一代参数的时候就会失败)。 -**示例** +**配置示例:** ```yaml # config.yml @@ -141,7 +141,7 @@ tuner: > 名称:**SMAC** -**当前 SMAC 不支持在 WIndows 下运行。 原因参考:[github issue](https://github.com/automl/SMAC3/issues/483).** +**当前 SMAC 不支持在 WIndows 下运行。 原因参考:[GitHub issue](https://github.com/automl/SMAC3/issues/483)。** **安装** @@ -155,12 +155,12 @@ nnictl package install --name=SMAC 与 TPE 类似,SMAC 也是一个可以被用在各种场景中的黑盒 Tuner。在计算资源有限时,也可以使用。 此算法为离散超参而优化,因此,如果大部分超参是离散值时,建议使用此算法。 [详细说明](./SmacTuner.md) -**参数** +**classArgs 要求:** -* **optimize_mode** (*maximize 或 minimize, 可选项, 默认值为 maximize*) - 如果为 'maximize',表示 Tuner 的目标是将指标最大化。 如果为 'minimize',表示 Tuner 的目标是将指标最小化。 +* **optimize_mode** (*maximize 或 minimize, 可选项, 默认值为 maximize*) - 如果为 'maximize',表示 Tuner 会试着最大化指标。 如果为 'minimize',表示 Tuner 的目标是将指标最小化。 * **config_dedup** (*True 或 False, 可选, 默认为 False*) - 如果为 True,则 Tuner 不会生成重复的配置。 如果为 False,则配置可能会重复生成,但对于相对较大的搜索空间,此概率较小。 -**示例** +**配置示例:** ```yaml # config.yml @@ -182,7 +182,7 @@ tuner: 如果 Experiment 配置已确定,可通过 `choice` 将它们罗列到搜索空间文件中运行即可。 [详细说明](./BatchTuner.md) -**示例** +**配置示例:** ```yaml # config.yml @@ -192,7 +192,7 @@ tuner:
      -注意 Batch Tuner 支持的搜索空间文件如下例: +注意,BatchTuner 的搜索空间如下所示: ```json { @@ -209,7 +209,7 @@ tuner: } ``` -搜索空间文件使用了键 `combine_params`。 参数类型必须是 `choice` ,并且 `values` 要包含所有需要 Experiment 的参数组合。 +搜索空间文件使用了高层的键 `combine_params`。 参数类型必须是 `choice` ,并且 `values` 要包含所有需要的参数组合。 @@ -221,9 +221,9 @@ tuner: 注意,搜索空间仅支持 `choice`, `quniform`, `randint`。 -当搜索空间比较小,能够遍历整个搜索空间。 [详细说明](./GridsearchTuner.md) +当搜索空间较小时,建议这样做。 建议使用在可以穷尽整个搜索空间的情况下。 [详细说明](./GridsearchTuner.md) -**示例** +**配置示例:** ```yaml # config.yml @@ -241,15 +241,15 @@ tuner: **建议场景** -当搜索空间很大,但计算资源有限时建议使用。 中间结果能够很好的反映最终结果的情况下,此算法会非常有效。 [详细说明](./HyperbandAdvisor.md) +当搜索空间很大,但计算资源有限时建议使用。 中间结果能够很好的反映最终结果的情况下,此算法会非常有效。 例如,当训练初期更准确的模型在以后也更准确的情况下。 [详细说明](./HyperbandAdvisor.md) -**参数** +**classArgs 要求:** -* **optimize_mode** (*maximize 或 minimize, 可选项, 默认值为 maximize*) - 如果为 'maximize',表示 Tuner 的目标是将指标最大化。 如果为 'minimize',表示 Tuner 的目标是将指标最小化。 +* **optimize_mode** (*maximize 或 minimize, 可选项, 默认值为 maximize*) - 如果为 'maximize',表示 Tuner 会试着最大化指标。 如果为 'minimize',表示 Tuner 的目标是将指标最小化。 * **R** (*int, 可选, 默认为 60*) - 分配给 Trial 的最大资源(可以是 mini-batches 或 epochs 的数值)。 每个 Trial 都需要用 TRIAL_BUDGET 来控制运行的步数。 * **eta** (*int, 可选, 默认为 3*) - `(eta-1)/eta` 是丢弃 Trial 的比例。 -**示例** +**配置示例:** ```yaml # config.yml @@ -275,17 +275,17 @@ NetworkMorphism 需要先安装 [PyTorch](https://pytorch.org/get-started/locall **建议场景** -需要将深度学习方法应用到自己的任务(自己的数据集)上,但不清楚该如何选择或设计网络。 可修改[示例](https://github.com/Microsoft/nni/tree/master/examples/trials/network_morphism/cifar10/cifar10_keras.py)来适配自己的数据集和数据增强方法。 也可以修改批处理大小,学习率或优化器。 它可以为不同的任务找到好的网络架构。 当前,此 Tuner 仅支持视觉领域。 [详细说明](./NetworkmorphismTuner.md) +需要将深度学习方法应用到自己的任务上,但不清楚该如何选择或设计网络。 可修改[示例](https://github.com/Microsoft/nni/tree/master/examples/trials/network_morphism/cifar10/cifar10_keras.py)来适配自己的数据集和数据增强方法。 也可以修改批处理大小,学习率或优化器。 当前,此 Tuner 仅支持视觉领域。 [详细说明](./NetworkmorphismTuner.md) -**参数** +**classArgs 要求:** -* **optimize_mode** (*maximize 或 minimize, 可选项, 默认值为 maximize*) - 如果为 'maximize',表示 Tuner 的目标是将指标最大化。 如果为 'minimize',表示 Tuner 的目标是将指标最小化。 -* **task** (*('cv'), 可选, 默认为 'cv'*) - 实验的领域,当前仅支持视觉(cv)。 +* **optimize_mode** (*maximize 或 minimize, 可选项, 默认值为 maximize*) - 如果为 'maximize',表示 Tuner 会试着最大化指标。 如果为 'minimize',表示 Tuner 的目标是将指标最小化。 +* **task** (*('cv'), 可选, 默认为 'cv'*) - 实验的领域。 当前,此 Tuner 仅支持计算机视觉(cv)领域。 * **input_width** (*int, 可选, 默认为 = 32*) - 输入图像的宽度 * **input_channel** (*int, 可选, 默认为 3*) - 输入图像的通道数 * **n_output_node** (*int, 可选, 默认为 10*) - 输出分类的数量 -**示例** +**配置示例:** ```yaml # config.yml @@ -311,13 +311,13 @@ tuner: **建议场景** -与 TPE 和 SMAC 类似,Metis 是黑盒 Tuner。 如果系统需要很长时间才能完成一次 Trial,Metis 就比随机搜索等其它方法要更合适。 此外,Metis 还为接下来的 Trial 提供了候选。 如何使用 Metis 的[示例](https://github.com/Microsoft/nni/tree/master/examples/trials/auto-gbdt/search_space_metis.json)。 通过调用 NNI 的 SDK,用户只需要发送`精度`这样的最终结果给 Tuner。 [详细说明](./MetisTuner.md) +与 TPE 和 SMAC 类似,Metis 是黑盒 Tuner。 如果系统需要很长时间才能完成一次 Trial,Metis 就比随机搜索等其它方法要更合适。 此外,Metis 还为接下来的 Trial 提供了候选。 如何使用 Metis 的[样例](https://github.com/Microsoft/nni/tree/master/examples/trials/auto-gbdt/search_space_metis.json)。 通过调用 NNI 的 SDK,用户只需要发送 `精度` 这样的最终结果给 Tuner。 [详细说明](./MetisTuner.md) -**参数** +**classArgs 要求:** * **optimize_mode** (*'maximize' 或 'minimize', 可选项, 默认值为 'maximize'*) - 如果为 'maximize',表示 Tuner 的目标是将指标最大化。 如果为 'minimize',表示 Tuner 的目标是将指标最小化。 -**示例** +**配置示例:** ```yaml # config.yml @@ -337,7 +337,7 @@ tuner: **安装** -BOHB Advisor 的使用依赖 [ConfigSpace](https://github.com/automl/ConfigSpace) 包,在第一次使用 BOHB 的时候,在命令行运行以下的指令来安装 ConfigSpace。 +BOHB Advisor 需要 [ConfigSpace](https://github.com/automl/ConfigSpace) 包。 可以使用以下命令安装 ConfigSpace。 ```bash nnictl package install --name=BOHB @@ -345,24 +345,24 @@ nnictl package install --name=BOHB **建议场景** -与 Hyperband 类似,当计算资源有限但搜索空间相对较大时,建议使用此方法。 中间结果能够很好的反映最终结果的情况下,此算法会非常有效。 在这种情况下,使用贝叶斯优化,可能会收敛到更好的配置。 [详细说明](./BohbAdvisor.md) +与 Hyperband 类似,当计算资源有限但搜索空间相对较大时,建议使用 BOHB。 中间结果能够很好的反映最终结果的情况下,此算法会非常有效。 在这种情况下,由于使用贝叶斯优化,它可能会收敛到比 Hyperband 更好的配置。 [详细说明](./BohbAdvisor.md) -**参数** +**classArgs 要求:** -* **optimize_mode** (*maximize 或 minimize, 可选项, 默认值为 maximize*) - 如果为 'maximize',表示 Tuner 的目标是将指标最大化。 如果为 'minimize',表示 Tuner 的目标是将指标最小化。 +* **optimize_mode** (*maximize 或 minimize, 可选项, 默认值为 maximize*) - 如果为 'maximize',表示 Tuner 会试着最大化指标。 如果为 'minimize',表示 Tuner 的目标是将指标最小化。 * **min_budget** (*整数, 可选项, 默认值为 1*) - 运行一个试验给予的最低计算资源(budget),这里的计算资源通常使用mini-batches 或者 epochs。 该参数必须为正数。 * **max_budget** (*整数, 可选项, 默认值为 3*) - 运行一个试验给予的最大计算资源(budget),这里的计算资源通常使用 mini-batches 或者 epochs。 该参数必须大于“min_budget”。 * **eta** (*整数, 可选项, 默认值为3*) - 在每次迭代中,执行完整的“连续减半”算法。 在这里,当一个使用相同计算资源的子集结束后,选择表现前 1/eta 好的参数,给予更高的优先级,进入下一轮比较(会获得更多计算资源)。 该参数必须大于等于 2。 -* **min_points_in_model**(*整数, 可选项, 默认值为None*): 建立核密度估计(KDE)要求的最小观察到的点。 默认值 None 表示 dim+1,当在该计算资源(budget)下试验过的参数已经大于等于`max{dim+1, min_points_in_model}` 时,BOHB 将会开始建立这个计算资源(budget)下对应的核密度估计(KDE)模型,然后用这个模型来指导参数的选取。 该参数必须为正数。(dim 指的是搜索空间中超参数的维度) -* **top_n_percent**(*整数, 可选项, 默认值为15*): 认为观察点为好点的百分数(在 1 到 99 之间,默认值为 15)。 区分表现好的点与坏的点是为了建立树形核密度估计模型。 比如,如果观察到了100个点的表现情况,同时把 top_n_percent 设置为 15,那么表现最好的 15个点将会用于创建表现好的点的分布 "l(x)",剩下的85个点将用于创建表现坏的点的分布 “g(x)”。 -* **num_samples** (*整数, 可选项, 默认值为64*): 用于优化 EI 值的采样个数(默认值为64)。 在这个例子中,将根据 l(x) 的分布采样“num_samples”(默认值为64)个点。若优化的目标为最大化指标,则会返回其中 l(x)/g(x) 的值最大的点作为下一个试验的参数。 否则,使用值最小的点。 +* **min_points_in_model**(*整数, 可选项, 默认值为None*): 建立核密度估计(KDE)要求的最小观察到的点。 默认值 None 表示 dim+1,当在该计算资源(budget)下试验过的参数已经大于等于`max{dim+1, min_points_in_model}` 时,BOHB 将会开始建立这个计算资源(budget)下对应的核密度估计(KDE)模型,然后用这个模型来指导参数的选取。 该参数必须为正数。 (dim 表示搜索空间中超参的数量) +* **top_n_percent**(*整数, 可选, 默认值为 15*): 认为观察点为好点的百分数 (在 1 到 99 之间)。 区分表现好的点与坏的点是为了建立树形核密度估计模型。 例如,如果有 100 个观察到的 Trial,top_n_percent 为 15,则前 15% 的点将用于构建好点模型 "l(x)"。 其余 85% 的点将用于构建坏点模型 "g(x)"。 +* **num_samples** (*整数, 可选项, 默认值为64*): 用于优化 EI 值的采样个数(默认值为64)。 在这种情况下,将对 "num_samples" 点进行采样,并比较 l(x)/g(x) 的结果。 然后,如果 optimize_mode 是 `maximize`,就会返回其中 l(x)/g(x) 值最大的点作为下一个配置参数。 否则,使用值最小的点。 * **random_fraction**(*浮点数, 可选项, 默认值为0.33*): 使用模型的先验(通常是均匀)来随机采样的比例。 -* **bandwidth_factor**(< 1>浮点数, 可选, 默认值为3.0 ): 为了鼓励多样性,把优化EI的点加宽,即把KDE中采样的点乘以这个因子,从而增加KDE中的带宽。 如果不熟悉 KDE,建议保留默认值。 -* **min_bandwidth**(< 1>float, 可选, 默认值 = 0.001 ): 为了保持多样性, 即使所有好的样本对其中一个参数具有相同的值,使用最小带宽 (默认值: 1e-3) 而不是零。 如果不熟悉 KDE,建议保留默认值。 +* **bandwidth_factor**(*浮点数, 可选, 默认值为 3.0 *): 为了鼓励多样性,把优化 EI 的点加宽,即把 KDE 中采样的点乘以这个因子,从而增加 KDE 中的带宽。 如果不熟悉 KDE,建议使用默认值。 +* **min_bandwidth**(< 1>float, 可选, 默认值 = 0.001 ): 为了保持多样性, 即使所有好的样本对其中一个参数具有相同的值,使用最小带宽 (默认值: 1e-3) 而不是零。 如果不熟悉 KDE,建议使用默认值。 -*目前 NNI 的浮点类型仅支持十进制表示,必须使用 0.333 来代替 1/3,0.001代替 1e-3。* +*请注意,浮点类型当前仅支持十进制表示。 必须使用 0.333 而不是 1/3 ,0.001 而不是 1e-3。* -**示例** +**配置示例:** ```yaml advisor: @@ -384,21 +384,21 @@ advisor: **建议场景** -作为序列的基于模型的全局优化(SMBO)算法,GP Tuner 使用了代理优化问题(找到采集函数的最大值)。虽然这仍然是个难题,但成本更低(从计算的角度来看),并且有通用的工具。 因此,GP Tuner 适合于函数的优化成本非常高时来使用。 GP 也可在计算资源非常有限时使用。 由于需要反转 Gram 矩阵,GP Tuner 的计算复杂度以 *O(N^3)* 的速度增长,因此不适合于需要大量 Trial 的情形。 [详细说明](./GPTuner.md) +作为序列的基于模型的全局优化(SMBO)算法,GP Tuner 使用了代理优化问题(找到采集函数的最大值)。虽然这仍然是个难题,但成本更低(从计算的角度来看),并且有通用的工具。 因此,GP Tuner 适合于函数的优化成本非常高时来使用。 GP 也可在计算资源非常有限时使用。 然后,由于需要反转 Gram 矩阵,GP Tuner 的计算复杂度以 *O(N^3)* 的速度增长,因此不适合于需要大量 Trial 的情形。 [详细说明](./GPTuner.md) -**参数** +**classArgs 要求:** * **optimize_mode** (*'maximize' 或 'minimize', 可选项, 默认值为 'maximize'*) - 如果为 'maximize',表示 Tuner 的目标是将指标最大化。 如果为 'minimize',表示 Tuner 的目标是将指标最小化。 * **utility** (*'ei', 'ucb' 或 'poi', 可选, 默认值为 'ei'*) - 工具函数的类型(采集函数)。 'ei', 'ucb' 和 'poi' 分别对应 '期望的改进(Expected Improvement)', '上限置信度边界(Upper Confidence Bound)' 和 '改进概率(Probability of Improvement)'。 -* **kappa** (*float, 可选, 默认值为 5*) - 用于 'ucb' 函数。 `kappa` 越大,Tuner 的探索性越高。 -* **xi** (*float, 可选, 默认值为 0*) - 用于 'ei' 和 'poi' 函数。 `xi` 越大,Tuner 的探索性越高。 +* **kappa** (*float, 可选, 默认值为 5*) - 用于 'ucb' 函数。 `kappa` 越大, Tuner 的探索性越强。 +* **xi** (*float, 可选, 默认为 0*) - 用于 'ei' 和 'poi' 工具函数。 `xi` 越大, Tuner 的探索性越强。 * **nu** (*float, 可选, 默认为 2.5*) - 用于指定 Matern 核。 nu 越小,近似函数的平滑度越低。 * **alpha** (*float, 可选, 默认值为 1e-6*) - 用于高斯过程回归器。 值越大,表示观察中的噪声水平越高。 * **cold_start_num** (*int, 可选, 默认值为 10*) - 在高斯过程前执行随机探索的数量。 随机探索可帮助提高探索空间的广泛性。 * **selection_num_warm_up** (*int, 可选, 默认为 1e5*) - 用于获得最大采集函数而评估的随机点数量。 * **selection_num_starting_points** (*int, 可选, 默认为 250*) - 预热后,从随机七十点运行 L-BFGS-B 的次数。 -**示例** +**配置示例:** ```yaml # config.yml @@ -426,9 +426,9 @@ tuner: **建议场景** -PPO Tuner 是基于 PPO 算法的强化学习 Tuner。 当在 Trial 代码中使用 NNI 的 NAS 接口进行神经网络架构搜索时,推荐使用 PPO Tuner。 一般来说,尽管PPO算法比其它强化学习算法效率更高,但强化学习算法需要更多的计算资源。 因此,建议在有大量计算资源时,再使用此 Tuner。 可以在简单的任务上尝试,如 [mnist-nas](https://github.com/microsoft/nni/tree/master/examples/trials/mnist-nas) 示例。 [查看详细信息](./PPOTuner.md) +PPO Tuner 是基于 PPO 算法的强化学习 Tuner。 PPOTuner 可用于使用 NNI NAS 接口进行的神经网络结构搜索。 一般来说,尽管 PPO 算法比其它强化学习算法效率更高,但强化学习算法需要更多的计算资源。 当有大量可用的计算资源时,才建议使用此 Tuner。 可以在简单的任务上尝试,如 [mnist-nas](https://github.com/microsoft/nni/tree/master/examples/trials/mnist-nas) 示例。 [查看详细信息](./PPOTuner.md) -**参数** +**classArgs 要求:** * **optimize_mode** (*'maximize' 或 'minimize'*) - 如果为 'maximize',表示 Tuner 的目标是将指标最大化。 如果为 'minimize',表示 Tuner 的目标是将指标最小化。 * **trials_per_update** (*int, 可选, 默认为 20*) - 每次更新的 Trial 数量。 此数字必须可被 minibatch_size 整除。 推荐将 `trials_per_update` 设为 `trialConcurrency` 的倍数,以提高 Trial 的并发效率。 @@ -442,7 +442,7 @@ PPO Tuner 是基于 PPO 算法的强化学习 Tuner。 当在 Trial 代码中使 * **lam** (*float, 可选, 默认为 0.95*) - Advantage estimation discounting factor (论文中的 lambda). * **cliprange** (*float, 可选, 默认为 0.2*) - PPO 算法的 cliprange, 为常数。 -**示例** +**配置示例:** ```yaml # config.yml diff --git a/docs/zh_CN/Tuner/EvolutionTuner.md b/docs/zh_CN/Tuner/EvolutionTuner.md index 3b878c5f08..743c9ad18b 100644 --- a/docs/zh_CN/Tuner/EvolutionTuner.md +++ b/docs/zh_CN/Tuner/EvolutionTuner.md @@ -2,4 +2,4 @@ ## Naïve Evolution(进化算法) -进化算法来自于 [Large-Scale Evolution of Image Classifiers](https://arxiv.org/pdf/1703.01041.pdf)。 它会基于搜索空间随机生成一个种群。 在每一代中,会选择较好的结果,并对其下一代进行一些变异(例如,改动一个超参,增加或减少一层)。 进化算法需要很多次 Trial 才能有效,但它也非常简单,也很容易扩展新功能。 \ No newline at end of file +进化算法来自于 [Large-Scale Evolution of Image Classifiers](https://arxiv.org/pdf/1703.01041.pdf)。 它会基于搜索空间随机生成一个种群。 在每一代中,会选择较好的结果,并对其下一代进行一些变异(例如,改动一个超参,增加或减少一层,等)。 进化算法需要很多次 Trial 才能有效,但它也非常简单,也很容易扩展新功能。 \ No newline at end of file diff --git a/docs/zh_CN/Tuner/GPTuner.md b/docs/zh_CN/Tuner/GPTuner.md index d1f1c117a2..5ec4719f92 100644 --- a/docs/zh_CN/Tuner/GPTuner.md +++ b/docs/zh_CN/Tuner/GPTuner.md @@ -4,7 +4,7 @@ 贝叶斯优化会构建一个能最好的描述优化目标的后验分布函数(使用高斯过程)。 随着观测值的增加,后验分布会得到改善,会在参数空间中确定哪些范围值得进一步探索,哪一些不值得。 -GP Tuner 被设计为通过最大化或最小化步数来找到最接近最优结果的参数组合。 GP Tuner 使用了代理优化问题(找到采集函数的最大值)。虽然这仍然是个难题,但成本更低(从计算的角度来看),并且有通用的工具。 因此,贝叶斯优化适合于采样函数的成本非常高时来使用。 +GP Tuner 被设计为通过最大化或最小化步数来找到最接近最优结果的参数组合。 GP Tuner 使用了代理优化问题(找到采集函数的最大值)。虽然这仍然是个难题,但成本更低(从计算的角度来看),并且适合于作为通用工具。 因此,贝叶斯优化适合于采样函数的成本非常高时来使用。 注意,搜索空间接受的类型包括 `randint`, `uniform`, `quniform`, `loguniform`, `qloguniform`,以及数值的 `choice`。 diff --git a/docs/zh_CN/Tuner/HyperbandAdvisor.md b/docs/zh_CN/Tuner/HyperbandAdvisor.md index 75a96e3a69..5cacc3cb05 100644 --- a/docs/zh_CN/Tuner/HyperbandAdvisor.md +++ b/docs/zh_CN/Tuner/HyperbandAdvisor.md @@ -6,9 +6,9 @@ ## 2. 实现并行 -首先,此示例是基于 MsgDispatcherBase 来实现的自动机器学习算法,而不是基于 Tuner 和Assessor。 这种实现方法下,Hyperband 集成了 Tuner 和 Assessor 两者的功能,因而将它叫做 Advisor。 +首先,此示例是基于 MsgDispatcherBase 来实现的自动机器学习算法,而不是基于 Tuner 和 Assessor。 这种实现方法下,Hyperband 集成了 Tuner 和 Assessor 两者的功能,因而将它叫做 Advisor。 -其次,本实现完全利用了 Hyperband 内部的并行性。 具体来说,下一个分组不会严格的在当前分组结束后再运行,只要有资源,就可以开始运行新的分组。 +其次,本实现完全利用了 Hyperband 内部的并行性。 具体来说,下一个分组不会严格的在当前分组结束后再运行。 只要有资源,就可以开始运行新的分组。 ## 3. 用法 @@ -26,7 +26,7 @@ optimize_mode: maximize -注意,一旦使用了 Advisor,就不能在配置文件中添加 Tuner 和 Assessor。 使用 Hyperband 时,Trial 代码收到的超参(如键值对)中,除了用户定义的超参,会多一个 `TRIAL_BUDGET`。 **使用 `TRIAL_BUDGET`,Trial 能够控制其运行的时间。

      +注意,一旦使用了 Advisor,就不能在配置文件中添加 Tuner 和 Assessor。 使用 Hyperband 时,Trial 代码收到的超参(如键值对)中,会多一个用户定义的 `TRIAL_BUDGET`。 **使用 `TRIAL_BUDGET`,Trial 能够控制其运行的时间。

      对于 Trial 代码中 `report_intermediate_result(metric)` 和 `report_final_result(metric)` 的**`指标` 应该是数值,或者用一个 dict,并保证其中有键值为 default 的项目,其值也为数值型**。 这是需要进行最大化或者最小化优化的数值,如精度或者损失度。 @@ -49,8 +49,8 @@ 关于如何实现 Trial 代码,参考 `examples/trials/mnist-hyperband/` 中的说明。 -## 4. 待改进 +## 4. 未来的改进 -当前实现的 Hyperband 算法可以通过改进支持的提前终止算法来提高,原因是最好的 `n/eta` 个配置并不一定都表现很好。 不好的配置可以更早的终止。 +当前实现的 Hyperband 算法可以通过改进支持的提前终止算法来提高,因为最好的 `n/eta` 个配置并不一定都表现很好。 不好的配置应该更早的终止。 在当前实现中,遵循了[此论文](https://arxiv.org/pdf/1603.06560.pdf)的设计,配置都是随机生成的。 要进一步提升,配置生成过程可以利用更高级的算法。 \ No newline at end of file diff --git a/docs/zh_CN/Tuner/HyperoptTuner.md b/docs/zh_CN/Tuner/HyperoptTuner.md index 390aecdd37..b1556920e3 100644 --- a/docs/zh_CN/Tuner/HyperoptTuner.md +++ b/docs/zh_CN/Tuner/HyperoptTuner.md @@ -6,7 +6,7 @@ Tree-structured Parzen Estimator (TPE) 是一种 sequential model-based optimiza ### TPE 的并行优化 -为了利用多个计算节点,TPE 方法是异步运行的,这样能避免浪费时间等待 Trial 评估的完成。 此算法设计的初衷是优化序列。 当在高并发情况下使用 TPE 时,性能会非常差。 通过 Constant Liar 算法优化了这种情况。 关于优化的原理,参考[文档](../CommunitySharings/ParallelizingTpeSearch.md)。 +为了利用多个计算节点,TPE 方法是异步运行的,这样能避免浪费时间等待 Trial 评估的完成。 对原始算法设计进行了顺序计算优化。 如果要大并发的使用 TPE,性能将会较差。 通过 Constant Liar 算法优化了这种情况。 关于优化的原理,参考[文档](../CommunitySharings/ParallelizingTpeSearch.md)。 ### 用法 @@ -21,15 +21,15 @@ tuner: constant_liar_type: min ``` -**参数** +**classArgs 要求:** -* **optimize_mode** (*maximize 或 minimize, 可选项, 默认值为 maximize*) - 如果为 'maximize',表示 Tuner 的目标是将指标最大化。 如果为 'minimize',表示 Tuner 的目标是将指标最小化。 +* **optimize_mode** (*maximize 或 minimize, 可选项, 默认值为 maximize*) - 如果为 'maximize',表示 Tuner 会试着最大化指标。 如果为 'minimize',表示 Tuner 的目标是将指标最小化。 * **parallel_optimize** (*bool, 可选, 默认值为 False*) - 如果为 True,TPE 会使用 Constant Liar 算法来优化并行超参调优。 否则,TPE 不会区分序列或并发的情况。 * **constant_liar_type** (*min、max 或 mean, 可选, 默认值为 min*) - 使用的 constant liar 类型,会在 X 点根据 y 的取值来确定。对应三个值:min{Y}, max{Y}, 和 mean{Y}。 ## Random Search(随机搜索) -[Random Search for Hyper-Parameter Optimization](http://www.jmlr.org/papers/volume13/bergstra12a/bergstra12a.pdf) 中介绍了随机搜索惊人的简单和效果。 建议当不清楚超参的先验分布时,采用随机搜索作为基准。 +[Random Search for Hyper-Parameter Optimization](http://www.jmlr.org/papers/volume13/bergstra12a/bergstra12a.pdf) 中介绍了随机搜索惊人的简单和效果。 建议在不知道超参数的先验分布时,使用随机搜索作为基准。 ## Anneal(退火算法) diff --git a/docs/zh_CN/Tuner/MetisTuner.md b/docs/zh_CN/Tuner/MetisTuner.md index 40511b737c..03a5096afe 100644 --- a/docs/zh_CN/Tuner/MetisTuner.md +++ b/docs/zh_CN/Tuner/MetisTuner.md @@ -2,18 +2,18 @@ ## Metis Tuner -大多数调参工具仅仅预测最优配置,而 [Metis](https://www.microsoft.com/zh-cn/research/publication/metis-robustly-tuning-tail-latencies-cloud-systems/) 的优势在于有两个输出:(a) 最优配置的当前预测结果, 以及 (b) 下一次 Trial 的建议。 不再需要随机猜测! +[Metis](https://www.microsoft.com/en-us/research/publication/metis-robustly-tuning-tail-latencies-cloud-systems/) 相对于别的调优算法,有几个优势。 大多数调参工具仅仅预测最优配置,而 Metis 具有两个输出,最优配置的预测, 以及下一次 Trial 的建议。 不再需要随机猜测! 大多数工具假设训练集没有噪声数据,但 Metis 会知道是否需要对某个超参重新采样。 大多数工具都有着重于在已有结果上继续发展的问题,而 Metis 的搜索策略可以在探索,发展和重新采样(可选)中进行平衡。 -Metis 属于基于序列的贝叶斯优化 (SMBO) 的类别,它也基于贝叶斯优化框架。 为了对超参-性能空间建模,Metis 同时使用了高斯过程(Gaussian Process)和高斯混合模型(GMM)。 由于每次 Trial 都可能有很高的时间成本,Metis 大量使用了已有模型来进行推理计算。 在每次迭代中,Metis 执行两个任务: +Metis 属于基于序列的贝叶斯优化 (SMBO) 算法的类别,它也基于贝叶斯优化框架。 为了对超参-性能空间建模,Metis 同时使用了高斯过程(Gaussian Process)和高斯混合模型(GMM)。 由于每次 Trial 都可能有很高的时间成本,Metis 大量使用了已有模型来进行推理计算。 在每次迭代中,Metis 执行两个任务: -在高斯过程空间中找到全局最优点。 这一点表示了最佳配置。 +* 在高斯过程空间中找到全局最优点。 这一点表示了最佳配置。 -它会标识出下一个超参的候选项。 这是通过对隐含信息的探索、挖掘和重采样来实现的。 +* 它会标识出下一个超参的候选项。 这是通过对隐含信息的探索、挖掘和重采样来实现的。 此 Tuner 搜索空间仅接受 `quniform`,`uniform`,`randint` 和数值的 `choice` 类型。 -更多详情,参考论文:https://www.microsoft.com/en-us/research/publication/metis-robustly-tuning-tail-latencies-cloud-systems/ \ No newline at end of file +更多详情,参考[论文](https://www.microsoft.com/en-us/research/publication/metis-robustly-tuning-tail-latencies-cloud-systems/)。 \ No newline at end of file diff --git a/docs/zh_CN/Tuner/NetworkmorphismTuner.md b/docs/zh_CN/Tuner/NetworkmorphismTuner.md index 60d924d6a5..03bc0ba4fa 100644 --- a/docs/zh_CN/Tuner/NetworkmorphismTuner.md +++ b/docs/zh_CN/Tuner/NetworkmorphismTuner.md @@ -4,7 +4,7 @@ [Autokeras](https://arxiv.org/abs/1806.10282) 是使用 Network Morphism 算法的流行的自动机器学习工具。 Autokeras 的基本理念是使用贝叶斯回归来预测神经网络架构的指标。 每次都会从父网络生成几个子网络。 然后使用朴素贝叶斯回归,从网络的历史训练结果来预测它的指标值。 接下来,会选择预测结果最好的子网络加入训练队列中。 在[此代码](https://github.com/jhfjhfj1/autokeras)的启发下,我们在 NNI 中实现了 Network Morphism 算法。 -要了解 Network Morphism Trial 的用法,参考 [Readme_zh_CN.md](https://github.com/Microsoft/nni/blob/master/examples/trials/network_morphism/README_zh_CN.md),了解更多细节。 +要了解 Network Morphism Trial 的用法,参考 [Readme_zh_CN.md](https://github.com/Microsoft/nni/blob/master/examples/trials/network_morphism/README_zh_CN.md)。 ## 2. 用法 @@ -52,7 +52,7 @@ net = build_graph_from_json(RCV_CONFIG) nni.report_final_result(best_acc) ``` -如果需要保存并**读取最佳模型**,推荐采用以下方法。 +如果需要保存并读取**最佳模型**,推荐采用以下方法。 ```python # 1. 使用 NNI API @@ -100,7 +100,7 @@ loaded_model = torch.load("model-{}.pt".format(model_id)) ## 3. 文件结构 -Tuner 有大量的文件、函数和类。 这里只简单介绍最重要的文件: +Tuner 有大量的文件、函数和类。 这里简单介绍最重要的文件: - `networkmorphism_tuner.py` 是使用 network morphism 算法的 Tuner。 @@ -117,13 +117,13 @@ Tuner 有大量的文件、函数和类。 这里只简单介绍最重要的文 - `layers.py` 包括模型中用到的所有层。 - `layer_transformer.py` 包含了一些层转换,包括变宽,变深,或在层中增加跳跃连接。 -- `nn.py` 包含生成初始化网的类。 +- `nn.py` 包括生成初始网络的类。 - `metric.py` 包括了一些指标类,如 Accuracy 和 MSE。 - `utils.py` 是使用 Keras 在数据集 `cifar10` 上搜索神经网络的示例。 ## 4. 网络表示的 JSON 示例 -这是定义的中间表示 JSON 示例,在架构搜索过程中会从 Tuner 传到 Trial。 可调用 "json\_to\_graph()" 函数来将 JSON 文件转化为 Pytoch 或 Keras 模型。 示例如下。 +这是定义的中间表示 JSON 示例,在架构搜索过程中会从 Tuner 传到 Trial。 可调用 Trial 代码中的 "json\_to\_graph()" 函数来将 JSON 文件转化为 Pytoch 或 Keras 模型。 ```json { @@ -216,19 +216,19 @@ Tuner 有大量的文件、函数和类。 这里只简单介绍最重要的文 } ``` -每个模型的定义都是一个 JSON 对象 (也可以认为模型是一个 [有向无环图](https://en.wikipedia.org/wiki/Directed_acyclic_graph)): +可将模型视为[有向无环图](https://en.wikipedia.org/wiki/Directed_acyclic_graph)。 每个模型的定义都是一个 JSON 对象: - `input_shape` 是整数的列表,不包括批量维度。 - `weighted` 表示是否权重和偏移值应该包含在此神经网络图中。 - `operation_history` 是保存了所有网络形态操作的列表。 -- `layer_id_to_input_node_ids` 是字典实例,将层的标识映射到输入节点标识。 -- `layer_id_to_output_node_ids` 是字典实例,将层的标识映射到输出节点标识。 -- `adj_list` 是二维列表。 是图的邻接列表。 第一维是张量标识。 在每条边的列表中,元素是两元组(张量标识,层标识)。 +- `layer_id_to_input_node_ids` 是字典,将层的标识映射到输入节点标识。 +- `layer_id_to_output_node_ids` 是字典,将层的标识映射到输出节点标识。 +- `adj_list` 是二维列表,是图的邻接表。 第一维是张量标识。 在每条边的列表中,元素是两元组(张量标识,层标识)。 - `reverse_adj_list` 是与 adj_list 格式一样的反向邻接列表。 - `node_list` 是一个整数列表。 列表的索引是标识。 - `layer_list` 是层的列表。 列表的索引是标识。 - - 对于 `StubConv (StubConv1d, StubConv2d, StubConv3d)`,后面的数字表示节点的输入 id(或 id 列表),节点输出 id,input_channel,filters,kernel_size,stride 和 padding。 + - 对于 `StubConv(StubConv1d, StubConv2d, StubConv3d)`,后面的数字表示节点的输入 id(或 id 列表),节点输出 id,input_channel,filters,kernel_size,stride 和 padding。 - 对于 `StubDense`,后面的数字表示节点的输入 id (或 id 列表),节点输出 id,input_units 和 units。 @@ -242,4 +242,4 @@ Tuner 有大量的文件、函数和类。 这里只简单介绍最重要的文 ## 5. TODO -下一步,会将 API 从固定的网络生成方法改为更多的网络操作生成方法。 此外,还会使用 ONNX 格式来替代 JSON 作为中间表示结果。 \ No newline at end of file +下一步,会将 API 从固定网络生成器,改为有更多可用操作的网络生成器。 会使用 ONNX 格式来替代 JSON 作为中间表示结果。 \ No newline at end of file diff --git a/docs/zh_CN/Tuner/PPOTuner.md b/docs/zh_CN/Tuner/PPOTuner.md index e09be75ba4..af233374ff 100644 --- a/docs/zh_CN/Tuner/PPOTuner.md +++ b/docs/zh_CN/Tuner/PPOTuner.md @@ -2,7 +2,7 @@ ## PPOTuner -这是通常用于 NAS 接口的 NNI Tuner,使用了 [PPO 算法](https://arxiv.org/abs/1707.06347)。 此实现继承了[这里](https://github.com/openai/baselines/tree/master/baselines/ppo2)的主要逻辑,(即 OpenAI 的 PPO2),并为 NAS 场景做了适配。 +这是一个用于 NNI 神经网络架构搜索(NAS)接口的 Tuner。 它使用了 [ppo 算法](https://arxiv.org/abs/1707.06347)。 此实现继承了 [OpenAI 的 ppo2 实现](https://github.com/openai/baselines/tree/master/baselines/ppo2)的主要逻辑,并为 NAS 场景做了适配。 它能成功调优 [mnist-nas 示例](https://github.com/microsoft/nni/tree/master/examples/trials/mnist-nas),结果如下: @@ -12,7 +12,7 @@ ![](../../img/enas_search_space.png) -上图是某个选定的架构,用来展示搜索空间。 每个方块是一层,其操作可从 6 个操作中选择。 每条虚线是直通连接,每个方块都可以有 0 或 1 条直通连接获得前面层的输出。 **注意**,在原始的宏搜索空间中,每个方块层可选择任意条直通连接,在此实现中,仅允许 0 或 1条。 +上图是所选的结构。 每个方块是一层,可从 6 个操作中选择。 每条虚线是直通连接,每个方块都可以有 0 或 1 条直通连接获得前面层的输出。 **注意**,在原始的宏搜索空间中,每个方块层可选择任意条直通连接,在此实现中,仅允许 0 或 1条。 结果如下图所示([配置文件](https://github.com/microsoft/nni/blob/master/examples/trials/nas_cifar10/config_ppo.yml)): diff --git a/docs/zh_CN/Tutorial/HowToUseDocker.md b/docs/zh_CN/Tutorial/HowToUseDocker.md index a584c42b14..32ffcabfe5 100644 --- a/docs/zh_CN/Tutorial/HowToUseDocker.md +++ b/docs/zh_CN/Tutorial/HowToUseDocker.md @@ -2,94 +2,94 @@ ## 概述 -[Docker](https://www.docker.com/) 是一种工具, 可通过启动容器, 使用户能够更轻松地根据自己的操作系统部署和运行应用程序。 Docker 不是虚拟机, 它不创建虚拟操作系统, 但是它允许不同的应用程序使用相同的操作系统内核, 并通过容器隔离不同的应用程序。 +[Docker](https://www.docker.com/) 是一种工具, 可通过启动容器, 使用户能够更轻松地根据自己的操作系统部署和运行应用程序。 Docker 不是虚拟机,它不创建虚拟操作系统,但它允许不同的应用程序使用相同的操作系统内核,并通过容器隔离不同的应用程序。 -用户可以使用docker进行 NNI 实验, NNI 在docker hub上提供了一个官方的镜像 [msranni/nni](https://hub.docker.com/r/msranni/nni)。 +用户可使用 Docker 来启动 NNI Experiment。 NNI 在 Docker Hub 上也提供了官方的 Docker 映像 [msranni/nni](https://hub.docker.com/r/msranni/nni)。 ## 在本机使用docker -### 第一步:docker的安装 +### 第一步:Docker 的安装 -在你开始使用docker进行NNI实验之前,你首先需要在本地机器上安装docker运行程序。 [参考](https://docs.docker.com/install/linux/docker-ce/ubuntu/) +在开始使用 Docker 运行 NNI Experiment 前,首先需要在本机安装 Docker 运行程序。 [参考这里](https://docs.docker.com/install/linux/docker-ce/ubuntu/)。 -### 第二步:启动docker容器 +### 第二步:启动 Docker 容器 -如果你已经在本地机器上安装了docker程序,你可以启动docker容器来运行NNI实验了。 因为NNI会在docker容器里面启动web UI进程,并且监听一个端口,因此你需要指定一个在主机和docker容器里面的端口映射,这个映射可以让你在容器外面访问docker容器里面的进程。 通过访问主机的ip和端口,你就可以访问容器里面的Web网页进程了。 +如果已经在本地机器上安装了 Docker 程序,可以启动 Docker 容器来运行 NNI 示例。 因为 NNI 会在 Docker 容器里启动 Web 界面进程,并监听端口,因此需要指定一个在主机和 Docker 容器映射的端口,可在容器外访问 Docker 容器里的进程。 通过访问主机的 IP 和端口,就可以访问容器里的 Web 网页进程了。 -例如,你可以通过如下命令来启动docker容器: +例如,通过如下命令来启动 Docker 容器: docker run -i -t -p [hostPort]:[containerPort] [image] --i: 使用交互模式启动docker +-i: 使用交互模式启动 Docker。 --t: Docker分配一个输入终端。 +-t: 为 Docker 分配一个输入终端。 -p: 端口映射,映射主机端口和容器端口。 -可以参考[这里](https://docs.docker.com/v17.09/edge/engine/reference/run/),获取更多的命令参考。 +更多命令信息,可[参考这里](https://docs.docker.com/v17.09/edge/engine/reference/run/)。 注意: - NNI只支持Ubuntu和macOS操作系统,请指定正确的docker镜像。如果你希望在docker里面使用gpu,请使用nvidia-docker。 + NNI 目前仅支持本机模式下的 Ubuntu 和 macOS 系统,请使用正确的 Docker 映像类型。 如果想要在 Docker 容器里面使用 GPU,请使用 nvidia-docker。 -### 步骤3:在docker容器里面运行NNI +### 第三步:在 Docker 容器里运行 NNI -如果你直接使用NNI的官方镜像`msranni/nni`来启动实验,你可以直接使用`nnictl`命令。 NNI的官方镜像有最基础的python环境和深度学习框架。 +如果直接使用 NNI 的官方镜像 `msranni/nni` 来启动 Experiment,可以直接使用 `nnictl` 命令。 NNI 官方镜像有最基础的 Python 环境和深度学习框架。 如果使用自己的 Docker 镜像,需要首先[安装 NNI](InstallationLinux.md)。 -如果你想要使用NNI的官方例子,你可以通过以下git命令来克隆NNI: +如果要使用 NNI 的官方示例,可以通过以下 git 命令来克隆 NNI: git clone https://github.com/Microsoft/nni.git -然后可以进入`nni/examples/trials`文件夹来启动实验。 +然后可以进入 `nni/examples/trials` 文件夹来启动 Experiment。 -等你准备完NNI环境,你可以通过`nnictl`命令来启动实验,[参考](QuickStart.md). +准备好 NNI 的环境后,可使用 `nnictl` 命令开始新的 Experiment。 [参考这里](QuickStart.md)。 -## 在远程平台上运行docker +## 在远程平台上运行 Docker -NNI支持在[远程平台](../TrainingService/RemoteMachineMode.md)上启动实验,在远程机器里运行任务。 因为docker可以运行独立的Ubuntu系统和SSH服务,因此docker容器可以作为远程平台来运行NNI. +NNI 支持在[远程平台](../TrainingService/RemoteMachineMode.md)上启动 Experiment,并在远程机器里运行 Trial。 因为 Docker 可以运行独立的 Ubuntu 系统和 SSH 服务,因此 Docker 容器可以作为远程平台来运行 NNI。 -### 步骤1:设置docker环境 +### 第一步:设置 Docker 环境 -你首先应该在远程机器上安装docker工具,[参考](https://docs.docker.com/install/linux/docker-ce/ubuntu/). +首先在远程机器上安装 Docker 工具,[参考这里](https://docs.docker.com/install/linux/docker-ce/ubuntu/)。 -为了保证docker容器可以被NNI实验连接上,你应该在你自己的docker容器里面安装SSH服务,并做SSH相关配置。 如果你想在docker容器里面使用SSH服务,你应该配置SSH密码登录或者私钥登录,[参考](https://docs.docker.com/engine/examples/running_ssh_service/)。 +为保证 Docker 容器可以被 NNI Experiment 连接上,要在自己的 Docker 容器里安装 SSH 服务,或使用已经配置好 SSH 的映像。 如果要在 Docker 容器里使用 SSH 服务,需要配置 SSH 密码登录或者私钥登录,[参考这里](https://docs.docker.com/engine/examples/running_ssh_service/)。 注意: - NNI的官方镜像msranni/nni暂时不支持SSH服务,你应该构建自己的带有SSH服务的镜像,或者使用其他的带有SSH服务的镜像。 + NNI 的官方镜像 msranni/nni 暂不支持 SSH 服务,应构建自己的带有 SSH 服务的映像,或者使用其他的带有 SSH 服务的镜像。 -### 第二步:在远程机器上启动docker容器 +### 第二步:在远程机器上启动 Dokcer 容器 -SSH容器需要一个端口,你需要把docker的SSH服务端口暴露给NNI作为连接端口。 例如,如果你设置容器的端口**`A`**作为SSH端口,你应该把端口**`A`**映射到主机的端口**`B`**,NNI会连接端口**`B`**作为SSH服务端口,你的主机会把连接到端口**`B`**的连接映射到端口**`A`**,NNI就可以连接到你的容器中了。 +SSH 服务需要端口,要把 Docker 的 SSH 服务端口暴露给 NNI 作为连接端口。 例如,如果设置容器的端口 **`A`** 作为 SSH 端口,应把端口 **`A`** 映射到主机的端口 **`B`**,NNI 会连接端口**`B`** 作为 SSH 服务端口,主机会把连接到端口 **`B`** 的连接映射到端口 **`A`**,NNI 就可以连接到容器中了。 -例如,你可以通过如下命令来启动docker容器: +例如,通过如下命令来启动 Docker 容器: docker run -dit -p [hostPort]:[containerPort] [image] -`containerPort`是在docker容器中指定的端口,`hostPort`是主机的端口。 你可以设置你的NNI配置,连接到`hostPort`,这个连接会被转移到你的docker容器中。 更多的命定信息,可以[参考](https://docs.docker.com/v17.09/edge/engine/reference/run/). +`containerPort`是在 Docker 容器中指定的端口,`hostPort` 是主机的端口。 可设置 NNI 配置,连接到 `hostPort`,这个连接会被转发到 Docker 容器。 更多命令信息,可[参考这里](https://docs.docker.com/v17.09/edge/engine/reference/run/)。 注意: - 如果你使用你自己构建的docker容器,请保证这个容器中有基础的python运行时环境和NNI SDK环境。 如果你想要在docker容器里面使用gpu,请使用nvidia-docker。 + 如果使用自己构建的 Docker 映像,确保有基础的 Python 运行时和 NNI SDK 环境。 如果想要在 Docker 容器里面使用 GPU,请使用 nvidia-docker。 -### 步骤三:运行NNI实验 +### 第三步:运行 NNI Experiment -你可以在你的配置文件中,设置训练平台为远程平台,然后设置`machineList`配置。[参考](../TrainingService/RemoteMachineMode.md)。 注意你应该设置正确的`port`,`username`, `passwd`或者`sshKeyPath`。 +可以在配置文件中,设置训练平台为远程平台,然后设置 `machineList` 配置。[参考这里](../TrainingService/RemoteMachineMode.md)。 注意应该设置正确的 `port`,`username`,以及 `passwd` 或 `sshKeyPath`。 -`port`: 主机的端口,映射到docker的SSH端口中。 +`port`: 主机的端口,映射到 Docker 的 SSH 端口。 -`username`: docker容器的用户名。 +`username`:Docker 容器的用户名。 -`passWd: ` docker容器的密码。 +`passWd: ` Docker 容器的密码。 -`sshKeyPath:` docker容器私钥的存储路径。 +`sshKeyPath:` Docker 容器私钥的存储路径。 -设置完配置文件,你就可以启动实验了,[参考](QuickStart.md)。 \ No newline at end of file +设置完配置文件,就可以启动 Experiment 了,[参考这里](QuickStart.md)。 \ No newline at end of file diff --git a/docs/zh_CN/Tutorial/InstallationLinux.md b/docs/zh_CN/Tutorial/InstallationLinux.md index aa48498c4e..54b06dc407 100644 --- a/docs/zh_CN/Tutorial/InstallationLinux.md +++ b/docs/zh_CN/Tutorial/InstallationLinux.md @@ -18,7 +18,7 @@ 先决条件:`python 64-bit >=3.5`, `git`, `wget` bash - git clone -b v1.3 https://github.com/Microsoft/nni.git + git clone -b v1.4 https://github.com/Microsoft/nni.git cd nni ./install.sh @@ -33,7 +33,7 @@ * 通过克隆源代码下载示例。 ```bash - git clone -b v1.3 https://github.com/Microsoft/nni.git + git clone -b v1.4 https://github.com/Microsoft/nni.git ``` * 运行 MNIST 示例。 @@ -71,7 +71,7 @@ You can use these commands to get more information about the experiment ----------------------------------------------------------------------- ``` -* 在浏览器中打开 `Web UI url`,可看到下图的 Experiment 详细信息,以及所有的 Trial 任务。 查看[这里](../Tutorial/WebUI.md)的更多页面。 +* 在浏览器中打开 `Web UI url`,可看到下图的实验详细信息,以及所有的尝试任务。 查看[这里](../Tutorial/WebUI.md)的更多页面。 ![概述](../../img/webui_overview_page.png) diff --git a/docs/zh_CN/Tutorial/InstallationWin.md b/docs/zh_CN/Tutorial/InstallationWin.md index 59320870ec..f045e94655 100644 --- a/docs/zh_CN/Tutorial/InstallationWin.md +++ b/docs/zh_CN/Tutorial/InstallationWin.md @@ -19,7 +19,7 @@ 先决条件:`python 64-bit >=3.5`, `git`, `PowerShell` ```bash - git clone -b v1.3 https://github.com/Microsoft/nni.git + git clone -b v1.4 https://github.com/Microsoft/nni.git cd nni powershell -ExecutionPolicy Bypass -file install.ps1 ``` @@ -31,7 +31,7 @@ * 通过克隆源代码下载示例。 ```bash - git clone -b v1.3 https://github.com/Microsoft/nni.git + git clone -b v1.4 https://github.com/Microsoft/nni.git ``` * 运行 MNIST 示例。 @@ -70,7 +70,7 @@ You can use these commands to get more information about the experiment ----------------------------------------------------------------------- ``` -* 在浏览器中打开 `Web UI url`,可看到下图的 Experiment 详细信息,以及所有的 Trial 任务。 查看[这里](../Tutorial/WebUI.md)的更多页面。 +* 在浏览器中打开 `Web UI url`,可看到下图的实验详细信息,以及所有的尝试任务。 查看[这里](../Tutorial/WebUI.md)的更多页面。 ![概述](../../img/webui_overview_page.png) @@ -106,7 +106,7 @@ You can use these commands to get more information about the experiment 检查 Trial 日志文件来了解详情。 -如果存在 stderr 文件,也需要查看其内容。 可能的错误情况包括: +如果存在 stderr 文件,也需要查看其内容。 两种可能的情况是: * 忘记将 Experiment 配置的 Trial 命令中的 `python3` 改为 `python`。 * 忘记安装 Experiment 的依赖,如 TensorFlow,Keras 等。 @@ -122,7 +122,7 @@ You can use these commands to get more information about the experiment 注意: -* 如果遇到 `Segmentation fault` 这样的错误,参考[常见问答](FAQ.md)。 +* 如果遇到 `Segmentation fault` 的错误,请参阅[常见问题](FAQ.md) ## 更多 diff --git a/docs/zh_CN/Tutorial/QuickStart.md b/docs/zh_CN/Tutorial/QuickStart.md index 164636e536..6b1f985ef0 100644 --- a/docs/zh_CN/Tutorial/QuickStart.md +++ b/docs/zh_CN/Tutorial/QuickStart.md @@ -2,7 +2,7 @@ ## 安装 -当前支持 Linux,macOS 和 Windows,在 Ubuntu 16.04 或更高版本,macOS 10.14.1 以及 Windows 10.1809 上进行了测试。 在 `python >= 3.5` 的环境中,只需要运行 `pip install` 即可完成安装。 +目前支持 Linux、macOS 和 Windows。 Ubuntu 16.04 或更高版本、macOS 10.14.1 和 Windows 10.1809 均经过测试并支持。 在 `python >= 3.5` 的环境中,只需要运行 `pip install` 即可完成安装。 **Linux 和 macOS** @@ -19,12 +19,12 @@ 注意: * 在 Linux 和 macOS 上,如果要将 NNI 安装到当前用户的 home 目录中,可使用 `--user`,则不需要特殊权限。 -* 如果遇到如`Segmentation fault` 这样的任何错误请参考[常见问题](FAQ.md)。 +* 如果遇到 `Segmentation fault` 这样的错误,参考[常见问答](FAQ.md)。 * 有关 NNI 的`系统要求`,参考[在 Linux 和 macOS 上安装](InstallationLinux.md) 或 [Windows](InstallationWin.md)。 ## MNIST 上的 "Hello World" -NNI 是一个能进行自动机器学习实验的工具包。 它可以自动进行获取超参、运行 Trial,测试结果,调优超参的循环。 下面会展示如何使用 NNI 来找到最佳超参组合。 +NNI 是一个能进行自动机器学习实验的工具包。 它可以自动进行获取超参、运行 Trial,测试结果,调优超参的循环。 在这里,将演示如何使用 NNI 帮助找到 MNIST 模型的最佳超参数。 这是还**没有 NNI** 的示例代码,用 CNN 在 MNIST 数据集上训练: @@ -67,7 +67,7 @@ NNI 用来帮助超参调优。它的流程如下: 7: return 最好的实验结果 ``` -如果需要使用 NNI 来自动训练模型,找到最佳超参,需要如下三步: +如果需要使用 NNI 来自动训练模型,找到最佳超参,需要根据代码,进行如下三步改动: **启动 Experiment 的三个步骤** @@ -114,7 +114,7 @@ NNI 用来帮助超参调优。它的流程如下: *实现代码:[mnist.py](https://github.com/Microsoft/nni/tree/master/examples/trials/mnist-tfv1/mnist.py)* -**第三步**:定义 YAML 格式的`配置`文件,其中声明了搜索空间和 Trial 文件的`路径`,以及`其它信息`,如调优算法,最大尝试次数,最大运行时间等等。 +**第三步**:定义 YAML 格式的`配置`文件,其中声明了搜索空间和 Trial 文件的`路径`。 它还提供其他信息,例如调整算法,最大 Trial 运行次数和最大持续时间的参数。 ```yaml authorName: default @@ -135,7 +135,7 @@ trial: gpuNum: 0 ``` -注意:**在 Windows 上,需要将 Trial 命令的 `python3` 改为 `python`** +注意:**在 Windows 上,需要将 Trial 命令的 `python3` 改为 `python`**。 *实现代码:[config.yml](https://github.com/Microsoft/nni/tree/master/examples/trials/mnist-tfv1/config.yml)* @@ -190,7 +190,7 @@ You can use these commands to get more information about the experiment ----------------------------------------------------------------------- ``` -如果根据上述步骤准备好了相应 `Trial`, `搜索空间` 和 `配置`,并成功创建的 NNI 任务。NNI 会自动开始通过配置的搜索空间来运行不同的超参集合,搜索最好的超参。 通过 Web 界面可看到 NNI 的进度。 +如果根据上述步骤准备好了相应 `Trial`, `搜索空间`和`配置`,并成功创建的 NNI 任务。NNI 会自动开始通过配置的搜索空间来运行不同的超参集合,搜索最好的超参。 通过 Web 界面可看到 NNI 的进度。 ## Web 界面 @@ -200,17 +200,17 @@ You can use these commands to get more information about the experiment Web 地址为:[IP 地址]:8080 ``` -在浏览器中打开 `Web 界面地址`(即:`[IP 地址]:8080`),就可以看到 Experiment 的详细信息,以及所有的 Trial 任务。 如果无法打开终端中的 Web 界面链接,可以参考 [FAQ](FAQ.md)。 +在浏览器中打开 `Web 界面地址`(即:`[IP 地址]:8080`),就可以看到 Experiment 的详细信息,以及所有的 Trial 任务。 如果无法打开终端中的 Web 界面链接,可以参考[常见问题](FAQ.md)。 ### 查看概要页面 -点击标签 "Overview"。 +点击 "Overview" 标签。 -Experiment 相关信息会显示在界面上,配置和搜索空间等。 可通过 **Download** 按钮来`下载信息和参数`。 可以在运行中或结束后,随时下载 Experiment 的结果。 +Experiment 相关信息会显示在界面上,配置和搜索空间等。 可通过 **Download** 按钮来下载信息和参数。 可以在 Experiment 运行时随时下载结果,也可以等到执行结束。 ![](../../img/QuickStart1.png) -前 10 个 Trial 结果也会列在 Overview 页面中,可以在 "Trials Detail" 部分浏览所有的 Trial。 +前 10 个 Trial 将列在 Overview 页上。 可以在 "Trials Detail" 页面上浏览所有 Trial。 ![](../../img/QuickStart2.png) @@ -233,10 +233,10 @@ Experiment 相关信息会显示在界面上,配置和搜索空间等。 可 下面是所有 Trial 的状态。 包括: -* Trial 详情:Trial 的 id,持续时间,开始时间,结束时间,状态,精度和搜索空间。 +* Trial 详情:Trial 的 id,持续时间,开始时间,结束时间,状态,精度和搜索空间文件。 * 如果在 OpenPAI 平台上运行,还可以看到 hdfsLog。 -* Kill: 可终止正在运行的任务。 -* 支持搜索某个特定的 Trial。 +* Kill: 可结束在 `Running` 状态的任务。 +* Support: 用于搜索某个指定的 Trial。 ![](../../img/QuickStart6.png) diff --git a/docs/zh_CN/Tutorial/WebUI.md b/docs/zh_CN/Tutorial/WebUI.md index 6ad6ac4234..2706926016 100644 --- a/docs/zh_CN/Tutorial/WebUI.md +++ b/docs/zh_CN/Tutorial/WebUI.md @@ -4,7 +4,7 @@ 点击标签 "Overview"。 -* 查看 Experiment Trial 配置、搜索空间以及结果好的 Trial。 +* 在 Overview 标签上,可看到 Experiment Trial 的概况、搜索空间、以及最好的 Trial 结果。 ![](../../img/webui-img/over1.png) ![](../../img/webui-img/over2.png) @@ -16,11 +16,11 @@ ![](../../img/webui-img/download.png) -* 如果 Experiment 状态为 ERROR,可点击图标,查看 Experiment 错误日志。 +* 如果实验的状态为错误,可以单击错误框中的感叹号来查看日志消息。 ![](../../img/webui-img/log-error.png) ![](../../img/webui-img/review-log.png) -* 点击 "Feedback" 反馈问题。 +* 可点击 "Feedback" 报告任何问题。 ## 查看任务默认指标 @@ -55,17 +55,15 @@ Trial 可能在训练过程中有大量中间结果。 为了更清楚的理解一些 Trial 的趋势,可以为中间结果图设置过滤。 -这样可以发现 Trial 在某个中间结果上会变得更好或更差。 换句话说,这是一个重要的中间结果。 如果要仔细查看这个点,可以在 #Intermediate 中输入其横坐标。 - -并输入这个中间结果的指标范围。 如下图所示,选择了第 4 个中间结果, 并将指标范围设置为了 0.8 -1。 +这样可以发现 Trial 在某个中间结果上会变得更好或更差。 这表明它是一个重要的并相关的中间结果。 如果要仔细查看这个点,可以在 #Intermediate 中输入其 X 坐标。 并输入这个中间结果的指标范围。 在下图中,选择了 No。 并将指标范围设置为了 0.8 -1。 ![](../../img/webui-img/filter-intermediate.png) ## 查看 Trial 状态 -点击 "Trials Detail" 标签查看所有 Trial 的状态。 包括: +点击 "Trials Detail" 标签查看所有 Trial 的状态。 特别是: -* Trial 详情:Trial 的 id,持续时间,开始时间,结束时间,状态,精度和搜索空间。 +* Trial 详情:Trial 的 id,持续时间,开始时间,结束时间,状态,精度和搜索空间文件。 ![](../../img/webui-img/detail-local.png) diff --git a/docs/zh_CN/autotune_ref.md b/docs/zh_CN/autotune_ref.md new file mode 100644 index 0000000000..7c6cff6cc6 --- /dev/null +++ b/docs/zh_CN/autotune_ref.md @@ -0,0 +1,80 @@ +# 自动调优的 Python API 参考 + +```eval_rst +.. contents:: +``` + +## Trial + +```eval_rst +.. autofunction:: nni.get_next_parameter +.. autofunction:: nni.get_current_parameter +.. autofunction:: nni.report_intermediate_result +.. autofunction:: nni.report_final_result +.. autofunction:: nni.get_experiment_id +.. autofunction:: nni.get_trial_id +.. autofunction:: nni.get_sequence_id +``` + +## Tuner + +```eval_rst +.. autoclass:: nni.tuner.Tuner + :members: + +.. autoclass:: nni.hyperopt_tuner.hyperopt_tuner.HyperoptTuner + :members: + +.. autoclass:: nni.evolution_tuner.evolution_tuner.EvolutionTuner + :members: + +.. autoclass:: nni.smac_tuner.SMACTuner + :members: + +.. autoclass:: nni.gridsearch_tuner.GridSearchTuner + :members: + +.. autoclass:: nni.networkmorphism_tuner.networkmorphism_tuner.NetworkMorphismTuner + :members: + +.. autoclass:: nni.metis_tuner.metis_tuner.MetisTuner + :members: + +.. autoclass:: nni.ppo_tuner.PPOTuner + :members: + +.. autoclass:: nni.batch_tuner.batch_tuner.BatchTuner + :members: + +.. autoclass:: nni.gp_tuner.gp_tuner.GPTuner + :members: +``` + +## Assessor + +```eval_rst +.. autoclass:: nni.assessor.Assessor + :members: + +.. autoclass:: nni.assessor.AssessResult + :members: + +.. autoclass:: nni.curvefitting_assessor.CurvefittingAssessor + :members: + +.. autoclass:: nni.medianstop_assessor.MedianstopAssessor + :members: +``` + +## Advisor + +```eval_rst +.. autoclass:: nni.msg_dispatcher_base.MsgDispatcherBase + :members: + +.. autoclass:: nni.hyperband_advisor.hyperband_advisor.Hyperband + :members: + +.. autoclass:: nni.bohb_advisor.bohb_advisor.BOHB + :members: +``` diff --git a/docs/zh_CN/builtin_assessor.rst b/docs/zh_CN/builtin_assessor.rst index f108f7677d..7ed1aa8d68 100644 --- a/docs/zh_CN/builtin_assessor.rst +++ b/docs/zh_CN/builtin_assessor.rst @@ -1,13 +1,13 @@ 内置 Assessor ================= -为了节省计算资源,在 NNI 中可通过创建 **Assessor**,来配置提前终止策略。 +为了节省计算资源,NNI 支持提前终止策略,并且通过叫做 **Assessor** 的接口来执行此操作。 -Assessor 从 Trial 中接收中间结果,并通过指定的算法决定此 Trial 是否应该终止。 一旦 Trial 满足了提前终止策略(这表示 Assessor 认为最终结果不会太好),Assessor 会终止此 Trial,并将其状态标志为 `"EARLY_STOPPED"`。 +Assessor 从 Trial 中接收中间结果,并通过指定的算法决定此 Trial 是否应该终止。 一旦 Trial 满足了提前终止策略(这表示 Assessor 认为最终结果不会太好),Assessor 会终止此 Trial,并将其状态标志为 `EARLY_STOPPED`。 -这是 MNIST 在使用了 'Curvefitting' Assessor 的 'maximize' 模式后的实验结果,可以看到 Assessor 成功的将大量最终结果不好的 Trial **提前结束** 。 使用 Assessor,能在相同的计算资源下,得到更好的结果。 +这是 MNIST 在 "最大化" 模式下使用 "曲线拟合" Assessor 的实验结果。 可以看到 Assessor 成功的 **提前终止** 了许多结果不好超参组合的 Trial。 使用 Assessor,能在相同的计算资源下,得到更好的结果。 -*实现代码:config_assessor.yml * +*实现代码:[config_assessor.yml](https://github.com/Microsoft/nni/blob/master/examples/trials/mnist-tfv1/config_assessor.yml) * .. image:: ../img/Assessor.png diff --git a/docs/zh_CN/conf.py b/docs/zh_CN/conf.py index d5bec553af..f0f9b1b567 100644 --- a/docs/zh_CN/conf.py +++ b/docs/zh_CN/conf.py @@ -28,7 +28,7 @@ # The short X.Y version version = '' # The full version, including alpha/beta/rc tags -release = 'v1.3' +release = 'v1.4' # -- General configuration --------------------------------------------------- diff --git a/docs/zh_CN/hpo_advanced.rst b/docs/zh_CN/hpo_advanced.rst index 55edf20c26..a3fe19d78e 100644 --- a/docs/zh_CN/hpo_advanced.rst +++ b/docs/zh_CN/hpo_advanced.rst @@ -2,6 +2,8 @@ ================= .. toctree:: + :maxdepth: 2 + 启用多阶段 编写新的 Tuner 编写新的 Assessor diff --git a/docs/zh_CN/hyperparameter_tune.rst b/docs/zh_CN/hyperparameter_tune.rst index c86223e940..0e5a6afd0b 100644 --- a/docs/zh_CN/hyperparameter_tune.rst +++ b/docs/zh_CN/hyperparameter_tune.rst @@ -3,7 +3,7 @@ ############################# 自动调优是 NNI 提供的关键功能之一,主要应用场景是 -超参调优。 Trial 代码是需要被调优的,这里提供了一些常见的 +超参调优。 应用于 Trial 代码的调优。 提供了很多流行的 自动调优算法(称为 Tuner )和一些提前终止算法(称为 Assessor)。 NNI 支持在各种培训平台上运行 Trial,例如,在本地计算机上运行, 在多台服务器上分布式运行,或在 OpenPAI,Kubernetes 等平台上。 @@ -12,7 +12,7 @@ NNI 的其它重要功能,例如模型压缩,特征工程,也可以进一 通过自动调优来提高,这会在介绍具体功能时提及。 NNI 具有高扩展性,高级用户可以定制自己的 Tuner、 Assessor,以及训练平台 -根据自己的需求。 +来适应不同的需求。 .. toctree:: :maxdepth: 2 diff --git a/docs/zh_CN/model_compression.rst b/docs/zh_CN/model_compression.rst index 9e4b00dabf..8c23fb3eca 100644 --- a/docs/zh_CN/model_compression.rst +++ b/docs/zh_CN/model_compression.rst @@ -21,3 +21,4 @@ NNI 中也内置了一些流程的模型压缩算法。 Quantizer 模型加速 自动模型压缩 + 实现 diff --git a/docs/zh_CN/pruners.rst b/docs/zh_CN/pruners.rst new file mode 100644 index 0000000000..ca63d67070 --- /dev/null +++ b/docs/zh_CN/pruners.rst @@ -0,0 +1,16 @@ +############################ +支持的剪枝算法 +############################ + +.. toctree:: + :maxdepth: 1 + + Level Pruner + AGP Pruner + Lottery Ticket Pruner + FPGM Pruner + L1Filter Pruner + L2Filter Pruner + ActivationAPoZRankFilterPruner + ActivationMeanRankFilterPruner + Slim Pruner diff --git a/docs/zh_CN/quantizers.rst b/docs/zh_CN/quantizers.rst new file mode 100644 index 0000000000..74f39e3181 --- /dev/null +++ b/docs/zh_CN/quantizers.rst @@ -0,0 +1,11 @@ +################################# +支持的量化算法 +################################# + +.. toctree:: + :maxdepth: 1 + + Naive Quantizer + QAT Quantizer + DoReFa Quantizer + BNN Quantizer \ No newline at end of file diff --git a/docs/zh_CN/sdk_reference.rst b/docs/zh_CN/sdk_reference.rst index 14e892ed0e..53c1375060 100644 --- a/docs/zh_CN/sdk_reference.rst +++ b/docs/zh_CN/sdk_reference.rst @@ -1,72 +1,10 @@ -########################### +#################### Python API 参考 -########################### +#################### -Trial(尝试) ------------------------- -.. autofunction:: nni.get_next_parameter -.. autofunction:: nni.get_current_parameter -.. autofunction:: nni.report_intermediate_result -.. autofunction:: nni.report_final_result -.. autofunction:: nni.get_experiment_id -.. autofunction:: nni.get_trial_id -.. autofunction:: nni.get_sequence_id +.. toctree:: + :maxdepth: 1 -Tuner(调参器) ------------------------- -.. autoclass:: nni.tuner.Tuner - :members: - -.. autoclass:: nni.hyperopt_tuner.hyperopt_tuner.HyperoptTuner - :members: - -.. autoclass:: nni.evolution_tuner.evolution_tuner.EvolutionTuner - :members: - -.. autoclass:: nni.smac_tuner.SMACTuner - :members: - -.. autoclass:: nni.gridsearch_tuner.GridSearchTuner - :members: - -.. autoclass:: nni.networkmorphism_tuner.networkmorphism_tuner.NetworkMorphismTuner - :members: - -.. autoclass:: nni.metis_tuner.metis_tuner.MetisTuner - :members: - -.. autoclass:: nni.ppo_tuner.PPOTuner - :members: - -.. autoclass:: nni.batch_tuner.batch_tuner.BatchTuner - :members: - -.. autoclass:: nni.gp_tuner.gp_tuner.GPTuner - :members: - -Assessor(评估器) ------------------------- -.. autoclass:: nni.assessor.Assessor - :members: - -.. autoclass:: nni.assessor.AssessResult - :members: - -.. autoclass:: nni.curvefitting_assessor.CurvefittingAssessor - :members: - -.. autoclass:: nni.medianstop_assessor.MedianstopAssessor - :members: - - -Advisor ------------------------- -.. autoclass:: nni.msg_dispatcher_base.MsgDispatcherBase - :members: - -.. autoclass:: nni.hyperband_advisor.hyperband_advisor.Hyperband - :members: - -.. autoclass:: nni.bohb_advisor.bohb_advisor.BOHB - :members: + 自动调优 + NAS \ No newline at end of file diff --git a/docs/zh_CN/training_services.rst b/docs/zh_CN/training_services.rst index 8e75af2ae7..f7bdab9338 100644 --- a/docs/zh_CN/training_services.rst +++ b/docs/zh_CN/training_services.rst @@ -9,3 +9,4 @@ NNI 支持的训练平台介绍 OpenPAI Yarn 模式<./TrainingService/PaiYarnMode> Kubeflow<./TrainingService/KubeflowMode> FrameworkController<./TrainingService/FrameworkControllerMode> + OpenPAI<./TrainingService/DLTSMode> From c261146a209d3963c85c8e0fea26036517c3c0e7 Mon Sep 17 00:00:00 2001 From: Yang yaming Date: Mon, 30 Mar 2020 15:03:56 +0800 Subject: [PATCH 08/12] Add evalutaion scripts for TextNAS (#2220) * Add evalutaion scripts for TextNAS. * Remove duplicated run_sst2.sh and run_sst5.sh. * Minor fixes. * Bug fix. * Adjust the running paramters. * Adopted to nni interface. Co-authored-by: Yaming Yang --- examples/nas/textnas/dataloader.py | 3 +- examples/nas/textnas/retrain.py | 536 ++++++++++++++++++++++++++++ examples/nas/textnas/run_retrain.sh | 41 +++ 3 files changed, 579 insertions(+), 1 deletion(-) create mode 100644 examples/nas/textnas/retrain.py create mode 100755 examples/nas/textnas/run_retrain.sh diff --git a/examples/nas/textnas/dataloader.py b/examples/nas/textnas/dataloader.py index e5a4ed363f..083f1c7413 100644 --- a/examples/nas/textnas/dataloader.py +++ b/examples/nas/textnas/dataloader.py @@ -241,7 +241,8 @@ def init_trainable_embedding(embedding_path, word_id_dict, embed_dim=300): embedding = np.random.random([len(word_id_dict), embed_dim]).astype(np.float32) / 2.0 - 0.25 embedding[0] = np.zeros(embed_dim) # PAD embedding[1] = (np.random.rand(embed_dim) - 0.5) / 2 # UNK - for word, idx in word_id_dict.items(): + for word in sorted(word_id_dict.keys()): + idx = word_id_dict[word] if idx == 0 or idx == 1: continue if word in word_embed_model["mapping"]: diff --git a/examples/nas/textnas/retrain.py b/examples/nas/textnas/retrain.py new file mode 100644 index 0000000000..ab8f5c661c --- /dev/null +++ b/examples/nas/textnas/retrain.py @@ -0,0 +1,536 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +import sys +import os +import logging +import pickle +import shutil +import random +import math + +import time +import datetime +import argparse +import distutils.util + +import numpy as np +import torch +from torch import nn +from torch import optim +from torch.utils.data import DataLoader +import torch.nn.functional as Func + +from model import Model +from nni.nas.pytorch.fixed import apply_fixed_architecture +from dataloader import read_data_sst + + +logger = logging.getLogger("nni.textnas") + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--reset_output_dir", + type=distutils.util.strtobool, + default=True, + help="Whether to clean the output dir if existed. (default: %(default)s)") + parser.add_argument( + "--child_fixed_arc", + type=str, + required=True, + help="Architecture json file. (default: %(default)s)") + parser.add_argument( + "--data_path", + type=str, + default="data", + help="Directory containing the dataset and embedding file. (default: %(default)s)") + parser.add_argument( + "--output_dir", + type=str, + default="output", + help="The output directory. (default: %(default)s)") + parser.add_argument( + "--child_lr_decay_scheme", + type=str, + default="cosine", + help="Learning rate annealing strategy, only 'cosine' supported. (default: %(default)s)") + parser.add_argument( + "--batch_size", + type=int, + default=128, + help="Number of samples each batch for training. (default: %(default)s)") + parser.add_argument( + "--eval_batch_size", + type=int, + default=128, + help="Number of samples each batch for evaluation. (default: %(default)s)") + parser.add_argument( + "--class_num", + type=int, + default=5, + help="The number of categories. (default: %(default)s)") + parser.add_argument( + "--global_seed", + type=int, + default=1234, + help="Seed for reproduction. (default: %(default)s)") + parser.add_argument( + "--max_input_length", + type=int, + default=64, + help="The maximum length of the sentence. (default: %(default)s)") + parser.add_argument( + "--num_epochs", + type=int, + default=10, + help="The number of training epochs. (default: %(default)s)") + parser.add_argument( + "--child_num_layers", + type=int, + default=24, + help="The layer number of the architecture. (default: %(default)s)") + parser.add_argument( + "--child_out_filters", + type=int, + default=256, + help="The dimension of hidden states. (default: %(default)s)") + parser.add_argument( + "--child_out_filters_scale", + type=int, + default=1, + help="The scale of hidden state dimension. (default: %(default)s)") + parser.add_argument( + "--child_lr_T_0", + type=int, + default=10, + help="The length of one cycle. (default: %(default)s)") + parser.add_argument( + "--child_lr_T_mul", + type=int, + default=2, + help="The multiplication factor per cycle. (default: %(default)s)") + parser.add_argument( + "--min_count", + type=int, + default=1, + help="The threshold to cut off low frequent words. (default: %(default)s)") + parser.add_argument( + "--train_ratio", + type=float, + default=1.0, + help="The sample ratio for the training set. (default: %(default)s)") + parser.add_argument( + "--valid_ratio", + type=float, + default=1.0, + help="The sample ratio for the dev set. (default: %(default)s)") + parser.add_argument( + "--child_grad_bound", + type=float, + default=5.0, + help="The threshold for gradient clipping. (default: %(default)s)") + parser.add_argument( + "--child_lr", + type=float, + default=0.02, + help="The initial learning rate. (default: %(default)s)") + parser.add_argument( + "--cnn_keep_prob", + type=float, + default=0.8, + help="Keep prob for cnn layer. (default: %(default)s)") + parser.add_argument( + "--final_output_keep_prob", + type=float, + default=1.0, + help="Keep prob for the last output layer. (default: %(default)s)") + parser.add_argument( + "--lstm_out_keep_prob", + type=float, + default=0.8, + help="Keep prob for the RNN layer. (default: %(default)s)") + parser.add_argument( + "--embed_keep_prob", + type=float, + default=0.8, + help="Keep prob for the embedding layer. (default: %(default)s)") + parser.add_argument( + "--attention_keep_prob", + type=float, + default=0.8, + help="Keep prob for the self-attention layer. (default: %(default)s)") + parser.add_argument( + "--child_l2_reg", + type=float, + default=3e-6, + help="Weight decay factor. (default: %(default)s)") + parser.add_argument( + "--child_lr_max", + type=float, + default=0.002, + help="The max learning rate. (default: %(default)s)") + parser.add_argument( + "--child_lr_min", + type=float, + default=0.001, + help="The min learning rate. (default: %(default)s)") + parser.add_argument( + "--child_optim_algo", + type=str, + default="adam", + help="Optimization algorithm. (default: %(default)s)") + parser.add_argument( + "--checkpoint_dir", + type=str, + default="best_checkpoint", + help="Path for saved checkpoints. (default: %(default)s)") + parser.add_argument( + "--output_type", + type=str, + default="avg", + help="Opertor type for the time steps reduction. (default: %(default)s)") + parser.add_argument( + "--multi_path", + type=distutils.util.strtobool, + default=False, + help="Search for multiple path in the architecture. (default: %(default)s)") + parser.add_argument( + "--is_binary", + type=distutils.util.strtobool, + default=False, + help="Binary label for sst dataset. (default: %(default)s)") + parser.add_argument( + "--is_cuda", + type=distutils.util.strtobool, + default=True, + help="Specify the device type. (default: %(default)s)") + parser.add_argument( + "--is_mask", + type=distutils.util.strtobool, + default=True, + help="Apply mask. (default: %(default)s)") + parser.add_argument( + "--fixed_seed", + type=distutils.util.strtobool, + default=True, + help="Fix the seed. (default: %(default)s)") + parser.add_argument( + "--load_checkpoint", + type=distutils.util.strtobool, + default=False, + help="Wether to load checkpoint. (default: %(default)s)") + parser.add_argument( + "--log_every", + type=int, + default=50, + help="How many steps to log. (default: %(default)s)") + parser.add_argument( + "--eval_every_epochs", + type=int, + default=1, + help="How many epochs to eval. (default: %(default)s)") + + global FLAGS + + FLAGS = parser.parse_args() + + +def set_random_seed(seed): + logger.info("set random seed for data reading: {}".format(seed)) + random.seed(seed) + os.environ['PYTHONHASHSEED'] = str(seed) + np.random.seed(seed) + random.seed(seed) + torch.manual_seed(seed) + if FLAGS.is_cuda: + torch.cuda.manual_seed(seed) + torch.backends.cudnn.deterministic = True + + +def get_model(embedding, num_layers): + logger.info("num layers: {0}".format(num_layers)) + assert FLAGS.child_fixed_arc is not None, "Architecture should be provided." + + child_model = Model( + embedding=embedding, + hidden_units=FLAGS.child_out_filters_scale * FLAGS.child_out_filters, + num_layers=num_layers, + num_classes=FLAGS.class_num, + choose_from_k=5 if FLAGS.multi_path else 1, + lstm_keep_prob=FLAGS.lstm_out_keep_prob, + cnn_keep_prob=FLAGS.cnn_keep_prob, + att_keep_prob=FLAGS.attention_keep_prob, + att_mask=FLAGS.is_mask, + embed_keep_prob=FLAGS.embed_keep_prob, + final_output_keep_prob=FLAGS.final_output_keep_prob, + global_pool=FLAGS.output_type) + + apply_fixed_architecture(child_model, FLAGS.child_fixed_arc) + return child_model + + +def eval_once(child_model, device, eval_set, criterion, valid_dataloader=None, test_dataloader=None): + if eval_set == "test": + assert test_dataloader is not None + dataloader = test_dataloader + elif eval_set == "valid": + assert valid_dataloader is not None + dataloader = valid_dataloader + else: + raise NotImplementedError("Unknown eval_set '{}'".format(eval_set)) + + tot_acc = 0 + tot = 0 + losses = [] + + with torch.no_grad(): # save memory + for batch in dataloader: + (sent_ids, mask), labels = batch + + sent_ids = sent_ids.to(device, non_blocking=True) + mask = mask.to(device, non_blocking=True) + labels = labels.to(device, non_blocking=True) + + logits = child_model((sent_ids, mask)) # run + + loss = criterion(logits, labels.long()) + loss = loss.mean() + preds = logits.argmax(dim=1).long() + acc = torch.eq(preds, labels.long()).long().sum().item() + + losses.append(loss) + tot_acc += acc + tot += len(labels) + + losses = torch.tensor(losses) + loss = losses.mean() + if tot > 0: + final_acc = float(tot_acc) / tot + else: + final_acc = 0 + logger.info("Error in calculating final_acc") + return final_acc, loss + + +def print_user_flags(FLAGS, line_limit=80): + log_strings = "\n" + "-" * line_limit + "\n" + for flag_name in sorted(vars(FLAGS)): + value = "{}".format(getattr(FLAGS, flag_name)) + log_string = flag_name + log_string += "." * (line_limit - len(flag_name) - len(value)) + log_string += value + log_strings = log_strings + log_string + log_strings = log_strings + "\n" + log_strings += "-" * line_limit + logger.info(log_strings) + + +def count_model_params(trainable_params): + num_vars = 0 + for var in trainable_params: + num_vars += np.prod([dim for dim in var.size()]) + return num_vars + + +def update_lr( + optimizer, + epoch, + l2_reg=1e-4, + lr_warmup_val=None, + lr_init=0.1, + lr_decay_scheme="cosine", + lr_max=0.002, + lr_min=0.000000001, + lr_T_0=4, + lr_T_mul=1, + sync_replicas=False, + num_aggregate=None, + num_replicas=None): + if lr_decay_scheme == "cosine": + assert lr_max is not None, "Need lr_max to use lr_cosine" + assert lr_min is not None, "Need lr_min to use lr_cosine" + assert lr_T_0 is not None, "Need lr_T_0 to use lr_cosine" + assert lr_T_mul is not None, "Need lr_T_mul to use lr_cosine" + + T_i = lr_T_0 + t_epoch = epoch + last_reset = 0 + while True: + t_epoch -= T_i + if t_epoch < 0: + break + last_reset += T_i + T_i *= lr_T_mul + + T_curr = epoch - last_reset + + def _update(): + rate = T_curr / T_i * 3.1415926 + lr = lr_min + 0.5 * (lr_max - lr_min) * (1.0 + math.cos(rate)) + return lr + + learning_rate = _update() + else: + raise ValueError("Unknown learning rate decay scheme {}".format(lr_decay_scheme)) + + #update lr in optimizer + for params_group in optimizer.param_groups: + params_group['lr'] = learning_rate + return learning_rate + + +def train(device, data_path, output_dir, num_layers): + logger.info("Build dataloader") + train_dataset, valid_dataset, test_dataset, embedding = \ + read_data_sst(data_path, + FLAGS.max_input_length, + FLAGS.min_count, + train_ratio=FLAGS.train_ratio, + valid_ratio=FLAGS.valid_ratio, + is_binary=FLAGS.is_binary) + train_dataloader = DataLoader(train_dataset, batch_size=FLAGS.batch_size, shuffle=True, pin_memory=True) + test_dataloader = DataLoader(test_dataset, batch_size=FLAGS.eval_batch_size, pin_memory=True) + valid_dataloader = DataLoader(valid_dataset, batch_size=FLAGS.eval_batch_size, pin_memory=True) + + logger.info("Build model") + child_model = get_model(embedding, num_layers) + logger.info("Finish build model") + + #for name, var in child_model.named_parameters(): + # logger.info(name, var.size(), var.requires_grad) # output all params + + num_vars = count_model_params(child_model.parameters()) + logger.info("Model has {} params".format(num_vars)) + + for m in child_model.modules(): # initializer + if isinstance(m, (nn.Conv1d, nn.Linear)): + nn.init.xavier_uniform_(m.weight) + + criterion = nn.CrossEntropyLoss() + + # get optimizer + if FLAGS.child_optim_algo == "adam": + optimizer = optim.Adam(child_model.parameters(), eps=1e-3, weight_decay=FLAGS.child_l2_reg) # with L2 + else: + raise ValueError("Unknown optim_algo {}".format(FLAGS.child_optim_algo)) + + child_model.to(device) + criterion.to(device) + + logger.info("Start training") + start_time = time.time() + step = 0 + + # save path + model_save_path = os.path.join(FLAGS.output_dir, "model.pth") + best_model_save_path = os.path.join(FLAGS.output_dir, "best_model.pth") + best_acc = 0 + start_epoch = 0 + if FLAGS.load_checkpoint: + if os.path.isfile(model_save_path): + checkpoint = torch.load(model_save_path, map_location = torch.device('cpu')) + step = checkpoint['step'] + start_epoch = checkpoint['epoch'] + child_model.load_state_dict(checkpoint['child_model_state_dict']) + optimizer.load_state_dict(checkpoint['optimizer_state_dict']) + + for epoch in range(start_epoch, FLAGS.num_epochs): + lr = update_lr(optimizer, + epoch, + l2_reg=FLAGS.child_l2_reg, + lr_warmup_val=None, + lr_init=FLAGS.child_lr, + lr_decay_scheme=FLAGS.child_lr_decay_scheme, + lr_max=FLAGS.child_lr_max, + lr_min=FLAGS.child_lr_min, + lr_T_0=FLAGS.child_lr_T_0, + lr_T_mul=FLAGS.child_lr_T_mul) + child_model.train() + for batch in train_dataloader: + (sent_ids, mask), labels = batch + + sent_ids = sent_ids.to(device, non_blocking=True) + mask = mask.to(device, non_blocking=True) + labels = labels.to(device, non_blocking=True) + + step += 1 + + logits = child_model((sent_ids, mask)) # run + + loss = criterion(logits, labels.long()) + loss = loss.mean() + preds = logits.argmax(dim=1).long() + acc = torch.eq(preds, labels.long()).long().sum().item() + + optimizer.zero_grad() + loss.backward() + grad_norm = 0 + trainable_params = child_model.parameters() + + assert FLAGS.child_grad_bound is not None, "Need grad_bound to clip gradients." + # compute the gradient norm value + grad_norm = nn.utils.clip_grad_norm_(trainable_params, 99999999) + for param in trainable_params: + nn.utils.clip_grad_norm_(param, FLAGS.child_grad_bound) # clip grad + + optimizer.step() + + if step % FLAGS.log_every == 0: + curr_time = time.time() + log_string = "" + log_string += "epoch={:<6d}".format(epoch) + log_string += "ch_step={:<6d}".format(step) + log_string += " loss={:<8.6f}".format(loss) + log_string += " lr={:<8.4f}".format(lr) + log_string += " |g|={:<8.4f}".format(grad_norm) + log_string += " tr_acc={:<3d}/{:>3d}".format(acc, logits.size()[0]) + log_string += " mins={:<10.2f}".format(float(curr_time - start_time) / 60) + logger.info(log_string) + + epoch += 1 + save_state = { + 'step' : step, + 'epoch' : epoch, + 'child_model_state_dict' : child_model.state_dict(), + 'optimizer_state_dict' : optimizer.state_dict()} + torch.save(save_state, model_save_path) + child_model.eval() + logger.info("Epoch {}: Eval".format(epoch)) + eval_acc, eval_loss = eval_once(child_model, device, "test", criterion, test_dataloader=test_dataloader) + logger.info("ch_step={} {}_accuracy={:<6.4f} {}_loss={:<6.4f}".format(step, "test", eval_acc, "test", eval_loss)) + if eval_acc > best_acc: + best_acc = eval_acc + logger.info("Save best model") + save_state = { + 'step' : step, + 'epoch' : epoch, + 'child_model_state_dict' : child_model.state_dict(), + 'optimizer_state_dict' : optimizer.state_dict()} + torch.save(save_state, best_model_save_path) + + return eval_acc + + +def main(): + parse_args() + if not os.path.isdir(FLAGS.output_dir): + logger.info("Path {} does not exist. Creating.".format(FLAGS.output_dir)) + os.makedirs(FLAGS.output_dir) + elif FLAGS.reset_output_dir: + logger.info("Path {} exists. Remove and remake.".format(FLAGS.output_dir)) + shutil.rmtree(FLAGS.output_dir, ignore_errors=True) + os.makedirs(FLAGS.output_dir) + + print_user_flags(FLAGS) + + if FLAGS.fixed_seed: + set_random_seed(FLAGS.global_seed) + + device = torch.device("cuda" if FLAGS.is_cuda else "cpu") + train(device, FLAGS.data_path, FLAGS.output_dir, FLAGS.child_num_layers) + + +if __name__ == "__main__": + main() diff --git a/examples/nas/textnas/run_retrain.sh b/examples/nas/textnas/run_retrain.sh new file mode 100755 index 0000000000..5c8ea66ae9 --- /dev/null +++ b/examples/nas/textnas/run_retrain.sh @@ -0,0 +1,41 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +export PYTHONPATH="$(pwd)" +export CUDA_VISIBLE_DEVICES=0 + +python -u retrain.py \ + --train_ratio=1.0 \ + --valid_ratio=1.0 \ + --min_count=1 \ + --is_mask=True \ + --is_binary=True \ + --child_lr_decay_scheme="cosine" \ + --data_path="data" \ + --class_num=2 \ + --child_optim_algo="adam" \ + --output_dir="output_sst2" \ + --global_seed=1234 \ + --max_input_length=64 \ + --batch_size=128 \ + --eval_batch_size=128 \ + --num_epochs=10 \ + --log_every=50 \ + --eval_every_epochs=1 \ + --child_num_layers=24 \ + --child_out_filters=256 \ + --child_l2_reg=1e-6 \ + --cnn_keep_prob=0.8 \ + --final_output_keep_prob=1.0 \ + --embed_keep_prob=0.8 \ + --lstm_out_keep_prob=0.8 \ + --attention_keep_prob=0.8 \ + --child_lr=0.02 \ + --child_lr_max=0.002 \ + --child_lr_min=5e-6 \ + --child_lr_T_0=10 \ + --child_lr_T_mul=2 \ + --multi_path=True \ + --child_fixed_arc="./checkpoints/architecture_00.json" \ + --fixed_seed=True \ + "$@" From a82b4a3bf7c64e18172cc3a93de567ae6e607a76 Mon Sep 17 00:00:00 2001 From: RayMeng8 Date: Mon, 30 Mar 2020 19:17:28 +0800 Subject: [PATCH 09/12] add PBT tuner (#2139) --- docs/en_US/Tuner/BuiltinTuner.md | 29 ++ docs/en_US/Tuner/PBTTuner.md | 12 + docs/en_US/builtin_tuner.rst | 1 + .../trials/mnist-pbt-tuner-pytorch/.gitignore | 1 + .../mnist-pbt-tuner-pytorch/__init__.py | 0 .../trials/mnist-pbt-tuner-pytorch/config.yml | 22 ++ .../trials/mnist-pbt-tuner-pytorch/mnist.py | 187 +++++++++++++ .../mnist-pbt-tuner-pytorch/search_space.json | 6 + .../rest_server/restValidationSchemas.ts | 2 +- src/sdk/pynni/nni/constants.py | 4 +- .../nni/evolution_tuner/evolution_tuner.py | 91 +----- .../evolution_tuner/test_evolution_tuner.py | 2 +- src/sdk/pynni/nni/pbt_tuner/__init__.py | 0 src/sdk/pynni/nni/pbt_tuner/pbt_tuner.py | 260 ++++++++++++++++++ src/sdk/pynni/nni/utils.py | 92 +++++++ src/sdk/pynni/tests/test_builtin_tuners.py | 34 ++- tools/nni_cmd/config_schema.py | 12 + 17 files changed, 659 insertions(+), 96 deletions(-) create mode 100644 docs/en_US/Tuner/PBTTuner.md create mode 100644 examples/trials/mnist-pbt-tuner-pytorch/.gitignore create mode 100644 examples/trials/mnist-pbt-tuner-pytorch/__init__.py create mode 100644 examples/trials/mnist-pbt-tuner-pytorch/config.yml create mode 100644 examples/trials/mnist-pbt-tuner-pytorch/mnist.py create mode 100644 examples/trials/mnist-pbt-tuner-pytorch/search_space.json create mode 100644 src/sdk/pynni/nni/pbt_tuner/__init__.py create mode 100755 src/sdk/pynni/nni/pbt_tuner/pbt_tuner.py diff --git a/docs/en_US/Tuner/BuiltinTuner.md b/docs/en_US/Tuner/BuiltinTuner.md index 176c299131..a0dd4bea36 100644 --- a/docs/en_US/Tuner/BuiltinTuner.md +++ b/docs/en_US/Tuner/BuiltinTuner.md @@ -21,6 +21,7 @@ Currently, we support the following algorithms: |[__BOHB__](#BOHB)|BOHB is a follow-up work to Hyperband. It targets the weakness of Hyperband that new configurations are generated randomly without leveraging finished trials. For the name BOHB, HB means Hyperband, BO means Bayesian Optimization. BOHB leverages finished trials by building multiple TPE models, a proportion of new configurations are generated through these models. [Reference Paper](https://arxiv.org/abs/1807.01774)| |[__GP Tuner__](#GPTuner)|Gaussian Process Tuner is a sequential model-based optimization (SMBO) approach with Gaussian Process as the surrogate. [Reference Paper](https://papers.nips.cc/paper/4443-algorithms-for-hyper-parameter-optimization.pdf), [Github Repo](https://github.com/fmfn/BayesianOptimization)| |[__PPO Tuner__](#PPOTuner)|PPO Tuner is a Reinforcement Learning tuner based on PPO algorithm. [Reference Paper](https://arxiv.org/abs/1707.06347)| +|[__PBT Tuner__](#PBTTuner)|PBT Tuner is a simple asynchronous optimization algorithm which effectively utilizes a fixed computational budget to jointly optimize a population of models and their hyperparameters to maximize performance. [Reference Paper](https://arxiv.org/abs/1711.09846v1)| ## Usage of Built-in Tuners @@ -453,6 +454,34 @@ tuner: classArgs: optimize_mode: maximize ``` + + + +![](https://placehold.it/15/1589F0/000000?text=+) `PBT Tuner` + +> Built-in Tuner Name: **PBTTuner** + +**Suggested scenario** + +Population Based Training (PBT) which bridges and extends parallel search methods and sequential optimization methods. It has a wallclock run time that is no greater than that of a single optimization process, does not require sequential runs, and is also able to use fewer computational resources than naive search methods. Therefore, it's effective when you want to save computational resources and time. Besides, PBT returns hyperparameter scheduler instead of configuration. If you don't need to get a specific configuration, but just expect good results, you can choose this tuner. It should be noted that, in our implementation, the operation of checkpoint storage location is involved. A trial is considered as several traning epochs of training, so the loading and saving of checkpoint must be specified in the trial code, which is different with other tuners. Otherwise, if the experiment is not local mode, users should provide a path in a shared storage which can be accessed by all the trials. You could try it on very simple task, such as the [mnist-pbt-tuner-pytorch](https://github.com/microsoft/nni/tree/master/examples/trials/mnist-pbt-tuner-pytorch) example. [See details](./PBTTuner.md) + +**classArgs requirements:** + +* **optimize_mode** (*'maximize' or 'minimize'*) - If 'maximize', the tuner will target to maximize metrics. If 'minimize', the tuner will target to minimize metrics. +* **all_checkpoint_dir** (*str, optional, default = None*) - Directory for trials to load and save checkpoint, if not specified, the directory would be "~/nni/checkpoint/". Note that if the experiment is not local mode, users should provide a path in a shared storage which can be accessed by all the trials. +* **population_size** (*int, optional, default = 10*) - Number of trials for each step. In our implementation, one step is running each trial by specific training epochs set by users. +* **factors** (*tuple, optional, default = (1.2, 0.8)*) - Factors for perturbation of hyperparameters. +* **fraction** (*float, optional, default = 0.2*) - Fraction for selecting bottom and top trials. + +**Usage example** + +```yaml +# config.yml +tuner: + builtinTunerName: PBTTuner + classArgs: + optimize_mode: maximize +``` ## **Reference and Feedback** * To [report a bug](https://github.com/microsoft/nni/issues/new?template=bug-report.md) for this feature in GitHub; * To [file a feature or improvement request](https://github.com/microsoft/nni/issues/new?template=enhancement.md) for this feature in GitHub; diff --git a/docs/en_US/Tuner/PBTTuner.md b/docs/en_US/Tuner/PBTTuner.md new file mode 100644 index 0000000000..4a178fdbc6 --- /dev/null +++ b/docs/en_US/Tuner/PBTTuner.md @@ -0,0 +1,12 @@ +PBT Tuner on NNI +=== + +## PBTTuner + +Population Based Training (PBT) comes from [Population Based Training of Neural Networks](https://arxiv.org/abs/1711.09846v1). It's a simple asynchronous optimization algorithm which effectively utilizes a fixed computational budget to jointly optimize a population of models and their hyperparameters to maximize performance. Importantly, PBT discovers a schedule of hyperparameter settings rather than following the generally sub-optimal strategy of trying to find a single fixed set to use for the whole course of training. + +PBTTuner initializes a population with several trials. Users can set a specific number of training epochs. After a certain number of epochs, the parameters and hyperparameters in the trial with bad metrics will be replaced with a better trial (exploit). Then the hyperparameters are perturbed (explore). + +In our implementation, training epochs in the trial code is regarded as a step of PBT, different with other tuners. At the end of each step, PBT tuner will do exploitation and exploration -- replacing some trials with new trials. This is implemented by constantly modifying the values of `load_checkpoint_dir` and `save_checkpoint_dir`. We can directly change `load_checkpoint_dir` to replace parameters and hyperparameters, and `save_checkpoint_dir` to save a checkpoint that will be loaded in the next step. To this end, we need a shared folder which is accessible to all trials. + +If the experiment is running in local mode, users could provide an argument `all_checkpoint_dir` which will be the base folder of `load_checkpoint_dir` and `save_checkpoint_dir` (`checkpoint_dir` is set to `all_checkpoint_dir//`). By default, `all_checkpoint_dir` is set to be `~/nni/experiments//checkpoint`. If the experiment is in non-local mode, then users should provide a path in a shared storage folder which is mounted at `all_checkpoint_dir` on worker machines (but it's not necessarily available on the machine which runs tuner). diff --git a/docs/en_US/builtin_tuner.rst b/docs/en_US/builtin_tuner.rst index d2785ea848..7acd002808 100644 --- a/docs/en_US/builtin_tuner.rst +++ b/docs/en_US/builtin_tuner.rst @@ -23,3 +23,4 @@ Tuner receives metrics from `Trial` to evaluate the performance of a specific pa Hyperband BOHB PPO Tuner + PBT Tuner diff --git a/examples/trials/mnist-pbt-tuner-pytorch/.gitignore b/examples/trials/mnist-pbt-tuner-pytorch/.gitignore new file mode 100644 index 0000000000..a9a5aecf42 --- /dev/null +++ b/examples/trials/mnist-pbt-tuner-pytorch/.gitignore @@ -0,0 +1 @@ +tmp diff --git a/examples/trials/mnist-pbt-tuner-pytorch/__init__.py b/examples/trials/mnist-pbt-tuner-pytorch/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/trials/mnist-pbt-tuner-pytorch/config.yml b/examples/trials/mnist-pbt-tuner-pytorch/config.yml new file mode 100644 index 0000000000..508278e69f --- /dev/null +++ b/examples/trials/mnist-pbt-tuner-pytorch/config.yml @@ -0,0 +1,22 @@ +authorName: default +experimentName: example_mnist_pbt_tuner_pytorch +trialConcurrency: 3 +maxExecDuration: 2h +maxTrialNum: 100 +#choice: local, remote, pai +trainingServicePlatform: local +searchSpacePath: search_space.json +#choice: true, false +useAnnotation: false +tuner: +# codeDir: ~/nni/src/sdk/pynni/nni/pbt_tuner +# classFileName: pbt_tuner.py +# className: PBTTuner + builtinTunerName: PBTTuner + classArgs: + #choice: maximize, minimize + optimize_mode: maximize +trial: + command: python3 mnist.py + codeDir: . + gpuNum: 1 diff --git a/examples/trials/mnist-pbt-tuner-pytorch/mnist.py b/examples/trials/mnist-pbt-tuner-pytorch/mnist.py new file mode 100644 index 0000000000..2161191e9e --- /dev/null +++ b/examples/trials/mnist-pbt-tuner-pytorch/mnist.py @@ -0,0 +1,187 @@ +import argparse +import logging + +import os +import nni +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.optim as optim +from torchvision import datasets, transforms + + +logger = logging.getLogger('mnist_pbt_tuner_pytorch_AutoML') + +class Net(nn.Module): + def __init__(self, hidden_size): + super(Net, self).__init__() + self.conv1 = nn.Conv2d(1, 20, 5, 1) + self.conv2 = nn.Conv2d(20, 50, 5, 1) + self.fc1 = nn.Linear(4*4*50, hidden_size) + self.fc2 = nn.Linear(hidden_size, 10) + + def forward(self, x): + x = F.relu(self.conv1(x)) + x = F.max_pool2d(x, 2, 2) + x = F.relu(self.conv2(x)) + x = F.max_pool2d(x, 2, 2) + x = x.view(-1, 4*4*50) + x = F.relu(self.fc1(x)) + x = self.fc2(x) + return F.log_softmax(x, dim=1) + + +def train(args, model, device, train_loader, optimizer, epoch): + model.train() + for batch_idx, (data, target) in enumerate(train_loader): + data, target = data.to(device), target.to(device) + optimizer.zero_grad() + output = model(data) + loss = F.nll_loss(output, target) + loss.backward() + optimizer.step() + if batch_idx % args['log_interval'] == 0: + logger.info('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format( + epoch, batch_idx * len(data), len(train_loader.dataset), + 100. * batch_idx / len(train_loader), loss.item())) + + +def test(args, model, device, test_loader): + model.eval() + test_loss = 0 + correct = 0 + with torch.no_grad(): + for data, target in test_loader: + data, target = data.to(device), target.to(device) + output = model(data) + # sum up batch loss + test_loss += F.nll_loss(output, target, reduction='sum').item() + # get the index of the max log-probability + pred = output.argmax(dim=1, keepdim=True) + correct += pred.eq(target.view_as(pred)).sum().item() + + test_loss /= len(test_loader.dataset) + + accuracy = 100. * correct / len(test_loader.dataset) + + logger.info('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format( + test_loss, correct, len(test_loader.dataset), accuracy)) + + return accuracy + + +def save_checkpoint(model, checkpoint_path): + torch.save(model.state_dict(), checkpoint_path) + + +def load_checkpoint(checkpoint_path): + model_state_dict = torch.load(checkpoint_path) + return model_state_dict + + +def main(args): + use_cuda = not args['no_cuda'] and torch.cuda.is_available() + + torch.manual_seed(args['seed']) + + device = torch.device("cuda" if use_cuda else "cpu") + + kwargs = {'num_workers': 1, 'pin_memory': True} if use_cuda else {} + + data_dir = os.path.join(args['data_dir'], nni.get_trial_id()) + + train_loader = torch.utils.data.DataLoader( + datasets.MNIST(data_dir, train=True, download=True, + transform=transforms.Compose([ + transforms.ToTensor(), + transforms.Normalize((0.1307,), (0.3081,)) + ])), + batch_size=args['batch_size'], shuffle=True, **kwargs) + test_loader = torch.utils.data.DataLoader( + datasets.MNIST(data_dir, train=False, transform=transforms.Compose([ + transforms.ToTensor(), + transforms.Normalize((0.1307,), (0.3081,)) + ])), + batch_size=1000, shuffle=True, **kwargs) + + hidden_size = args['hidden_size'] + + model = Net(hidden_size=hidden_size).to(device) + + save_checkpoint_dir = args['save_checkpoint_dir'] + save_checkpoint_path = os.path.join(save_checkpoint_dir, 'model.pth') + load_checkpoint_path = os.path.join(args['load_checkpoint_dir'], 'model.pth') + + if os.path.isfile(load_checkpoint_path): + model_state_dict = load_checkpoint(load_checkpoint_path) + logger.info("test : " + load_checkpoint_path) + logger.info(type(model_state_dict)) + model.load_state_dict(model_state_dict) + + optimizer = optim.SGD(model.parameters(), lr=args['lr'], + momentum=args['momentum']) + + #epoch is perturbation interval + for epoch in range(1, args['epochs'] + 1): + train(args, model, device, train_loader, optimizer, epoch) + test_acc = test(args, model, device, test_loader) + + if epoch < args['epochs']: + # report intermediate result + nni.report_intermediate_result(test_acc) + logger.debug('test accuracy %g', test_acc) + logger.debug('Pipe send intermediate result done.') + else: + # report final result + nni.report_final_result(test_acc) + logger.debug('Final result is %g', test_acc) + logger.debug('Send final result done.') + + if not os.path.exists(save_checkpoint_dir): + os.makedirs(save_checkpoint_dir) + save_checkpoint(model, save_checkpoint_path) + + +def get_params(): + # Training settings + parser = argparse.ArgumentParser(description='PyTorch MNIST Example') + parser.add_argument("--data_dir", type=str, + default='./tmp/pytorch/mnist/input_data', help="data directory") + parser.add_argument('--batch_size', type=int, default=64, metavar='N', + help='input batch size for training (default: 64)') + parser.add_argument("--hidden_size", type=int, default=512, metavar='N', + help='hidden layer size (default: 512)') + parser.add_argument('--lr', type=float, default=0.01, metavar='LR', + help='learning rate (default: 0.01)') + parser.add_argument('--momentum', type=float, default=0.5, metavar='M', + help='SGD momentum (default: 0.5)') + parser.add_argument('--epochs', type=int, default=10, metavar='N', + help='number of epochs to train (default: 10)') + parser.add_argument('--seed', type=int, default=1, metavar='S', + help='random seed (default: 1)') + parser.add_argument('--no_cuda', action='store_true', default=False, + help='disables CUDA training') + parser.add_argument('--log_interval', type=int, default=1000, metavar='N', + help='how many batches to wait before logging training status') + + parser.add_argument('--save_checkpoint_dir', type=str, + help='where to save checkpoint of this trial') + parser.add_argument('--load_checkpoint_dir', type=str, + help='where to load the model') + + + args, _ = parser.parse_known_args() + return args + + +if __name__ == '__main__': + try: + # get parameters form tuner + tuner_params = nni.get_next_parameter() + logger.debug(tuner_params) + params = vars(get_params()) + params.update(tuner_params) + main(params) + except Exception as exception: + logger.exception(exception) + raise \ No newline at end of file diff --git a/examples/trials/mnist-pbt-tuner-pytorch/search_space.json b/examples/trials/mnist-pbt-tuner-pytorch/search_space.json new file mode 100644 index 0000000000..c26cdce369 --- /dev/null +++ b/examples/trials/mnist-pbt-tuner-pytorch/search_space.json @@ -0,0 +1,6 @@ +{ + "batch_size": {"_type":"choice", "_value": [16, 32, 64, 128]}, + "hidden_size":{"_type":"choice","_value":[128, 256, 512, 1024]}, + "lr":{"_type":"choice","_value":[0.0001, 0.001, 0.01, 0.1]}, + "momentum":{"_type":"uniform","_value":[0, 1]} +} diff --git a/src/nni_manager/rest_server/restValidationSchemas.ts b/src/nni_manager/rest_server/restValidationSchemas.ts index 1592f57b89..06ed3f3f15 100644 --- a/src/nni_manager/rest_server/restValidationSchemas.ts +++ b/src/nni_manager/rest_server/restValidationSchemas.ts @@ -178,7 +178,7 @@ export namespace ValidationSchemas { gpuIndices: joi.string() }), tuner: joi.object({ - builtinTunerName: joi.string().valid('TPE', 'Random', 'Anneal', 'Evolution', 'SMAC', 'BatchTuner', 'GridSearch', 'NetworkMorphism', 'MetisTuner', 'GPTuner', 'PPOTuner'), + builtinTunerName: joi.string().valid('TPE', 'Random', 'Anneal', 'Evolution', 'SMAC', 'BatchTuner', 'GridSearch', 'NetworkMorphism', 'MetisTuner', 'GPTuner', 'PPOTuner', 'PBTTuner'), codeDir: joi.string(), classFileName: joi.string(), className: joi.string(), diff --git a/src/sdk/pynni/nni/constants.py b/src/sdk/pynni/nni/constants.py index 1c20dffee6..211d320b89 100644 --- a/src/sdk/pynni/nni/constants.py +++ b/src/sdk/pynni/nni/constants.py @@ -15,7 +15,8 @@ 'Curvefitting': 'nni.curvefitting_assessor.curvefitting_assessor', 'MetisTuner': 'nni.metis_tuner.metis_tuner', 'GPTuner': 'nni.gp_tuner.gp_tuner', - 'PPOTuner': 'nni.ppo_tuner.ppo_tuner' + 'PPOTuner': 'nni.ppo_tuner.ppo_tuner', + 'PBTTuner': 'nni.pbt_tuner.pbt_tuner' } ClassName = { @@ -30,6 +31,7 @@ 'MetisTuner':'MetisTuner', 'GPTuner':'GPTuner', 'PPOTuner': 'PPOTuner', + 'PBTTuner': 'PBTTuner', 'Medianstop': 'MedianstopAssessor', 'Curvefitting': 'CurvefittingAssessor' diff --git a/src/sdk/pynni/nni/evolution_tuner/evolution_tuner.py b/src/sdk/pynni/nni/evolution_tuner/evolution_tuner.py index c51a1d9afe..120d2b5dc1 100644 --- a/src/sdk/pynni/nni/evolution_tuner/evolution_tuner.py +++ b/src/sdk/pynni/nni/evolution_tuner/evolution_tuner.py @@ -10,97 +10,8 @@ import numpy as np from nni.tuner import Tuner -from nni.utils import NodeType, OptimizeMode, extract_scalar_reward, split_index +from nni.utils import OptimizeMode, extract_scalar_reward, split_index, json2parameter, json2space -import nni.parameter_expressions as parameter_expressions - - -def json2space(x, oldy=None, name=NodeType.ROOT): - """ - Change search space from json format to hyperopt format - - """ - y = list() - if isinstance(x, dict): - if NodeType.TYPE in x.keys(): - _type = x[NodeType.TYPE] - name = name + '-' + _type - if _type == 'choice': - if oldy is not None: - _index = oldy[NodeType.INDEX] - y += json2space(x[NodeType.VALUE][_index], - oldy[NodeType.VALUE], name=name+'[%d]' % _index) - else: - y += json2space(x[NodeType.VALUE], None, name=name) - y.append(name) - else: - for key in x.keys(): - y += json2space(x[key], oldy[key] if oldy else None, name+"[%s]" % str(key)) - elif isinstance(x, list): - for i, x_i in enumerate(x): - if isinstance(x_i, dict): - if NodeType.NAME not in x_i.keys(): - raise RuntimeError('\'_name\' key is not found in this nested search space.') - y += json2space(x_i, oldy[i] if oldy else None, name + "[%d]" % i) - return y - -def json2parameter(x, is_rand, random_state, oldy=None, Rand=False, name=NodeType.ROOT): - """ - Json to pramaters. - - """ - if isinstance(x, dict): - if NodeType.TYPE in x.keys(): - _type = x[NodeType.TYPE] - _value = x[NodeType.VALUE] - name = name + '-' + _type - Rand |= is_rand[name] - if Rand is True: - if _type == 'choice': - _index = random_state.randint(len(_value)) - y = { - NodeType.INDEX: _index, - NodeType.VALUE: json2parameter( - x[NodeType.VALUE][_index], - is_rand, - random_state, - None, - Rand, - name=name+"[%d]" % _index - ) - } - else: - y = getattr(parameter_expressions, _type)(*(_value + [random_state])) - else: - y = copy.deepcopy(oldy) - else: - y = dict() - for key in x.keys(): - y[key] = json2parameter( - x[key], - is_rand, - random_state, - oldy[key] if oldy else None, - Rand, - name + "[%s]" % str(key) - ) - elif isinstance(x, list): - y = list() - for i, x_i in enumerate(x): - if isinstance(x_i, dict): - if NodeType.NAME not in x_i.keys(): - raise RuntimeError('\'_name\' key is not found in this nested search space.') - y.append(json2parameter( - x_i, - is_rand, - random_state, - oldy[i] if oldy else None, - Rand, - name + "[%d]" % i - )) - else: - y = copy.deepcopy(x) - return y class Individual: """ diff --git a/src/sdk/pynni/nni/evolution_tuner/test_evolution_tuner.py b/src/sdk/pynni/nni/evolution_tuner/test_evolution_tuner.py index d1bef3d098..bb99841cc1 100644 --- a/src/sdk/pynni/nni/evolution_tuner/test_evolution_tuner.py +++ b/src/sdk/pynni/nni/evolution_tuner/test_evolution_tuner.py @@ -9,7 +9,7 @@ from unittest import TestCase, main -from nni.evolution_tuner.evolution_tuner import json2space, json2parameter +from nni.utils import json2space, json2parameter class EvolutionTunerTestCase(TestCase): diff --git a/src/sdk/pynni/nni/pbt_tuner/__init__.py b/src/sdk/pynni/nni/pbt_tuner/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/sdk/pynni/nni/pbt_tuner/pbt_tuner.py b/src/sdk/pynni/nni/pbt_tuner/pbt_tuner.py new file mode 100755 index 0000000000..9e4acd586c --- /dev/null +++ b/src/sdk/pynni/nni/pbt_tuner/pbt_tuner.py @@ -0,0 +1,260 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +import copy +import logging +import os +import numpy as np + +import nni +from nni.tuner import Tuner +from nni.utils import OptimizeMode, extract_scalar_reward, split_index, json2parameter, json2space + + +logger = logging.getLogger('pbt_tuner_AutoML') + + +def exploit_and_explore(bot_trial_info, top_trial_info, factors, epoch, search_space): + """ + Replace checkpoint of bot_trial with top, and perturb hyperparameters + + Parameters + ---------- + bot_trial_info : TrialInfo + bottom model whose parameters should be replaced + top_trial_info : TrialInfo + better model + factors : float + factors for perturbation + epoch : int + step of PBTTuner + search_space : dict + search_space to keep perturbed hyperparameters in range + """ + bot_checkpoint_dir = bot_trial_info.checkpoint_dir + top_hyper_parameters = top_trial_info.hyper_parameters + hyper_parameters = copy.deepcopy(top_hyper_parameters) + # TODO think about different type of hyperparameters for 1.perturbation 2.within search space + for key in hyper_parameters.keys(): + if key == 'load_checkpoint_dir': + hyper_parameters[key] = hyper_parameters['save_checkpoint_dir'] + elif key == 'save_checkpoint_dir': + hyper_parameters[key] = os.path.join(bot_checkpoint_dir, str(epoch)) + elif isinstance(hyper_parameters[key], float): + perturb = np.random.choice(factors) + val = hyper_parameters[key] * perturb + lb, ub = search_space[key]["_value"][:2] + if search_space[key]["_type"] in ("uniform", "normal"): + val = np.clip(val, lb, ub).item() + hyper_parameters[key] = val + else: + continue + bot_trial_info.hyper_parameters = hyper_parameters + bot_trial_info.clean_id() + + +class TrialInfo: + """ + Information of each trial, refresh for each epoch + + """ + + def __init__(self, checkpoint_dir=None, hyper_parameters=None, parameter_id=None, score=None): + self.checkpoint_dir = checkpoint_dir + self.hyper_parameters = hyper_parameters + self.parameter_id = parameter_id + self.score = score + + def clean_id(self): + self.parameter_id = None + + +class PBTTuner(Tuner): + def __init__(self, optimize_mode="maximize", all_checkpoint_dir=None, population_size=10, factors=(1.2, 0.8), fraction=0.2): + """ + Initialization + + Parameters + ---------- + optimize_mode : str + maximize or minimize + all_checkpoint_dir : str + directory to store training model checkpoint + population_size : int + number of trials for each epoch + factors : tuple + factors for perturbation + fraction : float + fraction for selecting bottom and top trials + """ + self.optimize_mode = OptimizeMode(optimize_mode) + if all_checkpoint_dir is None: + all_checkpoint_dir = os.getenv('NNI_CHECKPOINT_DIRECTORY') + logger.info("Checkpoint dir is set to %s by default.", all_checkpoint_dir) + self.all_checkpoint_dir = all_checkpoint_dir + self.population_size = population_size + self.factors = factors + self.fraction = fraction + # defined in trial code + #self.perturbation_interval = perturbation_interval + + self.population = None + self.pos = -1 + self.param_ids = [] + self.running = {} + self.finished = [] + self.credit = 0 + self.finished_trials = 0 + self.epoch = 0 + + self.searchspace_json = None + self.space = None + + self.send_trial_callback = None + + logger.info('PBT tuner initialization') + + def update_search_space(self, search_space): + """ + Get search space + + Parameters + ---------- + search_space : dict + Search space + """ + logger.info('Update search space %s', search_space) + self.searchspace_json = search_space + self.space = json2space(self.searchspace_json) + + self.random_state = np.random.RandomState() + self.population = [] + is_rand = dict() + + for item in self.space: + is_rand[item] = True + + for i in range(self.population_size): + hyper_parameters = json2parameter( + self.searchspace_json, is_rand, self.random_state) + checkpoint_dir = os.path.join(self.all_checkpoint_dir, str(i)) + hyper_parameters['load_checkpoint_dir'] = os.path.join(checkpoint_dir, str(self.epoch)) + hyper_parameters['save_checkpoint_dir'] = os.path.join(checkpoint_dir, str(self.epoch)) + self.population.append(TrialInfo(checkpoint_dir=checkpoint_dir, hyper_parameters=hyper_parameters)) + + def generate_multiple_parameters(self, parameter_id_list, **kwargs): + """ + Returns multiple sets of trial (hyper-)parameters, as iterable of serializable objects. + + Parameters + ---------- + parameter_id_list : list of int + Unique identifiers for each set of requested hyper-parameters. + These will later be used in :meth:`receive_trial_result`. + **kwargs + Used for send_trial_callback. + + Returns + ------- + list + A list of newly generated configurations + """ + result = [] + self.send_trial_callback = kwargs['st_callback'] + for parameter_id in parameter_id_list: + had_exception = False + try: + logger.debug("generating param for %s", parameter_id) + res = self.generate_parameters(parameter_id, **kwargs) + except nni.NoMoreTrialError: + had_exception = True + if not had_exception: + result.append(res) + return result + + def generate_parameters(self, parameter_id, **kwargs): + """ + Generate parameters, if no trial configration for now, self.credit plus 1 to send the config later + + Parameters + ---------- + parameter_id : int + Unique identifier for requested hyper-parameters. + This will later be used in :meth:`receive_trial_result`. + **kwargs + Not used + + Returns + ------- + dict + One newly generated configuration + + """ + if self.pos == self.population_size - 1: + logger.debug('Credit added by one in parameters request') + self.credit += 1 + self.param_ids.append(parameter_id) + raise nni.NoMoreTrialError('No more parameters now.') + self.pos += 1 + trial_info = self.population[self.pos] + trial_info.parameter_id = parameter_id + self.running[parameter_id] = trial_info + logger.info('Generate parameter : %s', trial_info.hyper_parameters) + return split_index(trial_info.hyper_parameters) + + def receive_trial_result(self, parameter_id, parameters, value, **kwargs): + """ + Receive trial's result. if the number of finished trials equals ``self.population_size``, start the next epoch to + train the model. + + Parameters + ---------- + parameter_id : int + Unique identifier of used hyper-parameters, same with :meth:`generate_parameters`. + parameters : dict + Hyper-parameters generated by :meth:`generate_parameters`. + value : dict + Result from trial (the return value of :func:`nni.report_final_result`). + """ + logger.info('Get one trial result, id = %d, value = %s', parameter_id, value) + value = extract_scalar_reward(value) + if self.optimize_mode == OptimizeMode.Minimize: + value = -value + trial_info = self.running.pop(parameter_id, None) + trial_info.score = value + self.finished.append(trial_info) + self.finished_trials += 1 + if self.finished_trials == self.population_size: + logger.info('Proceeding to next epoch') + self.epoch += 1 + self.population = [] + self.pos = -1 + self.running = {} + #exploit and explore + self.finished = sorted(self.finished, key=lambda x: x.score, reverse=True) + cutoff = int(np.ceil(self.fraction * len(self.finished))) + tops = self.finished[:cutoff] + bottoms = self.finished[self.finished_trials - cutoff:] + for bottom in bottoms: + top = np.random.choice(tops) + exploit_and_explore(bottom, top, self.factors, self.epoch, self.searchspace_json) + for trial in self.finished: + if trial not in bottoms: + trial.clean_id() + trial.hyper_parameters['load_checkpoint_dir'] = trial.hyper_parameters['save_checkpoint_dir'] + trial.hyper_parameters['save_checkpoint_dir'] = os.path.join(trial.checkpoint_dir, str(self.epoch)) + self.finished_trials = 0 + for _ in range(self.population_size): + trial_info = self.finished.pop() + self.population.append(trial_info) + while self.credit > 0 and self.pos + 1 < len(self.population): + self.credit -= 1 + self.pos += 1 + parameter_id = self.param_ids.pop() + trial_info = self.population[self.pos] + trial_info.parameter_id = parameter_id + self.running[parameter_id] = trial_info + self.send_trial_callback(parameter_id, split_index(trial_info.hyper_parameters)) + + def import_data(self, data): + pass diff --git a/src/sdk/pynni/nni/utils.py b/src/sdk/pynni/nni/utils.py index 22351f717d..173203ab72 100644 --- a/src/sdk/pynni/nni/utils.py +++ b/src/sdk/pynni/nni/utils.py @@ -2,13 +2,16 @@ # Licensed under the MIT license. import os +import copy import functools from enum import Enum, unique import json_tricks +from . import parameter_expressions from .common import init_logger from .env_vars import dispatcher_env_vars + to_json = functools.partial(json_tricks.dumps, allow_nan=True) @unique @@ -124,3 +127,92 @@ def init_dispatcher_logger(): if dispatcher_env_vars.NNI_LOG_DIRECTORY is not None: logger_file_path = os.path.join(dispatcher_env_vars.NNI_LOG_DIRECTORY, logger_file_path) init_logger(logger_file_path, dispatcher_env_vars.NNI_LOG_LEVEL) + + +def json2space(x, oldy=None, name=NodeType.ROOT): + """ + Change search space from json format to hyperopt format + + """ + y = list() + if isinstance(x, dict): + if NodeType.TYPE in x.keys(): + _type = x[NodeType.TYPE] + name = name + '-' + _type + if _type == 'choice': + if oldy is not None: + _index = oldy[NodeType.INDEX] + y += json2space(x[NodeType.VALUE][_index], + oldy[NodeType.VALUE], name=name+'[%d]' % _index) + else: + y += json2space(x[NodeType.VALUE], None, name=name) + y.append(name) + else: + for key in x.keys(): + y += json2space(x[key], oldy[key] if oldy else None, name+"[%s]" % str(key)) + elif isinstance(x, list): + for i, x_i in enumerate(x): + if isinstance(x_i, dict): + if NodeType.NAME not in x_i.keys(): + raise RuntimeError('\'_name\' key is not found in this nested search space.') + y += json2space(x_i, oldy[i] if oldy else None, name + "[%d]" % i) + return y + + +def json2parameter(x, is_rand, random_state, oldy=None, Rand=False, name=NodeType.ROOT): + """ + Json to pramaters. + + """ + if isinstance(x, dict): + if NodeType.TYPE in x.keys(): + _type = x[NodeType.TYPE] + _value = x[NodeType.VALUE] + name = name + '-' + _type + Rand |= is_rand[name] + if Rand is True: + if _type == 'choice': + _index = random_state.randint(len(_value)) + y = { + NodeType.INDEX: _index, + NodeType.VALUE: json2parameter( + x[NodeType.VALUE][_index], + is_rand, + random_state, + None, + Rand, + name=name+"[%d]" % _index + ) + } + else: + y = getattr(parameter_expressions, _type)(*(_value + [random_state])) + else: + y = copy.deepcopy(oldy) + else: + y = dict() + for key in x.keys(): + y[key] = json2parameter( + x[key], + is_rand, + random_state, + oldy[key] if oldy else None, + Rand, + name + "[%s]" % str(key) + ) + elif isinstance(x, list): + y = list() + for i, x_i in enumerate(x): + if isinstance(x_i, dict): + if NodeType.NAME not in x_i.keys(): + raise RuntimeError('\'_name\' key is not found in this nested search space.') + y.append(json2parameter( + x_i, + is_rand, + random_state, + oldy[i] if oldy else None, + Rand, + name + "[%d]" % i + )) + else: + y = copy.deepcopy(x) + return y diff --git a/src/sdk/pynni/tests/test_builtin_tuners.py b/src/sdk/pynni/tests/test_builtin_tuners.py index 38f784fff2..de0c130403 100644 --- a/src/sdk/pynni/tests/test_builtin_tuners.py +++ b/src/sdk/pynni/tests/test_builtin_tuners.py @@ -8,6 +8,7 @@ import random import shutil import sys +from collections import deque from unittest import TestCase, main from nni.batch_tuner.batch_tuner import BatchTuner @@ -16,6 +17,8 @@ from nni.gridsearch_tuner.gridsearch_tuner import GridSearchTuner from nni.hyperopt_tuner.hyperopt_tuner import HyperoptTuner from nni.metis_tuner.metis_tuner import MetisTuner +from nni.msg_dispatcher import _pack_parameter, MsgDispatcher +from nni.pbt_tuner.pbt_tuner import PBTTuner try: from nni.smac_tuner.smac_tuner import SMACTuner @@ -23,6 +26,7 @@ assert sys.platform == "win32" from nni.tuner import Tuner + logging.basicConfig(level=logging.INFO) logger = logging.getLogger('test_tuner') @@ -44,18 +48,29 @@ def setUp(self): self.params_each_round = 50 self.exhaustive = False + def send_trial_callback(self, param_queue): + def receive(*args): + param_queue.append(tuple(args)) + return receive + def search_space_test_one(self, tuner_factory, search_space): tuner = tuner_factory() self.assertIsInstance(tuner, Tuner) tuner.update_search_space(search_space) for i in range(self.test_round): + queue = deque() parameters = tuner.generate_multiple_parameters(list(range(i * self.params_each_round, - (i + 1) * self.params_each_round))) + (i + 1) * self.params_each_round)), + st_callback=self.send_trial_callback(queue)) logger.debug(parameters) self.check_range(parameters, search_space) for k in range(min(len(parameters), self.params_each_round)): tuner.receive_trial_result(self.params_each_round * i + k, parameters[k], random.uniform(-100, 100)) + while queue: + id_, params = queue.popleft() + self.check_range([params], search_space) + tuner.receive_trial_result(id_, params, random.uniform(-100, 100)) if not parameters and not self.exhaustive: raise ValueError("No parameters generated") @@ -65,6 +80,9 @@ def check_range(self, generated_params, search_space): if self._testMethodName == "test_batch": param = {list(search_space.keys())[0]: param} for k, v in param.items(): + if k == "load_checkpoint_dir" or k == "save_checkpoint_dir": + self.assertIsInstance(v, str) + continue if k.startswith("_mutable_layer"): _, block, layer, choice = k.split("/") cand = search_space[block]["_value"][layer].get(choice) @@ -124,8 +142,8 @@ def search_space_test_all(self, tuner_factory, supported_types=None, ignore_type if any(single.startswith(t) for t in ignore_types): continue expected_fail = not any(single.startswith(t) for t in supported_types) or \ - any(single.startswith(t) for t in fail_types) or \ - "fail" in single # name contains fail (fail on all) + any(single.startswith(t) for t in fail_types) or \ + "fail" in single # name contains fail (fail on all) single_search_space = {single: space} if not expected_fail: # supports this key @@ -270,6 +288,16 @@ def test_networkmorphism(self): def test_ppo(self): pass + def test_pbt(self): + self.search_space_test_all(lambda: PBTTuner( + all_checkpoint_dir=os.path.expanduser("~/nni/checkpoint/test/"), + population_size=12 + )) + self.search_space_test_all(lambda: PBTTuner( + all_checkpoint_dir=os.path.expanduser("~/nni/checkpoint/test/"), + population_size=100 + )) + def tearDown(self): file_list = glob.glob("smac3*") + ["param_config_space.pcs", "scenario.txt", "model_path"] for file in file_list: diff --git a/tools/nni_cmd/config_schema.py b/tools/nni_cmd/config_schema.py index b6df38724b..a67695c1f4 100644 --- a/tools/nni_cmd/config_schema.py +++ b/tools/nni_cmd/config_schema.py @@ -153,6 +153,18 @@ def setPathCheck(key): Optional('includeIntermediateResults'): setType('includeIntermediateResults', bool), Optional('gpuIndices'): Or(int, And(str, lambda x: len([int(i) for i in x.split(',')]) > 0), error='gpuIndex format error!'), }, + 'PBTTuner': { + 'builtinTunerName': 'PBTTuner', + 'classArgs': { + 'optimize_mode': setChoice('optimize_mode', 'maximize', 'minimize'), + Optional('all_checkpoint_dir'): setType('all_checkpoint_dir', str), + Optional('population_size'): setNumberRange('population_size', int, 0, 99999), + Optional('factors'): setType('factors', tuple), + Optional('fraction'): setType('fraction', float), + }, + Optional('includeIntermediateResults'): setType('includeIntermediateResults', bool), + Optional('gpuIndices'): Or(int, And(str, lambda x: len([int(i) for i in x.split(',')]) > 0), error='gpuIndex format error!'), + }, 'customized': { 'codeDir': setPathCheck('codeDir'), 'classFileName': setType('classFileName', str), From a2e524d32859bf9c435b50a18b039d992e62ad04 Mon Sep 17 00:00:00 2001 From: liuzhe-lz <40699903+liuzhe-lz@users.noreply.github.com> Date: Mon, 30 Mar 2020 19:26:01 +0800 Subject: [PATCH 10/12] update nasui yarn.lock (#2245) --- src/nasui/yarn.lock | 217 +++----------------------------------------- 1 file changed, 12 insertions(+), 205 deletions(-) diff --git a/src/nasui/yarn.lock b/src/nasui/yarn.lock index 9c1270d20f..560b3f0747 100644 --- a/src/nasui/yarn.lock +++ b/src/nasui/yarn.lock @@ -1784,11 +1784,6 @@ abab@^2.0.0: resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a" integrity sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg== -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" @@ -1949,19 +1944,11 @@ anymatch@~3.1.1: normalize-path "^3.0.0" picomatch "^2.0.4" -aproba@^1.0.3, aproba@^1.1.1: +aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== -are-we-there-yet@~1.1.2: - version "1.1.5" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" - integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" - argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -3029,11 +3016,6 @@ console-browserify@^1.1.0: resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= - constants-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" @@ -3504,7 +3486,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: dependencies: ms "2.0.0" -debug@^3.0.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: +debug@^3.0.0, debug@^3.1.1, debug@^3.2.5: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -3540,11 +3522,6 @@ deep-equal@^1.0.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -3605,11 +3582,6 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= - depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -3628,11 +3600,6 @@ destroy@~1.0.4: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= -detect-libc@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= - detect-newline@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" @@ -4678,13 +4645,6 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-minipass@^1.2.5: - version "1.2.7" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" - integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== - dependencies: - minipass "^2.6.0" - fs-minipass@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" @@ -4730,20 +4690,6 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - gensync@^1.0.0-beta.1: version "1.0.0-beta.1" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" @@ -4937,11 +4883,6 @@ has-symbols@^1.0.0, has-symbols@^1.0.1: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= - has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" @@ -5191,7 +5132,7 @@ hyphenate-style-name@^1.0.3: resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz#097bb7fa0b8f1a9cf0bd5c734cf95899981a9b48" integrity sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ== -iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: +iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -5222,13 +5163,6 @@ iferr@^0.1.5: resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= -ignore-walk@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" - integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== - dependencies: - minimatch "^3.0.4" - ignore@^3.3.5: version "3.3.10" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" @@ -5325,7 +5259,7 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@^1.3.5, ini@~1.3.0: +ini@^1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== @@ -6888,14 +6822,6 @@ minipass-pipeline@^1.2.2: dependencies: minipass "^3.0.0" -minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" - integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== - dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" - minipass@^3.0.0, minipass@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.1.tgz#7607ce778472a185ad6d89082aa2070f79cedcd5" @@ -6903,13 +6829,6 @@ minipass@^3.0.0, minipass@^3.1.1: dependencies: yallist "^4.0.0" -minizlib@^1.2.1: - version "1.3.3" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" - integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== - dependencies: - minipass "^2.9.0" - mississippi@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" @@ -6942,7 +6861,7 @@ mixin-object@^2.0.1: for-in "^0.1.3" is-extendable "^0.1.1" -mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: +mkdirp@0.5.1, mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= @@ -7021,15 +6940,6 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -needle@^2.2.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.3.2.tgz#3342dea100b7160960a450dc8c22160ac712a528" - integrity sha512-DUzITvPVDUy6vczKKYTnWc/pBZ0EnjMJnQ3y+Jo5zfKFimJs7S3HFCxCRZYB9FUZcrzUQr3WsmvZgddMEIZv6w== - dependencies: - debug "^3.2.6" - iconv-lite "^0.4.4" - sax "^1.2.4" - negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -7113,22 +7023,6 @@ node-notifier@^5.4.2: shellwords "^0.1.1" which "^1.3.0" -node-pre-gyp@*: - version "0.14.0" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz#9a0596533b877289bcad4e143982ca3d904ddc83" - integrity sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA== - dependencies: - detect-libc "^1.0.2" - mkdirp "^0.5.1" - needle "^2.2.1" - nopt "^4.0.1" - npm-packlist "^1.1.6" - npmlog "^4.0.2" - rc "^1.2.7" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^4.4.2" - node-releases@^1.1.47, node-releases@^1.1.50: version "1.1.50" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.50.tgz#803c40d2c45db172d0410e4efec83aa8c6ad0592" @@ -7136,14 +7030,6 @@ node-releases@^1.1.47, node-releases@^1.1.50: dependencies: semver "^6.3.0" -nopt@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" - integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= - dependencies: - abbrev "1" - osenv "^0.1.4" - normalize-package-data@^2.3.2: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -7186,27 +7072,6 @@ normalize-url@^3.0.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== -npm-bundled@^1.0.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" - integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== - dependencies: - npm-normalize-package-bin "^1.0.1" - -npm-normalize-package-bin@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" - integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== - -npm-packlist@^1.1.6: - version "1.4.8" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" - integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== - dependencies: - ignore-walk "^3.0.1" - npm-bundled "^1.0.1" - npm-normalize-package-bin "^1.0.1" - npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -7214,16 +7079,6 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -npmlog@^4.0.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" - nth-check@^1.0.2, nth-check@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" @@ -7430,11 +7285,6 @@ os-browserify@^0.3.0: resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= - os-locale@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" @@ -7444,19 +7294,11 @@ os-locale@^3.0.0: lcid "^2.0.0" mem "^4.0.0" -os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: +os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= -osenv@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" - integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - p-defer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" @@ -8727,16 +8569,6 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" -rc@^1.2.7: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - react-app-polyfill@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-1.0.6.tgz#890f8d7f2842ce6073f030b117de9130a5f385f0" @@ -8912,7 +8744,7 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -9254,7 +9086,7 @@ rimraf@2.6.3: dependencies: glob "^7.1.3" -rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3, rimraf@^2.7.1: +rimraf@^2.5.4, rimraf@^2.6.3, rimraf@^2.7.1: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -9397,7 +9229,7 @@ selfsigned@^1.10.7: dependencies: node-forge "0.9.0" -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -9459,7 +9291,7 @@ serve-static@1.14.1: parseurl "~1.3.3" send "0.17.1" -set-blocking@^2.0.0, set-blocking@~2.0.0: +set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= @@ -9866,7 +9698,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1: +string-width@^2.0.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -9989,11 +9821,6 @@ strip-json-comments@^3.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - style-loader@0.23.1: version "0.23.1" resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.23.1.tgz#cb9154606f3e771ab6c4ab637026a1049174d925" @@ -10081,19 +9908,6 @@ tapable@^1.0.0, tapable@^1.1.3: resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== -tar@^4.4.2: - version "4.4.13" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" - integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== - dependencies: - chownr "^1.1.1" - fs-minipass "^1.2.5" - minipass "^2.8.6" - minizlib "^1.2.1" - mkdirp "^0.5.0" - safe-buffer "^5.1.2" - yallist "^3.0.3" - terser-webpack-plugin@2.3.4: version "2.3.4" resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-2.3.4.tgz#ac045703bd8da0936ce910d8fb6350d0e1dee5fe" @@ -10784,13 +10598,6 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -wide-align@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== - dependencies: - string-width "^1.0.2 || 2" - word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" @@ -11017,7 +10824,7 @@ xtend@^4.0.0, xtend@~4.0.1: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== -yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: +yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== From c61700f39bdf7d461789f0b1a1f05da8d64586f4 Mon Sep 17 00:00:00 2001 From: Yang yaming Date: Fri, 3 Apr 2020 19:57:59 +0800 Subject: [PATCH 11/12] Add doc of TextNAS (#2260) --- docs/en_US/NAS/TextNAS.md | 80 +++++++++ docs/en_US/nas.rst | 1 + examples/nas/textnas/README.md | 6 +- examples/nas/textnas/arc/final_arc.json | 212 ++++++++++++++++++++++++ examples/nas/textnas/run_retrain.sh | 4 +- 5 files changed, 300 insertions(+), 3 deletions(-) create mode 100644 docs/en_US/NAS/TextNAS.md create mode 100644 examples/nas/textnas/arc/final_arc.json diff --git a/docs/en_US/NAS/TextNAS.md b/docs/en_US/NAS/TextNAS.md new file mode 100644 index 0000000000..7c455534ec --- /dev/null +++ b/docs/en_US/NAS/TextNAS.md @@ -0,0 +1,80 @@ +# TextNAS + +## Introduction + +This is the implementation of the TextNAS algorithm proposed in the paper [TextNAS: A Neural Architecture Search Space tailored for Text Representation](https://arxiv.org/pdf/1912.10729.pdf). TextNAS is a neural architecture search algorithm tailored for text representation, more specifically, TextNAS is based on a novel search space consists of operators widely adopted to solve various NLP tasks, and TextNAS also supports multi-path ensemble within a single network to balance the width and depth of the architecture. + +The search space of TextNAS contains: + + * 1-D convolutional operator with filter size 1, 3, 5, 7 + * recurrent operator (bi-directional GRU) + * self-attention operator + * pooling operator (max/average) + +Following the ENAS algorithm, TextNAS also utilizes parameter sharing to accelerate the search speed and adopts a reinforcement-learning controller for the architecture sampling and generation. Please refer to the paper for more details of TextNAS. + +## Preparation + +Prepare the word vectors and SST dataset, and organize them in data directory as shown below: + +``` +textnas +├── data +│ ├── sst +│ │ └── trees +│ │ ├── dev.txt +│ │ ├── test.txt +│ │ └── train.txt +│ └── glove.840B.300d.txt +├── dataloader.py +├── model.py +├── ops.py +├── README.md +├── search.py +└── utils.py +``` + +The following link might be helpful for finding and downloading the corresponding dataset: + +* [GloVe: Global Vectors for Word Representation](https://nlp.stanford.edu/projects/glove/) + * [glove.840B.300d.txt](http://nlp.stanford.edu/data/glove.840B.300d.zip) +* [Recursive Deep Models for Semantic Compositionality Over a Sentiment Treebank](https://nlp.stanford.edu/sentiment/) + * [trainDevTestTrees_PTB.zip](https://nlp.stanford.edu/sentiment/trainDevTestTrees_PTB.zip) + +## Examples + +### Search Space + +[Example code](https://github.com/microsoft/nni/tree/master/examples/nas/textnas) + +```bash +# In case NNI code is not cloned. If the code is cloned already, ignore this line and enter code folder. +git clone https://github.com/Microsoft/nni.git + +# search the best architecture +cd examples/nas/textnas + +# view more options for search +python3 search.py -h +``` + +After each search epoch, 10 sampled architectures will be tested directly. Their performances are expected to be 40% - 42% after 10 epochs. + +By default, 20 sampled architectures will be exported into `checkpoints` directory for next step. + +### retrain + +```bash +# In case NNI code is not cloned. If the code is cloned already, ignore this line and enter code folder. +git clone https://github.com/Microsoft/nni.git + +# search the best architecture +cd examples/nas/textnas + +# default to retrain on sst-2 +sh run_retrain.sh +``` + +## Reference + +TextNAS directly uses EnasTrainer, please refer to [ENAS](./ENAS.md) for the trainer APIs. diff --git a/docs/en_US/nas.rst b/docs/en_US/nas.rst index 0a56caa742..f5a06c5c9a 100644 --- a/docs/en_US/nas.rst +++ b/docs/en_US/nas.rst @@ -26,5 +26,6 @@ For details, please refer to the following tutorials: SPOS CDARTS ProxylessNAS + TextNAS Customize a NAS Algorithm API Reference diff --git a/examples/nas/textnas/README.md b/examples/nas/textnas/README.md index fb261ad04d..f8ebe24afd 100644 --- a/examples/nas/textnas/README.md +++ b/examples/nas/textnas/README.md @@ -42,4 +42,8 @@ By default, 20 sampled architectures will be exported into `checkpoints` directo ## Retrain -Not ready. +``` +sh run_retrain.sh +``` + +By default, the script will retrain the architecture provided by the author on the SST-2 dataset. diff --git a/examples/nas/textnas/arc/final_arc.json b/examples/nas/textnas/arc/final_arc.json new file mode 100644 index 0000000000..c1e12c2d4b --- /dev/null +++ b/examples/nas/textnas/arc/final_arc.json @@ -0,0 +1,212 @@ +{ + "LayerChoice1": [ + false, false, false, false, false, true, false, false + ], + "InputChoice2": [ + true + ], + "LayerChoice3": [ + false, false, false, false, false, false, false, true + ], + "InputChoice4": [ + false + ], + "InputChoice5": [ + true, false + ], + "LayerChoice6": [ + false, false, false, true, false, false, false, false + ], + "InputChoice7": [ + false, false + ], + "InputChoice8": [ + false, false, true + ], + "LayerChoice9": [ + false, false, false, false, false, false, true, false + ], + "InputChoice10": [ + false, true, true + ], + "InputChoice11": [ + false, false, true, false + ], + "LayerChoice12": [ + false, true, false, false, false, false, false, false + ], + "InputChoice13": [ + false, true, false, false + ], + "InputChoice14": [ + false, false, false, false, true + ], + "LayerChoice15": [ + false, true, false, false, false, false, false, false + ], + "InputChoice16": [ + false, false, true, false, true + ], + "InputChoice17": [ + false, false, false, false, true + ], + "LayerChoice18": [ + true, false, false, false, false, false, false, false + ], + "InputChoice19": [ + false, false, true, true, true, true + ], + "InputChoice20": [ + true, false, false, false, false + ], + "LayerChoice21": [ + false, false, false, false, false, false, true, false + ], + "InputChoice22": [ + false, true, true, false, false, false, false + ], + "InputChoice23": [ + false, true, false, false, false + ], + "LayerChoice24": [ + false, false, false, false, false, true, false, false + ], + "InputChoice25": [ + false, true, false, true, true, false, true, true + ], + "InputChoice26": [ + false, false, true, false, false + ], + "LayerChoice27": [ + false, false, false, false, false, true, false, false + ], + "InputChoice28": [ + false, false, false, false, false, true, false, true, true + ], + "InputChoice29": [ + true, false, false, false, false + ], + "LayerChoice30": [ + false, false, false, false, false, false, false, true + ], + "InputChoice31": [ + true, true, false, false, true, false, false, true, true, false + ], + "InputChoice32": [ + true, false, false, false, false + ], + "LayerChoice33": [ + false, false, false, false, true, false, false, false + ], + "InputChoice34": [ + true, false, false, true, true, true, true, false, false, false, false + ], + "InputChoice35": [ + false, false, false, true, false + ], + "LayerChoice36": [ + false, true, false, false, false, false, false, false + ], + "InputChoice37": [ + true, true, false, true, false, true, false, false, true, false, false, false + ], + "InputChoice38": [ + false, false, false, true, false + ], + "LayerChoice39": [ + false, false, true, false, false, false, false, false + ], + "InputChoice40": [ + true, true, false, false, false, false, true, false, false, true, true, false, true + ], + "InputChoice41": [ + false, false, false, true, false + ], + "LayerChoice42": [ + true, false, false, false, false, false, false, false + ], + "InputChoice43": [ + false, false, true, false, false, false, true, true, true, false, true, true, false, false + ], + "InputChoice44": [ + false, false, false, false, true + ], + "LayerChoice45": [ + false, false, false, true, false, false, false, false + ], + "InputChoice46": [ + true, false, false, false, false, false, true, false, false, false, true, true, false, false, true + ], + "InputChoice47": [ + false, false, false, true, false + ], + "LayerChoice48": [ + false, false, true, false, false, false, false, false + ], + "InputChoice49": [ + false, false, false, false, false, false, false, false, false, true, true, false, true, false, true, false + ], + "InputChoice50": [ + false, false, false, false, true + ], + "LayerChoice51": [ + false, false, false, false, true, false, false, false + ], + "InputChoice52": [ + false, true, true, true, true, false, false, true, false, true, false, false, false, false, true, false, false + ], + "InputChoice53": [ + false, false, true, false, false + ], + "LayerChoice54": [ + false, false, false, true, false, false, false, false + ], + "InputChoice55": [ + false, false, false, false, false, true, false, false, false, false, false, false, false, true, true, true, false, true + ], + "InputChoice56": [ + false, false, true, false, false + ], + "LayerChoice57": [ + false, false, false, true, false, false, false, false + ], + "InputChoice58": [ + false, false, false, true, false, false, false, false, false, false, true, false, false, false, true, false, false, false, false + ], + "InputChoice59": [ + false, true, false, false, false + ], + "LayerChoice60": [ + false, false, false, false, false, true, false, false + ], + "InputChoice61": [ + true, true, false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, true, false, false + ], + "InputChoice62": [ + true, false, false, false, false + ], + "LayerChoice63": [ + false, false, false, false, false, false, false, true + ], + "InputChoice64": [ + false, true, true, true, false, false, false, true, false, true, true, true, true, false, true, false, false, false, false, false, false + ], + "InputChoice65": [ + false, false, false, false, true + ], + "LayerChoice66": [ + false, false, false, false, false, false, false, true + ], + "InputChoice67": [ + false, false, true, true, true, true, false, true, false, true, true, false, false, false, false, true, false, false, false, false, false, true + ], + "InputChoice68": [ + false, false, false, true, false + ], + "LayerChoice69": [ + false, false, false, true, false, false, false, false + ], + "InputChoice70": [ + true, false, false, true, false, false, false, true, false, false, false, false, true, false, false, false, true, false, false, false, false, false, false + ] +} diff --git a/examples/nas/textnas/run_retrain.sh b/examples/nas/textnas/run_retrain.sh index 5c8ea66ae9..1f02121e31 100755 --- a/examples/nas/textnas/run_retrain.sh +++ b/examples/nas/textnas/run_retrain.sh @@ -4,7 +4,7 @@ export PYTHONPATH="$(pwd)" export CUDA_VISIBLE_DEVICES=0 -python -u retrain.py \ +python3 -u retrain.py \ --train_ratio=1.0 \ --valid_ratio=1.0 \ --min_count=1 \ @@ -36,6 +36,6 @@ python -u retrain.py \ --child_lr_T_0=10 \ --child_lr_T_mul=2 \ --multi_path=True \ - --child_fixed_arc="./checkpoints/architecture_00.json" \ + --child_fixed_arc="./arc/final_arc.json" \ --fixed_seed=True \ "$@" From d2c57770967fc14fe9d6172606b8c2e1903d4e3b Mon Sep 17 00:00:00 2001 From: RayMeng8 Date: Wed, 8 Apr 2020 02:18:57 +0800 Subject: [PATCH 12/12] Add supported data types for PBT tuner (#2271) --- .../mnist-pbt-tuner-pytorch/__init__.py | 0 .../trials/mnist-pbt-tuner-pytorch/mnist.py | 4 +- src/sdk/pynni/nni/pbt_tuner/pbt_tuner.py | 124 +++++++++++++++--- 3 files changed, 111 insertions(+), 17 deletions(-) delete mode 100644 examples/trials/mnist-pbt-tuner-pytorch/__init__.py diff --git a/examples/trials/mnist-pbt-tuner-pytorch/__init__.py b/examples/trials/mnist-pbt-tuner-pytorch/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/examples/trials/mnist-pbt-tuner-pytorch/mnist.py b/examples/trials/mnist-pbt-tuner-pytorch/mnist.py index 2161191e9e..b8653b40dc 100644 --- a/examples/trials/mnist-pbt-tuner-pytorch/mnist.py +++ b/examples/trials/mnist-pbt-tuner-pytorch/mnist.py @@ -155,8 +155,8 @@ def get_params(): help='learning rate (default: 0.01)') parser.add_argument('--momentum', type=float, default=0.5, metavar='M', help='SGD momentum (default: 0.5)') - parser.add_argument('--epochs', type=int, default=10, metavar='N', - help='number of epochs to train (default: 10)') + parser.add_argument('--epochs', type=int, default=1, metavar='N', + help='number of epochs to train (default: 1)') parser.add_argument('--seed', type=int, default=1, metavar='S', help='random seed (default: 1)') parser.add_argument('--no_cuda', action='store_true', default=False, diff --git a/src/sdk/pynni/nni/pbt_tuner/pbt_tuner.py b/src/sdk/pynni/nni/pbt_tuner/pbt_tuner.py index 9e4acd586c..e943752e84 100755 --- a/src/sdk/pynni/nni/pbt_tuner/pbt_tuner.py +++ b/src/sdk/pynni/nni/pbt_tuner/pbt_tuner.py @@ -4,9 +4,11 @@ import copy import logging import os +import random import numpy as np import nni +import nni.parameter_expressions from nni.tuner import Tuner from nni.utils import OptimizeMode, extract_scalar_reward, split_index, json2parameter, json2space @@ -14,7 +16,42 @@ logger = logging.getLogger('pbt_tuner_AutoML') -def exploit_and_explore(bot_trial_info, top_trial_info, factors, epoch, search_space): +def perturbation(hyperparameter_type, value, resample_probablity, uv, ub, lv, lb, random_state): + """ + Perturbation for hyperparameters + + Parameters + ---------- + hyperparameter_type : str + type of hyperparameter + value : list + parameters for sampling hyperparameter + resample_probability : float + probability for resampling + uv : float/int + upper value after perturbation + ub : float/int + upper bound + lv : float/int + lower value after perturbation + lb : float/int + lower bound + random_state : RandomState + random state + """ + if random.random() < resample_probablity: + if hyperparameter_type == "choice": + return value.index(nni.parameter_expressions.choice(value, random_state)) + else: + return getattr(nni.parameter_expressions, hyperparameter_type)(*(value + [random_state])) + else: + if random.random() > 0.5: + return min(uv, ub) + else: + return max(lv, lb) + + +def exploit_and_explore(bot_trial_info, top_trial_info, factor, resample_probability, epoch, search_space): """ Replace checkpoint of bot_trial with top, and perturb hyperparameters @@ -24,8 +61,10 @@ def exploit_and_explore(bot_trial_info, top_trial_info, factors, epoch, search_s bottom model whose parameters should be replaced top_trial_info : TrialInfo better model - factors : float - factors for perturbation + factor : float + factor for perturbation + resample_probability : float + probability for resampling epoch : int step of PBTTuner search_space : dict @@ -34,21 +73,72 @@ def exploit_and_explore(bot_trial_info, top_trial_info, factors, epoch, search_s bot_checkpoint_dir = bot_trial_info.checkpoint_dir top_hyper_parameters = top_trial_info.hyper_parameters hyper_parameters = copy.deepcopy(top_hyper_parameters) - # TODO think about different type of hyperparameters for 1.perturbation 2.within search space + random_state = np.random.RandomState() for key in hyper_parameters.keys(): + hyper_parameter = hyper_parameters[key] if key == 'load_checkpoint_dir': hyper_parameters[key] = hyper_parameters['save_checkpoint_dir'] + continue elif key == 'save_checkpoint_dir': hyper_parameters[key] = os.path.join(bot_checkpoint_dir, str(epoch)) - elif isinstance(hyper_parameters[key], float): - perturb = np.random.choice(factors) - val = hyper_parameters[key] * perturb + continue + elif search_space[key]["_type"] == "choice": + choices = search_space[key]["_value"] + ub, uv = len(choices) - 1, choices.index(hyper_parameter["_value"]) + 1 + lb, lv = 0, choices.index(hyper_parameter["_value"]) - 1 + elif search_space[key]["_type"] == "randint": lb, ub = search_space[key]["_value"][:2] - if search_space[key]["_type"] in ("uniform", "normal"): - val = np.clip(val, lb, ub).item() - hyper_parameters[key] = val + ub -= 1 + uv = hyper_parameter + 1 + lv = hyper_parameter - 1 + elif search_space[key]["_type"] == "uniform": + lb, ub = search_space[key]["_value"][:2] + perturb = (ub - lb) * factor + uv = hyper_parameter + perturb + lv = hyper_parameter - perturb + elif search_space[key]["_type"] == "quniform": + lb, ub, q = search_space[key]["_value"][:3] + multi = round(hyper_parameter / q) + uv = (multi + 1) * q + lv = (multi - 1) * q + elif search_space[key]["_type"] == "loguniform": + lb, ub = search_space[key]["_value"][:2] + perturb = (np.log(ub) - np.log(lb)) * factor + uv = np.exp(min(np.log(hyper_parameter) + perturb, np.log(ub))) + lv = np.exp(max(np.log(hyper_parameter) - perturb, np.log(lb))) + elif search_space[key]["_type"] == "qloguniform": + lb, ub, q = search_space[key]["_value"][:3] + multi = round(hyper_parameter / q) + uv = (multi + 1) * q + lv = (multi - 1) * q + elif search_space[key]["_type"] == "normal": + sigma = search_space[key]["_value"][1] + perturb = sigma * factor + uv = ub = hyper_parameter + perturb + lv = lb = hyper_parameter - perturb + elif search_space[key]["_type"] == "qnormal": + q = search_space[key]["_value"][2] + uv = ub = hyper_parameter + q + lv = lb = hyper_parameter - q + elif search_space[key]["_type"] == "lognormal": + sigma = search_space[key]["_value"][1] + perturb = sigma * factor + uv = ub = np.exp(np.log(hyper_parameter) + perturb) + lv = lb = np.exp(np.log(hyper_parameter) - perturb) + elif search_space[key]["_type"] == "qlognormal": + q = search_space[key]["_value"][2] + uv = ub = hyper_parameter + q + lv, lb = hyper_parameter - q, 1E-10 else: + logger.warning("Illegal type to perturb: %s", search_space[key]["_type"]) continue + if search_space[key]["_type"] == "choice": + idx = perturbation(search_space[key]["_type"], search_space[key]["_value"], + resample_probability, uv, ub, lv, lb, random_state) + hyper_parameters[key] = {'_index': idx, '_value': choices[idx]} + else: + hyper_parameters[key] = perturbation(search_space[key]["_type"], search_space[key]["_value"], + resample_probability, uv, ub, lv, lb, random_state) bot_trial_info.hyper_parameters = hyper_parameters bot_trial_info.clean_id() @@ -70,7 +160,8 @@ def clean_id(self): class PBTTuner(Tuner): - def __init__(self, optimize_mode="maximize", all_checkpoint_dir=None, population_size=10, factors=(1.2, 0.8), fraction=0.2): + def __init__(self, optimize_mode="maximize", all_checkpoint_dir=None, population_size=10, factor=0.2, + resample_probability=0.25, fraction=0.2): """ Initialization @@ -82,8 +173,10 @@ def __init__(self, optimize_mode="maximize", all_checkpoint_dir=None, population directory to store training model checkpoint population_size : int number of trials for each epoch - factors : tuple - factors for perturbation + factor : float + factor for perturbation + resample_probability : float + probability for resampling fraction : float fraction for selecting bottom and top trials """ @@ -93,7 +186,8 @@ def __init__(self, optimize_mode="maximize", all_checkpoint_dir=None, population logger.info("Checkpoint dir is set to %s by default.", all_checkpoint_dir) self.all_checkpoint_dir = all_checkpoint_dir self.population_size = population_size - self.factors = factors + self.factor = factor + self.resample_probability = resample_probability self.fraction = fraction # defined in trial code #self.perturbation_interval = perturbation_interval @@ -237,7 +331,7 @@ def receive_trial_result(self, parameter_id, parameters, value, **kwargs): bottoms = self.finished[self.finished_trials - cutoff:] for bottom in bottoms: top = np.random.choice(tops) - exploit_and_explore(bottom, top, self.factors, self.epoch, self.searchspace_json) + exploit_and_explore(bottom, top, self.factor, self.resample_probability, self.epoch, self.searchspace_json) for trial in self.finished: if trial not in bottoms: trial.clean_id()