MXNET深度学习框架-26-Hybridize:更快更好地移植

        到目前为止我们所使用的全部是命令式编程,事实上我们用的大部分Python代码都是采用的命令式编程,比如下面这个例子:

def add(x,y):
    return x+y
def func_add(A,B,C,D):
    E=add(A,B)
    F=add(C,D)
    G=add(E,F)
    return G
resut=func_add(1,2,3,4)
print(resut)

结果:
在这里插入图片描述
        这是我们绝大多数人编程时所采用的惯有方法,然而,这种方法也有它的不足:速度慢。这是因为它要跟Python的运行环境打交道,如上面的例子所示,一共进行了3次加法,也就是说它要跟Python环境打3次交道,并且,在程序没有结束前,它会一直保留之前计算的结果,比如例子中的“E”和“F”。

        这里有一种方式叫做"符号式编程",大部分的深度学习框架如TensorFlow、Theano等都用了这个方式。通常这个方式包含以下三个步骤:
        1)定义计算流程;
        2)编译成可执行的程序;
        3)给定输入,调用编译过的程序。

# 符号式编程
def add_str():
    return ''' # 返回一个str类型的结果
def add(x,y):
    return x+y
    '''
def func_add_str():
    return '''
def func_add(A,B,C,D):
    E=add(A,B)
    F=add(C,D)
    G=add(E,F)
    return G
    '''
def evok_str():
    return add_str()+func_add_str()+'''
resut=func_add(1,2,3,4)
print(resut)
    '''
pro=evok_str()
print(pro)
y=compile(pro,'','exec')
exec(y)

结果:
在这里插入图片描述
        从上面的例子可以看到,我们只是返回了三个计算流程,之后我们编译再执行。在编译的时候系统能看到整个程序,因此有更多的优化空间。其实,在一些主流的深度学习框架中应用的就是这种方法。

        在mxnet中能够提升运算速度的方法是Hybridize,下面我们来做一个简单的测试,定义一个简单的模型,看看不使用Hybridize和使用Hybridize之后的程序有什么不同:

def get_net():
    net=gn.nn.HybridSequential()
    with net.name_scope():
        net.add(gn.nn.Dense(128,activation="relu"))
        net.add(gn.nn.Dense(64, activation="relu"))
        net.add(gn.nn.Dense(2))
    return net
net=get_net()
net.initialize()
x=nd.random_normal(shape=(1,512))
# print(net(x)) #不使用Hybridize
print("不使用Hybridize后的结果为:",net(x))
net.hybridize() #使用Hybridize

print("使用Hybridize后的结果为:",net(x))

结果:
在这里插入图片描述
        我们可以看到,计算结果其实都是一样的。需要注意的是,只有继承HybridBlock类的层才会被优化计算。下面我们来比较一下性能:

def bench(net,x):
    start=time()
    for i in range(1000):
        y=net(x)
    nd.waitall()   # 等待所有计算完成
    return time()-start
net=get_net()
net.initialize()
x=nd.random_normal(shape=(1,512))
print("不使用Hybridize后的运行时间为:%.4f s"%(bench(net,x)))
net.hybridize()
print("使用Hybridize后的运行时间为:%.4f s"%(bench(net,x)))

运行结果:
在这里插入图片描述
        可以看到,使用了Hybridize之后的程序是没有使用Hybridize的3倍。

        我们同样也可以使用一个变量,之后再给变量赋值:

import mxnet.symbol as symbol
k=symbol.var(name="data") # 声明一个变量
print(net(k))
print(net(k).tojson()) #查看json文件

        当然了,这样出来的结果也是一个symbol类型的值,因为我们并没有给它赋值。而且,使用了Hybridize之后,还可以打印json文件,一般来说,Python只是用来训练,如果真正要用在产品线上的话,Python很难移植,而使用json文件可能会起到作用。结果:
在这里插入图片描述
        虽然Hybridize执行效率高,可移植性也高,但是,它的灵活性比较差,下面演示一个例子来做说明:

class Hybridize_net(gn.nn.HybridBlock):
    def __init__(self, **kwargs):
        super(Hybridize_net, self).__init__(**kwargs)
        self.d1 = gn.nn.Dense(10)
        self.out = gn.nn.Dense(2)

    def hybrid_forward(self, F, x):# 如果使用了Hybridize吗,F就是一个变量,否则,就是一个ndArray
        print("F:",F)
        print("x:",x)
        x=F.relu(self.d1(x))
        print("hidden:",x)
        return self.out(x)

如果不使用Hybridize的话,F就是一个ndArray:

net=Hybridize_net()
net.initialize()
x=nd.random_normal(shape=(1,3))
print(net(x))

运行结果如下:
在这里插入图片描述
如果使用了Hybridize,F就是一个变量(symbol),那么会有什么结果?

#使用Hybridize
net.hybridize()
print(net(x))

结果:
在这里插入图片描述
        从中可以看到,F是一个symbol,输入x也变成了symbol(之前是一个ndArray),中间层也变成了symbol。并且再次执行的时候,就只有输出了(Notebook执行),中间打印的部分全都没有了,因为它只执行一次,所以能够加快速度。当然,它还有一个非常大的弊端:无法Debug!!!,比如,if、for这样的语句它是无法使用的,所以在编程的时候需要结合实际来选择是否要使用Hybridize。

本章测试使用代码:

import matplotlib.pyplot as plt
import mxnet as mx
import mxnet.ndarray as nd
from time import *
from mxnet import image
import mxnet.initializer as init
import mxnet.gluon as gn
import mxnet.symbol as symbol
import mxnet.autograd as ag

# 命令式编程
# def add(x,y):
#     return x+y
# def func_add(A,B,C,D):
#     E=add(A,B)
#     F=add(C,D)
#     G=add(E,F)
#     return G
# resut=func_add(1,2,3,4)
# print(resut)
#
# # 符号式编程
# def add_str():
#     return ''' # 返回一个str类型的结果
# def add(x,y):
#     return x+y
#     '''
# def func_add_str():
#     return '''
# def func_add(A,B,C,D):
#     E=add(A,B)
#     F=add(C,D)
#     G=add(E,F)
#     return G
#     '''
# def evok_str():
#     return add_str()+func_add_str()+'''
# resut=func_add(1,2,3,4)
# print(resut)
#     '''
# pro=evok_str()
# print(pro)
# y=compile(pro,'','exec')
# exec(y)

'''---Hybridize---'''


def get_net():
    net = gn.nn.HybridSequential()
    with net.name_scope():
        net.add(gn.nn.Dense(128, activation="relu"))
        net.add(gn.nn.Dense(64, activation="relu"))
        net.add(gn.nn.Dense(2))
    return net


# net=get_net()
# net.initialize()
# x=nd.random_normal(shape=(1,512))
# # print(net(x)) #不使用Hybridize
# print("不使用Hybridize后的结果为:",net(x))
# net.hybridize() #使用Hybridize
#
# print("使用Hybridize后的结果为:",net(x))


# def bench(net,x):
#     start=time()
#     for i in range(1000):
#         y=net(x)
#     nd.waitall()   # 等待所有计算完成
#     return time()-start
# net=get_net()
# net.initialize()
# x=nd.random_normal(shape=(1,512))
#
# print("不使用Hybridize后的运行时间为:%.4f s"%(bench(net,x)))
# net.hybridize()
# print("使用Hybridize后的运行时间为:%.4f s"%(bench(net,x)))
#
# # 变量的使用
# k=symbol.var(name="data") # 声明一个变量
# print(net(k))
# print(net(k).tojson()) #查看json文件

class Hybridize_net(gn.nn.HybridBlock):
    def __init__(self, **kwargs):
        super(Hybridize_net, self).__init__(**kwargs)
        self.d1 = gn.nn.Dense(10)
        self.out = gn.nn.Dense(2)

    def hybrid_forward(self, F, x):# 如果使用了Hybridize,F就是一个变量,否则,就是一个ndArray
        print("F:",F)
        print("x:",x)
        x=F.relu(self.d1(x))
        print("hidden:",x)
        return self.out(x)
# 不使用Hybridize
net=Hybridize_net()
net.initialize()
x=nd.random_normal(shape=(1,3))
print(net(x))
#使用Hybridize
net.hybridize()
print(net(x))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值