我们将深入讲解模型参数的访问和初始化,以及如何在多个层之间共享同一份参数。

之前我们一直在使用默认的初始函数,net.initialize()。

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)

这里我们从 MXNet 中导入了 init 这个包,它包含了多种模型初始化方法。

访问模型参数

我们知道可以通过 [] 来访问 Sequential 类构造出来的网络的特定层。对于带有模型参数的层,我们可以通过 Block 类的 params 属性来得到它包含的所有参数。例如我们查看隐藏层的参数:

net[0].params
# output
dense0_ (
Parameter dense0_weight (shape=(256, 20), dtype=float32)
Parameter dense0_bias (shape=(256,), dtype=float32)
)

我们得到了一个由参数名称映射到参数实例的字典。第一个参数的名称为 dense0_weight,它由 net[0] 的名称(dense0_)和自己的变量名(weight)组成。而且可以看到它参数的形状为 (256, 20),且数据类型为 32 位浮点数。

为了访问特定参数,我们既可以通过名字来访问字典里的元素,也可以直接使用它的变量名。下面两种方法是等价的,但通常后者的代码可读性更好。

net[0].params['dense0_weight'], net[0].weight

Gluon 里参数类型为 Parameter 类,其包含参数权重和它对应的梯度,它们可以分别通过 data 和 grad 函数来访问。因为我们随机初始化了权重,所以它是一个由随机数组成的形状为 (256, 20) 的 NDArray.

net[0].weight.data()
# output
[[ 0.06700657 -0.00369488 0.0418822 ..., -0.05517294 -0.01194733
-0.00369594]
...,
[ 0.00297424 -0.0281784 -0.06881659 ..., -0.04047417 0.00457048
0.05696651]]
<NDArray 256x20 @cpu(0)>

梯度的形状跟权重一样。但由于我们还没有进行反向传播计算,所以它的值全为 0.

net[0].weight.grad()
# output
[[ 0. 0. 0. ..., 0. 0. 0.]
...,
[ 0. 0. 0. ..., 0. 0. 0.]]
<NDArray 256x20 @cpu(0)>

类似我们可以访问其他的层的参数。例如输出层的偏差权重:

net[1].bias.data()

最后,我们可以 collect_params 函数来获取 net 实例所有嵌套(例如通过 add 函数嵌套)的层所包含的所有参数。它返回的同样是一个参数名称到参数实例的字典。

net.collect_params()
# output
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)
)

初始化模型参数

当使用默认的模型初始化,Gluon 会将权重参数元素初始化为 [-0.07, 0.07] 之间均匀分布的随机数,偏差参数则全为 0. 但经常我们需要使用其他的方法来初始话权重,MXNet 的 init 模块里提供了多种预设的初始化方法。例如下面例子我们将权重参数初始化成均值为 0,标准差为 0.01 的正态分布随机数。

# 非首次对模型初始化需要指定 force_reinit。
net.initialize(init=init.Normal(sigma=0.01), force_reinit=True)
net[0].weight.data()[0]

如果想只对某个特定参数进行初始化,我们可以调用 Paramter 类的 initialize 函数,它的使用跟 Block 类提供的一致。下例中我们对第一个隐藏层的权重使用 Xavier 初始化方法。

net[0].weight.initialize(init=init.Xavier(), force_reinit=True)
net[0].weight.data()[0]

自定义初始化方法

有时候我们需要的初始化方法并没有在 init 模块中提供。这时,我们可以实现一个 Initializer 类的子类使得我们可以跟前面使用 init.Normal 那样使用它。通常,我们只需要实现 _init_weight 这个函数,将其传入的 NDArray 修改成需要的内容。下面例子里我们把权重初始化成 [-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]

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

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

共享模型参数

在有些情况下,我们希望在多个层之间共享模型参数。我们在 “模型构造” 一节看到了如何在 Block 类里 forward 函数里多次调用同一个类来完成。

这里将介绍另外一个方法,它在构造层的时候指定使用特定的参数。如果不同层使用同一份参数,那么它们不管是在前向计算还是反向传播时都会共享共同的参数。

我们让模型的第二隐藏层和第三隐藏层共享模型参数。

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) net[1].weight.data()[0] == net[2].weight.data()[0] # output
[ 1. 1. 1. 1. 1. 1. 1. 1.]
<NDArray 8 @cpu(0)>

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

延后的初始

注意到前面使用 Gluon 的章节里,我们在创建全连接层时都没有指定输入大小。例如在一直使用的多层感知机例子里,我们创建了输出大小为 256 的隐藏层。但是当在调用 initialize 函数的时候,我们并不知道这个层的参数到底有多大,因为它的输入大小仍然是未知。

只有在当我们将形状是 (2,20) 的 x 输入进网络时,我们这时候才知道这一层的参数大小应该是 (256,20)。所以这个时候我们才能真正开始初始化参数。

使用 MyInit 实例来进行初始化:

from mxnet import init, nd
from mxnet.gluon import nn class MyInit(init.Initializer):
def _init_weight(self, name, data):
print('Init', name, data.shape)
# 实际的初始化逻辑在此省略了。 net = nn.Sequential()
net.add(nn.Dense(256, activation='relu'))
net.add(nn.Dense(10)) net.initialize(init=MyInit())
# 注意到 MyInit 在调用时会打印信息,但当前我们并没有看到相应的日志。下面我们执行前向计算。 x = nd.random.uniform(shape=(2,20))
y = net(x)
# output
Init dense0_weight (256, 20)
Init dense1_weight (10, 256)

我们将这个系统将真正的参数初始化延后到获得了足够信息到时候称之为延后初始化。它可以让模型创建更加简单,因为我们只需要定义每个层的输出大小,而不用去推测它们的的输入大小。这个对于之后将介绍的多达数十甚至数百层的网络尤其有用。

当然延后初始化也可能会造成一定的困解。在调用第一次前向计算之前我们无法直接操作模型参数。例如无法使用 data 和 set_data 函数来获取和改写参数。所以经常我们会额外调用一次 net(x) 来是的参数被真正的初始化。

避免延后初始化

当系统在调用 initialize 函数时能够知道所有参数形状,那么延后初始化就不会发生。我们这里给两个这样的情况。

第一个是模型已经被初始化过,而且我们要对模型进行重新初始化时。因为我们知道参数大小不会变,所以能够立即进行重新初始化。

net.initialize(init=MyInit(), force_reinit=True)

第二种情况是我们在创建层到时候指定了每个层的输入大小,使得系统不需要额外的信息来推测参数形状。下例中我们通过 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(init=MyInit())

MXNET:深度学习计算-模型参数的更多相关文章

  1. MXNET:深度学习计算-模型构建

    进入更深的层次:模型构造.参数访问.自定义层和使用 GPU. 模型构建 在多层感知机的实现中,我们首先构造 Sequential 实例,然后依次添加两个全连接层.其中第一层的输出大小为 256,即隐藏 ...

  2. NVIDIA GPUs上深度学习推荐模型的优化

    NVIDIA GPUs上深度学习推荐模型的优化 Optimizing the Deep Learning Recommendation Model on NVIDIA GPUs 推荐系统帮助人在成倍增 ...

  3. MXNet深度学习库简介

    MXNet深度学习库简介 摘要: MXNet是一个深度学习库, 支持C++, Python, R, Scala, Julia, Matlab以及JavaScript等语言; 支持命令和符号编程; 可以 ...

  4. MXNET:深度学习计算-GPU

    mxnet的设备管理 MXNet 使用 context 来指定用来存储和计算的设备,例如可以是 CPU 或者 GPU.默认情况下,MXNet 会将数据创建在主内存,然后利用 CPU 来计算.在 MXN ...

  5. MXNET:深度学习计算-自定义层

    虽然 Gluon 提供了大量常用的层,但有时候我们依然希望自定义层.本节将介绍如何使用 NDArray 来自定义一个 Gluon 的层,从而以后可以被重复调用. 不含模型参数的自定义层 我们先介绍如何 ...

  6. 深度学习VGG16模型核心模块拆解

    原文连接:https://blog.csdn.net/qq_40027052/article/details/79015827 注:这篇文章是上面连接作者的文章.在此仅作学习记录作用. 如今深度学习发 ...

  7. mxnet深度学习实战学习笔记-9-目标检测

    1.介绍 目标检测是指任意给定一张图像,判断图像中是否存在指定类别的目标,如果存在,则返回目标的位置和类别置信度 如下图检测人和自行车这两个目标,检测结果包括目标的位置.目标的类别和置信度 因为目标检 ...

  8. Caffe深度学习计算框架

    Caffe | Deep Learning Framework是一个清晰而高效的深度学习框架,其作者是博士毕业于UC Berkeley的 Yangqing Jia,目前在Google工作.Caffe是 ...

  9. Tensorflow学习教程------模型参数和网络结构保存且载入,输入一张手写数字图片判断是几

    首先是模型参数和网络结构的保存 #coding:utf-8 import tensorflow as tf from tensorflow.examples.tutorials.mnist impor ...

随机推荐

  1. presistence

    每一个神都是从弱到强的,像继科,在2011年之前,人很浮躁,球不稳,只是偶尔打出高质量而已:在输了无数场球之后,球厚了,人也定了(刘国梁评价),才抓住的机会成就了最快大满贯,并且创造了之后的辉煌,继科 ...

  2. git一些实践命令,关于reabse和merge的区别在最后做了一个比较

    git命令 一. 克隆,提交,拉取代码 1. 克隆,提交代码 git clone:远端仓库地址 git remote add upstream: 上游远端仓库地址(fork代码的原地址).添加上游远端 ...

  3. svn版本管理工具的使用

    安装参考http://www.cnblogs.com/macula/archive/2012/02/12/2347637.html 主要的使用步骤如下: 建立版本管理库: svnadmin creat ...

  4. 深入理解 Array.prototype.map()

    深入理解 Array.prototype.map() map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果. 语法 let new_array = arr.map ...

  5. BZOJ.1190.[HNOI2007]梦幻岛宝珠(分层背包DP)

    题目链接 把重量表示为\(a\times2^b\)的形式,然后按\(b\)排序. 从高到低枚举每一位,\(f[i]\)表示当前位容量为\(i\)时的最大价值(容量即\(a\times2^{bit}\) ...

  6. Asp.Net Mvc项目初始化说明

    文件夹说明: App_Start 项目的启动配置 Content css以及图片资源存放位置 Controllers.Models.View控制器.模型.视图存放位置 Scripts js以及js插件 ...

  7. 在windows 10下使用docker

    准备工作 Windows 10下的Docker是依赖于Hyper-v的,首先我们需要启用它:控制面板 -> 程序 -> 启用或关闭Windows功能 -> 选中Hyper-V 安装D ...

  8. c# 以换行(\r\n)拆分字符串

    c# 以换行(\r\n)拆分字符串 字符串数组形式: string[] striparr = strip.Split(new string[] { "\r\n" }, String ...

  9. WordPress主题开发实例:利用侧边栏工具显示联系方式

    利用侧边栏显示联系方式是很方便的 一.先开启侧边栏工具,在functions.php加上 $args = array( 'name' => __( '分类侧边栏'), 'id' => 'c ...

  10. iOS 跳转到系统指定设置界面

    在需要调转的按钮动作中添加如下的代码,就会跳转到设置中自己的app的设置界面,这里会有通知和位置权限的设置 NSURL * url = [NSURLURLWithString:UIApplicatio ...