面向对象方法使用gluon
一、面向過程與面向對象的優缺點
面向過程使用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)說明:
三、自定義層
層與模型沒有本質區別,從語言角度講是一樣的,二者的數據結構都是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) """說明:
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的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【macOS】Desktop桌面文件突然
- 下一篇: python网站用什么数据库_PyMyS