Gluon Package

Gluon包有以下API:只选择高频api介绍。

1. Parameter-参数相关

1)class mxnet.gluon.Parameter(name, grad_req='write', shape=None, dtype=<type 'numpy.float32'="">, lr_mult=1.0, wd_mult=1.0, init=None, allow_deferred_init=False, differentiable=True, stype='default', grad_stype='default'

Parameter类包含了Blocks的参数。当指定了Parameter.initialize(...)之后,Parameter在每个context,也就是设备上都会有一份参数的copy。

ctx = mx.gpu(0)
x = mx.nd.zeros((16, 100), ctx=ctx)
w = mx.gluon.Parameter('fc_weight', shape=(64, 100), init=mx.init.Xavier())
b = mx.gluon.Parameter('fc_bias', shape=(64,), init=mx.init.Zero())
w.initialize(ctx=ctx)
b.initialize(ctx=ctx)
out = mx.nd.FullyConnected(x, w.data(ctx), b.data(ctx), num_hidden=64)

代码利用到了参数的initialize方法:

>>> weight = mx.gluon.Parameter('weight', shape=(2, 2))
>>> weight.initialize(ctx=mx.cpu(0))
>>> weight.data()
[[-0.01068833  0.01729892]
 [ 0.02042518 -0.01618656]]

>>> weight.grad()
[[ 0.  0.]
 [ 0.  0.]]

>>> weight.initialize(ctx=[mx.gpu(0), mx.gpu(1)])
>>> weight.data(mx.gpu(0))
[[-0.00873779 -0.02834515]
 [ 0.05484822 -0.06206018]]

>>> weight.data(mx.gpu(1))
[[-0.00873779 -0.02834515]
 [ 0.05484822 -0.06206018]]

 

2)class mxnet.gluon.Constant(name, value)[source]

不可变tensor,常量。常量被autograd和Trainer忽略,因此它们的值在训练期间不会改变。但是仍然可以使用set_data方法手动更新它们的值。
常量s可以用以下任一项创建:

const = mx.gluon.Constant('const', [[1,2],[3,4]])
# or:
class Block(gluon.Block):
    def __init__(self, **kwargs):
        super(Block, self).__init__(**kwargs)
        self.const = self.params.get_constant('const', [[1,2],[3,4]])

 

3)class mxnet.gluon.ParameterDict(prefix='', shared=None)

管理参数的字典。

 

 

 

2. Containers-容器


1. class mxnet.gluon.Block(prefix=None, params=None)

所有神经网络层和模型的基类。

from mxnet.gluon import Block, nn
from mxnet import ndarray as F

class Model(Block):
    def __init__(self, **kwargs):
        super(Model, self).__init__(**kwargs)
        # use name_scope to give child Blocks appropriate names.
        with self.name_scope():
            self.dense0 = nn.Dense(20)
            self.dense1 = nn.Dense(20)

    def forward(self, x):
        x = F.relu(self.dense0(x))
        return F.relu(self.dense1(x))

model = Model()
model.initialize(ctx=mx.cpu(0))
model(F.zeros((10, 10), ctx=mx.cpu(0)))

Block类主要方法:

1) collect_params(select=None)

返回一个ParameterDict类型,包含了Block和它孩子的参数。也可以选择一部分参数返回。例如选择指定的参数:[‘conv1_weight’, ‘conv1_bias’, ‘fc_weight’, ‘fc_bias’]:

model.collect_params('conv1_weight|conv1_bias|fc_weight|fc_bias')

或者搜集所有名字里有‘weight’或者‘bias’的参数,可以用正则匹配:

model.collect_params('.*weight|.*bias')
>>>model.collect_params()
Out[5]: 
model1_ (
  Parameter model1_dense0_weight (shape=(20, 10), dtype=float32)
  Parameter model1_dense0_bias (shape=(20,), dtype=float32)
  Parameter model1_dense1_weight (shape=(20, 20), dtype=float32)
  Parameter model1_dense1_bias (shape=(20,), dtype=float32)
)
>>>model.collect_params('.*bias')
Out[6]: 
model1_ (
  Parameter model1_dense0_bias (shape=(20,), dtype=float32)
  Parameter model1_dense1_bias (shape=(20,), dtype=float32)
)

 

2) initialize(init=, ctx=None, verbose=False, force_reinit=False)

初始化Block及其孩子的参数,等效于:block.collect_params().initialize(...)

model.collect_params().initialize(ctx=mx.cpu(0))
# or:
model.initialize(ctx=mx.cpu(0))

 

3)load_parameters(filename, ctx=None, allow_missing=False, ignore_extra=False, cast_dtype=False, dtype_source='current')

save_parameters(filename

保存和载入模型。具体操作见这里

注意的是:利用save_parameters保存的参数只能由load_parameters载入,这个方法只是保存了参数,没有保存网络。所以你要载入就得先初始化模型。而利用HybridBlock.export()方法可以同时保存模型和参数。

 

2. class mxnet.gluon.HybridBlock(prefix=None, params=None)[source]

 这个混合block同时支持Symbol和NDArray的前向类似于上面的Block,有点不同:

import mxnet as mx
from mxnet.gluon import HybridBlock, nn

class Model(HybridBlock):         # 区别1
    def __init__(self, **kwargs):
        super(Model, self).__init__(**kwargs)
        # use name_scope to give child Blocks appropriate names.
        with self.name_scope():
            self.dense0 = nn.Dense(20)
            self.dense1 = nn.Dense(20)

    def hybrid_forward(self, F, x):        # 区别2
        x = F.relu(self.dense0(x))
        return F.relu(self.dense1(x))

model = Model()
model.initialize(ctx=mx.cpu(0))
model.hybridize()       # 区别3
model(mx.nd.zeros((10, 10), ctx=mx.cpu(0)))

如上有三处区别,利用hybrid就和symbol一起工作,变成了静态图,没法像NDArray那样索引。在使用hybridize()激活之前,HybridBlock的工作方式与普通Block类似。激活后,HybridBlock将创建一个表示正向计算的符号图并将其缓存。在随后的前向过程中,将使用缓存的图而不是hybrid_forward()。说白了就是hybrid是令gluon变成Module那样可以利用Symbol的办法。这里有个ref:Hybrid - Faster training and easy deployment

方法的话介绍一个吧:export(path, epoch=0, remove_amp_cast=True)

主要是保存hybridblock的模型和参数,将分别保存成json和param后缀的文件。⚠️这个保存后的结果有两种载入方式:一种就是SymbolBlock.imports,,另一种是mxnet.mod.Module 或C++ interface。demo见这里。

 

 

3. class mxnet.gluon.SymbolBlock(outputs, inputs, params=None)[source]

根据symbol来构建block。这对于利用预训练的模型作为特征提取器时是有用的。例如,可以从alexnet的fc2层来得到输出。

参数中的outputs就是你想要得到的哪一层的输出,inputs就是输入变量,这两都是symbol类型或者symbol的列表类型。params是ParameterDict类型,就是关于参数argumetns和辅助参数auxililary的一个字典。例子好懂:

# To extract the feature from fc1 and fc2 layers of AlexNet:
alexnet = gluon.model_zoo.vision.alexnet(pretrained=True, ctx=mx.cpu(),      # 搞一个预训练的gluon模型,返回的是HybridBlock类型:
inputs = mx.sym.var('data')      # 输入变量,symbol类型
out = alexnet(inputs)            # 上面提到了HybridBlock同时支持symbol和ndarray的前向
internals = out.get_internals()       # 得到所有层信息
print(internals.list_outputs())       
outputs = [internals['model_dense0_relu_fwd_output'],internals['model_dense1_relu_fwd_output']]   # 想分别得到fc1和fc2的输出
# Create SymbolBlock that shares parameters with alexnet
feat_model = gluon.SymbolBlock(outputs, inputs, params=alexnet.collect_params())   # 建立这个symbolblock,把预训练参数搞进来
x = mx.nd.random.normal(shape=(16, 3, 224, 224))
print(feat_model(x)) 

上面也提到了,这个SymbolBlock有个方法imports,可用来加载json类型的网络结构和参数params:

static imports(symbol_file, input_names, param_file=None, ctx=None)[source]

>>> net1 = gluon.model_zoo.vision.resnet18_v1(
...     prefix='resnet', pretrained=True)
>>> net1.hybridize()
>>> x = mx.nd.random.normal(shape=(1, 3, 32, 32))
>>> out1 = net1(x)
>>> net1.export('net1', epoch=1)       # 上面提到hybridblock保存模型的方法
>>>
>>> net2 = gluon.SymbolBlock.imports(      # 可用symbolblock来载入模型和参数
...     'net1-symbol.json', ['data'], 'net1-0001.params')
>>> out2 = net2(x)

 

 

4. class mxnet.gluon.nn.Sequential(prefix=None, params=None)[source]

序列化的堆叠Blocks:

net = nn.Sequential()
# use net's name_scope to give child Blocks appropriate names.
with net.name_scope():
    net.add(nn.Dense(10, activation='relu'))
    net.add(nn.Dense(20))

 

5. class mxnet.gluon.nn.HybridSequential(prefix=None, params=None)[source]

序列化堆叠HybridBlocks:

net = nn.HybridSequential()
# use net's name_scope to give child Blocks appropriate names.
with net.name_scope():
    net.add(nn.Dense(10, activation='relu'))
    net.add(nn.Dense(20))
net.hybridize()

 

 

 

3. Trainer-训练器

class mxnet.gluon.Trainer(params, optimizer, optimizer_params=None, kvstore='device', compression_params=None, update_on_kvstore=None)[source] 

给参数施加优化器,Trainer应当与autograd一起使用。对于下面的情况,不可以把update_on_kvstore置为False:

  • dist kvstore with sparse weights or sparse gradients
  • dist async kvstore
  • optimizer.lr_scheduler is not None

例子:

 from mxnet import autograd as ag  

train_data, val_data = get_data_iters(dataset, batch_size, opt) net.collect_params().reset_ctx(ctx) trainer = gluon.Trainer(net.collect_params(), 'sgd', # trainer用法 optimizer_params={'learning_rate': opt.lr, 'wd': opt.wd, 'momentum': opt.momentum, 'multi_precision': True}, kvstore=kv) loss = gluon.loss.SoftmaxCrossEntropyLoss() total_time = 0 num_epochs = 0 best_acc = [0] for epoch in range(opt.start_epoch, opt.epochs): trainer = update_learning_rate(opt.lr, trainer, epoch, opt.lr_factor, lr_steps) tic = time.time() train_data.reset() metric.reset() btic = time.time() for i, batch in enumerate(train_data): data = gluon.utils.split_and_load(batch.data[0].astype(opt.dtype), ctx_list=ctx, batch_axis=0) label = gluon.utils.split_and_load(batch.label[0].astype(opt.dtype), ctx_list=ctx, batch_axis=0) outputs = [] Ls = [] with ag.record(): # trainer与autograd一起用 for x, y in zip(data, label): z = net(x) L = loss(z, y) # store the loss and do backward after we have done forward # on all GPUs for better speed on multiple GPUs. Ls.append(L) outputs.append(z) ag.backward(Ls) trainer.step(batch.data[0].shape[0]) # step方法在backward后更新参数,主要是在record之外 metric.update(label, outputs) if opt.log_interval and not (i+1)%opt.log_interval: name, acc = metric.get() logger.info('Epoch[%d] Batch [%d]\tSpeed: %f samples/sec\t%s=%f, %s=%f'%( epoch, i, batch_size/(time.time()-btic), name[0], acc[0], name[1], acc[1]))

 

 

 

4. Utilities-数据划分包

1. mxnet.gluon.utils.split_data(data, num_slice, batch_axis=0, even_split=True)[source]

针对NDArray类型数据沿着batch的维度划分,通常用在数据并行,每个设备需要一部分数据。

num_slice:要划分的数据份数

返回:列表,NDArray类型

 

2. mxnet.gluon.utils.split_and_load(data, ctx_list, batch_axis=0, even_split=True)[source] 

与上面唯一不同的地方是num_slice变成了ctx_list,也就是设备列表,划分数据的份数=列表长度,也就是设备数目。

        for i, batch in enumerate(train_data):
            data = gluon.utils.split_and_load(batch.data[0].astype(opt.dtype), ctx_list=ctx, batch_axis=0)
            label = gluon.utils.split_and_load(batch.label[0].astype(opt.dtype), ctx_list=ctx, batch_axis=0)

 

3. mxnet.gluon.utils.clip_global_norm(arrays, max_norm, check_isfinite=True)[source]

缩放NDArray,使得所有元素的2范数之和小雨max_norm。

posted @ 2020-06-09 11:36  三年一梦  阅读(476)  评论(0编辑  收藏  举报