MXNET CNN + LSTM сохранить / сериализовать в JSON - PullRequest
0 голосов
/ 07 ноября 2018

Мне трудно понять, как правильно определить сеть mxnet, чтобы я мог сериализовать / преобразовать эту модель в файл json.

Трубопровод состоит из CNN + biLSTM + CTC.

Теперь я должен использовать HybridBlock и hybridize (), но я не могу заставить его работать, или если это вообще возможно, или если есть какие-то другие способы.

Я уверен, что с моей стороны это недостаточно знаний, и удивительно, что кто-то может помочь.

Вот чистое определение в python:

NUM_HIDDEN = 200
NUM_CLASSES = 13550
NUM_LSTM_LAYER = 1
p_dropout = 0.5
SEQ_LEN = 32

def get_featurizer():
    featurizer = gluon.nn.HybridSequential()
    # conv layer
    featurizer.add(gluon.nn.Conv2D(kernel_size=(3,3), padding=(1,1), channels=32, activation="relu"))
    featurizer.add(gluon.nn.BatchNorm())

    ....
    featurizer.hybridize()
    return featurizer

class EncoderLayer(gluon.Block):
    def __init__(self, **kwargs):
        super(EncoderLayer, self).__init__(**kwargs)
        with self.name_scope():
            self.lstm = mx.gluon.rnn.LSTM(NUM_HIDDEN, NUM_LSTM_LAYER, bidirectional=True)
    def forward(self, x):
        x = x.transpose((0,3,1,2))
        x = x.flatten()
        x = x.split(num_outputs=SEQ_LEN, axis = 1) # (SEQ_LEN, N, CHANNELS)
        x = nd.concat(*[elem.expand_dims(axis=0) for elem in x], dim=0)
        x = self.lstm(x)
        x = x.transpose((1, 0, 2)) # (N, SEQ_LEN, HIDDEN_UNITS)
        return x

def get_encoder():
    encoder = gluon.nn.Sequential()
    encoder.add(EncoderLayer())
    encoder.add(gluon.nn.Dropout(p_dropout))
    return encoder

def get_decoder():
    decoder = mx.gluon.nn.Dense(units=ALPHABET_SIZE, flatten=False)
    decoder.hybridize()
    return decoder

def get_net():
    net = gluon.nn.Sequential()
    with net.name_scope():
        net.add(get_featurizer())
        net.add(get_encoder())
        net.add(get_decoder())
    return net

Любая помощь будет принята с благодарностью. Большое спасибо.

1 Ответ

0 голосов
/ 08 ноября 2018

Существует несколько требований к экспорту модели в Gluon в json:

  1. Он должен быть гибридизуемым, что означает, что каждый дочерний блок также должен быть гибридизуемым, и модель работает в обоих режимах

  2. Все параметры должны быть инициализированы. Так как Gluon использует отложенную инициализацию параметров, это означает, что вы должны выполнить передачу вперед хотя бы один раз, прежде чем сможете сохранить модель.

Я сделал некоторые исправления для вашего кода, также вводя новые константы, когда мне было нужно. Наиболее значимые изменения:

  1. Не используйте split, если вы можете избежать этого, потому что он возвращает список NDArrays. Используйте функцию изменения формы, которая, похоже, работает и с Symbol.

  2. Начиная с версии 1.3.0 MXNet, LSTM также является гибридизуемым, поэтому вы можете обернуть его в гибридный блок, а не просто в блок.

  3. Использовать HybridSequential.

Ниже приведен скорректированный код с примером внизу, как сохранить модель и загрузить ее обратно. Вы можете найти больше информации в этом уроке .

import mxnet as mx
from mxnet import gluon
from mxnet import nd

BATCH_SIZE = 1
CHANNELS = 100
ALPHABET_SIZE = 1000
NUM_HIDDEN = 200
NUM_CLASSES = 13550
NUM_LSTM_LAYER = 1
p_dropout = 0.5
SEQ_LEN = 32
HEIGHT = 100
WIDTH = 100


def get_featurizer():
    featurizer = gluon.nn.HybridSequential()
    featurizer.add(
        gluon.nn.Conv2D(kernel_size=(3, 3), padding=(1, 1), channels=32, activation="relu"))
    featurizer.add(gluon.nn.BatchNorm())

    return featurizer


class EncoderLayer(gluon.HybridBlock):
    def __init__(self, **kwargs):
        super(EncoderLayer, self).__init__(**kwargs)

        with self.name_scope():
            self.lstm = mx.gluon.rnn.LSTM(NUM_HIDDEN, NUM_LSTM_LAYER, bidirectional=True)

    def hybrid_forward(self, F, x):
        x = x.transpose((0, 3, 1, 2))
        x = x.flatten()
        x = x.reshape(shape=(SEQ_LEN, -1, CHANNELS)) #x.split(num_outputs=SEQ_LEN, axis=1)  # (SEQ_LEN, N, CHANNELS)
        x = self.lstm(x)
        x = x.transpose((1, 0, 2))  # (N, SEQ_LEN, HIDDEN_UNITS)
        return x


def get_encoder():
    encoder = gluon.nn.HybridSequential()
    encoder.add(EncoderLayer())
    encoder.add(gluon.nn.Dropout(p_dropout))
    return encoder


def get_decoder():
    decoder = mx.gluon.nn.Dense(units=ALPHABET_SIZE, flatten=False)
    return decoder


def get_net():
    net = gluon.nn.HybridSequential()

    with net.name_scope():
        net.add(get_featurizer())
        net.add(get_encoder())
        net.add(get_decoder())

    return net


if __name__ == '__main__':
    net = get_net()
    net.initialize()
    net.hybridize()

    fake_data = mx.random.uniform(shape=(BATCH_SIZE, HEIGHT, WIDTH, CHANNELS))
    out = net(fake_data)

    net.export("mymodel")

    deserialized_net = gluon.nn.SymbolBlock.imports("mymodel-symbol.json", ['data'],
                                                    "mymodel-0000.params", ctx=mx.cpu())

    out2 = deserialized_net(fake_data)
    # just to check that we get the same results
    assert (out - out2).sum().asscalar() == 0
...