动手学深度学习 4 Deep-learning-computation

4. deep-learning-computaion

4.1 模型构造

4.1.1 继承Block来构造模型

Block类是nn模块里提供的一个模型构造类,我们可以继承它来定义我们想要的模型也可以继承它来构造层。

事实上,Sequential类继承自Block类。当模型的前向计算为简单串联各个层的计算时,可以通过更加简单的方式定义模型。这正是Sequential类的目的:它提供add函数来逐一添加串联的Block子类实例,而模型的前向计算就是将这些实例按添加的顺序逐一计算,且无需定义forward函数。

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)  # 前向计算

4.1.2 构造复杂的模型

在构造复杂的网络时,需要直接继承Block类来拓展模型构造的灵活性。

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()

net = FancyMLP()
net.initialize()
print(net)  # 打印网络结构
net(X)  # 计算最后一个隐藏层

4.2 模型参数的访问、初始化和共享

4.2.1 访问模型参数

对于使用Sequential类构造的神经网络,我们可以通过方括号[]来访问网络的任一层。对于Sequential实例中含模型参数的层,我们可以通过Block类的params属性来访问该层包含的所有参数。

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)  

net[0].params, type(net[0].params)

输出

(dense0_ (
   Parameter dense0_weight (shape=(256, 20), dtype=float32)
   Parameter dense0_bias (shape=(256,), dtype=float32)
 ),
 mxnet.gluon.parameter.ParameterDict)

访问模型参数

net[0].weight  # 权重的情况
net[0].weight.data()  # 权重数据
net[0].weight.grad()  # 梯度数据

可以使用collect_params函数来获取net变量所有嵌套(例如通过add函数嵌套)的层所包含的所有参数。返回的是一个由参数名称到参数实例的字典。这个函数也可以通过正则表达式来匹配参数名,从而筛选需要的参数。

net.collect_params()

输出

sequential0_ (
  Parameter dense0_weight (shape=(256, 20), dtype=float32)
  Parameter dense0_bias (shape=(256,), dtype=float32)
  Parameter dense1_weight (shape=(10, 256), dtype=float32)
  Parameter dense1_bias (shape=(10,), dtype=float32)
)

4.2.2 访问模型参数

MXNet的init模块里提供了多种预设的初始化方法。

# 非首次对模型初始化需要指定force_reinit为真
net.initialize(init=init.Normal(sigma=0.01), force_reinit=True)  # 初始化成均值为0,标准差为0.01的正态分布随机数,并将偏差参数清零
net.initialize(init=init.Constant(1), force_reinit=True)  # 常数初始化
net[0].weight.initialize(init=init.Xavier(), force_reinit=True)  # Xavier随机初始化

4.2.3 自定义初始化方法

有时候需要的初始化方法并没有在init模块中提供,可以实现一个Initializer类的子类。通常,我们只需要实现_init_weight这个函数,并将其传入的NDArray修改成初始化的结果。在下面的例子里,我们令权重有一半概率初始化为0,有另一半概率初始化为\([-10,-5]\)和\([5,10]\)两个区间里均匀分布的随机数。

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)
net[0].weight.data()[0]

输出

Init dense0_weight (256, 20)
Init dense1_weight (10, 256)

[ 0.        -0.         9.97694    6.8228035 -7.011034   8.092955
  7.36252   -9.248813  -6.750141   0.         0.        -6.6630497
 -7.5236006  5.5810204  6.960165   7.298666   6.1463795 -0.
  0.        -7.200548 ]
<NDArray 20 @cpu(0)>

此外,我们还可以通过Parameter类的set_data函数来直接改写模型参数。例如,在下例中我们将隐藏层参数在现有的基础上加1。

net[0].weight.set_data(net[0].weight.data() + 1)
net[0].weight.data()[0]

4.2.4 共享模型参数

有些情况下,我们希望在多个层之间共享模型参数。如果不同层使用同一份参数,那么它们在前向计算和反向传播时都会共享相同的参数。下面在构造层的时候使用特定的参数使得参数共享。

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()

X = nd.random.uniform(shape=(2, 20))
net(X)

我们在构造第三隐藏层时通过params来指定它使用第二隐藏层的参数。因为模型参数里包含了梯度,所以在反向传播计算时,第二隐藏层和第三隐藏层的梯度都会被累加在shared.params.grad()里。

4.3 模型参数的延后初始化

系统根据输入的形状自动推断出所有层的权重参数的形状。系统在创建这些参数之后,调用初始化函数函数对它们进行初始化,然后再进行前向计算。初始化只会在第一次前向计算时被调用。之后再运行net(X)时则不会重新初始化。

系统将真正的参数初始化延后到获得足够信息时才执行的行为叫作延后初始化(deferred initialization)。它可以让模型的创建更加简单:只需要定义每个层的输出大小,而不用人工推测它们的输入个数。这对于之后将介绍的定义多达数十甚至数百层的网络来说尤其方便。

然而,任何事物都有两面性。正如本节开头提到的那样,延后初始化也可能会带来一定的困惑。在第一次前向计算之前,我们无法直接操作模型参数,例如无法使用data函数和set_data函数来获取和修改参数。因此,我们经常会额外做一次前向计算来迫使参数被真正地初始化。

4.3.1 避免延后初始化

如果系统在调用initialize函数能够知道所有参数的形状,那么延后初始化就不会发生。

第一种情况是我们要对已初始化的模型重新初始化时。因为参数形状不会发生变化,所以系统能够立即进行重新初始化。

第二种情况是在创建层的时候指定了它的输入个数。可以通过in_units来指定每个全连接层的输入个数。

net = nn.Sequential()
net.add(nn.Dense(256, in_units=20, activation='relu'))
net.add(nn.Dense(10, in_units=256))

net.initialize()

4.4 自定义层

深度学习的一个魅力在于神经网络中各式各样的层,例如全连接层和后面章节中将要介绍的卷积层、池化层与循环层。虽然Gluon提供了大量常用的层,但有时候我们依然希望自定义层。

4.4.1 不含模型参数的自定义层

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.4.2 含模型参数的自定义层

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 读取和存储

在训练模型时,需要将内存中训练好的模型参数存储在硬盘上供后序读取使用。

4.5.1 读写NDArray

我们可以直接使用save函数和load函数分别存储和读取NDArray。下面的例子创建了NDArray变量x,并将其存在文件名同为x的文件里。这是一个二进制文件,也可以将数据从存储的文件读回内存。

from mxnet import nd
from mxnet.gluon import nn

x = nd.ones(3)
nd.save('x', x)
x2 = nd.load('x')

4.5.2 读写Gluon模型的参数

除NDArray以外,我们还可以读写Gluon模型的参数。Gluon的Block类提供了save_parameters函数和load_parameters函数来读写模型参数。

4.6 GPU计算

由于电脑没有 NVIDIA GPU,该节暂时不看

原文地址:https://www.cnblogs.com/haricotvert/p/12289592.html

时间: 2024-08-15 11:29:38

动手学深度学习 4 Deep-learning-computation的相关文章

分享《动手学深度学习》高清PDF+视频教程-李沐著

下载:https://pan.baidu.com/s/10EKdEe-vjp9i97xP1FWuGA 更多资料学习:http://blog.51cto.com/14087171 由 MXNet 创始人李沐大神.Aston Zhang 等人所著的交互式书籍<动手学深度学习>推出了在线预览版,面向在校学生.工程师和研究人员,旨在帮助读者从入门到深入.动手学习深度学习,即使是零基础的读者也完全适用. 在本书上线之前,李沐等人还推出了<动手学深度学习>系列视频,这门课程的第一季已经于今年

分享《动手学深度学习(李沐等著)》PDF+源代码+《神经网络与深度学习(吴岸城)著》PDF

下载:https://pan.baidu.com/s/11O2m_uxSWJGbkXHF2Zh94w 更多资料:https://pan.baidu.com/s/1g4hv05UZ_w92uh9NNNkCaA <动手学深度学习(李沐等著)> 中文PDF,530页,带书签目录,文字可以复制粘贴.配套源代码. <神经网络与深度学习 (吴岸城)著>中文PDF,233页,带书签目录. 如图: 原文地址:https://www.cnblogs.com/limujun/p/10054478.ht

对比《动手学深度学习》 PDF代码+《神经网络与深度学习 》PDF

随着AlphaGo与李世石大战的落幕,人工智能成为话题焦点.AlphaGo背后的工作原理"深度学习"也跳入大众的视野.什么是深度学习,什么是神经网络,为何一段程序在精密的围棋大赛中可以大获全胜?人工智终将会取代人类智慧吗? <神经网络与深度学习>是一本介绍神经网络和深度学习算法基本原理及相关实例的书籍,它不是教科书,作者已尽量把公式减少到最少,以适应绝大部分人的阅读基础和知识储备.<神经网络与深度学习>涵盖了神经网络的研究历史.基础原理.深度学习中的自编码器.深

【动手学深度学习】Jupyter notebook中 import mxnet出错

问题描述 打开d2l-zh目录,使用jupyter notebook打开文件运行,import mxnet 出现无法导入mxnet模块的问题, 但是命令行运行是可以导入mxnet模块的. 原因: 激活环境是能够运行代码的前提. 解决方法: 在d2l-zh目录运行conda activate gluon命令,然后再打开jupyter notebook,则可以正常导入mxnet模块. 参考 1. d2l-zh-doc; 2. [动手学深度学习]中Jupyter notebook中 import mx

小白学习之pytorch框架(2)-动手学深度学习(begin)

在这向大家推荐一本书-花书-动手学深度学习pytorch版,原书用的深度学习框架是MXNet,这个框架经过Gluon重新再封装,使用风格非常接近pytorch,但是由于pytorch越来越火,个人又比较执着,想学pytorch,好,有个大神来了,把<动手学深度学习>整本书用pytorch代码重现了,其GitHub网址为:https://github.com/ShusenTang/Dive-into-DL-PyTorch   原书GitHub网址为:https://github.com/d2l-

DataWhale 动手学深度学习PyTorch版-task3+4+5:文本预处理;语言模型;循环神经网络基础

课程引用自伯禹平台:https://www.boyuai.com/elites/course/cZu18YmweLv10OeV <动手学深度学习>官方网址:http://zh.gluon.ai/ ——面向中文读者的能运行.可讨论的深度学习教科书. 第二次打卡: Task03: 过拟合.欠拟合及其解决方案:梯度消失.梯度爆炸:循环神经网络进阶 Task04:机器翻译及相关技术:注意力机制与Seq2seq模型:Transformer Task05:卷积神经网络基础:leNet:卷积神经网络进阶 有

如何正确理解深度学习(Deep Learning)的概念

现在深度学习在机器学习领域是一个很热的概念,不过经过各种媒体的转载播报,这个概念也逐渐变得有些神话的感觉:例如,人们可能认为,深度学习是一种能够模拟出人脑的神经结构的机器学习方式,从而能够让计算机具有人一样的智慧:而这样一种技术在将来无疑是前景无限的.那么深度学习本质上又是一种什么样的技术呢? 深度学习是什么 深度学习是机器学习领域中对模式(声音.图像等等)进行建模的一种方法,它也是一种基于统计的概率模型.在对各种模式进行建模之后,便可以对各种模式进行识别了,例如待建模的模式是声音的话,那么这种

深度学习研究组Deep Learning Research Groups

Deep Learning Research Groups Some labs and research groups that are actively working on deep learning: University of Toronto - Machine Learning Group (Geoffrey Hinton, Rich Zemel, Ruslan Salakhutdinov, Brendan Frey, Radford Neal) Université de Montr

动手学深度学习第一课:从上手到多类分类-Autograd

使用autograd来自动求导 在机器学习中,我们通常使用梯度下降来更新模型参数从而求解.损失函数关于模型参数的梯度指向一个可以降低损失函数值的方向,我们不断地沿着梯度的方向更新模型从而最小化损失函数.虽然梯度计算比较直观,但对于复杂的模型,例如多达数十层的神经网络,手动计算梯度非常困难. 为此MXNet提供autograd包来自动化求导过程.虽然大部分的深度学习框架要求编译计算图来自动求导,mxnet.autograd可以对正常的命令式程序进行求导,它每次在后端实时创建计算图从而可以立即得到梯

动手学深度学习 3-5 Others

其他问题 1. 模型选择.欠拟合和过拟合 1.1 训练误差和泛化误差 1.2 模型选择 1.2.1 验证数据集 1.2.2 \(K\) 折交叉验证 由于验证数据集不参与模型训练,当训练数据不够用时,预留大量的验证数据显得太奢侈.一种改善的方法是\(K\)折交叉验证(\(K\)-fold cross-validation).在\(K\)折交叉验证中,我们把原始训练数据集分割成\(K\)个不重合的子数据集,然后我们做\(K\)次模型训练和验证.每一次,我们使用一个子数据集验证模型,并使用其他\(K-