"vscode:/vscode.git/clone" did not exist on "a6756bf8eb626cfd92816e60d125b91925b9b228"
search.py 5.13 KB
Newer Older
1
import os
2
import random
3
from pathlib import Path
4

5
import nni
QuanluZhang's avatar
QuanluZhang committed
6
import torch
7
8
import torch.nn.functional as F
# remember to import nni.retiarii.nn.pytorch as nn, instead of torch.nn as nn
9
import nni.retiarii.nn.pytorch as nn
10
import nni.retiarii.strategy as strategy
11
12
from nni.retiarii import model_wrapper
from nni.retiarii.evaluator import FunctionalEvaluator
13
from nni.retiarii.experiment.pytorch import RetiariiExeConfig, RetiariiExperiment, debug_mutated_model
14
15
16
from torch.utils.data import DataLoader
from torchvision import transforms
from torchvision.datasets import MNIST
17

18

QuanluZhang's avatar
QuanluZhang committed
19
20
21
22
23
24
25
26
27
class DepthwiseSeparableConv(nn.Module):
    def __init__(self, in_ch, out_ch):
        super().__init__()
        self.depthwise = nn.Conv2d(in_ch, in_ch, kernel_size=3, groups=in_ch)
        self.pointwise = nn.Conv2d(in_ch, out_ch, kernel_size=1)

    def forward(self, x):
        return self.pointwise(self.depthwise(x))

28

29
@model_wrapper
30
class Net(nn.Module):
QuanluZhang's avatar
QuanluZhang committed
31
    def __init__(self):
32
        super().__init__()
QuanluZhang's avatar
QuanluZhang committed
33
        self.conv1 = nn.Conv2d(1, 32, 3, 1)
34
        # LayerChoice is used to select a layer between Conv2d and DwConv.
QuanluZhang's avatar
QuanluZhang committed
35
36
37
38
        self.conv2 = nn.LayerChoice([
            nn.Conv2d(32, 64, 3, 1),
            DepthwiseSeparableConv(32, 64)
        ])
39
40
41
        # ValueChoice is used to select a dropout rate.
        # ValueChoice can be used as parameter of modules wrapped in `nni.retiarii.nn.pytorch`
        # or customized modules wrapped with `@basic_unit`.
QuanluZhang's avatar
QuanluZhang committed
42
43
44
        self.dropout1 = nn.Dropout(nn.ValueChoice([0.25, 0.5, 0.75]))
        self.dropout2 = nn.Dropout(0.5)
        feature = nn.ValueChoice([64, 128, 256])
45
        # Same value choice can be used multiple times
QuanluZhang's avatar
QuanluZhang committed
46
47
        self.fc1 = nn.Linear(9216, feature)
        self.fc2 = nn.Linear(feature, 10)
48
49
50

    def forward(self, x):
        x = F.relu(self.conv1(x))
QuanluZhang's avatar
QuanluZhang committed
51
52
53
        x = F.max_pool2d(self.conv2(x), 2)
        x = torch.flatten(self.dropout1(x), 1)
        x = self.fc2(self.dropout2(F.relu(self.fc1(x))))
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
        return x


def train_epoch(model, device, train_loader, optimizer, epoch):
    loss_fn = torch.nn.CrossEntropyLoss()
    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 = loss_fn(output, target)
        loss.backward()
        optimizer.step()
        if batch_idx % 10 == 0:
            print('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_epoch(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)
            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)

    print('\nTest set: Accuracy: {}/{} ({:.0f}%)\n'.format(
        correct, len(test_loader.dataset), accuracy))

    return accuracy


def evaluate_model(model_cls):
    # "model_cls" is a class, need to instantiate
    model = model_cls()

98
99
100
101
102
    # export model for visualization
    if 'NNI_OUTPUT_DIR' in os.environ:
        torch.onnx.export(model, (torch.randn(1, 1, 28, 28), ),
                          Path(os.environ['NNI_OUTPUT_DIR']) / 'model.onnx')

103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
    optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
    transf = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])
    train_loader = DataLoader(MNIST('data/mnist', download=True, transform=transf), batch_size=64, shuffle=True)
    test_loader = DataLoader(MNIST('data/mnist', download=True, train=False, transform=transf), batch_size=64)

    device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')

    for epoch in range(3):
        # train the model for one epoch
        train_epoch(model, device, train_loader, optimizer, epoch)
        # test the model for one epoch
        accuracy = test_epoch(model, device, test_loader)
        # call report intermediate result. Result can be float or dict
        nni.report_intermediate_result(accuracy)

    # report final test result
    nni.report_final_result(accuracy)

121
122

if __name__ == '__main__':
QuanluZhang's avatar
QuanluZhang committed
123
    base_model = Net()
124

125
126
    search_strategy = strategy.Random()
    model_evaluator = FunctionalEvaluator(evaluate_model)
127

128
    exp = RetiariiExperiment(base_model, model_evaluator, [], search_strategy)
129
130
131
132

    exp_config = RetiariiExeConfig('local')
    exp_config.experiment_name = 'mnist_search'
    exp_config.trial_concurrency = 2
QuanluZhang's avatar
QuanluZhang committed
133
    exp_config.max_trial_number = 20
134
    exp_config.training_service.use_active_gpu = False
135
    export_formatter = 'dict'
136

137
138
139
    # uncomment this for graph-based execution engine
    # exp_config.execution_engine = 'base'
    # export_formatter = 'code'
140
141

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