Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Retiarii] support hypermodule: autoactivation #3868

Merged
merged 10 commits into from
Jul 15, 2021

Conversation

QuanluZhang
Copy link
Contributor

@QuanluZhang QuanluZhang commented Jun 24, 2021

hypermodule is a (PyTorch) module which contains many architecture/hyperparameter candidates for this module. By using hypermodule, NNI will automatically find the best architecture/hyperparameter for this module. This follows the design philosophy of Retiarii that users write DNN model as a space.

This pr is implementing AutoActivation from the paper "Search for activation Functions"

  • AutoActivation with one core unit
  • AutoActivation with two or more core units
  • add document and docstring
  • add test

@sayakpaul
Copy link

@QuanluZhang do you have a minimal example that shows how to use this one?

@QuanluZhang
Copy link
Contributor Author

@QuanluZhang do you have a minimal example that shows how to use this one?

I have not added the example in this pr. you can refer to the following example:

import random

from nni.retiarii import model_wrapper
import nni.retiarii.nn.pytorch as nn
import nni.retiarii.strategy as strategy
import nni.retiarii.evaluator.pytorch.lightning as pl
import torch.nn.functional as F
from nni.retiarii import serialize
from nni.retiarii.experiment.pytorch import RetiariiExeConfig, RetiariiExperiment, debug_mutated_model
from torch.utils.data import DataLoader
from torchvision import transforms
from torchvision.datasets import MNIST
#import nni


@model_wrapper
class Net(nn.Module):
    def __init__(self, hidden_size):
        #super(Net, self).__init__()
        super().__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.fc1 = nn.LayerChoice([
        #    nn.Linear(4*4*50, hidden_size),
        #    nn.Linear(4*4*50, hidden_size, bias=False)
        #])
        #repeat = nn.Linear(hidden_size, hidden_size)
        #self.repeat = nn.Repeat(repeat, [1, 4])
        #self.act_cell = nn.Cell([nn.ReLU(), nn.Dropout(), nn.Linear(hidden_size, hidden_size)], 3, 3)
        #self.fc2 = nn.Linear(hidden_size*3, 10)
        self.fc2 = nn.Linear(hidden_size, 10)
        self.act = nn.AutoActivation()

    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.repeat(x)
        #x = self.act_cell([x])
        x = self.act(self.fc2(x))
        return F.log_softmax(x, dim=1)


if __name__ == '__main__':
    base_model = Net(128)
    transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])
    train_dataset = serialize(MNIST, root='data/mnist', train=True, download=True, transform=transform)
    test_dataset = serialize(MNIST, root='data/mnist', train=False, download=True, transform=transform)
    trainer = pl.Classification(train_dataloader=pl.DataLoader(train_dataset, batch_size=100),
                                val_dataloaders=pl.DataLoader(test_dataset, batch_size=100),
                                max_epochs=1, gpus=1)

    # uncomment the following two lines to debug a generated model
    #debug_mutated_model(base_model, trainer, [])
    #exit(0)

    #simple_strategy = strategy.PolicyBasedRL()
    simple_strategy = strategy.GridSearch()

    exp = RetiariiExperiment(base_model, trainer, [], simple_strategy)

    exp_config = RetiariiExeConfig('local')
    exp_config.experiment_name = 'mnist_search'
    exp_config.trial_concurrency = 3
    exp_config.max_trial_number = 250
    exp_config.trial_gpu_number = 1
    exp_config.training_service.use_active_gpu = False
    exp_config.execution_engine = 'py'

    exp.run(exp_config, 8081 + random.randint(0, 100))
    print('Final model:')
    for model_code in exp.export_top_models(formatter='dict'):
        print(model_code)

@sayakpaul
Copy link

Sure. Will give it a try. Would you folks prefer to have Colab Notebooks as they don't require any non-trivial setups and might be easier for anyone to get started?

@QuanluZhang QuanluZhang marked this pull request as ready for review June 28, 2021 13:53
Copy link
Contributor

@ultmaster ultmaster left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

super().__init__()
self.unaries = nn.ModuleList()
self.binaries = nn.ModuleList()
self.first_unary = LayerChoice([eval('{}()'.format(unary)) for unary in unary_modules], label='one_unary')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest globals()(unary)(), which is semantically more clear that eval.

self.binaries = nn.ModuleList()
self.first_unary = LayerChoice([eval('{}()'.format(unary)) for unary in unary_modules], label='one_unary')
for _ in range(unit_num):
one_unary = LayerChoice([eval('{}()'.format(unary)) for unary in unary_modules], label='one_unary')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think they should have the same label?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, you are right, this is for easy testing, i will remove the label then.

@@ -544,3 +561,21 @@ def forward(self, x):
except InvalidMutation:
continue
self.assertTrue(self._get_converted_pytorch_model(model)(torch.randn(2, 10)).size() == torch.Size([2, 16]))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can write one in the base class and Python will automatically inherit it.

@QuanluZhang QuanluZhang merged commit 5eb43ea into microsoft:master Jul 15, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants