Mxnet基础知识(二)
1 混合式編程
深度學習框架中,pytorch采用命令式編程,tensorflow采用符號式編程。mxnet的gluon則嘗試將命令式編程和符號式編程結合。
1.1 符號式編程和命令式編程
符號式編程更加靈活,便于理解和調(diào)試;命令式編程能對代碼進行優(yōu)化,執(zhí)行起來效率更高,如下所示:
命令式編程:代碼會根據(jù)執(zhí)行順序,逐行執(zhí)行
#命令式編程
def add(a, b):
return a + b
def fancy_func(a, b, c, d):
e = add(a, b)
f = add(c, d)
g = add(e, f)
return g
fancy_func(1, 2, 3, 4)
符號式編程:下面代碼會通過字符串的形式傳給compile,compile能看到所有的代碼,能對代碼結構和內(nèi)存進行優(yōu)化,加快代碼執(zhí)行效率
#符號式編程
def add_str():
return '''
def add(a, b):
return a + b
'''
def fancy_func_str():
return '''
def fancy_func(a, b, c, d):
e = add(a, b)
f = add(c, d)
g = add(e, f)
return g
'''
def evoke_str():
return add_str() + fancy_func_str() + '''
print(fancy_func(1, 2, 3, 4))
'''
prog = evoke_str()
print(prog)
y = compile(prog, '', 'exec')
exec(y)
mxnet構建網(wǎng)絡時除了nn.Block和nn.Sequential外,還有nn.HybridBlock和nn.HybridSequential, 實現(xiàn)在構建時通過命令式編程方式,代碼執(zhí)行時轉變成符號式編程。HybridBlock和HybridSequential構建的網(wǎng)絡net,通過net.hybride()可以將網(wǎng)絡轉變成符號網(wǎng)絡圖(symbolic graph),對代碼結構進行優(yōu)化,而且mxnet會緩存符號圖,隨后的前向傳遞中重復使用符號圖。
#coding:utf-8
from mxnet.gluon import nn
from mxnet import nd
class HybridNet(nn.HybridBlock):
def __init__(self, **kwargs):
super(HybridNet, self).__init__(**kwargs)
self.hidden = nn.Dense(10)
self.output = nn.Dense(2)
def hybrid_forward(self, F, x):
print('F: ', F)
print('x: ', x)
x = F.relu(self.hidden(x))
print('hidden: ', x)
return self.output(x)
#按原始命令式編程方程,逐行執(zhí)行
net = HybridNet()
net.initialize()
x = nd.random.normal(shape=(1, 4))
net(x)
#net.hybridize()會對代碼結構進行優(yōu)化,轉變成符號式編程
net.hybridize()
net(x)
#再次執(zhí)行時,不會打印代碼中的print部分,這是因為hybride后,構建成符號式代碼網(wǎng)絡,mxnet會緩存符號圖,直接執(zhí)行符號圖,不會再去調(diào)用python原始代碼
net(x)
另外,繼承自HybridBlock的網(wǎng)絡需要實現(xiàn)的是hybrid_forward()相比于forward()多了一個參數(shù)F,F(xiàn)會根據(jù)輸入的x類型選擇執(zhí)行,即x若為mxnet.ndarry,則F調(diào)用ndarry的方法;若x若為mxnet.symbol,則調(diào)用symbol的方法。
2. 延遲初始化
在構建網(wǎng)絡時,mxnet支持不指明參數(shù)的輸入尺寸,只需指明參數(shù)的輸出尺寸。這是通過延遲初始化實現(xiàn)
from mxnet import init, nd
from mxnet.gluon import nn
def getnet():
net = nn.Sequential()
net.add(nn.Dense(256, activation='relu'))
net.add(nn.Dense(10))
return net
#網(wǎng)絡參數(shù)未初始化,無具體值
net = getnet()
print(1, net.collect_params()) #print(1, net[0].weight.data())
#網(wǎng)絡參數(shù)未初始化,無具體值
net.initialize()
print(2, net.collect_params()) #print(2, net[0].weight.data())
#根據(jù)輸入x的尺寸,網(wǎng)絡推斷出各層參數(shù)的尺寸,然后進行初始化
x = nd.random.uniform(shape=(2, 30))
net(x)
print(3, net.collect_params())
print(3, net[0].weight.data())
#第二次執(zhí)行時,不會再進行初始化
net(x)
init提供了許多初始化方法,如下:
init.Zero() #初始化為常數(shù)0 init.One() #初始化為常數(shù)1 init.Constant(value=0.05) #初始化為常數(shù)0.05 init.Orthogonal() #初始化為正交矩陣 init.Uniform(scale=0.07) #(-0.07, 0.07)之間的隨機分布 init.Normal(sigma=0.01) #均值為0, 標準差為0.01的正態(tài)分布 init.Xavier(magnitude=3) # magnitude初始化, 適合tanh init.MSRAPrelu(slope=0.25) #凱明初始化,適合relu
自定義初始化:
#第一層和第二層采用不同的方法進行初始化, # force_reinit:無論網(wǎng)絡是否初始化,都重新初始化 net[0].weight.initialize(init=init.Xavier(), force_reinit=True) net[1].initialize(init=init.Constant(42), force_reinit=True)
#自定義初始化,需要繼承init.Initializer, 并實現(xiàn) _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() >= 5 # 絕對值小于5的賦值為0, 大于等于5的保持不變
net.initialize(MyInit(), force_reinit=True)
net[0].weight.data()[0]
3. 參數(shù)和模塊命名
mxnet網(wǎng)絡中的parameter和block都有命名(prefix), parameter的名字由用戶指定,block的名字由用戶或mxnet自動創(chuàng)建
mydense = nn.Dense(100, prefix="mydense_") print(mydense.prefix) #mydense_ print(mydense.collect_params()) #mydense_weight, mydense_bias dense0 = nn.Dense(100) print(dense0.prefix) #dense0_ print(dense0.collect_params()) #dense0_weight, dense0_bias dense1 = nn.Dense(100) print(dense1.prefix) #dense1_ print(dense1.collect_params()) #dense1_weight, dense1_bias
每一個block都有一個name_scope(), 在其上下文中創(chuàng)建的子block,會采用其名字作為前綴, 注意下面model0和model1的名字差別
from mxnet import gluon
import mxnet as mx
class Model(gluon.Block):
def __init__(self, **kwargs):
super(Model, self).__init__(**kwargs)
with self.name_scope():
self.dense0 = gluon.nn.Dense(20)
self.dense1 = gluon.nn.Dense(20)
self.mydense = gluon.nn.Dense(20, prefix='mydense_')
def forward(self, x):
x = mx.nd.relu(self.dense0(x))
x = mx.nd.relu(self.dense1(x))
return mx.nd.relu(self.mydense(x))
model0 = Model()
model0.initialize()
model0(mx.nd.zeros((1, 20)))
print(model0.prefix) #model0_
print(model0.dense0.prefix) #model0_dense0_
print(model0.dense1.prefix) #model0_dense1_
print(model0.mydense.prefix) #model0_mydense_
model1 = Model()
model1.initialize()
model1(mx.nd.zeros((1, 20)))
print(model1.prefix) #model1_
print(model1.dense0.prefix) #model1_dense0_
print(model1.dense1.prefix) #model1_dense1_
print(model1.mydense.prefix) #model1_mydense_
不同的命名,其保存的參數(shù)名字也會有差別,在保存和加載模型參數(shù)時會引起錯誤,如下所示:
#如下方式保存和加載:model0保存的參數(shù),model1加載會報錯
model0.collect_params().save('model.params')
try:
model1.collect_params().load('model.params', mx.cpu())
except Exception as e:
print(e)
print(model0.collect_params(), '
')
print(model1.collect_params())
#如下方式保存和加載:model0保存的參數(shù),model1加載不會報錯
model0.save_parameters('model.params')
model1.load_parameters('model.params')
print(mx.nd.load('model.params').keys())
在加載預訓練的模型,進行finetune時,注意命名空間, 如下所示:
#加載預訓練模型,最后一層為1000類別的分類器
alexnet = gluon.model_zoo.vision.alexnet(pretrained=True)
print(alexnet.output)
print(alexnet.output.prefix)
#修改最后一層結構為 100類別的分類器,進行finetune
with alexnet.name_scope():
alexnet.output = gluon.nn.Dense(100)
alexnet.output.initialize()
print(alexnet.output)
Sequential創(chuàng)建的net獲取參數(shù):
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() # Use the default initialization method
x = nd.random.uniform(shape=(2, 20))
net(x) # Forward computation
print(net[0].params)
print(net[1].params)
#通過屬性獲取
print(net[1].bias)
print(net[1].bias.data())
print(net[0].weight.grad())
#通過字典方式獲取
print(net[0].params['dense0_weight'])
print(net[0].params['dense0_weight'].data())
#獲取所有參數(shù)
print(net.collect_params())
print(net[0].collect_params())
net.collect_params()['dense1_bias'].data()
#正則匹配
print(net.collect_params('.*weight'))
print(net.collect_params('dense0.*'))
Block創(chuàng)建網(wǎng)絡獲取參數(shù):
from mxnet import gluon
import mxnet as mx
class Model(gluon.Block):
def __init__(self, **kwargs):
super(Model, self).__init__(**kwargs)
with self.name_scope():
self.dense0 = gluon.nn.Dense(20)
self.dense1 = gluon.nn.Dense(20)
self.mydense = gluon.nn.Dense(20, prefix='mydense_')
def forward(self, x):
x = mx.nd.relu(self.dense0(x))
x = mx.nd.relu(self.dense1(x))
return mx.nd.relu(self.mydense(x))
model0 = Model()
model0.initialize()
model0(mx.nd.zeros((1, 20)))
#通過有序字典_children
print(model0._children)
print(model0._children['dense0'].weight._data)
print(model0._children['dense0'].bias._data)
#通過收集所有參數(shù)
print(model0.collect_params()['model0_dense0_weight']._data)
print(model0.collect_params()['model0_dense0_bias']._data)
Parameter和ParameterDict
gluon.Parameter類能夠創(chuàng)建網(wǎng)絡中的參數(shù),gluon.ParameterDict類是字典,建立了parameter name和parameter實例之間的映射,通過ParameterDict也可以創(chuàng)建parameter.
Parameter的使用
class MyDense(nn.Block):
def __init__(self, units, in_units, **kwargs):
# units: the number of outputs in this layer
# in_units: the number of inputs in this layer
super(MyDense, self).__init__(**kwargs)
self.weight = gluon.Parameter('weight', shape=(in_units, units)) #創(chuàng)建名為weight的參數(shù)
self.bias = gluon.Parameter('bias', shape=(units,)) #創(chuàng)建名為bias的參數(shù)
def forward(self, x):
linear = nd.dot(x, self.weight.data()) + self.bias.data()
return nd.relu(linear)
net = nn.Sequential()
net.add(MyDense(units=8, in_units=64),
MyDense(units=1, in_units=8))
#初始化參數(shù)
for block in net:
if hasattr(block, "weight"):
block.weight.initialize()
if hasattr(block, "bias"):
block.bias.initialize()
print(net(nd.random.uniform(shape=(2, 64))))
print(net)
ParameterDict使用
#創(chuàng)建一個parameterdict,包含一個名為param2的parameter
params = gluon.ParameterDict() params.get('param2', shape=(2, 3)) print(params) print(params.keys()) print(params['param2'])
總結
以上是生活随笔為你收集整理的Mxnet基础知识(二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux如何实现网络高级编程,嵌入式L
- 下一篇: 9050 端口 linux 进程,win