深度学习:Neural Network Layers Understanding-36大数据

我是创始人李岩:很抱歉!给自己产品做个广告,点击进来看看。  

深度学习:Neural Network Layers Understanding-36大数据

Inner Product Layer

Inner Product Layer即全连接层,对于IP层的理解,可以简单的将其视为矩阵1*N和矩阵N*M相乘后得到1*M的维度向量。

举个简单的例子,比如输入全连接层的是一个3*56*56维度的数据,假设未知的权重维度为N*M,假设全连接层的输出为num_ouput = 4096,为了计算全连接层的输出,全连接层会将输入的数据3*56*56 reshape 成为1*N的形式,即1x(56x56x3) = 1×9408,所以:

					N = 9408
					M = num_ouput = 4096
				

由此,我们做了一个(1×9408)矩阵和(9408×4096)矩阵的乘法。如果num_output的值改变成为100,则做的是一个(1×9408)矩阵和(9408×100)矩阵的乘法。Inner Product layer(常被称为全连接层)将输入视为一个vector,输出也是一个vector(height和width被设为1)。下面是IP层的示意图

深度学习:Neural Network Layers Understanding-36大数据

图片摘自 Why GEMM is at the heart of deep learning

增大num_output会使得模型需要学习的权重参数增加。IP层一个典型的例子:

					layer {
					name: "ip1"
					type: "InnerProduct"
					bottom: "pool2"
					top: "ip1"
					# learning rate and decay multipliers for the weights
					param {
					lr_mult: 1
					}
					# learning rate and decay multipliers for the biases
					param {
					lr_mult: 2
					}
					inner_product_param {
					num_output: 500
					weight_filler {
					type: "xavier"
					}
					bias_filler {
					type: "constant"
					}
					}
					}
				

有了上面对IP层的理解,对 caffe inner_product_layer.cpp 中Forward的理解就比较自然了。下面是Caffe的IP层在CPU上的实现:

					template <typename Dtype>
					void InnerProductLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
					const vector<Blob<Dtype>*>& top) {
					const Dtype* bottom_data = bottom[0]->cpu_data();
					Dtype* top_data = top[0]->mutable_cpu_data();
					const Dtype* weight = this->blobs_[0]->cpu_data();
					caffe_cpu_gemm<Dtype>(CblasNoTrans, transpose_ ? CblasNoTrans : CblasTrans,
					M_, N_, K_, (Dtype)1.,
					bottom_data, weight, (Dtype)0., top_data);
					if (bias_term_) {
					caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasNoTrans, M_, N_, 1, (Dtype)1.,
					bias_multiplier_.cpu_data(),
					this->blobs_[1]->cpu_data(), (Dtype)1., top_data);
					}
					}
				

上面完成矩阵与矩阵相乘的函数是caffe_cpu_gemm<Dtype>(见 math_functions.cpp ),caffe_cpu_gemm函数矩阵相乘的具体数学表示形式为:

C = a l p h a T r a n s A ( A ) T r a n s B ( B ) + b e t a C C=alpha∗TransA(A)∗TransB(B)+beta∗C

上式中TransX是对X做的一种矩阵变换,比如转置、共轭等,具体是cblas.h中定义的为枚举类型。在 math_functions.cpp 中,除了定义矩阵与矩阵相乘的caffe_cpu_gemm外,还定义了矩阵与向量的相乘,具体的函数为caffe_cpu_gemv,其数学表示形式为:

C = a l p h a T r a n s A ( A ) y + b e t a y C=alpha∗TransA(A)∗y+beta∗y

上面表达式中,y是向量,不是标量。

参考

  1. Why GEMM is at the heart of deep learning
  2. What is the output of fully connected layer in CNN?
  3. caffe_cpu_gemm函数
  4. Caffe学习:Layers
  5. Caffe Layers

GEMM

在上面的IP层中,我们已经涉及到了GEMM的知识,在这一小节里面,不妨对该知识点做一个延伸。

GEMM是BLAS (Basic Linear Algebra Subprograms)库的一部分,该库在1979年首次创建。为什么GEMM在深度学习中如此重要呢?我们可以先来看一个图:

深度学习:Neural Network Layers Understanding-36大数据

图片摘自Yangqing Jia的thesis

上图是采用AlexNet对CNN网络中不同layer GPU和CPU的时间消耗,从更底层的实现可以看到CNN网络的主要时间消耗用在了FC (for fully-connected)和Conv (for convolution),而FC和Conv在实现上都将其转为了矩阵相乘的形式。举个例子:

深度学习:Neural Network Layers Understanding-36大数据

图片摘自 cuDNN: Efficient Primitives for Deep Learning

上面Conv在Caffe中具体实现的时候,会将每一个小的patch拉成一个向量,很多patch构成的向量会构成一个大的矩阵,同样的对于多个卷积核展成一个矩阵形式,从而将图像的卷积转成了矩阵与矩阵的相乘(更形象化的解释参阅 在 Caffe 中如何计算卷积? )。上面可以看到在FC和Conv上消耗的时间GPU占95%,CPU上占89%。因而GEMM的实现高效与否对于整个网络的效率有很大的影响。

那么什么是GEMM呢?GEMM的全称是GEneral Matrix to Matrix Multiplication,正如其字面意思所表达的,GEMM即表示两个输入矩阵进行相乘,得到一个输出的矩阵。两个矩阵在进行相乘的时候,通常会进行百万次的浮点运算。对于一个典型网络中的某一层,比如一个256 row1152 column的矩阵和一个1152 row192 column的矩阵,二者相乘57 million (256 x 1152 x 192)的浮点运算。因而,通常我们看到的情形是,一个网络在处理一帧的时候,需要几十亿的FLOPs(Floating-point operations per second,每秒浮点计算)。

既然知道了GEMM是限制整个网络时间消耗的主要部分,那么我们是不是可以对GEMM做优化调整呢?答案是否定的,GEMM采用Fortran编程语言实现,经过了科学计算编程人员几十年的优化,性能已经极致,所以很难再去进一步的优化,在Nvidia的论文 cuDNN: Efficient Primitives for Deep Learning 中指出了还存在着其他的一些方法,但是他们最后采用的还是改进的GEMM版本实现。GEMM可匹敌的对手是傅里叶变换,将卷积转为频域的相乘,但是由于在图像的卷积中存在strides,使得傅里叶变换方式很难保持高效。

End.

转载请注明来自36大数据(36dsj.com): 36大数据 » 深度学习:Neural Network Layers Understanding

随意打赏

深度数据
提交建议
微信扫一扫,分享给好友吧。