test_compressor.py 5.23 KB
Newer Older
1
from unittest import TestCase, main
2
import numpy as np
3
import tensorflow as tf
4
5
import torch
import torch.nn.functional as F
6
import nni.compression.torch as torch_compressor
7

8
9
10
if tf.__version__ >= '2.0':
    import nni.compression.tensorflow as tf_compressor

11
def get_tf_model():
12
    model = tf.keras.models.Sequential([
13
        tf.keras.layers.Conv2D(filters=5, kernel_size=7, input_shape=[28, 28, 1], activation='relu', padding="SAME"),
14
        tf.keras.layers.MaxPooling2D(pool_size=2),
15
        tf.keras.layers.Conv2D(filters=10, kernel_size=3, activation='relu', padding="SAME"),
16
17
18
19
20
21
22
23
24
25
        tf.keras.layers.MaxPooling2D(pool_size=2),
        tf.keras.layers.Flatten(),
        tf.keras.layers.Dense(units=128, activation='relu'),
        tf.keras.layers.Dropout(0.5),
        tf.keras.layers.Dense(units=10, activation='softmax'),
    ])
    model.compile(loss="sparse_categorical_crossentropy",
        optimizer=tf.keras.optimizers.SGD(lr=1e-3),
        metrics=["accuracy"])
    return model
26

27
class TorchModel(torch.nn.Module):
28
29
    def __init__(self):
        super().__init__()
30
31
32
33
        self.conv1 = torch.nn.Conv2d(1, 5, 5, 1)
        self.conv2 = torch.nn.Conv2d(5, 10, 5, 1)
        self.fc1 = torch.nn.Linear(4 * 4 * 10, 100)
        self.fc2 = torch.nn.Linear(100, 10)
34
35
36
37
38
39

    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)
40
        x = x.view(-1, 4 * 4 * 10)
41
42
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
43
44
        return F.log_softmax(x, dim=1)

45
def tf2(func):
46
    def test_tf2_func(*args):
47
        if tf.__version__ >= '2.0':
48
            func(*args)
49
    return test_tf2_func
50

51
52
53
54
55
56
57
58
k1 = [[1]*3]*3
k2 = [[2]*3]*3
k3 = [[3]*3]*3
k4 = [[4]*3]*3
k5 = [[5]*3]*3

w = [[k1, k2, k3, k4, k5]] * 10

59
class CompressorTestCase(TestCase):
60
61
    def test_torch_level_pruner(self):
        model = TorchModel()
chicm-ms's avatar
chicm-ms committed
62
        configure_list = [{'sparsity': 0.8, 'op_types': ['default']}]
QuanluZhang's avatar
QuanluZhang committed
63
        torch_compressor.LevelPruner(model, configure_list).compress()
64

65
66
67
68
    @tf2
    def test_tf_level_pruner(self):
        configure_list = [{'sparsity': 0.8, 'op_types': ['default']}]
        tf_compressor.LevelPruner(get_tf_model(), configure_list).compress()
69

70
71
    def test_torch_naive_quantizer(self):
        model = TorchModel()
Cjkkkk's avatar
Cjkkkk committed
72
73
74
75
76
77
78
79
        configure_list = [{
            'quant_types': ['weight'],
            'quant_bits': {
                'weight': 8,
            },
            'op_types':['Conv2d', 'Linear']
        }]
        torch_compressor.NaiveQuantizer(model, configure_list).compress()
80

81
    @tf2
82
83
    def test_tf_naive_quantizer(self):
        tf_compressor.NaiveQuantizer(get_tf_model(), [{'op_types': ['default']}]).compress()
84

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
    def test_torch_fpgm_pruner(self):
        """
        With filters(kernels) defined as above (k1 - k5), it is obvious that k3 is the Geometric Median
        which minimize the total geometric distance by defination of Geometric Median in this paper:
        Filter Pruning via Geometric Median for Deep Convolutional Neural Networks Acceleration,
        https://arxiv.org/pdf/1811.00250.pdf

        So if sparsity is 0.2, the expected masks should mask out all k3, this can be verified through:
        `all(torch.sum(masks, (0, 2, 3)).numpy() == np.array([90., 90., 0., 90., 90.]))`

        If sparsity is 0.6, the expected masks should mask out all k2, k3, k4, this can be verified through:
        `all(torch.sum(masks, (0, 2, 3)).numpy() == np.array([90., 0., 0., 0., 90.]))`
        """

        model = TorchModel()
        config_list = [{'sparsity': 0.2, 'op_types': ['Conv2d']}, {'sparsity': 0.6, 'op_types': ['Conv2d']}]
        pruner = torch_compressor.FPGMPruner(model, config_list)

        model.conv2.weight.data = torch.tensor(w).float()
        layer = torch_compressor.compressor.LayerInfo('conv2', model.conv2)
        masks = pruner.calc_mask(layer, config_list[0])
        assert all(torch.sum(masks, (0, 2, 3)).numpy() == np.array([90., 90., 0., 90., 90.]))

        pruner.update_epoch(1)
        model.conv2.weight.data = torch.tensor(w).float()
        masks = pruner.calc_mask(layer, config_list[1])
        assert all(torch.sum(masks, (0, 2, 3)).numpy() == np.array([90., 0., 0., 0., 90.]))
112
113
114

    @tf2
    def test_tf_fpgm_pruner(self):
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
        model = get_tf_model()
        config_list = [{'sparsity': 0.2, 'op_types': ['Conv2D']}, {'sparsity': 0.6, 'op_types': ['Conv2D']}]

        pruner = tf_compressor.FPGMPruner(model, config_list)
        weights = model.layers[2].weights
        weights[0] = np.array(w).astype(np.float32).transpose([2, 3, 0, 1]).transpose([0, 1, 3, 2])
        model.layers[2].set_weights([weights[0], weights[1].numpy()])

        layer = tf_compressor.compressor.LayerInfo(model.layers[2])
        masks = pruner.calc_mask(layer, config_list[0]).numpy()
        masks = masks.transpose([2, 3, 0, 1]).transpose([1, 0, 2, 3])

        assert all(masks.sum((0, 2, 3)) == np.array([90., 90., 0., 90., 90.]))

        pruner.update_epoch(1)
        model.layers[2].set_weights([weights[0], weights[1].numpy()])
        masks = pruner.calc_mask(layer, config_list[1]).numpy()
        masks = masks.transpose([2, 3, 0, 1]).transpose([1, 0, 2, 3])

        assert all(masks.sum((0, 2, 3)) == np.array([90., 0., 0., 0., 90.]))
135

136
137
138

if __name__ == '__main__':
    main()