你好,游客 登录
背景:
阅读新闻

资源 | 如何利用VGG-16等模型在CPU上测评各深度学习框架

[日期:2017-10-17] 来源:机器之心  作者: [字体: ]

本项目对比了各深度学习框架在 CPU 上运行相同模型(VGG-16 和 MobileNet)单次迭代所需要的时间。作者提供了所有的测试代码,读者可以尝试测评以完善该结果。

项目地址:https://github.com/peisuke/DeepLearningSpeedComparison

在本项目中,作者测评了流行深度学习框架在 CPU 上的运行相同模型所需要的时间,作者采取测试的模型为 VGG-16 和 MobileNet。所有的测试代码都已经加入 Docker 文件,因此测试环境将很容易设置。目前这两个网络的参数都是随机生成的,因为我们只需要测试输入数据通过神经网络的测试时间。最后的结果并不能保证绝对正确,但作者希望能与我们共同测试并更新结果。

以下是该测试涉及的深度学习架构,

  • Caffe

  • Caffe2

  • Chainer

  • MxNet

  • TensorFlow

  • NNabla

对于这些深度学习框架,作者准备了多种安装设置,例如是否带有 MKL、pip 或 build。

安装与运行

因为我们将所有试验代码和环境都加入了 Docker 文件,所以我们需要下载 Dockerfile 并运行它。

  1. $ docker build -t {NAME}.

  2. $ docker run -it --rm {NAME}

在创建的 Docker 容器中,复制该 GitHub 项目的代码库并运行测试代码。

  1. # git clone https://github.com/peisuke/dl_samples.git

  2. # cd dl_samples/{FRAMEWORK}/vgg16

  3. # python3 (or python) predict.py

当前结果

当前的结果还有很多误差,首先当前结果是模型在各个框架下的一个估计,例如第一项为单次迭代运行时间的样本均值,第二项为单次迭代时间的标准差。

从作者的测试代码可知,每一次迭代的运行时间采取的是 20 次迭代的均值,每一次迭代投入的都是批量为 1 的图片集。且每张图片都是维度为(224,224,3)的随机生成样本,且每一个生成的元素都服从正态分布。若再加上随机生成的权重,那么整个测试仅仅能测试各深度学习框架的在 CPU 上运行相同模型的时间。

以下分别展示了 20 次迭代(有点少)的平均运行时间和标准差,其中每种模型是否使用了 MKL 等 CPU 加速库也展示在结果中。

  1. caffe(openblas,1.0)

  2. caffe-vgg-16:13.900894(sd 0.416803)

  3. caffe-mobilenet :0.121934(sd 0.007861)

  4.  

  5. caffe(mkl,1.0)

  6. caffe-vgg-16:3.005638(sd 0.129965)

  7. caffe-mobilenet:0.044592(sd 0.010633)

  8.  

  9. caffe2(1.0)

  10. caffe2-vgg-16:1.351302(sd 0.053903)

  11. caffe2-mobilenet :0.069122(sd 0.003914)

  12.  

  13. caffe2(mkl,1.0)

  14. caffe2-vgg-16:0.526263(sd 0.026561)

  15. caffe2-mobilenet :0.041188(sd 0.007531)

  16.  

  17. mxnet(0.11)

  18. mxnet-vgg-16:0.896940(sd 0.258074)

  19. mxnet-mobilenet :0.209141(sd 0.060472)

  20.  

  21. mxnet(mkl)

  22. mxnet-vgg-16:0.176063(sd 0.239229)

  23. mxnet-mobilenet :0.022441(sd 0.018798)

  24.  

  25. pytorch

  26. pytorch-vgg-16:0.477001(sd 0.011902)

  27. pytorch-mobilenet :0.094431(sd 0.008181)

  28.  

  29. nnabla

  30. nnabla-vgg-16:1.472355(sd 0.040928)

  31. nnabla-mobilenet :3.984539(sd 0.018452)

  32.  

  33. tensorflow(pip,r1.3)

  34. tensorflow-vgg-16:0.275986(sd 0.009202)

  35. tensorflow-mobilenet :0.029405(sd 0.004876)

  36.  

  37. tensorflow(opt,r1.3)

  38. tensorflow-vgg-16:0.144360(sd 0.009217)

  39. tensorflow-mobilenet :0.022406(sd 0.007655)

  40.  

  41. tensorflow(opt,XLA,r1.3)

  42. tensorflow-vgg-16:0.151689(sd 0.006856)

  43. tensorflow-mobilenet :0.022838(sd 0.007777)

  44.  

  45. tensorflow(mkl,r1.0)

  46. tensorflow-vgg-16:0.163384(sd 0.011794)

  47. tensorflow-mobilenet :0.034751(sd 0.011750)

  48.  

  49. chainer(2.0)

  50. chainer-vgg-16:0.497946(sd 0.024975)

  51. chainer-mobilenet :0.120230(sd 0.013276)

  52.  

  53. chainer(2.1,numpy withmkl)

  54. chainer-vgg-16:0.329744(sd 0.013079)

  55. chainer-vgg-16:0.078193(sd 0.017298)

以下为各深度学习框架在 CPU 上执行 VGG-16 的平均运行速度,其中 TensorFlow 的单次迭代(批量大小为 1)平均速度较快:

以下为 MobileNet 的单次迭代平均速度:

以下展示了不使用 MKL 等 CPU 加速库和使用时的速度区别,我们看到使用 MKL 加速库的各深度学习框架在平均迭代时间上有明显的降低。

以下展示了 MobileNet 的加速情况,令人惊讶的是 TensorFlow 使用 MKL CPU 加速库却令单次平均迭代时间增多了。

以上是作者在 CPU 上运行与测试各个深度学习框架的结果,其中我们还是用了 mkl 等 CPU 加速库。以下是作者使用的各个深度学习框架训练 VGG-16 和 MobileNet 的代码。

Caffe2/VGG-16

  1. importnumpy asnp

  2. importtqdm

  3. importos

  4. importshutil

  5. importtime

  6. importcaffe2.python.predictor.predictor_exporter aspe

  7. fromcaffe2.python importcore,model_helper,net_drawer,workspace,visualize,brew

  8.  

  9. core.GlobalInit(['caffe2','--caffe2_log_level=0'])

  10.  

  11. defAddLeNetModel(model,data):

  12. conv1_1 =brew.conv(model,data,'conv1_1',dim_in=3,dim_out=64,kernel=3,pad=1)

  13. conv1_1 =brew.relu(model,conv1_1,conv1_1)

  14. conv1_2 =brew.conv(model,conv1_1,'conv1_2',dim_in=64,dim_out=64,kernel=3,pad=1)

  15. conv1_2 =brew.relu(model,conv1_2,conv1_2)

  16. pool1 =brew.max_pool(model,conv1_2,'pool1',kernel=2,stride=2)

  17.  

  18. conv2_1 =brew.conv(model,pool1,'conv2_1',dim_in=64,dim_out=128,kernel=3,pad=1)

  19. conv2_1 =brew.relu(model,conv2_1,conv2_1)

  20. conv2_2 =brew.conv(model,conv2_1,'conv2_2',dim_in=128,dim_out=128,kernel=3,pad=1)

  21. conv2_2 =brew.relu(model,conv2_2,conv2_2)

  22. pool2 =brew.max_pool(model,conv2_2,'pool2',kernel=2,stride=2)

  23.  

  24. conv3_1 =brew.conv(model,pool2,'conv3_1',dim_in=128,dim_out=256,kernel=3,pad=1)

  25. conv3_1 =brew.relu(model,conv3_1,conv3_1)

  26. conv3_2 =brew.conv(model,conv3_1,'conv3_2',dim_in=256,dim_out=256,kernel=3,pad=1)

  27. conv3_2 =brew.relu(model,conv3_2,conv3_2)

  28. conv3_3 =brew.conv(model,conv3_2,'conv3_3',dim_in=256,dim_out=256,kernel=3,pad=1)

  29. conv3_3 =brew.relu(model,conv3_3,conv3_3)

  30. pool3 =brew.max_pool(model,conv3_3,'pool3',kernel=2,stride=2)

  31.  

  32. conv4_1 =brew.conv(model,pool3,'conv4_1',dim_in=256,dim_out=512,kernel=3,pad=1)

  33. conv4_1 =brew.relu(model,conv4_1,conv4_1)

  34. conv4_2 =brew.conv(model,conv4_1,'conv4_2',dim_in=512,dim_out=512,kernel=3,pad=1)

  35. conv4_2 =brew.relu(model,conv4_2,conv4_2)

  36. conv4_3 =brew.conv(model,conv4_2,'conv4_3',dim_in=512,dim_out=512,kernel=3,pad=1)

  37. conv4_3 =brew.relu(model,conv4_3,conv4_3)

  38. pool4 =brew.max_pool(model,conv4_3,'pool4',kernel=2,stride=2)

  39.  

  40. conv5_1 =brew.conv(model,pool4,'conv5_1',dim_in=512,dim_out=512,kernel=3,pad=1)

  41. conv5_1 =brew.relu(model,conv5_1,conv5_1)

  42. conv5_2 =brew.conv(model,conv5_1,'conv5_2',dim_in=512,dim_out=512,kernel=3,pad=1)

  43. conv5_2 =brew.relu(model,conv5_2,conv5_2)

  44. conv5_3 =brew.conv(model,conv5_2,'conv5_3',dim_in=512,dim_out=512,kernel=3,pad=1)

  45. conv5_3 =brew.relu(model,conv5_3,conv5_3)

  46. pool5 =brew.max_pool(model,conv5_3,'pool5',kernel=2,stride=2)

  47.  

  48. fc6 =brew.fc(model,pool5,'fc6',dim_in=25088,dim_out=4096)

  49. fc6 =brew.relu(model,fc6,fc6)

  50. fc7 =brew.fc(model,fc6,'fc7',dim_in=4096,dim_out=4096)

  51. fc7 =brew.relu(model,fc7,fc7)

  52. pred =brew.fc(model,fc7,'pred',4096,1000)

  53. softmax =brew.softmax(model,pred,'softmax')

  54. returnsoftmax

  55.  

  56. model =model_helper.ModelHelper(name="vgg",init_params=True)

  57.  

  58. softmax =AddLeNetModel(model,"data")

  59.  

  60. workspace.RunNetOnce(model.param_init_net)

  61.  

  62. data =np.zeros([1,3,224,224],np.float32)

  63. workspace.FeedBlob("data",data)

  64. workspace.CreateNet(model.net)

  65.  

  66. nb_itr =20

  67. timings =[]

  68. fori intqdm.tqdm(range(nb_itr)):

  69. data =np.random.randn(1,3,224,224).astype(np.float32)

  70. start_time =time.time()

  71. workspace.FeedBlob("data",data)

  72. workspace.RunNet(model.net.Proto().name)

  73. ref_out =workspace.FetchBlob("softmax")

  74. timings.append(time.time()-start_time)

  75. print('%10s : %f (sd %f)'%('caffe2-vgg-16',np.array(timings).mean(),np.array(timings).std()))

MXNet/MobileNet

  1. importnumpy asnp

  2. importos

  3. importgzip

  4. importstruct

  5. importtime

  6. importtqdm

  7. fromcollections importnamedtuple

  8. importmxnet asmx

  9.  

  10. defconv_bn(inputs,oup,stride,name):

  11. conv =mx.symbol.Convolution(name=name,data=inputs,num_filter=oup,pad=(1,1),kernel=(3,3),stride=(stride,stride),no_bias=True)

  12. conv_bn =mx.symbol.BatchNorm(name=name+'_bn',data=conv,fix_gamma=False,eps=0.000100)

  13. out =mx.symbol.Activation(name=name+'relu',data=conv_bn,act_type='relu')

  14. returnout 

  15.  

  16. defconv_dw(inputs,inp,oup,stride,name):

  17. conv_dw =mx.symbol.Convolution(name=name+'_dw',data=inputs,num_filter=inp,pad=(1,1),kernel=(3,3),stride=(stride,stride),no_bias=True,num_group=inp)

  18. conv_dw_bn =mx.symbol.BatchNorm(name=name+'dw_bn',data=conv_dw,fix_gamma=False,eps=0.000100)

  19. out1 =mx.symbol.Activation(name=name+'_dw',data=conv_dw_bn,act_type='relu')

  20.  

  21. conv_sep =mx.symbol.Convolution(name=name+'_sep',data=out1,num_filter=oup,pad=(0,0),kernel=(1,1),stride=(1,1),no_bias=True)

  22. conv_sep_bn =mx.symbol.BatchNorm(name=name+'_sep_bn',data=conv_sep,fix_gamma=False,eps=0.000100)

  23. out2 =mx.symbol.Activation(name=name+'_sep',data=conv_sep_bn,act_type='relu')

  24. returnout2

  25.  

  26. defcreate_network():

  27. data =mx.sym.Variable('data')

  28. net =conv_bn(data,32,stride=2,name='conv_bn')

  29. net =conv_dw(net,32,64,stride=1,name='conv_ds_2')

  30. net =conv_dw(net,64,128,stride=2,name='conv_ds_3')

  31. net =conv_dw(net,128,128,stride=1,name='conv_ds_4')

  32. net =conv_dw(net,128,256,stride=2,name='conv_ds_5')

  33. net =conv_dw(net,256,256,stride=1,name='conv_ds_6')

  34. net =conv_dw(net,256,512,stride=2,name='conv_ds_7')

  35.  

  36. net =conv_dw(net,512,512,stride=1,name='conv_ds_8')

  37. net =conv_dw(net,512,512,stride=1,name='conv_ds_9')

  38. net =conv_dw(net,512,512,stride=1,name='conv_ds_10')

  39. net =conv_dw(net,512,512,stride=1,name='conv_ds_11')

  40. net =conv_dw(net,512,512,stride=1,name='conv_ds_12')

  41.  

  42. net =conv_dw(net,512,1024,stride=2,name='conv_ds_13')

  43. net =conv_dw(net,1024,1024,stride=1,name='conv_ds_14')

  44. net =mx.symbol.Pooling(data=net,global_pool=True,kernel=(7,7),pool_type='avg',name='pool1')

  45.  

  46. returnmx.sym.softmax(net)

  47.  

  48. mlp =create_network()

  49. mod =mx.mod.Module(symbol=mlp,context=mx.cpu(),label_names=None)

  50. mod.bind(data_shapes=[('data',(1,3,224,224))],for_training=False)

  51. mod.init_params(initializer=mx.init.Xavier(magnitude=2.))

  52. Batch=namedtuple('Batch',['data'])

  53.  

  54. nb_itr =20

  55. timings =[]

  56. fori intqdm.tqdm(range(nb_itr)):

  57. data =np.random.randn(1,3,224,224).astype(np.float32)

  58. start_time =time.time()

  59. batch =Batch([mx.nd.array(data)])

  60. mod.forward(batch)

  61. prob =mod.get_outputs()[0].asnumpy()

  62. timings.append(time.time()-start_time)

  63. print('%10s : %f (sd %f)'%('mxnet-mobilenet',np.array(timings).mean(),np.array(timings).std()))

PyTorch/MobileNet

  1. importnumpy asnp

  2. importtqdm

  3. importtime

  4. importtorch

  5. importtorch.nn asnn

  6. importtorch.nn.functional asF

  7. importtorch.optim asoptim

  8. fromtorchvision importdatasets,transforms

  9. fromtorch.autograd importVariable

  10.  

  11. defconv_bn(inp,oup,stride):

  12. returnnn.Sequential(

  13. nn.Conv2d(inp,oup,3,stride,1,bias=False),

  14. nn.BatchNorm2d(oup),

  15. nn.ReLU(inplace=True)

  16. )

  17.  

  18. defconv_dw(inp,oup,stride):

  19. returnnn.Sequential(

  20. nn.Conv2d(inp,inp,3,stride,1,groups=inp,bias=False),

  21. nn.BatchNorm2d(inp),

  22. nn.ReLU(inplace=True),

  23.  

  24. nn.Conv2d(inp,oup,1,1,0,bias=False),

  25. nn.BatchNorm2d(oup),

  26. nn.ReLU(inplace=True),

  27. )

  28.  

  29. classMobileNet(nn.Module):

  30. def__init__(self):

  31. super(MobileNet,self).__init__()

  32. self.model =nn.Sequential(

  33. conv_bn(3,32,2),

  34. conv_dw(32,64,1),

  35. conv_dw(64,128,2),

  36. conv_dw(128,128,1),

  37. conv_dw(128,256,2),

  38. conv_dw(256,256,1),

  39. conv_dw(256,512,2),

  40. conv_dw(512,512,1),

  41. conv_dw(512,512,1),

  42. conv_dw(512,512,1),

  43. conv_dw(512,512,1),

  44. conv_dw(512,512,1),

  45. conv_dw(512,1024,2),

  46. conv_dw(1024,1024,1),

  47. nn.AvgPool2d(7),

  48. )

  49. self.fc =nn.Linear(1024,1000)

  50.  

  51. defforward(self,x):

  52. x =self.model(x)

  53. x =x.view(-1,1024)

  54. x =self.fc(x)

  55. returnF.softmax(x)

  56.  

  57. model =MobileNet()

  58. model.eval()

  59.  

  60. nb_itr =20

  61. timings =[]

  62. fori intqdm.tqdm(range(nb_itr)):

  63. data =np.random.randn(1,3,224,224).astype(np.float32)

  64. data =torch.from_numpy(data)

  65. start_time =time.time()

  66. data =Variable(data)

  67. output =model(data)

  68. timings.append(time.time()-start_time)

  69. print('%10s : %f (sd %f)'%('pytorch-mobilenet',np.array(timings).mean(),np.array(timings).std()))

MobileNet 模型的结构:

首先定义两个函数:

conv_bn:卷积、batch 归一化、ReLU;

conv_dw:卷积、batch 归一化、ReLU、卷积、batch 归一化、ReLU;

然后将网络经过 1 次 conv_bn 和 13 次 conv_dw 计算,和 1 次平均池化,最后使用 softmax 函数输出。

TensorFlow/VGG-16

  1. # -*- coding: utf-8 -*-

  2. importtensorflow astf

  3. importnumpy asnp

  4. importtqdm

  5. importtime

  6.  

  7. defvgg(x):

  8. conv1_1 =tf.layers.conv2d(x,64,3,padding='same',activation=tf.nn.relu)

  9. conv1_2 =tf.layers.conv2d(conv1_1,64,3,padding='same',activation=tf.nn.relu)

  10. pool1 =tf.layers.max_pooling2d(conv1_2,2,2)

  11.  

  12. conv2_1 =tf.layers.conv2d(pool1,128,3,padding='same',activation=tf.nn.relu)

  13. conv2_2 =tf.layers.conv2d(conv2_1,128,3,padding='same',activation=tf.nn.relu)

  14. pool2 =tf.layers.max_pooling2d(conv2_2,2,2)

  15.  

  16. conv3_1 =tf.layers.conv2d(pool2,256,3,padding='same',activation=tf.nn.relu)

  17. conv3_2 =tf.layers.conv2d(conv3_1,256,3,padding='same',activation=tf.nn.relu)

  18. conv3_3 =tf.layers.conv2d(conv3_2,256,3,padding='same',activation=tf.nn.relu)

  19. pool3 =tf.layers.max_pooling2d(conv3_3,2,2)

  20.  

  21. conv4_1 =tf.layers.conv2d(pool3,512,3,padding='same',activation=tf.nn.relu)

  22. conv4_2 =tf.layers.conv2d(conv4_1,512,3,padding='same',activation=tf.nn.relu)

  23. conv4_3 =tf.layers.conv2d(conv4_2,512,3,padding='same',activation=tf.nn.relu)

  24. pool4 =tf.layers.max_pooling2d(conv4_3,2,2)

  25.  

  26. conv5_1 =tf.layers.conv2d(pool4,512,3,padding='same',activation=tf.nn.relu)

  27. conv5_2 =tf.layers.conv2d(conv5_1,512,3,padding='same',activation=tf.nn.relu)

  28. conv5_3 =tf.layers.conv2d(conv5_2,512,3,padding='same',activation=tf.nn.relu)

  29. pool5 =tf.layers.max_pooling2d(conv5_3,2,2)

  30. flat5 =tf.contrib.layers.flatten(pool5)

  31.  

  32. d1 =tf.layers.dense(flat5,4096)

  33. d2 =tf.layers.dense(d1,4096)

  34. out =tf.layers.dense(d2,1000)

  35. returntf.nn.softmax(out)

  36.  

  37. # tf Graph input

  38. X =tf.placeholder("float",[None,224,224,3])

  39. Y =vgg(X)

  40.  

  41. init =tf.initialize_all_variables()

  42.  

  43. config =tf.ConfigProto()

  44. config.graph_options.optimizer_options.global_jit_level =tf.OptimizerOptions.ON_1

  45.  

  46. sess =tf.Session(config=config)

  47. sess.run(init)

  48.  

  49. nb_itr =20

  50. timings =[]

  51. fori intqdm.tqdm(range(nb_itr)):

  52. batch_xs =np.random.randn(1,224,224,3).astype(np.float32)

  53. start_time =time.time()

  54. ret =sess.run(Y,feed_dict={X:batch_xs})

  55. timings.append(time.time()-start_time)

  56. print('%10s : %f (sd %f)'%('tensorflow-vgg-16',np.array(timings).mean(),np.array(timings).std()))

VGG-16 模型的结构:

2 个卷积层,1 个池化层;

2 个卷积层,1 个池化层;

3 个卷积层,1 个池化层;

3 个卷积层,1 个池化层;

3 个卷积层,1 个池化层;

1 个 flatten 层;

然后是 1 个 3 层全连接神经网络;

最后用 softmax 函数输出。

激活函数都是 ReLU 函数。

和 TensorFlow 相比,PyTorch 由于不需要定义计算图,非常接近 Python 的使用体验,其函数的定义过程和模型运算要简洁得多,代码格式也更加清晰明了。

收藏 推荐 打印 | 录入:Cstor | 阅读:
相关新闻      
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数
点评:
       
评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款