From e240b8957496f09eec023c8aadf5fc2ad82e4221 Mon Sep 17 00:00:00 2001 From: jimbojumbo Date: Wed, 20 Nov 2019 00:16:36 -0600 Subject: [PATCH 1/5] =?UTF-8?q?fix(examples/trails/sklearn)-fix=20the=20in?= =?UTF-8?q?correct=20input=20name=20for=20run=20fun=E2=80=A6=20(#1751)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/trials/sklearn/classification/main.py | 6 +++--- examples/trials/sklearn/regression/main.py | 14 ++++++-------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/examples/trials/sklearn/classification/main.py b/examples/trials/sklearn/classification/main.py index 280dc26fb8..6839e830f6 100644 --- a/examples/trials/sklearn/classification/main.py +++ b/examples/trials/sklearn/classification/main.py @@ -23,13 +23,13 @@ import logging import numpy as np - LOG = logging.getLogger('sklearn_classification') def load_data(): '''Load dataset, use 20newsgroups dataset''' digits = load_digits() - X_train, X_test, y_train, y_test = train_test_split(digits.data, digits.target, random_state=99, test_size=0.25) + X_train, X_test, y_train, y_test = train_test_split( + digits.data, digits.target, random_state=99, test_size=0.25) ss = StandardScaler() X_train = ss.fit_transform(X_train) @@ -59,7 +59,7 @@ def get_model(PARAMS): return model -def run(X_train, X_test, y_train, y_test, PARAMS): +def run(X_train, X_test, y_train, y_test, model): '''Train model and predict result''' model.fit(X_train, y_train) score = model.score(X_test, y_test) diff --git a/examples/trials/sklearn/regression/main.py b/examples/trials/sklearn/regression/main.py index d4dc3449e3..af54bf225f 100644 --- a/examples/trials/sklearn/regression/main.py +++ b/examples/trials/sklearn/regression/main.py @@ -33,23 +33,22 @@ def load_data(): '''Load dataset, use boston dataset''' boston = load_boston() - X_train, X_test, y_train, y_test = train_test_split(boston.data, boston.target, random_state=99, test_size=0.25) + X_train, X_test, y_train, y_test = train_test_split( + boston.data, boston.target, random_state=99, test_size=0.25) #normalize data ss_X = StandardScaler() ss_y = StandardScaler() X_train = ss_X.fit_transform(X_train) X_test = ss_X.transform(X_test) - y_train = ss_y.fit_transform(y_train[:, None])[:,0] - y_test = ss_y.transform(y_test[:, None])[:,0] + y_train = ss_y.fit_transform(y_train[:, None])[:, 0] + y_test = ss_y.transform(y_test[:, None])[:, 0] return X_train, X_test, y_train, y_test def get_default_parameters(): '''get default parameters''' - params = { - 'model_name': 'LinearRegression' - } + params = {'model_name': 'LinearRegression'} return params def get_model(PARAMS): @@ -76,8 +75,7 @@ def get_model(PARAMS): raise return model - -def run(X_train, X_test, y_train, y_test, PARAMS): +def run(X_train, X_test, y_train, y_test, model): '''Train model and predict result''' model.fit(X_train, y_train) predict_y = model.predict(X_test) From 82dea35523b0ef20a53dd94715d10fd21a01c116 Mon Sep 17 00:00:00 2001 From: QuanluZhang Date: Wed, 20 Nov 2019 15:31:35 +0800 Subject: [PATCH 2/5] support lottery ticket hypothesis (#1685) --- .../Compressor/LotteryTicketHypothesis.md | 23 +++ docs/en_US/Compressor/Overview.md | 1 + docs/en_US/Compressor/Pruner.md | 41 +++++ docs/img/lottery_ticket_mnist_fc.png | Bin 0 -> 127951 bytes .../model_compress/lottery_torch_mnist_fc.py | 83 ++++++++++ .../pynni/nni/compression/torch/__init__.py | 1 + .../pynni/nni/compression/torch/compressor.py | 10 +- .../nni/compression/torch/lottery_ticket.py | 148 ++++++++++++++++++ 8 files changed, 299 insertions(+), 8 deletions(-) create mode 100644 docs/en_US/Compressor/LotteryTicketHypothesis.md create mode 100644 docs/img/lottery_ticket_mnist_fc.png create mode 100644 examples/model_compress/lottery_torch_mnist_fc.py create mode 100644 src/sdk/pynni/nni/compression/torch/lottery_ticket.py diff --git a/docs/en_US/Compressor/LotteryTicketHypothesis.md b/docs/en_US/Compressor/LotteryTicketHypothesis.md new file mode 100644 index 0000000000..5ac64155fa --- /dev/null +++ b/docs/en_US/Compressor/LotteryTicketHypothesis.md @@ -0,0 +1,23 @@ +Lottery Ticket Hypothesis on NNI +=== + +## Introduction + +The paper [The Lottery Ticket Hypothesis: Finding Sparse, Trainable Neural Networks](https://arxiv.org/abs/1803.03635) is mainly a measurement and analysis paper, it delivers very interesting insights. To support it on NNI, we mainly implement the training approach for finding *winning tickets*. + +In this paper, the authors use the following process to prune a model, called *iterative prunning*: +>1. Randomly initialize a neural network f(x;theta_0) (where theta_0 follows D_{theta}). +>2. Train the network for j iterations, arriving at parameters theta_j. +>3. Prune p% of the parameters in theta_j, creating a mask m. +>4. Reset the remaining parameters to their values in theta_0, creating the winning ticket f(x;m*theta_0). +>5. Repeat step 2, 3, and 4. + +If the configured final sparsity is P (e.g., 0.8) and there are n times iterative pruning, each iterative pruning prunes 1-(1-P)^(1/n) of the weights that survive the previous round. + +## Reproduce Results + +We try to reproduce the experiment result of the fully connected network on MNIST using the same configuration as in the paper. The code can be referred [here](https://github.com/microsoft/nni/tree/master/examples/model_compress/lottery_torch_mnist_fc.py). In this experiment, we prune 10 times, for each pruning we train the pruned model for 50 epochs. + +![](../../img/lottery_ticket_mnist_fc.png) + +The above figure shows the result of the fully connected network. `round0-sparsity-0.0` is the performance without pruning. Consistent with the paper, pruning around 80% also obtain similar performance compared to non-pruning, and converges a little faster. If pruning too much, e.g., larger than 94%, the accuracy becomes lower and convergence becomes a little slower. A little different from the paper, the trend of the data in the paper is relatively more clear. diff --git a/docs/en_US/Compressor/Overview.md b/docs/en_US/Compressor/Overview.md index 7b3fcb0ce3..f992117ffa 100644 --- a/docs/en_US/Compressor/Overview.md +++ b/docs/en_US/Compressor/Overview.md @@ -12,6 +12,7 @@ We have provided two naive compression algorithms and three popular ones for use |---|---| | [Level Pruner](./Pruner.md#level-pruner) | Pruning the specified ratio on each weight based on absolute values of weights | | [AGP Pruner](./Pruner.md#agp-pruner) | Automated gradual pruning (To prune, or not to prune: exploring the efficacy of pruning for model compression) [Reference Paper](https://arxiv.org/abs/1710.01878)| +| [Lottery Ticket Pruner](./Pruner.md#agp-pruner) | The pruning process used by "The Lottery Ticket Hypothesis: Finding Sparse, Trainable Neural Networks". It prunes a model iteratively. [Reference Paper](https://arxiv.org/abs/1803.03635)| | [FPGM Pruner](./Pruner.md#fpgm-pruner) | Filter Pruning via Geometric Median for Deep Convolutional Neural Networks Acceleration [Reference Paper](https://arxiv.org/pdf/1811.00250.pdf)| | [Naive Quantizer](./Quantizer.md#naive-quantizer) | Quantize weights to default 8 bits | | [QAT Quantizer](./Quantizer.md#qat-quantizer) | Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference. [Reference Paper](http://openaccess.thecvf.com/content_cvpr_2018/papers/Jacob_Quantization_and_Training_CVPR_2018_paper.pdf)| diff --git a/docs/en_US/Compressor/Pruner.md b/docs/en_US/Compressor/Pruner.md index 5e06c02cd4..ce50d579ed 100644 --- a/docs/en_US/Compressor/Pruner.md +++ b/docs/en_US/Compressor/Pruner.md @@ -92,6 +92,47 @@ You can view example for more information *** +## Lottery Ticket Hypothesis +[The Lottery Ticket Hypothesis: Finding Sparse, Trainable Neural Networks](https://arxiv.org/abs/1803.03635), authors Jonathan Frankle and Michael Carbin,provides comprehensive measurement and analysis, and articulate the *lottery ticket hypothesis*: dense, randomly-initialized, feed-forward networks contain subnetworks (*winning tickets*) that -- when trained in isolation -- reach test accuracy comparable to the original network in a similar number of iterations. + +In this paper, the authors use the following process to prune a model, called *iterative prunning*: +>1. Randomly initialize a neural network f(x;theta_0) (where theta_0 follows D_{theta}). +>2. Train the network for j iterations, arriving at parameters theta_j. +>3. Prune p% of the parameters in theta_j, creating a mask m. +>4. Reset the remaining parameters to their values in theta_0, creating the winning ticket f(x;m*theta_0). +>5. Repeat step 2, 3, and 4. + +If the configured final sparsity is P (e.g., 0.8) and there are n times iterative pruning, each iterative pruning prunes 1-(1-P)^(1/n) of the weights that survive the previous round. + +### Usage + +PyTorch code +```python +from nni.compression.torch import LotteryTicketPruner +config_list = [{ + 'prune_iterations': 5, + 'sparsity': 0.8, + 'op_types': ['default'] +}] +pruner = LotteryTicketPruner(model, config_list, optimizer) +pruner.compress() +for _ in pruner.get_prune_iterations(): + pruner.prune_iteration_start() + for epoch in range(epoch_num): + ... +``` + +The above configuration means that there are 5 times of iterative pruning. As the 5 times iterative pruning are executed in the same run, LotteryTicketPruner needs `model` and `optimizer` (**Note that should add `lr_scheduler` if used**) to reset their states every time a new prune iteration starts. Please use `get_prune_iterations` to get the pruning iterations, and invoke `prune_iteration_start` at the beginning of each iteration. `epoch_num` is better to be large enough for model convergence, because the hypothesis is that the performance (accuracy) got in latter rounds with high sparsity could be comparable with that got in the first round. Simple reproducing results can be found [here](./LotteryTicketHypothesis.md). + + +*Tensorflow version will be supported later.* + +#### User configuration for LotteryTicketPruner + +* **prune_iterations:** The number of rounds for the iterative pruning, i.e., the number of iterative pruning. +* **sparsity:** The final sparsity when the compression is done. + +*** ## FPGM Pruner FPGM Pruner is an implementation of paper [Filter Pruning via Geometric Median for Deep Convolutional Neural Networks Acceleration](https://arxiv.org/pdf/1811.00250.pdf) diff --git a/docs/img/lottery_ticket_mnist_fc.png b/docs/img/lottery_ticket_mnist_fc.png new file mode 100644 index 0000000000000000000000000000000000000000..a9051705a8630f4743aa523ba44d6c5d36cd744a GIT binary patch literal 127951 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
*y+5PNjl$i1 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 literal 0 HcmV?d00001 diff --git a/examples/model_compress/lottery_torch_mnist_fc.py b/examples/model_compress/lottery_torch_mnist_fc.py new file mode 100644 index 0000000000..190414d716 --- /dev/null +++ b/examples/model_compress/lottery_torch_mnist_fc.py @@ -0,0 +1,83 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.utils.data +import torchvision.datasets as datasets +import torchvision.transforms as transforms +from nni.compression.torch import LotteryTicketPruner + +class fc1(nn.Module): + + def __init__(self, num_classes=10): + super(fc1, self).__init__() + self.classifier = nn.Sequential( + nn.Linear(28*28, 300), + nn.ReLU(inplace=True), + nn.Linear(300, 100), + nn.ReLU(inplace=True), + nn.Linear(100, num_classes), + ) + + def forward(self, x): + x = torch.flatten(x, 1) + x = self.classifier(x) + return x + +def train(model, train_loader, optimizer, criterion): + device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + model.train() + for batch_idx, (imgs, targets) in enumerate(train_loader): + optimizer.zero_grad() + imgs, targets = imgs.to(device), targets.to(device) + output = model(imgs) + train_loss = criterion(output, targets) + train_loss.backward() + optimizer.step() + return train_loss.item() + +def test(model, test_loader, criterion): + device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + 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) + test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss + pred = output.data.max(1, keepdim=True)[1] # get the index of the max log-probability + correct += pred.eq(target.data.view_as(pred)).sum().item() + test_loss /= len(test_loader.dataset) + accuracy = 100. * correct / len(test_loader.dataset) + return accuracy + + +if __name__ == '__main__': + transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]) + traindataset = datasets.MNIST('./data', train=True, download=True, transform=transform) + testdataset = datasets.MNIST('./data', train=False, transform=transform) + train_loader = torch.utils.data.DataLoader(traindataset, batch_size=60, shuffle=True, num_workers=0, drop_last=False) + test_loader = torch.utils.data.DataLoader(testdataset, batch_size=60, shuffle=False, num_workers=0, drop_last=True) + + model = fc1().to("cuda" if torch.cuda.is_available() else "cpu") + optimizer = torch.optim.Adam(model.parameters(), lr=1.2e-3) + criterion = nn.CrossEntropyLoss() + + configure_list = [{ + 'prune_iterations': 10, + 'sparsity': 0.96, + 'op_types': ['default'] + }] + pruner = LotteryTicketPruner(model, configure_list, optimizer) + pruner.compress() + + for i in pruner.get_prune_iterations(): + pruner.prune_iteration_start() + loss = 0 + accuracy = 0 + for epoch in range(50): + loss = train(model, train_loader, optimizer, criterion) + accuracy = test(model, test_loader, criterion) + print('current epoch: {0}, loss: {1}, accuracy: {2}'.format(epoch, loss, accuracy)) + print('prune iteration: {0}, loss: {1}, accuracy: {2}'.format(i, loss, accuracy)) + pruner.export_model('model.pth', 'mask.pth') diff --git a/src/sdk/pynni/nni/compression/torch/__init__.py b/src/sdk/pynni/nni/compression/torch/__init__.py index baf2f84628..ee7a95ac98 100644 --- a/src/sdk/pynni/nni/compression/torch/__init__.py +++ b/src/sdk/pynni/nni/compression/torch/__init__.py @@ -1,3 +1,4 @@ from .compressor import LayerInfo, Compressor, Pruner, Quantizer from .builtin_pruners import * from .builtin_quantizers import * +from .lottery_ticket import LotteryTicketPruner diff --git a/src/sdk/pynni/nni/compression/torch/compressor.py b/src/sdk/pynni/nni/compression/torch/compressor.py index 2f1c8da2cc..f7a7a74b6b 100644 --- a/src/sdk/pynni/nni/compression/torch/compressor.py +++ b/src/sdk/pynni/nni/compression/torch/compressor.py @@ -13,7 +13,6 @@ def __init__(self, name, module): self._forward = None - class Compressor: """ Abstract base PyTorch compressor @@ -37,7 +36,6 @@ def __init__(self, model, config_list): def detect_modules_to_compress(self): """ detect all modules should be compressed, and save the result in `self.modules_to_compress`. - The model will be instrumented and user should never edit it after calling this method. """ if self.modules_to_compress is None: @@ -49,7 +47,6 @@ def detect_modules_to_compress(self): self.modules_to_compress.append((layer, config)) return self.modules_to_compress - def compress(self): """ Compress the model with algorithm implemented by subclass. @@ -218,6 +215,8 @@ def export_model(self, model_path, mask_path=None, onnx_path=None, input_shape=N input_shape : list or tuple input shape to onnx model """ + if self.detect_modules_to_compress() and not self.mask_dict: + _logger.warning('You may not use self.mask_dict in base Pruner class to record masks') assert model_path is not None, 'model_path must be specified' for name, m in self.bound_model.named_modules(): if name == "": @@ -227,25 +226,20 @@ def export_model(self, model_path, mask_path=None, onnx_path=None, input_shape=N mask_sum = mask.sum().item() mask_num = mask.numel() _logger.info('Layer: %s Sparsity: %.2f', name, 1 - mask_sum / mask_num) - print('Layer: %s Sparsity: %.2f' % (name, 1 - mask_sum / mask_num)) m.weight.data = m.weight.data.mul(mask) else: _logger.info('Layer: %s NOT compressed', name) - print('Layer: %s NOT compressed' % name) torch.save(self.bound_model.state_dict(), model_path) _logger.info('Model state_dict saved to %s', model_path) - print('Model state_dict saved to %s' % model_path) if mask_path is not None: torch.save(self.mask_dict, mask_path) _logger.info('Mask dict saved to %s', mask_path) - print('Mask dict saved to %s' % mask_path) if onnx_path is not None: assert input_shape is not None, 'input_shape must be specified to export onnx model' # input info needed input_data = torch.Tensor(*input_shape) torch.onnx.export(self.bound_model, input_data, onnx_path) _logger.info('Model in onnx with input shape %s saved to %s', input_data.shape, onnx_path) - print('Model in onnx with input shape %s saved to %s' % (input_data.shape, onnx_path)) class Quantizer(Compressor): diff --git a/src/sdk/pynni/nni/compression/torch/lottery_ticket.py b/src/sdk/pynni/nni/compression/torch/lottery_ticket.py new file mode 100644 index 0000000000..d8e4f78c76 --- /dev/null +++ b/src/sdk/pynni/nni/compression/torch/lottery_ticket.py @@ -0,0 +1,148 @@ +import copy +import logging +import torch +from .compressor import Pruner + +_logger = logging.getLogger(__name__) + + +class LotteryTicketPruner(Pruner): + """ + This is a Pytorch implementation of the paper "The Lottery Ticket Hypothesis: Finding Sparse, Trainable Neural Networks", + following NNI model compression interface. + + 1. Randomly initialize a neural network f(x;theta_0) (where theta_0 follows D_{theta}). + 2. Train the network for j iterations, arriving at parameters theta_j. + 3. Prune p% of the parameters in theta_j, creating a mask m. + 4. Reset the remaining parameters to their values in theta_0, creating the winning ticket f(x;m*theta_0). + 5. Repeat step 2, 3, and 4. + """ + def __init__(self, model, config_list, optimizer, lr_scheduler=None, reset_weights=True): + """ + Parameters + ---------- + model : pytorch model + The model to be pruned + config_list : list + Supported keys: + - prune_iterations : The number of rounds for the iterative pruning. + - sparsity : The final sparsity when the compression is done. + optimizer : pytorch optimizer + The optimizer for the model + lr_scheduler : pytorch lr scheduler + The lr scheduler for the model if used + reset_weights : bool + Whether reset weights and optimizer at the beginning of each round. + """ + super().__init__(model, config_list) + self.curr_prune_iteration = None + self.prune_iterations = self._validate_config(config_list) + + # save init weights and optimizer + self.reset_weights = reset_weights + if self.reset_weights: + self._model = model + self._optimizer = optimizer + self._model_state = copy.deepcopy(model.state_dict()) + self._optimizer_state = copy.deepcopy(optimizer.state_dict()) + self._lr_scheduler = lr_scheduler + if lr_scheduler is not None: + self._scheduler_state = copy.deepcopy(lr_scheduler.state_dict()) + + def _validate_config(self, config_list): + prune_iterations = None + for config in config_list: + assert 'prune_iterations' in config, 'prune_iterations must exist in your config' + assert 'sparsity' in config, 'sparsity must exist in your config' + if prune_iterations is not None: + assert prune_iterations == config['prune_iterations'], 'The values of prune_iterations must be equal in your config' + prune_iterations = config['prune_iterations'] + return prune_iterations + + def _print_masks(self, print_mask=False): + torch.set_printoptions(threshold=1000) + for op_name in self.mask_dict.keys(): + mask = self.mask_dict[op_name] + print('op name: ', op_name) + if print_mask: + print('mask: ', mask) + # calculate current sparsity + mask_num = mask.sum().item() + mask_size = mask.numel() + print('sparsity: ', 1 - mask_num / mask_size) + torch.set_printoptions(profile='default') + + def _calc_sparsity(self, sparsity): + keep_ratio_once = (1 - sparsity) ** (1 / self.prune_iterations) + curr_keep_ratio = keep_ratio_once ** self.curr_prune_iteration + return max(1 - curr_keep_ratio, 0) + + def _calc_mask(self, weight, sparsity, op_name): + if self.curr_prune_iteration == 0: + mask = torch.ones(weight.shape).type_as(weight) + else: + curr_sparsity = self._calc_sparsity(sparsity) + assert self.mask_dict.get(op_name) is not None + curr_mask = self.mask_dict.get(op_name) + w_abs = weight.abs() * curr_mask + k = int(w_abs.numel() * curr_sparsity) + threshold = torch.topk(w_abs.view(-1), k, largest=False).values.max() + mask = torch.gt(w_abs, threshold).type_as(weight) + return mask + + def calc_mask(self, layer, config): + """ + Generate mask for the given ``weight``. + + Parameters + ---------- + layer : LayerInfo + The layer to be pruned + config : dict + Pruning configurations for this weight + + Returns + ------- + tensor + The mask for this weight + """ + assert self.mask_dict.get(layer.name) is not None, 'Please call iteration_start before training' + mask = self.mask_dict[layer.name] + return mask + + def get_prune_iterations(self): + """ + Return the range for iterations. + In the first prune iteration, masks are all one, thus, add one more iteration + + Returns + ------- + list + A list for pruning iterations + """ + return range(self.prune_iterations + 1) + + def prune_iteration_start(self): + """ + Control the pruning procedure on updated epoch number. + Should be called at the beginning of the epoch. + """ + if self.curr_prune_iteration is None: + self.curr_prune_iteration = 0 + else: + self.curr_prune_iteration += 1 + assert self.curr_prune_iteration < self.prune_iterations + 1, 'Exceed the configured prune_iterations' + + modules_to_compress = self.detect_modules_to_compress() + for layer, config in modules_to_compress: + sparsity = config.get('sparsity') + mask = self._calc_mask(layer.module.weight.data, sparsity, layer.name) + self.mask_dict.update({layer.name: mask}) + self._print_masks() + + # reinit weights back to original after new masks are generated + if self.reset_weights: + self._model.load_state_dict(self._model_state) + self._optimizer.load_state_dict(self._optimizer_state) + if self._lr_scheduler is not None: + self._lr_scheduler.load_state_dict(self._scheduler_state) From b38471869b1d167fece83d5850e5e0762d5ecaf4 Mon Sep 17 00:00:00 2001 From: QuanluZhang Date: Wed, 20 Nov 2019 15:54:30 +0800 Subject: [PATCH 3/5] update readme (#1754) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c3777bd416..9f68d804ad 100644 --- a/README.md +++ b/README.md @@ -352,6 +352,7 @@ With authors' permission, we listed a set of NNI usage examples and relevant art * Run [Neural Network Architecture Search](examples/trials/nas_cifar10/README.md) with NNI * [Automatic Feature Engineering](examples/trials/auto-feature-engineering/README.md) with NNI * [Hyperparameter Tuning for Matrix Factorization](https://github.com/microsoft/recommenders/blob/master/notebooks/04_model_select_and_optimize/nni_surprise_svd.ipynb) with NNI + * [scikit-nni](https://github.com/ksachdeva/scikit-nni) Hyper-parameter search for scikit-learn pipelines using NNI * ### **Relevant Articles** ### @@ -360,6 +361,7 @@ With authors' permission, we listed a set of NNI usage examples and relevant art * [Parallelizing a Sequential Algorithm TPE](docs/en_US/CommunitySharings/ParallelizingTpeSearch.md) * [Automatically tuning SVD with NNI](docs/en_US/CommunitySharings/RecommendersSvd.md) * [Automatically tuning SPTAG with NNI](docs/en_US/CommunitySharings/SptagAutoTune.md) + * [Find thy hyper-parameters for scikit-learn pipelines using Microsoft NNI](https://towardsdatascience.com/find-thy-hyper-parameters-for-scikit-learn-pipelines-using-microsoft-nni-f1015b1224c1) * **Blog (in Chinese)** - [AutoML tools (Advisor, NNI and Google Vizier) comparison](http://gaocegege.com/Blog/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/katib-new#%E6%80%BB%E7%BB%93%E4%B8%8E%E5%88%86%E6%9E%90) by [@gaocegege](https://github.com/gaocegege) - 总结与分析 section of design and implementation of kubeflow/katib ## **Feedback** From 430bfea5159d148c445095147b167a44ef8cf638 Mon Sep 17 00:00:00 2001 From: liuzhe-lz <40699903+liuzhe-lz@users.noreply.github.com> Date: Thu, 21 Nov 2019 10:21:53 +0800 Subject: [PATCH 4/5] Implement feature: customized trials --- src/nni_manager/common/manager.ts | 2 +- src/nni_manager/common/trainingService.ts | 5 - src/nni_manager/core/nnimanager.ts | 71 ++-- src/nni_manager/core/test/nnimanager.test.ts | 4 +- src/nni_manager/rest_server/restHandler.ts | 4 +- .../rest_server/test/mockedNNIManager.ts | 4 +- src/sdk/pynni/nni/msg_dispatcher.py | 7 +- .../{test_tuner.py => test_builtin_tuners.py} | 2 +- src/sdk/pynni/tests/test_msg_dispatcher.py | 11 +- .../src/components/Modal/CustomizedTrial.tsx | 317 ++++++++++++++++++ .../src/components/Modal/customized.scss | 71 ++++ .../src/components/trial-detail/TableList.tsx | 55 ++- 12 files changed, 486 insertions(+), 67 deletions(-) rename src/sdk/pynni/tests/{test_tuner.py => test_builtin_tuners.py} (99%) create mode 100644 src/webui/src/components/Modal/CustomizedTrial.tsx create mode 100644 src/webui/src/components/Modal/customized.scss diff --git a/src/nni_manager/common/manager.ts b/src/nni_manager/common/manager.ts index 98c5cd8b14..736f6124b4 100644 --- a/src/nni_manager/common/manager.ts +++ b/src/nni_manager/common/manager.ts @@ -105,7 +105,7 @@ abstract class Manager { public abstract importData(data: string): Promise; public abstract exportData(): Promise; - public abstract addCustomizedTrialJob(hyperParams: string): Promise; + public abstract addCustomizedTrialJob(hyperParams: string): Promise; public abstract cancelTrialJobByUser(trialJobId: string): Promise; public abstract listTrialJobs(status?: TrialJobStatus): Promise; diff --git a/src/nni_manager/common/trainingService.ts b/src/nni_manager/common/trainingService.ts index 2dfa0a9589..d6a8858e5c 100644 --- a/src/nni_manager/common/trainingService.ts +++ b/src/nni_manager/common/trainingService.ts @@ -58,11 +58,6 @@ interface TrialJobDetail { isEarlyStopped?: boolean; } -interface HostJobDetail { - readonly id: string; - readonly status: string; -} - /** * define TrialJobMetric */ diff --git a/src/nni_manager/core/nnimanager.ts b/src/nni_manager/core/nnimanager.ts index 4bae632333..ca56e2dbaf 100644 --- a/src/nni_manager/core/nnimanager.ts +++ b/src/nni_manager/core/nnimanager.ts @@ -50,13 +50,12 @@ class NNIManager implements Manager { private dispatcher: IpcInterface | undefined; private currSubmittedTrialNum: number; // need to be recovered private trialConcurrencyChange: number; // >0: increase, <0: decrease - private customizedTrials: string[]; // need to be recovered private log: Logger; private dataStore: DataStore; private experimentProfile: ExperimentProfile; private dispatcherPid: number; private status: NNIManagerStatus; - private waitingTrials: string[]; + private waitingTrials: TrialJobApplicationForm[]; private trialJobs: Map; private trialDataForTuner: string; private readonly: boolean; @@ -66,7 +65,6 @@ class NNIManager implements Manager { constructor() { this.currSubmittedTrialNum = 0; this.trialConcurrencyChange = 0; - this.customizedTrials = []; this.trainingService = component.get(TrainingService); assert(this.trainingService); this.dispatcherPid = 0; @@ -131,19 +129,34 @@ class NNIManager implements Manager { return this.dataStore.exportTrialHpConfigs(); } - public addCustomizedTrialJob(hyperParams: string): Promise { + public addCustomizedTrialJob(hyperParams: string): Promise { if (this.readonly) { return Promise.reject(new Error('Error: can not add customized trial job in readonly mode!')); } if (this.currSubmittedTrialNum >= this.experimentProfile.params.maxTrialNum) { - return Promise.reject( - new Error('reach maxTrialNum') - ); + return Promise.reject(new Error('reach maxTrialNum')); } - this.customizedTrials.push(hyperParams); + + // TODO: NNI manager should not peek tuner's internal protocol, let's refactor this later + const packedParameter = { + parameter_id: null, + parameter_source: 'customized', + parameters: JSON.parse(hyperParams) + } + + const form: TrialJobApplicationForm = { + sequenceId: this.experimentProfile.nextSequenceId++, + hyperParameters: { + value: JSON.stringify(packedParameter), + index: 0 + } + }; + this.waitingTrials.push(form); // trial id has not been generated yet, thus use '' instead - return this.dataStore.storeTrialJobEvent('ADD_CUSTOMIZED', '', hyperParams); + this.dataStore.storeTrialJobEvent('ADD_CUSTOMIZED', '', hyperParams); + + return Promise.resolve(form.sequenceId); } public async cancelTrialJobByUser(trialJobId: string): Promise { @@ -560,18 +573,7 @@ class NNIManager implements Manager { this.trialConcurrencyChange = requestTrialNum; } - const requestCustomTrialNum: number = Math.min(requestTrialNum, this.customizedTrials.length); - for (let i: number = 0; i < requestCustomTrialNum; i++) { - // ask tuner for more trials - if (this.customizedTrials.length > 0) { - const hyperParams: string | undefined = this.customizedTrials.shift(); - this.dispatcher.sendCommand(ADD_CUSTOMIZED_TRIAL_JOB, hyperParams); - } - } - - if (requestTrialNum - requestCustomTrialNum > 0) { - this.requestTrialJobs(requestTrialNum - requestCustomTrialNum); - } + this.requestTrialJobs(requestTrialNum); // check maxtrialnum and maxduration here // NO_MORE_TRIAL is more like a subset of RUNNING, because during RUNNING tuner @@ -609,26 +611,16 @@ class NNIManager implements Manager { this.currSubmittedTrialNum >= this.experimentProfile.params.maxTrialNum) { break; } - const hyperParams: string | undefined = this.waitingTrials.shift(); - if (hyperParams === undefined) { - throw new Error(`Error: invalid hyper-parameters for job submission: ${hyperParams}`); - } + const form = this.waitingTrials.shift() as TrialJobApplicationForm; this.currSubmittedTrialNum++; - const trialJobAppForm: TrialJobApplicationForm = { - sequenceId: this.experimentProfile.nextSequenceId++, - hyperParameters: { - value: hyperParams, - index: 0 - } - }; - this.log.info(`submitTrialJob: form: ${JSON.stringify(trialJobAppForm)}`); - const trialJobDetail: TrialJobDetail = await this.trainingService.submitTrialJob(trialJobAppForm); + this.log.info(`submitTrialJob: form: ${JSON.stringify(form)}`); + const trialJobDetail: TrialJobDetail = await this.trainingService.submitTrialJob(form); await this.storeExperimentProfile(); this.trialJobs.set(trialJobDetail.id, Object.assign({}, trialJobDetail)); const trialJobDetailSnapshot: TrialJobDetail | undefined = this.trialJobs.get(trialJobDetail.id); if (trialJobDetailSnapshot != undefined) { await this.dataStore.storeTrialJobEvent( - trialJobDetailSnapshot.status, trialJobDetailSnapshot.id, hyperParams, trialJobDetailSnapshot); + trialJobDetailSnapshot.status, trialJobDetailSnapshot.id, form.hyperParameters.value, trialJobDetailSnapshot); } else { assert(false, `undefined trialJobDetail in trialJobs: ${trialJobDetail.id}`); } @@ -734,7 +726,14 @@ class NNIManager implements Manager { this.log.warning('It is not supposed to receive more trials after NO_MORE_TRIAL is set'); this.setStatus('RUNNING'); } - this.waitingTrials.push(content); + const form: TrialJobApplicationForm = { + sequenceId: this.experimentProfile.nextSequenceId++, + hyperParameters: { + value: content, + index: 0 + } + }; + this.waitingTrials.push(form); break; case SEND_TRIAL_JOB_PARAMETER: const tunerCommand: any = JSON.parse(content); diff --git a/src/nni_manager/core/test/nnimanager.test.ts b/src/nni_manager/core/test/nnimanager.test.ts index 2eac8b1c8c..1aaaa6d398 100644 --- a/src/nni_manager/core/test/nnimanager.test.ts +++ b/src/nni_manager/core/test/nnimanager.test.ts @@ -121,7 +121,7 @@ describe('Unit test for nnimanager', function () { it('test addCustomizedTrialJob', () => { - return nniManager.addCustomizedTrialJob('hyperParams').then(() => { + return nniManager.addCustomizedTrialJob('"hyperParams"').then(() => { }).catch((error) => { assert.fail(error); @@ -273,7 +273,7 @@ describe('Unit test for nnimanager', function () { it('test addCustomizedTrialJob reach maxTrialNum', () => { // test currSubmittedTrialNum reach maxTrialNum - return nniManager.addCustomizedTrialJob('hyperParam').then(() => { + return nniManager.addCustomizedTrialJob('"hyperParam"').then(() => { nniManager.getTrialJobStatistics().then(function (trialJobStatistics) { if (trialJobStatistics[0].trialJobStatus === 'WAITING') expect(trialJobStatistics[0].trialJobNumber).to.be.equal(2); diff --git a/src/nni_manager/rest_server/restHandler.ts b/src/nni_manager/rest_server/restHandler.ts index 83c95a2987..a0aee979e7 100644 --- a/src/nni_manager/rest_server/restHandler.ts +++ b/src/nni_manager/rest_server/restHandler.ts @@ -236,8 +236,8 @@ class NNIRestHandler { private addTrialJob(router: Router): void { router.post('/trial-jobs', async (req: Request, res: Response) => { - this.nniManager.addCustomizedTrialJob(JSON.stringify(req.body)).then(() => { - res.send(); + this.nniManager.addCustomizedTrialJob(JSON.stringify(req.body)).then((sequenceId: number) => { + res.send({sequenceId}); }).catch((err: Error) => { this.handle_error(err, res); }); diff --git a/src/nni_manager/rest_server/test/mockedNNIManager.ts b/src/nni_manager/rest_server/test/mockedNNIManager.ts index 3c4a502ec8..e22783d4c9 100644 --- a/src/nni_manager/rest_server/test/mockedNNIManager.ts +++ b/src/nni_manager/rest_server/test/mockedNNIManager.ts @@ -65,8 +65,8 @@ export class MockedNNIManager extends Manager { return deferred.promise; } - public addCustomizedTrialJob(hyperParams: string): Promise { - return Promise.resolve(); + public addCustomizedTrialJob(hyperParams: string): Promise { + return Promise.resolve(99); } public resumeExperiment(): Promise { diff --git a/src/sdk/pynni/nni/msg_dispatcher.py b/src/sdk/pynni/nni/msg_dispatcher.py index dfde488ccc..420db8a566 100644 --- a/src/sdk/pynni/nni/msg_dispatcher.py +++ b/src/sdk/pynni/nni/msg_dispatcher.py @@ -136,7 +136,6 @@ def handle_add_customized_trial(self, data): # data: parameters id_ = _create_parameter_id() _customized_parameter_ids.add(id_) - send(CommandType.NewTrialJob, _pack_parameter(id_, data, customized=True)) def handle_report_metric_data(self, data): """ @@ -185,7 +184,7 @@ def _handle_final_metric_data(self, data): """ id_ = data['parameter_id'] value = data['value'] - if id_ in _customized_parameter_ids: + if not id_ or id_ in _customized_parameter_ids: if not hasattr(self.tuner, '_accept_customized'): self.tuner._accept_customized = False if not self.tuner._accept_customized: @@ -194,8 +193,8 @@ def _handle_final_metric_data(self, data): customized = True else: customized = False - self.tuner.receive_trial_result(id_, _trial_params[id_], value, customized=customized, - trial_job_id=data.get('trial_job_id')) + self.tuner.receive_trial_result(id_, _trial_params[id_], value, customized=customized, + trial_job_id=data.get('trial_job_id')) def _handle_intermediate_metric_data(self, data): """Call assessor to process intermediate results diff --git a/src/sdk/pynni/tests/test_tuner.py b/src/sdk/pynni/tests/test_builtin_tuners.py similarity index 99% rename from src/sdk/pynni/tests/test_tuner.py rename to src/sdk/pynni/tests/test_builtin_tuners.py index 04d9f3aaba..7f9936ffab 100644 --- a/src/sdk/pynni/tests/test_tuner.py +++ b/src/sdk/pynni/tests/test_builtin_tuners.py @@ -41,7 +41,7 @@ logger = logging.getLogger('test_tuner') -class TunerTestCase(TestCase): +class BuiltinTunersTestCase(TestCase): """ Targeted at testing functions of built-in tuners, including - [ ] load_checkpoint diff --git a/src/sdk/pynni/tests/test_msg_dispatcher.py b/src/sdk/pynni/tests/test_msg_dispatcher.py index 883e2349c3..15064ff1eb 100644 --- a/src/sdk/pynni/tests/test_msg_dispatcher.py +++ b/src/sdk/pynni/tests/test_msg_dispatcher.py @@ -80,8 +80,6 @@ def test_msg_dispatcher(self): send(CommandType.ReportMetricData, '{"parameter_id":0,"type":"PERIODICAL","value":10}') send(CommandType.ReportMetricData, '{"parameter_id":1,"type":"FINAL","value":11}') send(CommandType.UpdateSearchSpace, '{"name":"SS0"}') - send(CommandType.AddCustomizedTrialJob, '{"param":-1}') - send(CommandType.ReportMetricData, '{"parameter_id":2,"type":"FINAL","value":22}') send(CommandType.RequestTrialJobs, '1') send(CommandType.KillTrialJob, 'null') _restore_io() @@ -99,14 +97,7 @@ def test_msg_dispatcher(self): self._assert_params(0, 2, [], None) self._assert_params(1, 4, [], None) - command, data = receive() # this one is customized - data = json.loads(data) - self.assertIs(command, CommandType.NewTrialJob) - self.assertEqual(data['parameter_id'], 2) - self.assertEqual(data['parameter_source'], 'customized') - self.assertEqual(data['parameters'], {'param': -1}) - - self._assert_params(3, 6, [[1, 4, 11, False], [2, -1, 22, True]], {'name': 'SS0'}) + self._assert_params(2, 6, [[1, 4, 11, False]], {'name': 'SS0'}) self.assertEqual(len(_out_buf.read()), 0) # no more commands diff --git a/src/webui/src/components/Modal/CustomizedTrial.tsx b/src/webui/src/components/Modal/CustomizedTrial.tsx new file mode 100644 index 0000000000..ae12a1c9ed --- /dev/null +++ b/src/webui/src/components/Modal/CustomizedTrial.tsx @@ -0,0 +1,317 @@ +import * as React from 'react'; +import axios from 'axios'; +import { Row, Col, Input, Modal, Form, Button, Icon } from 'antd'; +import { MANAGER_IP } from '../../static/const'; +import { EXPERIMENT, TRIALS } from '../../static/datamodel'; +import { FormComponentProps } from 'antd/lib/form'; +const FormItem = Form.Item; +import './customized.scss'; + +interface CustomizeProps extends FormComponentProps { + visible: boolean; + copyTrialId: string; + closeCustomizeModal: () => void; +} + +interface CustomizeState { + isShowSubmitSucceed: boolean; + isShowSubmitFailed: boolean; + isShowWarning: boolean; + searchSpace: object; + copyTrialParameter: object; // user click the trial's parameters + customParameters: object; // customized trial, maybe user change trial's parameters + customID: number; // submit customized trial succeed, return the new customized trial id +} + +class Customize extends React.Component { + + constructor(props: CustomizeProps) { + super(props); + this.state = { + isShowSubmitSucceed: false, + isShowSubmitFailed: false, + isShowWarning: false, + searchSpace: EXPERIMENT.searchSpace, + copyTrialParameter: {}, + customParameters: {}, + customID: NaN + }; + } + + // [submit click] user add a new trial [submit a trial] + addNewTrial = () => { + const { searchSpace, copyTrialParameter } = this.state; + // get user edited hyperParameter, ps: will change data type if you modify the input val + const customized = this.props.form.getFieldsValue(); + // true: parameters are wrong + let flag = false; + Object.keys(customized).map(item => { + if (item !== 'tag') { + // unified data type + if (typeof copyTrialParameter[item] === 'number' && typeof customized[item] === 'string') { + customized[item] = JSON.parse(customized[item]); + } + if (searchSpace[item]._type === 'choice') { + if (searchSpace[item]._value.find((val: string | number) => + val === customized[item]) === undefined) { + flag = true; + return; + } + } else { + if (customized[item] < searchSpace[item]._value[0] + || customized[item] > searchSpace[item]._value[1]) { + flag = true; + return; + } + } + } + }); + if (flag !== false) { + // open the warning modal + this.setState(() => ({ isShowWarning: true, customParameters: customized })); + } else { + // submit a customized job + this.submitCustomize(customized); + } + + } + + warningConfirm = () => { + this.setState(() => ({ isShowWarning: false })); + const { customParameters } = this.state; + this.submitCustomize(customParameters); + } + + warningCancel = () => { + this.setState(() => ({ isShowWarning: false })); + } + + submitCustomize = (customized: Object) => { + // delete `tag` key + for (let i in customized) { + if (i === 'tag') { + delete customized[i]; + } + } + axios(`${MANAGER_IP}/trial-jobs`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + data: customized + }) + .then(res => { + if (res.status === 200) { + this.setState(() => ({ isShowSubmitSucceed: true, customID: res.data.sequenceId })); + this.props.closeCustomizeModal(); + } else { + this.setState(() => ({ isShowSubmitFailed: true })); + } + }) + .catch(error => { + this.setState(() => ({ isShowSubmitFailed: true })); + }); + } + + closeSucceedHint = () => { + // also close customized trial modal + this.setState(() => ({ isShowSubmitSucceed: false })); + this.props.closeCustomizeModal(); + } + + closeFailedHint = () => { + // also close customized trial modal + this.setState(() => ({ isShowSubmitFailed: false })); + this.props.closeCustomizeModal(); + } + + componentDidMount() { + const { copyTrialId } = this.props; + if (copyTrialId !== undefined && TRIALS.getTrial(copyTrialId) !== undefined) { + const originCopyTrialPara = TRIALS.getTrial(copyTrialId).description.parameters; + this.setState(() => ({ copyTrialParameter: originCopyTrialPara })); + } + } + + componentWillReceiveProps(nextProps: CustomizeProps) { + const { copyTrialId } = nextProps; + if (copyTrialId !== undefined && TRIALS.getTrial(copyTrialId) !== undefined) { + const originCopyTrialPara = TRIALS.getTrial(copyTrialId).description.parameters; + this.setState(() => ({ copyTrialParameter: originCopyTrialPara })); + } + } + + render() { + const { closeCustomizeModal, visible } = this.props; + const { isShowSubmitSucceed, isShowSubmitFailed, isShowWarning, customID, copyTrialParameter } = this.state; + const { + form: { getFieldDecorator }, + // form: { getFieldDecorator, getFieldValue }, + } = this.props; + const warning = 'The parameters you set are not in our search space, this may cause the tuner to crash, Are' + + ' you sure you want to continue submitting?'; + return ( + + {/* form: search space */} + + {/* search space form */} + +
    + { + Object.keys(copyTrialParameter).map(item => ( + + {item} + + + {getFieldDecorator(item, { + initialValue: copyTrialParameter[item], + })( + + )} + + + + ) + ) + } + + Tag + + + {getFieldDecorator('tag', { + initialValue: 'Customized', + })( + + )} + + + +
    +
    + + + + + {/* control button */} +
    + {/* clone: prompt succeed or failed */} + + + +

    + + + Submit successfully + +

    +
    + You can find your customized trial by Trial No.{customID} +
    +
    + + + +
    +
    + + + +

    + + Submit Failed + +

    +
    + Unknown error. +
    +
    + + + +
    +
    + {/* hyperParameter not match search space, warning modal */} + + + +

    + + Warning + +

    +
    + {warning} +
    +
    + + + + +
    +
    + +
    + + ); + } +} + +export default Form.create()(Customize); \ No newline at end of file diff --git a/src/webui/src/components/Modal/customized.scss b/src/webui/src/components/Modal/customized.scss new file mode 100644 index 0000000000..b17e697400 --- /dev/null +++ b/src/webui/src/components/Modal/customized.scss @@ -0,0 +1,71 @@ +.ant-modal-body{ + border-radius: none; +} +.ant-modal-title { + font-size: 18px; +} +/* resubmit confirm modal style */ +.resubmit{ + .title{ + font-size: 16px; + color: #000; + .color-warn, .color-error{ + color: red; + } + i{ + margin-right: 10px; + } + } + .hint{ + padding: 15px 0; + color: #333; + margin-left: 30px; + } + .color-succ{ + color: green; + } +} +.hyper-box{ + padding: 16px 18px 16px 16px; +} +.hyper-form{ + height: 32px; + margin-bottom: 8px; + .title{ + font-size: 14px; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + line-height: 32px; + } + .inputs{ + height: 32px; + } + input{ + height: 32px; + } +} +.tag-input{ + margin-top: 25px; +} + +/* submit & cancel buttons style*/ +.modal-button{ + text-align: right; + height: 28px; + /* cancel button style*/ + .cancelSty{ + width: 80px; + background-color: #dadada; + border: none; + color: #333; + } + .cancelSty:hover, .cancelSty:active, .cancelSty:focus{ + background-color: #dadada; + } + .distance{ + margin-right: 8px; + } +} + +.center{ + text-align: center; +} diff --git a/src/webui/src/components/trial-detail/TableList.tsx b/src/webui/src/components/trial-detail/TableList.tsx index a4829ad9c5..4b4856c50e 100644 --- a/src/webui/src/components/trial-detail/TableList.tsx +++ b/src/webui/src/components/trial-detail/TableList.tsx @@ -7,10 +7,11 @@ const Option = Select.Option; const CheckboxGroup = Checkbox.Group; import { MANAGER_IP, trialJobStatus, COLUMN_INDEX, COLUMNPro } from '../../static/const'; import { convertDuration, formatTimestamp, intermediateGraphOption, killJob } from '../../static/function'; -import { TRIALS } from '../../static/datamodel'; +import { EXPERIMENT, TRIALS } from '../../static/datamodel'; import { TableRecord } from '../../static/interface'; import OpenRow from '../public-child/OpenRow'; import Compare from '../Modal/Compare'; +import Customize from '../Modal/CustomizedTrial'; import '../../static/style/search.scss'; require('../../static/style/tableStatus.css'); require('../../static/style/logPath.scss'); @@ -45,6 +46,8 @@ interface TableListState { intermediateData: Array; // a trial's intermediate results (include dict) intermediateId: string; intermediateOtherKeys: Array; + isShowCustomizedModal: boolean; + copyTrialId: string; // user copy trial to submit a new customized trial } interface ColumnIndex { @@ -71,7 +74,9 @@ class TableList extends React.Component { selectedRowKeys: [], // close selected trial message after modal closed intermediateData: [], intermediateId: '', - intermediateOtherKeys: [] + intermediateOtherKeys: [], + isShowCustomizedModal: false, + copyTrialId: '' }; } @@ -236,17 +241,36 @@ class TableList extends React.Component { this.setState({ isShowCompareModal: false, selectedRowKeys: [], selectRows: [] }); } + // open customized trial modal + setCustomizedTrial = (trialId: string) => { + this.setState({ + isShowCustomizedModal: true, + copyTrialId: trialId + }); + } + + closeCustomizedTrial = () => { + this.setState({ + isShowCustomizedModal: false, + copyTrialId: '' + }); + } render() { const { pageSize, columnList } = this.props; const tableSource: Array = JSON.parse(JSON.stringify(this.props.tableSource)); const { intermediateOption, modalVisible, isShowColumn, - selectRows, isShowCompareModal, selectedRowKeys, intermediateOtherKeys } = this.state; + selectRows, isShowCompareModal, selectedRowKeys, intermediateOtherKeys, + isShowCustomizedModal, copyTrialId + } = this.state; const rowSelection = { selectedRowKeys: selectedRowKeys, onChange: (selected: string[] | number[], selectedRows: Array) => { this.fillSelectedRowsTostate(selected, selectedRows); } }; + // [supportCustomizedTrial: true] + const supportCustomizedTrial = (EXPERIMENT.multiPhase === true) ? false : true; + const disabledAddCustomizedTrial = ['DONE', 'ERROR', 'STOPPED'].includes(EXPERIMENT.status); let showTitle = COLUMNPro; const showColumn: Array = []; @@ -361,6 +385,22 @@ class TableList extends React.Component { } + {/* Add a new trial-customized trial */} + { + supportCustomizedTrial + ? + + : + null + } ); }, @@ -398,7 +438,7 @@ class TableList extends React.Component { expandedRowRender={this.openRow} dataSource={tableSource} className="commonTableStyle" - scroll={{x: 'max-content'}} + scroll={{ x: 'max-content' }} pagination={pageSize > 0 ? { pageSize } : false} /> {/* Intermediate Result Modal */} @@ -458,7 +498,14 @@ class TableList extends React.Component { className="titleColumn" /> + {/* compare trials based message */} + {/* clone trial parameters and could submit a customized trial */} + ); } From cb52d4418f1bae21880a0764f2d4b05b958434b4 Mon Sep 17 00:00:00 2001 From: liuzhe-lz <40699903+liuzhe-lz@users.noreply.github.com> Date: Thu, 21 Nov 2019 11:24:59 +0800 Subject: [PATCH 5/5] Print INFO log in "standalone" mode (#1761) * Print INFO log in "standalone" mode * init on importing --- src/sdk/pynni/nni/platform/standalone.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sdk/pynni/nni/platform/standalone.py b/src/sdk/pynni/nni/platform/standalone.py index 07da01d986..7f752786b7 100644 --- a/src/sdk/pynni/nni/platform/standalone.py +++ b/src/sdk/pynni/nni/platform/standalone.py @@ -19,9 +19,15 @@ # ================================================================================================== +import logging import json_tricks +# print INFO log to stdout +logging.basicConfig() +logging.getLogger('nni').setLevel(logging.INFO) + + def get_next_parameter(): pass