一、面向过程与面向对象的优缺点
面向过程使用mxnet,就是使用gluon封装好的对象,不加改动的表达机器学习的逻辑过程,其特点是方便、快捷,缺点是不够灵活(虽然可以应对90%以上的问题了),面向对象基于继承、多态的性质,对原有的gluon类进行了继承重写,并在不改变应用接口的情况下(基于多态),灵活的改写原有类,使之更加符合用户特殊需求。本文从自定义模型、自定义层、自定义初始化三个方面说明gluon的继承重写,这三个基本操作足够用户随心所欲的创造模型了。
二、自定义模型
1、定义静态模型
静态模型就是实例化后模型的结构就不能随便改变了,其代码如下:
from mxnet import nd
from mxnet.gluon import nn
class MLP(nn.Block):
# 声明带有模型参数的层,这里声明了两个全连接层
def __init__(self, **kwargs):
# 调用MLP父类Block的构造函数来进行必要的初始化。这样在构造实例时还可以指定其他函数
# 参数,如“模型参数的访问、初始化和共享”一节将介绍的模型参数params
super(MLP, self).__init__(**kwargs)
self.hidden = nn.Dense(256, activation='relu') # 隐藏层
self.output = nn.Dense(10) # 输出层
# 定义模型的前向计算,即如何根据输入x计算返回所需要的模型输出
def forward(self, x):
return self.output(self.hidden(x))
X = nd.random.uniform(shape=(2, 20))
net = MLP()
net.initialize()
net(X)
2、定义动态模型
动态模型就是在实例化以后,后续可以根据需要随时修改模型结构,下面只定义一个增加网络层的功能。
class MySequential(nn.Block):
def __init__(self, **kwargs):
super(MySequential, self).__init__(**kwargs)
def add(self, block):
# block是一个Block子类实例,假设它有一个独一无二的名字。我们将它保存在Block类的
# 成员变量_children里,其类型是OrderedDict。当MySequential实例调用
# initialize函数时,系统会自动对_children里所有成员初始化
self._children[block.name] = block
def forward(self, x):
# OrderedDict保证会按照成员添加时的顺序遍历成员
for block in self._children.values():
x = block(x)
return x
net = MySequential()
net.add(nn.Dense(256, activation