面向对象方法使用gluon
一、面向過程與面向?qū)ο蟮膬?yōu)缺點(diǎn)
面向過程使用mxnet,就是使用gluon封裝好的對(duì)象,不加改動(dòng)的表達(dá)機(jī)器學(xué)習(xí)的邏輯過程,其特點(diǎn)是方便、快捷,缺點(diǎn)是不夠靈活(雖然可以應(yīng)對(duì)90%以上的問題了),面向?qū)ο蠡诶^承、多態(tài)的性質(zhì),對(duì)原有的gluon類進(jìn)行了繼承重寫,并在不改變應(yīng)用接口的情況下(基于多態(tài)),靈活的改寫原有類,使之更加符合用戶特殊需求。本文從自定義模型、自定義層、自定義初始化三個(gè)方面說明gluon的繼承重寫,這三個(gè)基本操作足夠用戶隨心所欲的創(chuàng)造模型了。
二、自定義模型
1、定義靜態(tài)模型
靜態(tài)模型就是實(shí)例化后模型的結(jié)構(gòu)就不能隨便改變了,其代碼如下:
from mxnet import nd from mxnet.gluon import nnclass MLP(nn.Block):# 聲明帶有模型參數(shù)的層,這里聲明了兩個(gè)全連接層def __init__(self, **kwargs):# 調(diào)用MLP父類Block的構(gòu)造函數(shù)來(lái)進(jìn)行必要的初始化。這樣在構(gòu)造實(shí)例時(shí)還可以指定其他函數(shù)# 參數(shù),如“模型參數(shù)的訪問、初始化和共享”一節(jié)將介紹的模型參數(shù)paramssuper(MLP, self).__init__(**kwargs)self.hidden = nn.Dense(256, activation='relu') # 隱藏層self.output = nn.Dense(10) # 輸出層# 定義模型的前向計(jì)算,即如何根據(jù)輸入x計(jì)算返回所需要的模型輸出def forward(self, x):return self.output(self.hidden(x))X = nd.random.uniform(shape=(2, 20)) net = MLP() net.initialize() net(X)2、定義動(dòng)態(tài)模型
動(dòng)態(tài)模型就是在實(shí)例化以后,后續(xù)可以根據(jù)需要隨時(shí)修改模型結(jié)構(gòu),下面只定義一個(gè)增加網(wǎng)絡(luò)層的功能。
class MySequential(nn.Block):def __init__(self, **kwargs):super(MySequential, self).__init__(**kwargs)def add(self, block):# block是一個(gè)Block子類實(shí)例,假設(shè)它有一個(gè)獨(dú)一無(wú)二的名字。我們將它保存在Block類的# 成員變量_children里,其類型是OrderedDict。當(dāng)MySequential實(shí)例調(diào)用# initialize函數(shù)時(shí),系統(tǒng)會(huì)自動(dòng)對(duì)_children里所有成員初始化self._children[block.name] = blockdef forward(self, x):# OrderedDict保證會(huì)按照成員添加時(shí)的順序遍歷成員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之間是怎樣運(yùn)算的,gluon默認(rèn)的tensor流是簡(jiǎn)單的tensor乘法運(yùn)算,自定義就對(duì)tengsor流使用if判斷、for循環(huán)手段,構(gòu)造出更加復(fù)雜的tensor流,這一點(diǎn)在后面的卷積網(wǎng)絡(luò)、循環(huán)網(wǎng)絡(luò)中頻繁使用。
class FancyMLP(nn.Block):def __init__(self, **kwargs):super(FancyMLP, self).__init__(**kwargs)# 使用get_constant創(chuàng)建的隨機(jī)權(quán)重參數(shù)不會(huì)在訓(xùn)練中被迭代(即常數(shù)參數(shù))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)# 使用創(chuàng)建的常數(shù)參數(shù),以及NDArray的relu函數(shù)和dot函數(shù)x = nd.relu(nd.dot(x, self.rand_weight.data()) + 1)# 復(fù)用全連接層。等價(jià)于兩個(gè)全連接層共享參數(shù)x = self.dense(x)# 控制流,這里我們需要調(diào)用asscalar函數(shù)來(lái)返回標(biāo)量進(jìn)行比較while x.norm().asscalar() > 1:x /= 2if x.norm().asscalar() < 0.8:x *= 10return x.sum()net = FancyMLP() net.initialize() net(X)說明:
三、自定義層
層與模型沒有本質(zhì)區(qū)別,從語(yǔ)言角度講是一樣的,二者的數(shù)據(jù)結(jié)構(gòu)都是tensor+forward,只是用途不同而已。層可以理解為整個(gè)模型的一層或一部分,是一段網(wǎng)絡(luò),層的作用用來(lái)構(gòu)造模型。
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) """說明:
2、自定義無(wú)參數(shù)層
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沒有區(qū)別,都是tensor+forward,這里layer.weight.data()就會(huì)報(bào)錯(cuò),因?yàn)槭?個(gè)層;
3、自定義含參數(shù)層
自定義的層的意思是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)> """說明:從這個(gè)代碼中可以看出一個(gè)層的本質(zhì)就是一段網(wǎng)絡(luò);
4、層的應(yīng)用
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]說明:上面僅說明對(duì)weight初始化,gulon也提供了_init_bias,但是最后還是強(qiáng)制bias=0,也就是重寫的_init_bias沒有被調(diào)用,從機(jī)器學(xué)習(xí)的角度講,bias一般初始化為0;
總結(jié)
以上是生活随笔為你收集整理的面向对象方法使用gluon的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【macOS】Desktop桌面文件突然
- 下一篇: python网站用什么数据库_PyMyS