一、面向过程与面向对象的优缺点

面向过程使用mxnet,就是使用gluon封装好的对象,不加改动的表达机器学习的逻辑过程,其特点是方便、快捷,缺点是不够灵活(虽然可以应对90%以上的问题了),面向对象基于继承、多态的性质,对原有的gluon类进行了继承重写,并在不改变应用接口的情况下(基于多态),灵活的改写原有类,使之更加符合用户特殊需求。本文从自定义模型、自定义层、自定义初始化三个方面说明gluon的继承重写,这三个基本操作足够用户随心所欲的创造模型了。

二、自定义模型

1、定义静态模型

静态模型就是实例化后模型的结构就不能随便改变了,其代码如下:

from mxnet import nd
from mxnet.gluon import nnclass MLP(nn.Block):# 声明带有模型参数的层,这里声明了两个全连接层def __init__(self, **kwargs):# 调用MLP父类Block的构造函数来进行必要的初始化。这样在构造实例时还可以指定其他函数# 参数,如“模型参数的访问、初始化和共享”一节将介绍的模型参数paramssuper(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] = blockdef forward(self, x):# OrderedDict保证会按照成员添加时的顺序遍历成员for block in self._children.values():x = block(x)return xnet = MySequential()
net.add(nn.Dense(256, activation='relu'))
net.add(nn.Dense(10))
net.initialize()
net(X)

三、定义tensor流

tensor流就是tensor之间是怎样运算的,gluon默认的tensor流是简单的tensor乘法运算,自定义就对tengsor流使用if判断、for循环手段,构造出更加复杂的tensor流,这一点在后面的卷积网络、循环网络中频繁使用。

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 /= 2if x.norm().asscalar() < 0.8:x *= 10return x.sum()net = FancyMLP()
net.initialize()
net(X)

说明

  1. 以上三个方法是可以结合起来使用的,基于这三点用户可以使用gluon构造出各种卷积、循环网络。
  2. 以上三种继承方式中,forward函数必须定义重写,否则出现下面的错误,就是没找到forward propagation。
print(net(X))
out = self.forward(*args)
raise NotImplementedError
NotImplementedError

三、自定义层

层与模型没有本质区别,从语言角度讲是一样的,二者的数据结构都是tensor+forward,只是用途不同而已。层可以理解为整个模型的一层或一部分,是一段网络,层的作用用来构造模型。

1、gluon的层

Dense层:forward = (X * weight + bias).relu()

g_layer = nn.Dense(2)
g_layer.initialize(init=init.One())
X = nd.array([1, 2, 3, 4]).reshape((1, 4))
y = g_layer(X)
print('weight of g_layer:', g_layer.weight.data())
print('bias of g_layer:', g_layer.bias.data())
print('X:', X)
print('g_layer(X):', y)
print('structure of g_layer:', g_layer)"""
# output
weight of g_layer:
[[1. 1. 1. 1.][1. 1. 1. 1.]]
<NDArray 2x4 @cpu(0)>
bias of g_layer:
[0. 0.]
<NDArray 2 @cpu(0)>
X:
[[1. 2. 3. 4.]]
<NDArray 1x4 @cpu(0)>
g_layer(X):
[[10. 10.]]
<NDArray 1x2 @cpu(0)>
structure of g_layer: Dense(4 -> 2, linear)
"""

说明:

  1. 再次强调一遍,层和模型的要素是tensor + forward,上面的g_layer是gluon默认的forward,即进行简单的乘法运算(X * tensor);
  2. 因为上面的层从模型的角度看只有一个层,所以查看参数的时候使用g_layer.weight.data(),而不是g_layer[0].weight.data();

2、自定义无参数层

from mxnet import gluon, nd
from mxnet.gluon import nnclass CenteredLayer(nn.Block):def __init__(self, **kwargs):super(CenteredLayer, self).__init__(**kwargs)def forward(self, x):return x - x.mean()
layer = CenteredLayer()
layer(nd.array([1, 2, 3, 4, 5]))

说明: 与上面的g_layer没有区别,都是tensor+forward,这里layer.weight.data()就会报错,因为是0个层;

3、自定义含参数层

自定义的层的意思是tensor也要自定义,tensor就是weight + bias;

class MyDense(nn.Block):def __init__(self, units, in_units, **kwargs):super(MyDense, self).__init__(**kwargs)self.weight1 = self.params.get('haha_weight', shape=(in_units, units))self.bias1 = self.params.get('haha_bias', shape=(units,))def forward(self, x):linear = nd.dot(x, self.weight1.data()) + self.bias1.data()return nd.relu(linear)if __name__ == '__main__':dense = MyDense(units=3, in_units=5)dense.initialize()dense(nd.random.uniform(shape=(2, 5)))print(dense.weight1.data()[0])"""
[0.0068339  0.01299825 0.0301265 ]
<NDArray 3 @cpu(0)>
"""

说明:从这个代码中可以看出一个层的本质就是一段网络;

4、层的应用

net = nn.Sequential()
net.add(MyDense(8, in_units=64),MyDense(1, in_units=8))
net.initialize()
y = net(nd.random.uniform(shape=(2, 64)))
print('self_define tensor:', net[0].weight1.data()[0])"""
self_define tensor:
[0.0068339  0.01299825 0.0301265  0.04819721 0.01438687 0.050112390.00628365 0.04861524]
<NDArray 8 @cpu(0)>
"""

四、自定义初始化

1、_init_weight在做什么?

# -*- coding: utf-8 -*-
from mxnet import init, nd
from mxnet.gluon import nnclass MyInit(init.Initializer):def _init_weight(self, name, data):print('Init', name, data.shape)if __name__ == '__main__':net = nn.Sequential()net.add(nn.Dense(256, activation='relu'),nn.Dense(256, activation='relu'),nn.Dense(10))net.initialize(init=MyInit())X = nd.random.uniform(shape=(2, 20))print('---------1---------')Y = net(X)print('---------2---------')net.initialize(init=MyInit(), force_reinit=True)"""
# output
---------1---------
Init dense0_weight (256, 20)
Init dense1_weight (256, 256)
Init dense2_weight (10, 256)
---------2---------
Init dense0_weight (256, 20)
Init dense1_weight (256, 256)
Init dense2_weight (10, 256)
"""

2、怎么使用_init_weight自定义初始化?

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() >= 5net.initialize(MyInit(), force_reinit=True)
net[0].weight.data()[0]

说明:上面仅说明对weight初始化,gulon也提供了_init_bias,但是最后还是强制bias=0,也就是重写的_init_bias没有被调用,从机器学习的角度讲,bias一般初始化为0;

面向对象方法使用gluon相关推荐

  1. 结构化方法与面向对象方法之比较

    结构化方法与面向对象方法之比较 随着计算机软硬件各方面的飞速发展,计算机领域的观念和方法日新月异.面向对象方法始于20世纪60年代挪威奥斯陆大学和挪威计算机中心共同研究的SMULA语言.70 年代,人 ...

  2. 面向对象方法综述(工具<方法<思维<价值观)

    思想起源于上世纪六十年代(和结构化方法一样) 最早的OOPL:Simula67 最纯的OOPL:Smalltalk smalltalk的贡献:它在系统设计中强调对象概念的统一,引入对象,对象类,方法, ...

  3. 结构化方法与面向对象方法的比较

    本博客旨在通过基本概念.主要特征以及针对需求变更的项目所选用的开发模型等方面进行结构化方法与面向对象方法的比较. 软件开发方法指在项目投资规模和时间限制内,设计.实现符合用户需求的高质量软件,根据软件 ...

  4. 论面向对象方法与软件复用关系-z

    1.软件复用的特点和现状 软件复用就是将已有的软件成分用于构造新的软件系统.可以被复用的软件成分一般称作可复用构件,无论对可复用构件原封不动地使用还是作适当的修改后再使用,只要是用来构造新软件,则都可 ...

  5. java整数四则运算课设_用面向对象方法设计实现整数的四则运算(java)

    用面向对象方法设计实现整数的四则运算,并编写主程序演示该类用法.(要求:用继承或接口实现) import java.math.BigDecimal; public class OperationImp ...

  6. 『软件工程13』浅谈面向对象方法,统一建模语言UML

    浅谈面向对象方法UML 一.UML的含义 二.UML的主要内容 1.UML的概念模型 2.UML概念模型图例 三.UML的基本构造块 1.UML中的事物 (1)UML中的四种事物 (2)UML中各种事 ...

  7. 【渝粤题库】陕西师范大学200711 面向对象方法与C++ 作业

    <面向对象方法与C++>作业 一.填空题 1.若char *string="test";则如果要输出指针值,正确的语句是 . 2.在重载"[ ]"运 ...

  8. 【渝粤题库】广东开放大学 面向对象方法精粹 形成性考核

    选择题 题目: 单选 可行性分析研究的主要目的是 题目: ()指的是一个软件结构内,不同模块之间互联的紧密程度. 题目: 4 .()衡量一个模块内各个元素彼此结合的紧密程度. 题目:   5 .()是 ...

  9. 面向对象方法开发的方法

    面向对象分析首要的工作,是建立问题域的对象模型. 这个模型描述了现实世界中的"类与对象"以及它们之间的关系,表示了目标系统的静态数据结构.静态数据结构对应用细节依赖较少,比较容易确 ...

最新文章

  1. sudo命令 和限制root 远程登陆
  2. CSS3弹性伸缩布局(一)——box布局
  3. 安卓事件机制与页面跳转
  4. centos7.4二进制安装mysql
  5. 【转】契约测试的必要性
  6. 谷歌在线appspot平台教你学Hacker(由浅如深)-XSS篇
  7. 手把手带你玩摄像头模组
  8. TraceView issue
  9. 威纶通触摸屏MT6071IP如何使用宏指令编程设置密码登陆界面进行用户操作权限管理
  10. 简单的省市区级联SQL脚本
  11. 20多岁的年纪,做什么将来才不后悔?
  12. 重复高斯勒让德法则(gauss-legendre)求积分(python,数值积分)
  13. 微信公众号活动策划方案怎么写?看完你也可以直接套用
  14. 大学开设大数据专业,都安排了哪些课程?
  15. 翻斗式雨量计的组成与工作原理
  16. freescale S12X微控制器 模拟EEPROM 快速上手指南
  17. if test 用法
  18. 第 2 课 第二次鸦片战争(1856-1860 年) (咸丰帝在位)
  19. onenote需要密码才能同步此笔记本。 (错误代码: 0xE0000024)
  20. BIM模型轻量化的方法总结

热门文章

  1. 前端学习(516):两列布局的第三种解决方案
  2. 前端学习(8):HTML的基本属性和结构
  3. Linux 延时函数
  4. input框在ios中的阴影问题
  5. 页面性能优化参考建议
  6. 机器学习算法总结--K近邻
  7. 如何设置多级标题_办公技巧|标题样式amp;多级列表结合,再也不用手动修改1.1、1.2 ...!...
  8. ue4导入倾斜摄影_倾斜摄影建模干货|还怕搞不定CC空三?这里只要5分钟……
  9. Homebrew 安装使用
  10. openfalcon架构及相关服务配置详解