1.Block(Sequential类继承自Block)
Block类是nn模块中的一个模型构造类。
- __init__ 函数:创建模型参数
- forward函数:定义前向计算,自动绑定“()” 例:MLP(X)
ps:无需定义方向传播函数,系统将自动计算出相应的backward函数。
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)
# 定义模型的前向计算,即如何根据输出计算输出。
def forward(self, x):
return self.output(self.hidden(x))
Block类是一个可以自由组件的部件。(一层,一个模型,模型的一部分)。
Sequential类(简单串联):提供add函数来逐一添加串联的Block子类的实例。
练习:
- 如果不在
MLP
类的__init__
函数里调用父类的__init__
函数,会出现什么样的错误信息?
AttributeError: 'MLP' object has no attribute '_children'
- 如果去掉
FancyMLP
类里面的asscalar
函数,会有什么问题?
#API解释。一个标量就是一个单独的数,我们在使用标量时,一般都要明确给出它是那种类型的数
#代码中如果不使用标量,可能会导致向量/矩阵与1进行比较
Returns a scalar whose value is copied from this array.
This function is equivalent to self.asnumpy()[0]. This NDArray must have shape (1,).
Examples
>>> x = mx.nd.ones((1,), dtype='int32')
>>> x.asscalar()
1
>>> type(x.asscalar())
- 如果将
NestMLP
类中通过 Sequential 实例定义的self.net
改为self.net = [nn.Dense(64, activation='relu'), nn.Dense(32, activation='relu')]
,会有什么问题?
运行的时候变成了列表
2.模型参数
2.1 获取参数:
Sequential 类构造出来的⽹络的特定层的参数能够通过[ ]来访问。例如:参数的名称为dense0_weight,它由net[0] 的名称(dense0_)和⾃⼰的变量名(weight)组成。因此可以用以下两种方式获取:
- net[0].params['dense0_weight']
- net[0].weight
Parameter类,参数(.data())访问参数,(.grad())访问参数的梯度
collect_params 函数来获取net 实例所有嵌套(参数名称到参数实例的字典)。
2.2 不同方法初始化
#force_reinit=True,表示再次初始化(非首次赋值true,系统有保护多次初始化)
net.initialize(init=init.Normal(sigma=0.01), force_reinit=True)
- 系统还提供了很多种方法进行初始化,详情可参见:Initial_API
- 自定义初始化
class MyInit(init.Initializer): def _init_weight(self, name, data): print('Init', name, data.shape) data[:] = nd.random.uniform(low=-10, high=10, shape=data.shape) data *= data.abs() >= 5 net.initialize(MyInit(), force_reinit=True)
通过
Parameter
类的set_data
函数来直接改写模型参数net[0].weight.set_data(net[0].weight.data() + 1)
- 共享模型参数
在构造层的时候指定使用特定的参数。如果不同层使用同一份参数,那么它们在前向计算和反向传播时都会共享相同的参数
net = nn.Sequential()
shared = nn.Dense(8, activation='relu')
net.add(nn.Dense(8, activation='relu'),
shared,
nn.Dense(8, activation='relu', params=shared.params),#共享参数
nn.Dense(10))
net.initialize()
延后初始化:mxnet的一个特点,在初始化前,网络并不知道你的输入,同样也不知道中间的一些参数shape,等到你初始化,才能实际的定义好相关的shape。
- 查阅有关
init
模块的 MXNet 文档,了解不同的参数初始化方法。
- 尝试在
net.initialize()
后、net(x)
前访问模型参数,观察模型参数的形状。 - 构造一个含共享参数层的多层感知机并训练。在训练过程中,观察每一层的模型参数和梯度
讨论里面:前向传播会将参数乘两次,所以在反传是也应该分别求出梯度,分别更新参数,但是第二次会覆盖第一次的结果,所以我们只能看到一个值。
2.3序列化--读写模型(初级)
#mxnet
nd.save(filename, params)
params = nd.load(filename)
#mxnet.gloun
net.save_params(filename)
net2.load_params(filename,mx.cpu)
后续:跨平台读取模型。。。
3 gloun自定义层
1. 简单的层
class CenteredLayer(nn.Block):
def __init__(self, **kwargs):
super(CenteredLayer, self).__init__(**kwargs)
def forward(self, x):
return x - x.mean()
2. 带模型参数的层
- Parameter:它包含参数和梯度的数值,可以分别通过
data
和grad
函数来访问。 - ParameterDict:参数名称映射到参数实例的字典
params = gluon.ParameterDict() params.get('param2', shape=(2, 3))
class MyDense(nn.Block): # units:该层的输出个数;in_units:该层的输入个数。 def __init__(self, units, in_units, **kwargs): super(MyDense, self).__init__(**kwargs) self.weight = self.params.get('weight', shape=(in_units, units)) self.bias = self.params.get('bias', shape=(units,)) def forward(self, x): linear = nd.dot(x, self.weight.data()) + self.bias.data() return nd.relu(linear)
4 丢弃法(以一定概率丢弃某些神经元)
- 降低模型的复杂性
- 防止过拟合
5 AlexNet
6 VGG
通过重复的小网络实现深度网络
空间信息->语义信息 64->128->256->512 input-channls。input 变小1倍,channl变大1倍。