池化层在深度学习网络中的作用一般是用来缓解卷积层对位置的过度敏感性.池化层每次对输入数据的一个固定形状窗口(池化窗口的大小为pooling height, pooling width)中的元素计算输出,池化层直接计算池化窗口内元素的最大值或者平均值。该运算也分别叫做最大池化或平均池化。
实现如下:
void MaxPoolingLayer::Forwards(const std::vector<std::shared_ptr<Tensor<float>>> &inputs,
std::vector<std::shared_ptr<Tensor<float>>> &outputs) {
CHECK(this->op_ != nullptr); // 判断operator是否为空,是否类型正确
CHECK(this->op_->op_type_ == OpType::kOperatorMaxPooling);
CHECK(!inputs.empty());
// 获取到池化相关的属性
const uint32_t padding_h = this->op_->padding_height();
const uint32_t padding_w = this->op_->padding_width();
const uint32_t kernel_h = this->op_->pooling_height();
const uint32_t kernel_w = this->op_->pooling_width();
const uint32_t stride_h = this->op_->stride_height();
const uint32_t stride_w = this->op_->stride_width();
// 取得输入的批次数量
const uint32_t batch_size = inputs.size();
for (uint32_t i = 0; i < batch_size; ++i) {
const std::shared_ptr<Tensor<float>> &input_data_ = inputs.at(i)->Clone();
// 如果padding_h padding_w不为0的话,就做一个填充操作,周围填充一圈负无穷
input_data_->Padding({padding_h, padding_h, padding_w, padding_w}, std::numeric_limits<float>::lowest());
// 获得输入特征图的大小、通道数量
const uint32_t input_h = input_data_->rows();
const uint32_t input_w = input_data_->cols();
const uint32_t input_c = input_data_->channels();
const uint32_t output_c = input_c;
// input_h 输入的高度
// input_w 输入的宽度
// kernel_h 窗口的高度
// kernel_w 窗口的宽度
// 计算输出特征图的大小
const uint32_t output_h = uint32_t(std::floor((input_h - kernel_h) / stride_h + 1));
const uint32_t output_w = uint32_t(std::floor((input_w - kernel_w) / stride_w + 1));
CHECK(output_w > 0 && output_h > 0);
std::shared_ptr<Tensor<float>> output_data = std::make_shared<Tensor<float>>(output_c, output_h, output_w);
for (uint32_t ic = 0; ic < input_c; ++ic) {
const arma::fmat &input_channel = input_data_->at(ic);
// 池化操作也是逐个通道进行的
arma::fmat &output_channel = output_data->at(ic);
// 计算过程 行上的
for (uint32_t r = 0; r < input_h - kernel_h + 1; r += stride_h) {
// 列上的
for (uint32_t c = 0; c < input_w - kernel_w + 1; c += stride_w) {
// 已知窗口开始位置和结束位置的时候,取得这一块区域
const arma::fmat ®ion = input_channel.submat(r, c, r + kernel_h - 1, c + kernel_w - 1);
// 取合围范围内的最大值
output_channel.at(int(r / stride_h), int(c / stride_w)) = region.max();
}
}
}
其上分为三个部分:
1 获取信息:
要获得stride , padding_height , padding_width
CHECK(this->op_ != nullptr); // 判断operator是否为空,是否类型正确
CHECK(this->op_->op_type_ == OpType::kOperatorMaxPooling);
CHECK(!inputs.empty());
// 获取到池化相关的属性
const uint32_t padding_h = this->op_->padding_height();
const uint32_t padding_w = this->op_->padding_width();
const uint32_t kernel_h = this->op_->pooling_height();
const uint32_t kernel_w = this->op_->pooling_width();
const uint32_t stride_h = this->op_->stride_height();
const uint32_t stride_w = this->op_->stride_width();
2 如果需要池化,则需要在周围填充一圈负无穷:
先获取batch_size的大小,在每个batch上进行池化。
// 取得输入的批次数量
const uint32_t batch_size = inputs.size();
for (uint32_t i = 0; i < batch_size; ++i) {
const std::shared_ptr<Tensor<float>> &input_data_ = inputs.at(i)->Clone();
// 如果padding_h padding_w不为0的话,就做一个填充操作,周围填充一圈负无穷
input_data_->Padding({padding_h, padding_h, padding_w, padding_w}, std::numeric_limits<float>::lowest());
如果padding_h padding_w不为0的话,就做一个填充操作,周围填充一圈负无穷:
Padding函数如下:
padded_mat.fill((float) padding_value);
padded_mat.submat(pad_rows1, pad_cols1, pad_rows1 + sub_mat.n_rows - 1,
pad_cols1 + sub_mat.n_cols - 1) = sub_mat;
3 获取特征图的大小,在每个通道上进行池化操作。
// 获得输入特征图的大小、通道数量
const uint32_t input_h = input_data_->rows();
const uint32_t input_w = input_data_->cols();
const uint32_t input_c = input_data_->channels();
const uint32_t output_c = input_c;
// input_h 输入的高度
// input_w 输入的宽度
// kernel_h 窗口的高度
// kernel_w 窗口的宽度
// 计算输出特征图的大小
const uint32_t output_h = uint32_t(std::floor((input_h - kernel_h) / stride_h + 1));
const uint32_t output_w = uint32_t(std::floor((input_w - kernel_w) / stride_w + 1));
CHECK(output_w > 0 && output_h > 0);
std::shared_ptr<Tensor<float>> output_data = std::make_shared<Tensor<float>>(output_c, output_h, output_w);
for (uint32_t ic = 0; ic < input_c; ++ic) {
const arma::fmat &input_channel = input_data_->at(ic);
// 池化操作也是逐个通道进行的
arma::fmat &output_channel = output_data->at(ic);
// 计算过程 行上的
for (uint32_t r = 0; r < input_h - kernel_h + 1; r += stride_h) {
// 列上的
for (uint32_t c = 0; c < input_w - kernel_w + 1; c += stride_w) {
// 已知窗口开始位置和结束位置的时候,取得这一块区域
const arma::fmat ®ion = input_channel.submat(r, c, r + kernel_h - 1, c + kernel_w - 1);
// 取合围范围内的最大值
output_channel.at(int(r / stride_h), int(c / stride_w)) = region.max();
}
}
}
4 将outputdata放入我们的输出ector中,每个outputdata代表一个batch的输出特征层。
outputs.push_back(output_data);
5 最后将maxpooling的id和方法结合为map,insert到我们的registry中
LayerRegistererWrapper kMaxPoolingLayer(OpType::kOperatorMaxPooling, MaxPoolingLayer::CreateInstance);