Caffe源码阅读(1) 全连接层

发表于 2014-09-15   |  

今天看全连接层的实现。
主要看的是https://github.com/BVLC/caffe/blob/master/src/caffe/layers/inner_product_layer.cpp

主要是三个方法,setup,forward,backward

  • setup 初始化网络参数,包括了w和b
  • forward 前向传播的实现
  • backward 后向传播的实现

setup

主体的思路,作者的注释给的很清晰。
主要是要弄清楚一些变量对应的含义

1
2
3
M_ 表示的样本数
K_ 表示单个样本的特征长度
N_ 表示输出神经元的个数

为了打字方便,以下省略下划线,缩写为M,K,N

forward

实现的功能就是 y=wx+b

1
2
3
4
x为输入,维度 MxK
y为输出,维度 Nx1
w为权重,维度 NxK
b为偏置,维度 Nx1

具体到代码实现,用的是这个函数caffe_cpu_gemm,具体的函数头为

1
2
3
4
void caffe_cpu_gemm<float>(const CBLAS_TRANSPOSE TransA,
const CBLAS_TRANSPOSE TransB, const int M, const int N, const int K,
const float alpha, const float* A, const float* B, const float beta,
float* C)

略长,整理它的功能其实很直观,即C←αA×B+βC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const CBLAS_TRANSPOSE TransA  # A是否转置
const CBLAS_TRANSPOSE TransB # B是否转置 # 这部分都比较直观不用解释了
const int M
const int N
const int K
const float alpha
const float* A
const float* B
const float beta,
float* C # 其中A维度是MxK,B维度是KxN,C维度为MxN

从实际代码来算,全连接层的forward包括了两步:

1
2
3
4
5
6
7
8
# 这一步表示 y←wx,或者说是y←xw'
caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasTrans, M_, N_, K_, (Dtype)1.,
bottom_data, weight, (Dtype)0., top_data);
# 这一步表示 y←y+b
caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasNoTrans, M_, N_, 1, (Dtype)1.,
bias_multiplier_.cpu_data(),
this->blobs_[1]->cpu_data(), (Dtype)1., top_data);
# 所以两步连起来就等价于y=wx+b

backward

分成三步:

  • 更新w
  • 更新b
  • 计算delta

用公式来说是下面三条:

一步步来,先来第一步,更新w,对应代码是:

1
2
caffe_cpu_gemm<Dtype>(CblasTrans, CblasNoTrans, N_, K_, M_, (Dtype)1.,
top_diff, bottom_data, (Dtype)0., this->blobs_[0]->mutable_cpu_diff());

对照公式,有

1
2
3
需要更新的w的梯度的维度是NxK
公式中的a^(l)_j对应的是bottom_data,维度是KxM
公式中的\delta_(l+1)_i对应的是top_diff,维度是NxM

然后是第二步,更新b,对应代码是:

1
2
3
caffe_cpu_gemv<Dtype>(CblasTrans, M_, N_, (Dtype)1., top_diff,
bias_multiplier_.cpu_data(), (Dtype)0.,
this->blobs_[1]->mutable_cpu_diff());

这里用到了caffe_cpu_gemv,简单来说跟上面的caffe_cpu_gemm类似,不过前者是计算矩阵和向量之间的乘法的(从英文命名可以分辨,v for vector, m for matrix)。函数头:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void caffe_cpu_gemv<float>(const CBLAS_TRANSPOSE TransA, const int M,
const int N, const float alpha, const float* A, const float* x,
const float beta, float* y) # 实现的功能类似 Y←αAX + βY
# 其中A的维度为 MxN
# X是一个向量,维度为 Nx1
# Y是结果 ,也是一个向量,维度为Mx1 const CBLAS_TRANSPOSE TransA # 是否对A进行转置 # 下面的参数很直观,不描述了
const int M
const int N
const float alpha
const float* A
const float* x
const float beta
float* y

绕回到具体的代码实现。。如何更新b?根据公式b的梯度直接就是delta

1
2
3
4
# 所以对应的代码其实就是将top_diff转置后就可以了(忽略乘上bias_multiplier这步)
caffe_cpu_gemv<Dtype>(CblasTrans, M_, N_, (Dtype)1., top_diff,
bias_multiplier_.cpu_data(), (Dtype)0.,
this->blobs_[1]->mutable_cpu_diff());

第三步是计算delta,对应公式

这里面可以忽略掉最后一项f’,因为在caffe实现中,这是由Relu layer来实现的,这里只需要实现括号里面的累加就好了,这个累加其实可以等价于矩阵乘法

1
2
3
4
5
6
7
caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasNoTrans, M_, K_, N_, (Dtype)1.,
top_diff, this->blobs_[0]->cpu_data(), (Dtype)0.,
(*bottom)[0]->mutable_cpu_diff()); # top_diff为\delta^(l+1)_j 维度 MxN
# this->blobs_[0]->cpu_data()为W^(l)_ji 维度 NxK
# (*bottom)[0]->mutable_cpu_diff()是要计算的结果,也就是\delta^(l)_i 维度是MxK

附录

又及,这里具体计算矩阵相乘用的是blas的功能,描述页面我参考的是:https://developer.apple.com/library/mac/documentation/Accelerate/Reference/BLAS_Ref/Reference/reference.html#//apple_ref/c/func/cblas_sgemm

Caffe源码阅读(1) 全连接层的更多相关文章

  1. caffe源码阅读

    参考网址:https://www.cnblogs.com/louyihang-loves-baiyan/p/5149628.html 1.caffe代码层次熟悉blob,layer,net,solve ...

  2. caffe源码阅读(1)_整体框架和简介(摘录)

    原文链接:https://www.zhihu.com/question/27982282 1.Caffe代码层次.回答里面有人说熟悉Blob,Layer,Net,Solver这样的几大类,我比较赞同. ...

  3. caffe源码阅读(1)-数据流Blob

    Blob是Caffe中层之间数据流通的单位,各个layer之间的数据通过Blob传递.在看Blob源码之前,先看一下CPU和GPU内存之间的数据同步类SyncedMemory:使用GPU运算时,数据要 ...

  4. caffe源码阅读(3)-Datalayer

    DataLayer是把数据从文件导入到网络的层,从网络定义prototxt文件可以看一下数据层定义 layer { name: "data" type: "Data&qu ...

  5. caffe源码阅读(2)-Layer

    神经网络是由层组成的,深度神经网络就是层数多了.layer对应神经网络的层.数据以Blob的形式,在不同的layer之间流动.caffe定义的神经网络已protobuf形式定义.例如: layer { ...

  6. caffe源码阅读(一)convert_imageset.cpp注释

    PS:本系列为本人初步学习caffe所记,由于理解尚浅,其中多有不足之处和错误之处,有待改正. 一.实现方法 首先,将文件名与它对应的标签用 std::pair 存储起来,其中first存储文件名,s ...

  7. caffe 源码阅读

    bvlc:Berkeley Vision and Learning Center. 1. 目录结构 models(四个文件夹均有四个文件构成,deploy.prototxt, readme.md, s ...

  8. caffe源码 全连接层

    图示全连接层 如上图所示,该全链接层输入n * 4,输出为n * 2,n为batch 该层有两个参数W和B,W为系数,B为偏置项 该层的函数为F(x) = W*x + B,则W为4 * 2的矩阵,B ...

  9. 源码阅读经验谈-slim,darknet,labelimg,caffe(1)

    本文首先谈自己的源码阅读体验,然后给几个案例解读,选的例子都是比较简单.重在说明我琢磨的点线面源码阅读方法.我不是专业架构师,是从一个深度学习算法工程师的角度来谈的,不专业的地方请大家轻拍. 经常看别 ...

随机推荐

  1. ceph PG数量调整/PG的状态说明

    优化: PG Number PG和PGP数量一定要根据OSD的数量进行调整,计算公式如下,但是最后算出的结果一定要接近或者等于一个2的指数.调整PGP不会引起PG内的对象的分裂,但是会引起PG的分布的 ...

  2. Maven依赖中的scope详解

    scope的分类 compile 默认就是compile,什么都不配置也就是意味着compile.compile表示被依赖项目需要参与当前项目的编译,当然后续的测试,运行周期也参与其中,是一个比较强的 ...

  3. Ant Trip HDU - 3018(欧拉路的个数 + 并查集)

    题意: Ant Tony和他的朋友们想游览蚂蚁国各地. 给你蚂蚁国的N个点和M条边,现在问你至少要几笔才能所有边都画一遍.(一笔画的时候笔不离开纸) 保证这M条边都不同且不会存在同一点的自环边. 也就 ...

  4. 【转】九大排序算法-C语言实现及详解

    概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存. 我们这里说说八大排序就是内部排序. 当n较大, ...

  5. 洛谷 P3299 [SDOI2013]保护出题人 解题报告

    P3299 [SDOI2013]保护出题人 题目描述 出题人铭铭认为给SDOI2012出题太可怕了,因为总要被骂,于是他又给SDOI2013出题了. 参加SDOI2012的小朋友们释放出大量的僵尸,企 ...

  6. 牛客网小白月赛5I区间(差分数组)

    链接:https://www.nowcoder.com/acm/contest/135/I来源:牛客网 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 32768K,其他语言65536 ...

  7. CodeForces - 669D

    题目链接:http://codeforces.com/problemset/problem/669/D Little Artem is fond of dancing. Most of all dan ...

  8. 面试 -- fragment生命周期

    Android 3.0 (Api 11)引入: Fragment具有重用,易适配(平板和手机之间的)优点: 依赖Activity,生命周期受到Activity的生命周期影响: fragment生命周期 ...

  9. [codevs3342][绿色通道]

    codevs3342 思路: 既然是要求最小化最长空题段,直接二分答案.然后就是check函数的写法. 先考虑n方转移,假设当前二分的答案是x,用f[i]表示前i个题,第i道题写的最小花费时间.最后再 ...

  10. MyEclipse中的Tomcat跑大项目时内存溢出:permgen space

    点击菜单栏的“Run”-"Run Configurations",在打开的窗口中点击“Arguments”选项卡. 在VM arguments中内容最下边(加上)输入:-Xms25 ...