深度神经网络中的局部响应归一化LRN简介及实现
Alex、Hinton等人在2012年的NIPS論文《ImageNet Classification with Deep Convolutional Neural Networks》中將LRN應用于深度神經網絡中(AlexNet)。論文見:http://www.cs.toronto.edu/~hinton/absps/imagenet.pdf ,截圖如下:
公式解釋:
:ReLU處理后的神經元,作為LRN的輸入;
:LRN的輸出,LRN處理后的神經元;
N:kernal總數或通道數;
k、n、、:為常量,是超參數,k類似于bias,n對應于Caffe中的local_size,在論文中這幾個值分別為2、5、、0.75。
LRN(Local Response Normalization):局部響應歸一化,此層實現了” lateral inhibition”(側抑制),通過對局部輸入區域進行歸一化來執行一種”側抑制”。在AlexNet中,處理ReLU神經元時,LRN很有用,因為ReLU的響應結果是無界的,可以非常大,所以需要歸一化。當處理具有無限激活(unbounded activation)的神經元時(如ReLU),可以通過LRN對其歸一化(normalize),因為它允許檢測具有大神經元響應的高頻特征(high-frequency features),同時衰減局部周圍(local neighborhood)均勻大(uniformly large)的響應。它是一種正則化類型。一般應用在激活、池化后進行的一種處理方法。該層的輸出維數始終等于輸入維數。
在神經生物學有一個概念叫做側抑制(lateral inhibition),指的是被激活的神經元抑制相鄰的神經元。歸一化的目的是”抑制”,局部響應歸一化就是借鑒側抑制的思想來實現局部抑制。LRN層模仿生物神經系統的側抑制機制,對局部神經元的活動創建競爭機制,使得其中響應比較大的值變得相對更大,并抑制其它反饋較小的神經元,增強了模型泛化能力。
后來研究者發現LRN起不到太大作用,LRN的作用已被正則化技術(regularization techniques,例如dropout and batch normalization)、更好的初始化和訓練方法所取代。
在Caffe的caffe.proto中,LRN參數內容如下:分為通道間歸一化(local_size*1*1)和通道內歸一化(1*local_size*local_size)
// Message that stores parameters used by LRNLayer
message LRNParameter {optional uint32 local_size = 1 [default = 5];optional float alpha = 2 [default = 1.];optional float beta = 3 [default = 0.75];enum NormRegion {ACROSS_CHANNELS = 0;WITHIN_CHANNEL = 1;}optional NormRegion norm_region = 4 [default = ACROSS_CHANNELS];optional float k = 5 [default = 1.];enum Engine {DEFAULT = 0;CAFFE = 1;CUDNN = 2;}optional Engine engine = 6 [default = DEFAULT];
}
各參數介紹見:http://caffe.berkeleyvision.org/tutorial/layers/lrn.html
注:以上內容主要來自網絡整理。
以下是實現的測試代碼,僅實現通道間歸一化,包括C++和tensorflow:
tensorflow的實現如下:
import tensorflow as tf
import numpy as np
x = np.array([i for i in range(1, 33)]).reshape([2, 2, 2, 4])
y = tf.nn.lrn(input=x, depth_radius=2, bias=1, alpha=1, beta=0.75)
print("input:\n", x)
print("output:\n", y)
C++代碼實現如下:
lrn.hpp:
#ifndef FBC_NN_LRN_HPP_
#define FBC_NN_LRN_HPP_namespace ANN {enum class NormRegion {ACROSS_CHANNEL = 0,WITHIN_CHANNEL
};template<typename T = float>
class LRN {
public:LRN() = default;LRN(unsigned int local_size, T alpha, T beta, T bias, NormRegion norm_region) :local_size_(local_size), alpha_(alpha), beta_(beta), bias_(bias), norm_region_(norm_region) {}int run(const T* input, int batch, int channel, int height, int width, T* output) const;private:int across_channel(const T* input, int batch, int channel, int height, int width, T* output) const;int within_channel(const T* input, int batch, int channel, int height, int width, T* output) const;unsigned int local_size_ = 5; // nT alpha_ = 1.;T beta_ = 0.75;T bias_ = 1.; // kNormRegion norm_region_ = NormRegion::ACROSS_CHANNEL;
};} // namespace ANN#endif // FBC_NN_LRN_HPP_
lrn.cpp:
#include "lrn.hpp"
#include <algorithm>
#include <cmath>namespace ANN {template<typename T>
int LRN<T>::run(const T* input, int batch, int channel, int height, int width, T* output) const
{if (norm_region_ == NormRegion::ACROSS_CHANNEL)return across_channel(input, batch, channel, height, width, output);elsereturn within_channel(input, batch, channel, height, width, output);
}template<typename T>
int LRN<T>::across_channel(const T* input, int batch, int channel, int height, int width, T* output) const
{int size = channel * height * width;for (int p = 0; p < batch; ++p) {const T* in = input + size * p;T* out = output + size * p;// N = channel; n = local_size_; k = bias_for (int i = 0; i < channel; ++i) {for (int y = 0; y < height; ++y) {for (int x = 0; x < width; ++x) {T tmp = 0;for (int j = std::max(0, static_cast<int>(i - local_size_ / 2)); j <= std::min(channel - 1, static_cast<int>(i + local_size_ / 2)); ++j) {tmp += std::pow(in[j * height * width + width * y + x], 2);}out[i * height * width + width * y + x] = in[i * height * width + width * y + x] / std::pow(bias_ + alpha_ * tmp, beta_);}}}}return 0;
}template<typename T>
int LRN<T>::within_channel(const T* input, int batch, int channel, int height, int width, T* output) const
{fprintf(stderr, "not implemented\n");return -1;
}template class LRN<float>;} // namespace ANN
test_lrn.cpp:
int test_lrn()
{int batch = 2, channel = 4, height = 2, width = 2;std::vector<float> input{ 1., 5., 9., 13., 2., 6., 10., 14., 3., 7., 11., 15., 4., 8., 12., 16.,17., 21., 25., 29., 18., 22., 26., 30., 19., 23., 27., 31., 20., 24., 28., 32.};CHECK(batch * channel * height * width == input.size());std::unique_ptr<float[]> output(new float[input.size()]);ANN::LRN<> lrn;lrn.run(input.data(), batch, channel, height, width, output.get());auto print = [height, width](const float* data, int length) {int size = height * width;for (int i = 0; i < length / size; ++i) {const float* p = data + i * size;for (int j = 0; j < size; ++j) {fprintf(stdout, " %f", p[j]);}fprintf(stdout, "\n");}};fprintf(stdout, "input:\n"); print(input.data(), input.size());fprintf(stdout, "output:\n"); print(output.get(), input.size());return 0;
}
執行結果如下圖所示:由結果可知,C++實現與tensorflow一致
tensorflow執行結果如下:
C++執行結果如下:
GitHub:https://github.com/fengbingchun/NN_Test
總結
以上是生活随笔為你收集整理的深度神经网络中的局部响应归一化LRN简介及实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 日期与unix时间戳之间的转换C++实现
- 下一篇: 经典网络AlexNet介绍