深度学习:深度学习计算
作者:互联网
1、模型构造
- 可以通过继承
Block
类来构造模型。 Sequential
类继承自Block
类。- 虽然
Sequential
类可以使模型构造更加简单,但直接继承Block
类可以极大地拓展模型构造的灵活性。
1.1继承Block
类来构造模型
Block
类是nn
模块里提供的一个模型构造类,我们可以继承它来定义我们想要的模型。下面继承Block
类构造本节开头提到的多层感知机。这里定义的MLP
类重载了Block
类的__init__
函数和forward
函数。它们分别用于创建模型参数和定义前向计算。前向计算也即正向传播。
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))
1.2Sequential
类继承自Block
类
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
1.3构造复杂的模型
class FancyMLP(nn.Block):
def __init__(self, **kwargs):
super(FancyMLP, self).__init__(**kwargs)
# 使用get_constant创建的随机权重参数不会在训练中被迭代(即常数参数)
self.rand_weight = self.params.get_constant(
'rand_weight', nd.random.uniform(shape=(20, 20)))
self.dense = nn.Dense(20, activation='relu')
def forward(self, x):
x = self.dense(x)
# 使用创建的常数参数,以及NDArray的relu函数和dot函数
x = nd.relu(nd.dot(x, self.rand_weight.data()) + 1)
# 复用全连接层。等价于两个全连接层共享参数
x = self.dense(x)
# 控制流,这里我们需要调用asscalar函数来返回标量进行比较
while x.norm().asscalar() > 1:
x /= 2
if x.norm().asscalar() < 0.8:
x *= 10
return x.sum()
class NestMLP(nn.Block):
def __init__(self, **kwargs):
super(NestMLP, self).__init__(**kwargs)
self.net = nn.Sequential()
self.net.add(nn.Dense(64, activation='relu'),
nn.Dense(32, activation='relu'))
self.dense = nn.Dense(16, activation='relu')
def forward(self, x):
return self.dense(self.net(x))
net = nn.Sequential()
net.add(NestMLP(), nn.Dense(20), FancyMLP())
net.initialize()
net(X)
2、模型参数访问、初始化和共享
from mxnet import init, nd
from mxnet.gluon import nn
net = nn.Sequential()
net.add(nn.Dense(256, activation='relu'))
net.add(nn.Dense(10))
net.initialize() # 使用默认初始化方式
X = nd.random.uniform(shape=(2, 20))
Y = net(X) # 前向计算
2.2访问模型参数
对于使用Sequential
类构造的神经网络,我们可以通过方括号[]
来访问网络的任一层。
对于Sequential
实例中含模型参数的层,我们可以通过Block
类的params
属性来访问该层包含的所有参数。
下面,访问多层感知机net
中隐藏层的所有参数。索引0表示隐藏层为Sequential
实例最先添加的层。
2.3初始化模型参数
2.4自定义初始化方法
有时候我们需要的初始化方法并没有在init
模块中提供。这时,可以实现一个Initializer
类的子类,从而能够像使用其他初始化方法那样使用它。通常,我们只需要实现_init_weight
这个函数,并将其传入的NDArray
修改成初始化的结果。
在下面的例子里,我们令权重有一半概率初始化为0,有另一半概率初始化为\([-10,-5]\)和\([5,10]\)两个区间里均匀分布的随机数。
3、模型参数的延后与初始化
- 系统将真正的参数初始化延后到获得足够信息时才执行的行为叫作延后初始化。
- 延后初始化的主要好处是让模型构造更加简单。例如,我们无须人工推测每个层的输入个数。
- 也可以避免延后初始化。
3.1延后初始化
当调用initialize函数时,由于隐藏层输入个数依然未知,系统也无法得知该层权重参数的形状。
只有在当我们将形状是(2, 20)的输入X传进网络做前向计算net(X)时,系统才推断出该层的权重参数形状为(256, 20)。
因此,这时候我们才能真正开始初始化参数。
系统将真正的参数初始化延后到获得足够信息时才执行的行为叫作延后初始化(deferred initialization)。
它可以让模型的创建更加简单:只需要定义每个层的输出大小,而不用人工推测它们的输入个数。
这对于之后将介绍的定义多达数十甚至数百层的网络来说尤其方便。
3.2避免延后初始化
如果系统在调用initialize函数时能够知道所有参数的形状,那么延后初始化就不会发生。
- 第一种情况是我们要对已初始化的模型重新初始化时。因为参数形状不会发生变化,所以系统能够立即进行重新初始化。
- 第二种情况是我们在创建层的时候指定了它的输入个数,使系统不需要额外的信息来推测参数形状。
4、自定义层
4.1不含模型参数的自定义层
如何定义一个不含模型参数的自定义层:
下面的CenteredLayer
类通过继承Block
类自定义了一个将输入减掉均值后输出的层,并将层的计算定义在了forward
函数里。这个层里不含模型参数。
from mxnet import gluon, nd
from mxnet.gluon import nn
class CenteredLayer(nn.Block):
def __init__(self, **kwargs):
super(CenteredLayer, self).__init__(**kwargs)
def forward(self, x):
return x - x.mean()
4.2含模型参数的自定义层
自定义含模型参数的自定义层。其中的模型参数可以通过训练学出。
在自定义含模型参数的层时,我们可以利用Block
类自带的ParameterDict
类型的成员变量params
。
- 它是一个由字符串类型的参数名字映射到Parameter类型的模型参数的字典。我们可以通过
get
函数从ParameterDict
创建Parameter
实例。
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)
5、读取和存储
- 通过
save
函数和load
函数可以很方便地读写NDArray
。 - 通过
load_parameters
函数和save_parameters
函数可以很方便地读写Gluon模型的参数。
标签:__,初始化,nn,self,学习,参数,计算,深度,Block 来源: https://www.cnblogs.com/caolanying/p/16582781.html