我看了一下caffe里关于全连接层的实现,里面是这样写的:
// num_output:输出个数,对应VGG-16也就是4096
const int num_output = this->layer_param_.inner_product_param().num_output();
bias_term_ = this->layer_param_.inner_product_param().bias_term();
transpose_ = this->layer_param_.inner_product_param().transpose();
N_ = num_output;
const int axis = bottom[0]->CanonicalAxisIndex(
this->layer_param_.inner_product_param().axis());
// 从下面这段注释中可以看到,从axis开始的像素会被flattened成一个K_维的向量,
// 这里也说了,如果输入的尺寸是(batch_size, channel, height, weight),那么
// 一共会做batch_size次内积,每次做内积的是权值矩阵和一个channel*height*weight维的向量
// Dimensions starting from "axis" are "flattened" into a single
// length K_ vector. For example, if bottom[0]'s shape is (N, C, H, W),
// and axis == 1, N inner products with dimension CHW are performed.
// K_指的是输入全连接层的像素总数
K_ = bottom[0]->count(axis);
// Check if we need to set up the weights
if (this->blobs_.size() > 0) {
LOG(INFO) << "Skipping parameter initialization";
} else {
if (bias_term_) {
this->blobs_.resize(2);
} else {
this->blobs_.resize(1);
}
// Initialize the weights
// 从这里开始初始化全连接层的权值矩阵了
vector<int> weight_shape(2);
if (transpose_) {
weight_shape[0] = K_;
weight_shape[1] = N_;
} else {
weight_shape[0] = N_;
weight_shape[1] = K_;
}
// 很明显的,全连接层的矩阵是一个K_*N_或者N_*K_的矩阵对应到你说的VGG-16的例子,最后一个max-pooling层输出的是一个512*7*7(乘积是25088)的特征图,那么如果是caffe,这个特征图进入全连接层之后就会被flatten成一个25088维的向量,同时这个全连接层会初始化一个25088*4096的权值矩阵,这两个进行矩阵乘积运算,最后输出一个4096的一维向量。我大概知道你说的全局卷积是什么意思,核心思想应该是一致的,你可能指的是fully convolution?如果是fully convolution,那么你这种说法就没什么错误,但是毕竟你的专栏是面对小白,把全连接转为卷积运算可能会让人摸不着头脑,当然这是从我的角度。我个人觉得直接说把一个多维的Tensor直接平摊成一个一维的vector,然后送到全连接里会更加容易让人理解,尤其是小白,毕竟框架就是这么实现的。 |