简谈caffe中的LRN层

article/2025/9/16 0:11:55

昨日面试,被问道BN层和LRN层,一直以来用的都是bn,所以当时对LRN只剩下点印象,事后弥补了一下这边知识点的不足,浅谈自己对LRN层的理解,如果误导,欢迎指正.

LRN层的作用是对局部区域进行归一化,对响应比较大的值变得更大,对响应比较小的值抑制.先看一下caffe的代码

可以看到四个参数.

layer {name: "norm1"type: "LRN"bottom: "conv1"top: "norm1"lrn_param {//  norm_region:ACROSS_CHANNELS,表示相邻通道求和归一化,局部区域块形状为//local_size*1*1,如果是WITHIN_CHANNEL表示一个通道内部求和归一,局部区域形状为//1*local_zie*local_size// local_size: 5//默认值5,如果是跨通道的,表示求和的通道数;如果在通道内,表示求和正方形区域长度alpha: 0.0001//公式的参数,默认为1beta: 0.75//公式的参数,默认为5}
}

归一化时,每个输入值都将除以,公式里对应的公式就是参数里对应的值,当然,后面的caffe版本中1是K,也是一个参数值,看看源码.在caffe/src/caffe/layers/lrn_layer.cpp中,代码上有对应的注释

#include <vector>#include "caffe/layers/lrn_layer.hpp"
#include "caffe/util/math_functions.hpp"namespace caffe {template <typename Dtype>
void LRNLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top) {size_ = this->layer_param_.lrn_param().local_size();//提取size_的值CHECK_EQ(size_ % 2, 1) << "LRN only supports odd values for local_size";//检查是否为奇数,不是则报错pre_pad_ = (size_ - 1) / 2; //计算pad值,前后各补一半0alpha_ = this->layer_param_.lrn_param().alpha();//提取alphabeta_ = this->layer_param_.lrn_param().beta();//提取betak_ = this->layer_param_.lrn_param().k();//提取kif (this->layer_param_.lrn_param().norm_region() ==//如果是within_channel模式,还需要初始化一系列中间层,暂且不关注LRNParameter_NormRegion_WITHIN_CHANNEL) {// Set up split_layer_ to use inputs in the numerator and denominator.split_top_vec_.clear();split_top_vec_.push_back(&product_input_);split_top_vec_.push_back(&square_input_);LayerParameter split_param;split_layer_.reset(new SplitLayer<Dtype>(split_param));split_layer_->SetUp(bottom, split_top_vec_);// Set up square_layer_ to square the inputs.square_bottom_vec_.clear();square_top_vec_.clear();square_bottom_vec_.push_back(&square_input_);square_top_vec_.push_back(&square_output_);LayerParameter square_param;square_param.mutable_power_param()->set_power(Dtype(2));square_layer_.reset(new PowerLayer<Dtype>(square_param));square_layer_->SetUp(square_bottom_vec_, square_top_vec_);// Set up pool_layer_ to sum over square neighborhoods of the input.pool_top_vec_.clear();pool_top_vec_.push_back(&pool_output_);LayerParameter pool_param;pool_param.mutable_pooling_param()->set_pool(PoolingParameter_PoolMethod_AVE);pool_param.mutable_pooling_param()->set_pad(pre_pad_);pool_param.mutable_pooling_param()->set_kernel_size(size_);pool_layer_.reset(new PoolingLayer<Dtype>(pool_param));pool_layer_->SetUp(square_top_vec_, pool_top_vec_);// Set up power_layer_ to compute (1 + alpha_/N^2 s)^-beta_, where s is// the sum of a squared neighborhood (the output of pool_layer_).power_top_vec_.clear();power_top_vec_.push_back(&power_output_);LayerParameter power_param;power_param.mutable_power_param()->set_power(-beta_);power_param.mutable_power_param()->set_scale(alpha_);power_param.mutable_power_param()->set_shift(Dtype(1));power_layer_.reset(new PowerLayer<Dtype>(power_param));power_layer_->SetUp(pool_top_vec_, power_top_vec_);// Set up a product_layer_ to compute outputs by multiplying inputs by the// inverse demoninator computed by the power layer.product_bottom_vec_.clear();product_bottom_vec_.push_back(&product_input_);product_bottom_vec_.push_back(&power_output_);LayerParameter product_param;EltwiseParameter* eltwise_param = product_param.mutable_eltwise_param();eltwise_param->set_operation(EltwiseParameter_EltwiseOp_PROD);product_layer_.reset(new EltwiseLayer<Dtype>(product_param));product_layer_->SetUp(product_bottom_vec_, top);}
}template <typename Dtype>
void LRNLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top) {CHECK_EQ(4, bottom[0]->num_axes()) << "Input must have 4 axes, "<< "corresponding to (num, channels, height, width)";num_ = bottom[0]->num();channels_ = bottom[0]->channels();height_ = bottom[0]->height();width_ = bottom[0]->width();//初始化n\c\h\w的值switch (this->layer_param_.lrn_param().norm_region()) {//看参数normal_region选的是什么case LRNParameter_NormRegion_ACROSS_CHANNELS://如果是通道间归一化top[0]->Reshape(num_, channels_, height_, width_);//将top尺寸和bottom尺寸设置成一样大,这样归一化时,只需要
//scale_值乘bottom值就可以得到相应的top值scale_.Reshape(num_, channels_, height_, width_);break;case LRNParameter_NormRegion_WITHIN_CHANNEL://如果是空间区域归一化split_layer_->Reshape(bottom, split_top_vec_);square_layer_->Reshape(square_bottom_vec_, square_top_vec_);pool_layer_->Reshape(square_top_vec_, pool_top_vec_);power_layer_->Reshape(pool_top_vec_, power_top_vec_);product_layer_->Reshape(product_bottom_vec_, top);break;}
}template <typename Dtype>
void LRNLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top) {switch (this->layer_param_.lrn_param().norm_region()) {case LRNParameter_NormRegion_ACROSS_CHANNELS:CrossChannelForward_cpu(bottom, top);break;case LRNParameter_NormRegion_WITHIN_CHANNEL:WithinChannelForward(bottom, top);break;default:LOG(FATAL) << "Unknown normalization region.";}
}template <typename Dtype>
void LRNLayer<Dtype>::CrossChannelForward_cpu(
const vector<Blob<Dtype>*>& bottom, vector<Blob<Dtype>*>* top) {
const Dtype* bottom_data = bottom[0]->cpu_data();
Dtype* top_data = (*top)[0]->mutable_cpu_data();
Dtype* scale_data = scale_.mutable_cpu_data();//用指针获取每个Blob对象的内存地址,便于后面操作
// start with the constant value
for (int i = 0; i < scale_.count(); ++i) {//初始化值为1.0
scale_data[i] = 1.;
}
Blob<Dtype> padded_square(1, channels_ + size_ - 1, height_, width_);//补零后的Blob,第三维尺寸比bottom大了size_ - 1;
Dtype* padded_square_data = padded_square.mutable_cpu_data();
caffe_set(padded_square.count(), Dtype(0), padded_square_data);//先清零
Dtype alpha_over_size = alpha_ / size_;//预先计算公式中的alpha/n
// go through the images
for (int n = 0; n < num_; ++n) {//bottom的第四维尺寸num_,需要分解为单个来做归一化
// compute the padded square
caffe_sqr(channels_ * height_ * width_,bottom_data + bottom[0]->offset(n),padded_square_data + padded_square.offset(0, pre_pad_));//计算bottom的平方,放入padded_square矩阵中,前pre_pad_个位置依旧0
// Create the first channel scale
for (int c = 0; c < size_; ++c) {//对n个通道平方求和并乘以预先算好的(alpha/n),累加至scale_中(实现计算 1 + sum_under_i(x_i^2))caffe_axpy<Dtype>(height_ * width_, alpha_over_size,padded_square_data + padded_square.offset(0, c),scale_data + scale_.offset(n, 0));
}
for (int c = 1; c < channels_; ++c) {//这里使用了类似FIFO的形式计算其余scale_参数,每次向后移动一个单位,加头去尾,避免重复计算求和// copy previous scalecaffe_copy<Dtype>(height_ * width_,scale_data + scale_.offset(n, c - 1),scale_data + scale_.offset(n, c));// add headcaffe_axpy<Dtype>(height_ * width_, alpha_over_size,padded_square_data + padded_square.offset(0, c + size_ - 1),scale_data + scale_.offset(n, c));// subtract tailcaffe_axpy<Dtype>(height_ * width_, -alpha_over_size,padded_square_data + padded_square.offset(0, c - 1),scale_data + scale_.offset(n, c));
}
}// In the end, compute output
caffe_powx<Dtype>(scale_.count(), scale_data, -beta_, top_data);//计算求指数,由于将除法转换为乘法,故指数变负
caffe_mul<Dtype>(scale_.count(), top_data, bottom_data, top_data);//bottom .* scale_ -> top
}template <typename Dtype>
void LRNLayer<Dtype>::WithinChannelForward(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {split_layer_->Forward(bottom, split_top_vec_);square_layer_->Forward(square_bottom_vec_, square_top_vec_);pool_layer_->Forward(square_top_vec_, pool_top_vec_);power_layer_->Forward(pool_top_vec_, power_top_vec_);product_layer_->Forward(product_bottom_vec_, top);
}template <typename Dtype>
void LRNLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {switch (this->layer_param_.lrn_param().norm_region()) {case LRNParameter_NormRegion_ACROSS_CHANNELS:CrossChannelBackward_cpu(top, propagate_down, bottom);break;case LRNParameter_NormRegion_WITHIN_CHANNEL:WithinChannelBackward(top, propagate_down, bottom);break;default:LOG(FATAL) << "Unknown normalization region.";}
}template <typename Dtype>
void LRNLayer<Dtype>::CrossChannelBackward_cpu(const vector<Blob<Dtype>*>& top, const vector<bool>& propagate_down,const vector<Blob<Dtype>*>& bottom) {const Dtype* top_diff = top[0]->cpu_diff();const Dtype* top_data = top[0]->cpu_data();const Dtype* bottom_data = bottom[0]->cpu_data();const Dtype* scale_data = scale_.cpu_data();Dtype* bottom_diff = bottom[0]->mutable_cpu_diff();Blob<Dtype> padded_ratio(1, channels_ + size_ - 1, height_, width_);Blob<Dtype> accum_ratio(1, 1, height_, width_);Dtype* padded_ratio_data = padded_ratio.mutable_cpu_data();Dtype* accum_ratio_data = accum_ratio.mutable_cpu_data();// We hack a little bit by using the diff() to store an additional resultDtype* accum_ratio_times_bottom = accum_ratio.mutable_cpu_diff();caffe_set(padded_ratio.count(), Dtype(0), padded_ratio_data);Dtype cache_ratio_value = 2. * alpha_ * beta_ / size_;caffe_powx<Dtype>(scale_.count(), scale_data, -beta_, bottom_diff);caffe_mul<Dtype>(scale_.count(), top_diff, bottom_diff, bottom_diff);// go through individual dataint inverse_pre_pad = size_ - (size_ + 1) / 2;for (int n = 0; n < num_; ++n) {int block_offset = scale_.offset(n);// first, compute diff_i * y_i / s_icaffe_mul<Dtype>(channels_ * height_ * width_,top_diff + block_offset, top_data + block_offset,padded_ratio_data + padded_ratio.offset(0, inverse_pre_pad));caffe_div<Dtype>(channels_ * height_ * width_,padded_ratio_data + padded_ratio.offset(0, inverse_pre_pad),scale_data + block_offset,padded_ratio_data + padded_ratio.offset(0, inverse_pre_pad));// Now, compute the accumulated ratios and the bottom diffcaffe_set(accum_ratio.count(), Dtype(0), accum_ratio_data);for (int c = 0; c < size_ - 1; ++c) {caffe_axpy<Dtype>(height_ * width_, 1.,padded_ratio_data + padded_ratio.offset(0, c), accum_ratio_data);}for (int c = 0; c < channels_; ++c) {caffe_axpy<Dtype>(height_ * width_, 1.,padded_ratio_data + padded_ratio.offset(0, c + size_ - 1),accum_ratio_data);// compute bottom diffcaffe_mul<Dtype>(height_ * width_,bottom_data + top[0]->offset(n, c),accum_ratio_data, accum_ratio_times_bottom);caffe_axpy<Dtype>(height_ * width_, -cache_ratio_value,accum_ratio_times_bottom, bottom_diff + top[0]->offset(n, c));caffe_axpy<Dtype>(height_ * width_, -1.,padded_ratio_data + padded_ratio.offset(0, c), accum_ratio_data);}}
}template <typename Dtype>
void LRNLayer<Dtype>::WithinChannelBackward(const vector<Blob<Dtype>*>& top, const vector<bool>& propagate_down,const vector<Blob<Dtype>*>& bottom) {if (propagate_down[0]) {vector<bool> product_propagate_down(2, true);product_layer_->Backward(top, product_propagate_down, product_bottom_vec_);power_layer_->Backward(power_top_vec_, propagate_down, pool_top_vec_);pool_layer_->Backward(pool_top_vec_, propagate_down, square_top_vec_);square_layer_->Backward(square_top_vec_, propagate_down,square_bottom_vec_);split_layer_->Backward(split_top_vec_, propagate_down, bottom);}
}#ifdef CPU_ONLY
STUB_GPU(LRNLayer);
STUB_GPU_FORWARD(LRNLayer, CrossChannelForward);
STUB_GPU_BACKWARD(LRNLayer, CrossChannelBackward);
#endifINSTANTIATE_CLASS(LRNLayer);}  // namespace caffe

 


http://chatgpt.dhexx.cn/article/Z7qLhPKG.shtml

相关文章

LRN(Local Response Normalization)局部归一化分析

其中LRN的公式如下&#xff1a; 论文中说 Denoting by aix,y the activity of a neuron computed by applying kernel i at position (x, y) and then applying the ReLU nonlinearity, the response-normalized activity bix,y is given by the expression LRN是用在激活之后…

LRN学习笔记

LRN&#xff08;Local Response Normalization) 作用&#xff1a;将不同卷积核RELU后的feature归一化&#xff0c;平滑处理&#xff0c;能增加泛化能力。 原因&#xff1a;生物方面&#xff0c;玄学 Alexnet提出 公式&#xff1a; 其中i代表第i个卷积核 a x , y i , 表 示 第…

LRN(局部响应归一化)

原理&#xff1a; LRN层模仿了生物神经系统的“侧抑制”机制&#xff0c;对局部神经元的活动创建竞争环境&#xff0c;使得其中响应比较大的值变得相对更大&#xff0c;并抑制其他反馈较小的神经元&#xff0c;增强模型的泛化能力。 LRN对于ReLU这种没有上限边界的激活函数会比…

LRN局部响应标准化

全称&#xff1a;local response normalization最早来源于论文AlexNet中提到的&#xff1a;i&#xff1a;代表下标&#xff0c;你要计算像素值的下标&#xff0c;从0计算起 j&#xff1a;平方累加索引&#xff0c;代表从j&#xff5e;i的像素值平方求和 x,y&#xff1a;像素的位…

tensorflow下的局部响应归一化函数tf.nn.lrn

1、其中LRN就是局部响应归一化&#xff1a; 这个技术主要是深度学习训练时的一种提高准确度的技术方法。其中caffe、tensorflow等里面是很常见的方法&#xff0c;其跟激活函数是有区别的&#xff0c;LRN一般是在激活、池化后进行的一中处理方法。 AlexNet将LeNet的思想发扬光大…

局部响应归一化(Local Response Normalization,LRN)和批量归一化(Batch Normalization,BN)的区别

为什么要归一化&#xff1f; 归一化已成为深度神经网络中的一个重要步骤&#xff0c;它可以弥补ReLU、ELU等激活函数无界性的问题。有了这些激活函数&#xff0c;输出层就不会被限制在一个有限的范围内(比如tanh的 [ − 1 , 1 ] [-1,1] [−1,1])&#xff0c;而是可以根据训练需…

LRN 局部响应归一化层(Local Response Normalization)

局部响应归一化层&#xff08;Local Response Normalization&#xff09; 局部响应归一化层简称LRN&#xff0c;是在深度学习中提高准确度的技术方法。一般是在激活、池化后进行的一中处理方法&#xff0c;因在Alexnet中运用到&#xff0c;故做一下整理。 为什么要引入LRN层&a…

AlexNet中的LRN(Local Response Normalization)

神经网络初学者&#xff0c;没有什么理论基础&#xff0c;偶然看到个ImageNet&#xff0c;就准备从其入手&#xff0c;先弄懂每层的含义&#xff0c;其中这个LRN层真是让人百思不得其解&#xff0c;搜索了下&#xff0c;给出的介绍比较少。为什么会比较少呢&#xff0c;搜索到最…

Alexnet LRN层和conv2局部连接

LRN&#xff08;local response norm&#xff09;局部归一 AlexNet本文出自NIPS2012&#xff0c;作者是大神Alex Krizhevsky&#xff0c;属于多伦多大学Hinton组。当年取得了ImageNet最好成绩&#xff0c;也是在那年之后&#xff0c;更多的更深的神经网路被提出&#xff0c;比…

tensorflow中的lrn函数详解

LRN函数类似DROPOUT和数据增强作为relu激励之后防止数据过拟合而提出的一种处理方法,全称是 local response normalization--局部响应标准化。这个函数很少使用&#xff0c;基本上被类似DROPOUT这样的方法取代&#xff0c;具体原理还是值得一看的 函数原型 def lrn(input, de…

CNN中的LRN层

LRN层是按下述公式计算的&#xff1a;&#xff08;用处不大 可被dropout normalization替代&#xff09; 转自&#xff1a;https://blog.csdn.net/searobbers_duck/article/details/51645941

ImageNet 中的 LRN

LRN&#xff08;Local Response Normalization&#xff09; LRN 神经网络初学者&#xff0c;没有什么理论基础&#xff0c;偶然看到个ImageNet&#xff0c;就准备从其入手&#xff0c;先弄懂每层的含义&#xff0c;其中这个LRN层真是让人百思不得其解&#xff0c;搜索了下&am…

LRN局部响应归一化

这个技术主要是深度学习训练时的一种提高准确度的技术方法。其中caffe、tensorflow等里面是很常见的方法&#xff0c;其跟激活函数是有区别的&#xff0c;LRN一般是在激活、池化后进行的一中处理方法。   AlexNet将LeNet的思想发扬光大&#xff0c;把CNN的基本原理应用到了很…

深度神经网络中的局部响应归一化LRN简介及实现

Alex、Hinton等人在2012年的NIPS论文《ImageNet Classification with Deep Convolutional Neural Networks》中将LRN应用于深度神经网络中(AlexNet)。论文见&#xff1a;http://www.cs.toronto.edu/~hinton/absps/imagenet.pdf &#xff0c;截图如下&#xff1a; 公式解释&…

LRN

发展时间点 局部响应归一化这个方法流行于2012年的 AlexNet网络&#xff0c;它将这种方法付诸实践&#xff0c;验证了它的可行性。在caffe框架和tensorflow框架中&#xff0c;这都是经常和卷积、池化配合使用的方法。 作用时间点&#xff1a;LRN一般是在激活、池化后进行的一中…

LRN (Local Response Normalization,即局部响应归一化层)

LRN (Local Response Normalization&#xff0c;即局部响应归一化层) &#xff08;一&#xff09;先看看归一化吧 什么是归一化&#xff1f; 归一化化是归纳统一样本的统计分布性。就是要把你需要处理的数据经过处理后&#xff08;通过某种算法&#xff09;限制在你需要的一定范…

详解LRN(local response normalization--局部响应标准化)缓解过拟合

局部响应归一化层&#xff08;Local Response Normalization&#xff09; LRN全称为Local Response Normalization&#xff0c;即局部响应归一化层&#xff0c;LRN函数类似Dropout和数据增强作为relu激活函数之后防止数据过拟合而提出的一种处理方法。这个函数很少使用&#xf…

局部响应归一化LRN (Local Response Normalization)

一、LRN技术介绍&#xff1a; LRN&#xff08;Local Response Normalization&#xff09; 是一种提高深度学习准确度的技术方法。 LRN 一般是在激活、 池化函数后的一种方法。在 ALexNet 中&#xff0c; 提出了 LRN 层&#xff0c; 对局部神经元的活动创建竞争机制&#xff0c…

深度学习饱受争议的局部响应归一化(LRN)详解

前言&#xff1a;Local Response Normalization(LRN)技术主要是深度学习训练时的一种提高准确度的技术方法。其中caffe、tensorflow等里面是很常见的方法&#xff0c;其跟激活函数是有区别的&#xff0c;LRN一般是在激活、池化后进行的一种处理方法。LRN归一化技术首次在AlexNe…

压缩算法之算术编码浅析与实现

压缩算法之算术编码浅析与实现 简介实现思路实现代码参考资料 简介 算术编码&#xff0c;属于熵编码的范畴&#xff0c;常用于各种信息压缩场合&#xff0c;如图像、视频、音频压缩领域。 基本原理&#xff1a; 核心原则&#xff1a;出现频率高的信息&#xff0c;分配少的比特…