hybridize原理
什么是符號式編程
舉個沐神的例子
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()
y = compile(prog, '', 'exec')
exec(y)
上面代碼對應3個過程:
- 定義計算流程
- 編譯成可執(zhí)行的程序
- 給定輸入調(diào)用編譯好的程序

image.png
hybrid_forward(F, x)中的F
net(x)->__call__->forward->hybrid_forward
HybridBlock中hybrid_forward源碼:
def forward(self, x, *args):
"""Defines the forward computation. Arguments can be either
:py:class:`NDArray` or :py:class:`Symbol`."""
if isinstance(x, NDArray):
with x.context as ctx:
try:
if self._active:
return self._call_cached_op(x, *args)
params = {i: j.data(ctx) for i, j in self._reg_params.items()}
except DeferredInitializationError:
self._finish_deferred_init(self._active, x, *args)
if self._active:
return self._call_cached_op(x, *args)
params = {i: j.data(ctx) for i, j in self._reg_params.items()}
return self.hybrid_forward(ndarray, x, *args, **params)
assert isinstance(x, Symbol), \
"HybridBlock requires the first argument to forward be either " \
"Symbol or NDArray, but got %s"%type(x)
params = {i: j.var() for i, j in self._reg_params.items()}
with self.name_scope():
return self.hybrid_forward(symbol, x, *args, **params)
??15和倒數(shù)第一行分別傳給了hybrid_forward函數(shù)F為ndarray和symbol,這樣就達到了用戶使用的時候這個F取決于什么運行模式而運行不同的對象
hybridize過程
以下面一段代碼為例:
class HybridNet(nn.HybridBlock):
def __init__(self, **kwargs):
super(HybridNet, self).__init__(**kwargs)
with self.name_scope():
self.fc1 = nn.Dense(10)
self.fc2 = nn.Dense(2)
def hybrid_forward(self, F, x):
x = F.relu(self.fc1(x))
return self.fc2(x)
if __name__ == '__main__':
net = HybridNet()
net.initialize()
x = nd.random.normal(shape=(1, 4))
net.hybridize()
print net
y = net(x)
上述代碼__init__會執(zhí)行block源碼的__setattr__方法注冊HybridNet兩個子block:
def __setattr__(self, name, value):
"""Registers parameters."""
if hasattr(self, name):
existing = getattr(self, name)
if isinstance(existing, (Parameter, Block)) and not isinstance(value, type(existing)):
raise TypeError('Changing attribute type for {name} from {type1} to {type2}' \
'is not allowed.'.format(name=name,
type1=type(existing),
type2=type(value)))
if isinstance(existing, Block):
for i, c in enumerate(self._children):
if c is existing:
self._children[i] = value
elif isinstance(value, Block):
self.register_child(value)
elif isinstance(value, Block):
self.register_child(value)
super(Block, self).__setattr__(name, value)
register方法注冊子block:
def register_child(self, block):
"""Registers block as a child of self. :py:class:`Block` s assigned to self as
attributes will be registered automatically."""
self._children.append(block)
hybridize方法先把本block和所有的子block實例的_active置為true:
def hybridize(self, active=True):
self._active = active
print ('HybridBlock', self.name)
super(HybridBlock, self).hybridize(active)
def hybridize(self, active=True):
"""Activates or deactivates :py:class:`HybridBlock` s recursively. Has no effect on
non-hybrid children.
Parameters
----------
active : bool, default True
Whether to turn hybrid on or off.
"""
for cld in self._children:
cld.hybridize(active)
執(zhí)行過hybridize的net(計算圖)再執(zhí)行前向計算net(x)->__call__->forward
def forward(self, x, *args):
"""Defines the forward computation. Arguments can be either
:py:class:`NDArray` or :py:class:`Symbol`."""
if isinstance(x, NDArray):
with x.context as ctx:
try:
if self._active:
return self._call_cached_op(x, *args)
params = {i: j.data(ctx) for i, j in self._reg_params.items()}
except DeferredInitializationError:
self._finish_deferred_init(self._active, x, *args)
if self._active:
return self._call_cached_op(x, *args)
params = {i: j.data(ctx) for i, j in self._reg_params.items()}
return self.hybrid_forward(ndarray, x, *args, **params)
assert isinstance(x, Symbol), \
"HybridBlock requires the first argument to forward be either " \
"Symbol or NDArray, but got %s"%type(x)
params = {i: j.var() for i, j in self._reg_params.items()}
with self.name_scope():
return self.hybrid_forward(symbol, x, *args, **params)
從方法里面看出如果傳遞進來的是ndarray, 會_call_cached_op,獲取計算圖并執(zhí)行計算
打印圖:
客戶端代碼:
if __name__ == '__main__':
net = HybridNet()
net.initialize()
x = nd.random.normal(shape=(1, 4))
#x = mx.sym.Variable('data')
#net.hybridize()
#print net
y = net(x)
print y
—symbolic:
<Symbol hybridnet0_dense1_fwd>
hybridize:
雖然是圖,但是還是直接執(zhí)行結(jié)果:
[[ -2.95562204e-05 3.18562193e-03]]
<NDArray 1x2 @cpu(0)>
imperactive:
[[ -2.95562204e-05 3.18562193e-03]]
<NDArray 1x2 @cpu(0)>
參考:
https://github.com/mli/gluon-tutorials-zh/blob/master/chapter_gluon-advances/hybridize.md