今天来看一下全连接层的代码。首先,我们要知道全连接层在做什么。先来看一下caffe的官方文档,介绍如下:
可以看到,输入为n c h w,输出为n c_o 1 1
那么,它究竟做了什么那?
假设conv2的输入是256*27*27,那么conv2的输出即50*22*22,conv2的输入即
pool2的输入,pool2的输出为50*11*11,即ip1的输入,ip1的输出为500*1*1,那
么pool2->ip1的参数个数是多少呢?这里就要理解好什么是fully_connected了,
即wTx,x为列向量,w的长度与x相同。在本文的例子中x的维度为50*11*11,那么
pool2->ip1的参数个数为500*50*11*11 。50*11*11即是一个有50个通道大小为
11*11的图片,那么在做完全卷积的时候,需要把对所有通道一起作卷积,即把图
片转化成一个50*11*11的向量
上段话转自http://www.cnblogs.com/dupuleng/articles/4312149.html
我们再来看一下它的头文件,其中有以下参数:
同大部分layer层一样,它也必须实现setup、reshape、Forward_cpu、Backward_cpu。
* 其中,setup定义参数:M_样本个数、K_单个样本特征长度、N_全连接之后神经元的个数。
* Forward_cpu,主要是计算y=W’*x + b, X表示输入,y表示输出 。x为输入,维度 M_*K_ 、 y为输出,维度 M_*N_ 、W为权重,维度 N_*K_, W_diff权重的梯度维度也为N_*K_ 、 b为偏置,维度 N_*1_ 。
* Backward_cpu:反向传播就是在更新w、b,计算delta。
我们来具体看一下代码:
#include <vector>
#include "caffe/blob.hpp"
#include "caffe/common.hpp"
#include "caffe/filler.hpp"
#include "caffe/layer.hpp"
#include "caffe/util/math_functions.hpp"
#include "caffe/vision_layers.hpp"
namespace caffe {
/*
输入层:(M_, N_, 1, 1);
输出层: (M_, K_, 1, 1);
W矩阵:(N_,K_,1,1);
b矩阵:(N_,1,1,1);
M_样本个数,K_单个样本特征长度,N_全连接之后神经元的个数。
*/
template <typename Dtype>
void InnerProductLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
//通过读取配置proto文件获得输出神经元的个数及是否使用偏置项
const int num_output = this->layer_param_.inner_product_param().num_output();
bias_term_ = this->layer_param_.inner_product_param().bias_term();
//全连接之后输出的神经元的个数
N_ = num_output;
//全连接层输出的Blob维数为样本的个数*输出神经元的个数*1*1(M*N)
//这里axis=1,即从C开始展开,即,CHW
//输出:n_1 * (c_1 + c_2 + ... + c_K) * h * w
const int axis = bottom[0]->CanonicalAxisIndex(
this->layer_param_.inner_product_para