Zehaos / MobileNet

MobileNet build with Tensorflow
Apache License 2.0
1.62k stars 470 forks source link

为什么CPU的速度能那么快? #22

Open KeyKy opened 7 years ago

KeyKy commented 7 years ago

谢谢。

Zehaos commented 7 years ago

CPU的卷积实现通常会用到im2col(强烈建议看代码),也就是把feature map和卷积核排列成可以用矩阵相乘的形式,你会发现depthwise 1x1的卷积可以不用进行内存的重排,这是最关键的地方。然后通用矩阵乘法的CPU优化已经很到位了,并行程度很高,核越多按道理应该越快。我用的是4核的cpu,我想在移动端8核应该也不差。

KeyKy commented 7 years ago

@Zehaos 你的解释感觉Caffe已经实现了,这里是Caffe的源码

template <typename Dtype>
void BaseConvolutionLayer<Dtype>::forward_cpu_gemm(const Dtype* input,
    const Dtype* weights, Dtype* output, bool skip_im2col) {
  const Dtype* col_buff = input;
  if (!is_1x1_) {
    if (!skip_im2col) {
      conv_im2col_cpu(input, col_buffer_.mutable_cpu_data());
    }
    col_buff = col_buffer_.cpu_data();
  }
  for (int g = 0; g < group_; ++g) {
    caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasNoTrans, conv_out_channels_ /
        group_, conv_out_spatial_dim_, kernel_dim_,
        (Dtype)1., weights + weight_offset_ * g, col_buff + col_offset_ * g,
        (Dtype)0., output + output_offset_ * g);
  }
}

1x1的的确没有调用im2col进行重排,按照你的解释,Caffe的实现应该和TF的速度一样快吧?但是我发现并不是这样。

Zehaos commented 7 years ago

@KeyKy 对,关键在于depthwise。 Caffe 没有depthwise conv这个layer,是用conv + group参数来实现的(具体实现我没看,有人说是for循环)。 有人已经写了这个layer, click here. 你可以试试,如果你能回馈下测试结果我会非常感激。

泽浩

ccJia commented 7 years ago

@Zehaos CAFFE 是用循环来解决的 ,令 group_==channel 的个数,就是 MOBILE的对应通道相乘,而且感觉 ,你说的这个LAYER 没干什么事情 呢,调用的还是基类的卷积方法。

KeyKy commented 7 years ago

@jy001227801 这个LAYER 只写了CUDA的,CPU的实现貌似还是原来的Caffe conv+group

ccJia commented 7 years ago

@KeyKy cuda的貌似是拿的tensor 的,直接做卷积,没有IM2COL的过程,CPU的运算,我感觉MOBILE是靠 AVX进行的加速。GPU是可以快。

ccJia commented 7 years ago

@Zehaos 我也一直没想明白,CPU快在哪里,TF的Depthconv的代码我也看了,类似于 CAFFE 的IM2COL的过程它也有,于CAFFE的区别就是访问的方式不一样,如果不开多线程或是什么的话,按理说应该和CAFFE group==channel 的速度是类似的吧??求答疑解惑啊,困扰很久了。

Zehaos commented 7 years ago

@jy001227801 把代码链接贴出来一起研究下

ccJia commented 7 years ago

@Zehaos https://github.com/tensorflow/tensorflow/blob/359d6f9716c0bb9bd8201ce600da98b0481a8049/tensorflow/core/kernels/depthwise_conv_op.cc 里面的DepthwiseConv2DKernel
https://github.com/tensorflow/tensorflow/blob/2d23960e29c3efb1b30c5cb6f8f3368635b97f29/tensorflow/core/kernels/depthwise_conv_op.h 里面的DepthwiseInputCopyOp , 不知道我看的对不对。

Zehaos commented 7 years ago

@ccJia 刚看了下,里面有很多细节优化过,比如加pad有利与指令并行。 这个op优化过很多次,具体可以跟进这些历史看看。 depthwise conv op history

KeyKy commented 7 years ago

我的想法不知道对不对?我看了下源码,貌似TF的速度快 用了多线程 work_sharder + eigen?

ccJia commented 7 years ago

@KeyKy 是的,TF 是用了多线程,这个可以在编译时候,通过配置设定的。之前测试Eigen 和 Blas 没有特别大的速度差异。

Zehaos commented 7 years ago

@KeyKy @ccJia 不仅是多线程吧,benchmark里面给出的结果1个thread也很快。

KeyKy commented 7 years ago

@Zehaos @ccJia 你们有试过跑单线程的mobilenet速度是多少image/s?这个benchmark 2.7G items/s 还没看懂是什么意思. @ccJia 你能告诉我怎么设置吗?我是通过https://stackoverflow.com/questions/34389945/changing-the-number-of-threads-in-tensorflow-on-cifar10 去设置 可以吧?

Zehaos commented 7 years ago

@KeyKy 给的指标是带宽

ysh329 commented 7 years ago

鹿过……mark

liu6381810 commented 7 years ago

@Zehaos 您好,我运行python tools/time_benchmark.py 得到的结果大概是0.04 htop看到的cpu利用率大概560%,但是当我用下面代码去测试mobilenet的时候得到的速度是0.07左右,两个的区别是什么? `import tensorflow as tf

import mobilenet import time

batch_size = 100 height = 224 width = 224 with tf.Session() as sess: inputs = tf.random_uniform((batchsize, height, width, 3)) logits, = mobilenet.mobilenet(inputs) sess.run(tf.global_variables_initializer()) a = time.time() output = sess.run(logits) print(time.time() - a)`

Zehaos commented 7 years ago

@liu6381810 你这段代码没有 warm up, time_benchmark.py里面是有的,而且跑了很多次算平均。

liu6381810 commented 7 years ago

@Zehaos 谢谢回复!请问如何做warm up,还有warm up的作用会这么明显吗?刚才我跑了100次做平均时间没太大变化。

Zehaos commented 7 years ago

@liu6381810 就是先跑几个iter不算时间, warm up额。。。影响还是有的,因为执行指令都放到cache里了嘛,接下来cache的命中率就高。。。

liu6381810 commented 7 years ago

@Zehaos 嗯好的谢谢

CLAPoo commented 7 years ago

@Zehaos 想问下gpu的3ms是怎样的条件,我在TITANX上无法稳定复现3ms

Zehaos commented 7 years ago

@MrAprils My environment: Ubuntu 16.04 LTS, Xeon E3-1231 v3, 4 Cores @ 3.40GHz, GTX1060, TF 1.0.1, CUDA8.0, CUDNN5.1

Here is a script for time benchmarking.

CLAPoo commented 7 years ago

2017-08-15 10:53:28.821269: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1052] Creating TensorFlow device (/gpu:0) -> (device: 0, name: TITAN Xp, pci bus id: 0000:02:00.0, compute capability: 6.1) 2017-08-15 10:53:31.591376: step 0, duration = 0.008 2017-08-15 10:53:31.655964: step 10, duration = 0.005 2017-08-15 10:53:31.715869: step 20, duration = 0.004 2017-08-15 10:53:31.795379: step 30, duration = 0.008 2017-08-15 10:53:31.873261: step 40, duration = 0.008 2017-08-15 10:53:31.951907: step 50, duration = 0.005 2017-08-15 10:53:32.015416: step 60, duration = 0.008 2017-08-15 10:53:32.075358: step 70, duration = 0.007 2017-08-15 10:53:32.132694: step 80, duration = 0.004 2017-08-15 10:53:32.194687: step 90, duration = 0.009 2017-08-15 10:53:32.279380: Forward across 100 steps, 0.007 +/- 0.002 sec / batch 这是在我的环境上跑的结果,增大num_batches后中间有一段时间为3ms,但不稳定,请问您说的3ms指的是最低还是均值

CLAPoo commented 7 years ago

TITAN Xp 和 TITANX Pascal 均尝试过,这两个有很大差异吗?

Zehaos commented 7 years ago

@MrAprils Average time.

CLAPoo commented 7 years ago

2017-08-15 11:06:14.477316: step 890, duration = 0.006 2017-08-15 11:06:14.517504: step 900, duration = 0.005 2017-08-15 11:06:14.550230: step 910, duration = 0.003 2017-08-15 11:06:14.579801: step 920, duration = 0.003 2017-08-15 11:06:14.615377: step 930, duration = 0.005 2017-08-15 11:06:14.655590: step 940, duration = 0.003 2017-08-15 11:06:14.696793: step 950, duration = 0.005 2017-08-15 11:06:14.737471: step 960, duration = 0.004 2017-08-15 11:06:14.777763: step 970, duration = 0.003 2017-08-15 11:06:14.811013: step 980, duration = 0.003 2017-08-15 11:06:14.842362: step 990, duration = 0.003 2017-08-15 11:06:14.867415: Forward across 1000 steps, 0.004 +/- 0.001 sec / batch 这是 TITANX Pascal 1000次的结果,挺接近,有个问题 num_steps_burn_in 是为了去除刚开始不准确的时间吗?

Zehaos commented 7 years ago

@MrAprils YES.

CLAPoo commented 7 years ago

@Zehaos 有尝试过将inception或者其他的网络中的卷积转换为depthwise这种结构吗,我试验的结果不太理想

KeyKy commented 7 years ago

@MrAprils xception

BowieHsu commented 7 years ago

@MrAprils shufflenet

mollyStark commented 7 years ago

@Zehaos 你好~我用time_benchmark.py在k40上测试,前向速度是0.029 +/- 0.003 sec / batch,请问3ms是怎么做到的啊?有加什么配置吗?