caffe里面的误差的caffe 反向传播播怎么实现来的

业界 | Caffe2新增RNN支持,Facebook全面转 - 科技 - 科技头条
业界 | Caffe2新增RNN支持,Facebook全面转
业界 | Caffe2新增RNN支持,Facebook全面转向神经机器翻译
选自Caffe2 Blog
近日,Facebook 共享了 Caffe2 在支持循环神经网络(RNN)方面的最新成果。在 Caffe2 RNN 中,最突出的亮点是几乎零开销的 RNN 引擎,它不仅可执行任意 RNN 单元且难以置信地灵活,还可以进行束搜索;Caffe2 RNN 还允许每块 GPU 使用大批量数据进行训练,并实现了所谓的静态 RNN。通过 Caffe2 RNN,Facebook 的神经机器翻译的效率提升高达 2.5x,Facebook 全部的机器翻译模型从基于短语的系统转换为所有语言的神经模型。这意味着 Facebook 可在产品中部署神经机器翻译了。
我们很高兴共享 Caffe2 在支持循环神经网络(RNN)方面的最新成果。今年 4 月推出 Caffe2 时,它并不支持 RNN;为此,在过去的几个月,我们开发出了当前最优的 RNN 构件以支持 RNN 用例(比如机器翻译、语音识别)。 通过 Caffe2,我们显著提升了 Facebook 机器翻译系统的效率和质量,效率提升达 2.5x,使得 Facebook 可在产品中部署神经机器翻译。因此,Facebook 全部的机器翻译模型从基于短语的系统转换为所有语言的神经模型。此外,还有若干个产品团队,包括语音识别和广告排名团队在内,已经开始使用 Caffe2 训练 RNN 模型。 我们邀请了机器学习工程师和研究者体验 Caffe2 对 RNN 的支持,下面是更多关于 RNN 支持的实现和开源细节: Caffe2 RNN 的独特特征 Caffe2 提供了一个通用的 RNN 库,其中 RNN 引擎是用于执行任意 RNN 单元(cell)的几乎零开销的虚拟框;在底层,一个单元就是一个袖珍版的 Caffe2,具备 Caffe2 所有的主要性能。同样还有一组丰富的 API,允许人们使用现有的 RNN 单元,并可通过 Python 部署新单元。MultiRNNCell 允许将现有单元轻松组合成更复杂的单元。例如,你可以整合 LSTMCell 的若干个层,然后将 AttentionCell 放到顶部。 RNN 引擎难以置信地灵活。它允许你选择哪些输出有梯度,并需要通过时间传播;允许你定义单元彼此连接的方式,以及如何与外在世界相连接。每一个输入接收到了通过时间传播回来的正确梯度。
除了零开销的引擎之外,Caffe2 RNN 的另一个核心优势是内存,它允许我们每块 GPU 都使用大批量数据进行训练。RNN 引擎支持回收跨时间步的中间结果,并使你有权力决定要回收什么。关于计算的交易内存的更多分析细节请参阅 https://arxiv.org/abs/。
在上图中,后向传递的中间结果可以跨时间步重复使用。前向结果如果需要重复使用,则需要在后向传递中重新计算。Caffe2 允许你指定要丢弃的前向 blob 对象以节省计算资源。 静态 RNN Caffe2 也实现了所谓的静态 RNN。它可以在序列长度已知的情况下通过时间反向传播来使用。另外,循环网络也成为了图形的一部分,这可以让全局神经网络执行器 DAGNet 找到最佳的 RNN 并行执行方式。静态 RNN 引擎支持所有现有的 RNNCell,几乎无需更改代码即可插入使用。在多层 LSTM 模型中,我们可以看到静态 RNN 的速度比动态 RNN 高出 25%。 用于束搜索的 RNN 引擎 开发团队遵循在机器翻译中常见的在解码时使用束搜索(beam search)来提高模型输出预测表现的方法。我们使用 Caffe2 中 RNN 抽象的泛化性能来直接实现束搜索——使用单一前向网络计算(single forward network computation),这样可以提供快速而有效率的推理。无论底层模型的架构如何(RNN、CNN……),束搜索都可以作为循环网络解码器使用。束搜索推断功能已在 Caffe2 库中提供。
主页: https://caffe2.ai/
GitHub: /caffe2/caffe2
Facebook: /Caffe2AI
为了提高翻译质量,Facebook 近日决定将所有产品中基于短语的机器翻译模型转换成神经网络翻译系统。该公司称,目前所有业务中,每天会收到 2000 种翻译方向(translation direction),约 45 亿个翻译请求。新模型将提供更为准确和流畅的翻译,改善 Facebook 产品的用户体验。 带有注意力机制的序列到序列 LSTM:结合上下文 我们之前的基于短语的统计技术很有用,但也有其局限性。基于短语的系统最主要的缺点是它们将句子拆分成独立的单词或词组,系统在输出翻译结果时一次只考虑几个单词。这使得词序差别显著的语言间的翻译非常困难。为改进该系统、构建我们的神经网络系统,我们开始使用一种循环神经网络,叫作带有注意力机制的序列到序列 LSTM(长短期记忆)。该网络可以考虑到源语言句子的上下文和已经生成的所有内容,以创建更准确、流畅的译文。该网络允许长距调序,正如在英语和土耳其语翻译中遇到的那样。我们来看一下基于短语的土耳其语-英语翻译系统输出的译文:
再比较一下基于新型神经网络的土耳其语-英语翻译系统输出的译文:
我们看到,新系统在所有语言对上的 BLEU 值与基于短语的系统相比平均增长了 11 个百分点。 解决未知单词 在许多案例中,原句子中的单词并没有直接对应于目标词汇表中的翻译。这种情况下,神经系统将为未知单词生成占位符。所以我们采用注意力机制在源单词和目标单词中产生软对齐,从而将单词汇传递到目标句子中。然后,我们从使用训练数据构建的双语词典中查找该单词的翻译,并替代目标句子中的未知词。这个方法要比使用传统词典的方法更加鲁棒,特别是对于有噪声的输入。例如,在英语到西班牙语的翻译中, 我们能够将"tmrw(tomorrow)"翻译为「ma?ana」。虽然添加一个词典在 BLEU 分值上只有少量的提升,但是它令人们在 Facebook 翻译上有更高的评分。 词汇量简化 典型的神经机器翻译模型会计算所有单词在目标词汇表中的概率分布。该分布包含的单词量越大,计算所用的时间就越多。我们使用一种词汇量简化(vocabulary reduction)的建模技术在训练和推理时间上修正该问题。通过词汇量简化,我们将目标词汇中最常出现的单词与给定句子单个词汇的一组可能翻译相结合,以减少目标词汇的大小。过滤目标词汇会减少输出映射层的大小,这有助于使计算更快,而且不会使质量降低太多。 模型调参 神经网络基本上总是具有可调整的参数以控制模型的学习速率等模型性质。选择这些超参数的最优集非常有益于性能的提升。然而,调参对于大规模机器翻译提出了重大的挑战,因为具有独特超参数的唯一模型才能表征一个翻译方向。因为每个模型的最优值可能不同,因此我们必须独立地对生产中的每个系统进行超参数调整。我们在数月内进行了数千次端对端翻译实验,并利用 FBLearner Flow 平台微调超参数(如学习速率,注意力类型和集成大小等)。这对一些系统有重大影响。比如说我们仅基于调优模型超参数就可以令英语到西班牙语的 BLEU 分值相对提高了 3.7%。 使用 Caffe2 调整神经机器翻译的规模 向神经系统过渡的一个挑战是使模型运行的速度和效率适用于 Facebook 的规模。我们在深度学习框架 Caffe2 中实现了该翻译系统。Caffe2 追根溯源和灵活的本质使得我们可以在我们的 GPU 和 CPU 平台上进行训练和推断时,能够调整翻译模型的性能。 对于训练,我们实现了记忆优化,如 blob 循环(blob recycling)和 blob 再计算(blob recomputation),帮助我们训练更大规模的数据,更快地完成训练。关于推断,我们使用专门的向量数学库和加权量化来提高计算效率。现有模型的早期基准显示能够支持 2000 多个翻译方向的计算资源已经相当高了。但是,Caffe2 的灵活性本质和已经实现的优化使我们在效率上实现了 2.5x 的提升,这使得我们可以将神经机器翻译应用到产品中。 我们在解码时间使用束搜索来改善我们对模型输出的最高似然句子的评估,这是机器翻译中的常见做法。我们利用 RNN 抽象在 Caffe2 中的泛化性能来实现束搜索,将其作为单一前向网络计算,以获得快速高效的推断。 在整个过程中,我们开发了 RNN 构件,如 LSTM、乘法积分 LSTM(multiplicative integration LSTM)、注意力机制。我们很高兴能够将该技术作为 Caffe2 的一部分来分享,以及和开源社区共享我们的研究成果。 正在进行的工作 实现从基于短语的翻译系统向神经机器翻译的转变是我们向每个用户提供在 Facebook 上使用常用语言的经历的里程碑。我们将继续扩展神经机器翻译技术的边界,向每一个使用 Facebook 的用户提供更高质的翻译。 原文链接:https://caffe2.ai/blog//caffe2-adds-RNN-support.html Facebook博客:/posts/277/transitioning-entirely-to-neural-machine-translation/ 本文为机器之心编译,转载请联系本公众号获得授权。返回搜狐,查看更多
责任编辑:
&科技讯版权所有
京ICP备号-1
京公网安备 19号caffe系列:deeplab中的插值网络层前传和反传的实现分析 - 一亩半分地 - CSDN博客
caffe系列:deeplab中的插值网络层前传和反传的实现分析
最近在torch中实现一个网络,需要用到插值层,索性就看了下deeplab中插值网络层的实现,移植到torch中,废话少说,先粗略地写个放上来。
二、双线性插值理论
暂时没空写那么详细,双线性插值的理论可以看
https://en.wikipedia.org/wiki/Bilinear_interpolation
三、插值层的前向和反向传播的实现分析
插值网络层的实现:
interp_layer.cpp文件
https://bitbucket.org/aquariusjay/deeplab-public-ver2/src/071ef5a59aad8d9e6e1f5b8dff3d7a5c984a3d3a/src/caffe/layers/interp_layer.cpp?at=master&fileviewer=file-view-default
前传和反传的CPU实现interp.cpp
https://bitbucket.org/aquariusjay/deeplab-public-ver2/src/071ef5a59aad8d9e6e1f5b8dff3d7a5c984a3d3a/src/caffe/util/interp.cpp?at=master&fileviewer=file-view-default
我这里只分析CPU的实现,GPU没有时间看
(0)网络层中参数及概览
template &typename Dtype&
void InterpLayer&Dtype&::LayerSetUp(const vector&Blob&Dtype&*&& bottom,
const vector&Blob&Dtype&*&& top) {
InterpParameter interp_param = this-&layer_param_.interp_param();
pad_beg_ = interp_param.pad_beg();
pad_end_ = interp_param.pad_end();
// pad必须小于等于0,这里实际上是crop
CHECK_LE(pad_beg_, 0) && &Only supports non-pos padding (cropping) for now&;
CHECK_LE(pad_end_, 0) && &Only supports non-pos padding (cropping) for now&;
template &typename Dtype&
void InterpLayer&Dtype&::Reshape(const vector&Blob&Dtype&*&& bottom,
const vector&Blob&Dtype&*&& top) {
num_ = bottom[0]-&num();
channels_ = bottom[0]-&channels();
height_in_ = bottom[0]-&height();
width_in_ = bottom[0]-&width();
// crop之后的高度
height_in_eff_ = height_in_ + pad_beg_ + pad_end_;
// crop之后的宽度
width_in_eff_ = width_in_ + pad_beg_ + pad_end_;
InterpParameter interp_param = this-&layer_param_.interp_param();
if (interp_param.has_shrink_factor() &&
!interp_param.has_zoom_factor()) {
// 缩小因子
const int shrink_factor = interp_param.shrink_factor();
// 检查是否大于等于1
CHECK_GE(shrink_factor, 1) && &Shrink factor must be positive&;
// 计算缩小之后的大小,宽度和高度, 上取整
height_out_ = (height_in_eff_ - 1) / shrink_factor + 1;
width_out_ = (width_in_eff_ - 1) / shrink_factor + 1;
} else if (interp_param.has_zoom_factor() &&
!interp_param.has_shrink_factor()) {
// 放大因子
const int zoom_factor = interp_param.zoom_factor();
// 检查是否大于等于1
CHECK_GE(zoom_factor, 1) && &Zoom factor must be positive&;
// 这是什么鬼,为啥放大因子要减去1
height_out_ = height_in_eff_ + (height_in_eff_ - 1) * (zoom_factor - 1);
width_out_ = width_in_eff_ + (width_in_eff_ - 1) * (zoom_factor - 1);
} else if (interp_param.has_height() && interp_param.has_width()) {
// 如果直接给出输出大小
height_out_
= interp_param.height();
width_out_
= interp_param.width();
} else if (interp_param.has_shrink_factor() &&
interp_param.has_zoom_factor()) {
// 如果给出缩放因子和放大因子
const int shrink_factor = interp_param.shrink_factor();
const int zoom_factor = interp_param.zoom_factor();
CHECK_GE(shrink_factor, 1) && &Shrink factor must be positive&;
CHECK_GE(zoom_factor, 1) && &Zoom factor must be positive&;
// 先缩放,再放大之后的大小
height_out_ = (height_in_eff_ - 1) / shrink_factor + 1;
width_out_ = (width_in_eff_ - 1) / shrink_factor + 1;
height_out_ = height_out_ + (height_out_ - 1) * (zoom_factor - 1);
width_out_ = width_out_ + (width_out_ - 1) * (zoom_factor - 1);
LOG(FATAL);
CHECK_GT(height_in_eff_, 0) && &height should be positive&;
CHECK_GT(width_in_eff_, 0) && &width should be positive&;
CHECK_GT(height_out_, 0) && &height should be positive&;
CHECK_GT(width_out_, 0) && &width should be positive&;
// reshape输出的blob
top[0]-&Reshape(num_, channels_, height_out_, width_out_);
template &typename Dtype&
void InterpLayer&Dtype&::Forward_cpu(const vector&Blob&Dtype&*&& bottom,
const vector&Blob&Dtype&*&& top) {
// 调用具体的实现
caffe_cpu_interp2&Dtype,false&(num_ * channels_,
bottom[0]-&cpu_data(), - pad_beg_, - pad_beg_, height_in_eff_, width_in_eff_, height_in_, width_in_,
top[0]-&mutable_cpu_data(), 0, 0, height_out_, width_out_, height_out_, width_out_);
template &typename Dtype&
void InterpLayer&Dtype&::Backward_cpu(const vector&Blob&Dtype&*&& top,
const vector&bool&& propagate_down, const vector&Blob&Dtype&*&& bottom) {
if (!propagate_down[0]) { }
// 调用具体的实现
caffe_set(bottom[0]-&count(), Dtype(0), bottom[0]-&mutable_cpu_diff());
caffe_cpu_interp2_backward&Dtype,false&(num_ * channels_,
bottom[0]-&mutable_cpu_diff(), - pad_beg_, - pad_beg_, height_in_eff_, width_in_eff_, height_in_, width_in_,
top[0]-&cpu_diff(), 0, 0, height_out_, width_out_, height_out_, width_out_);
注释有部分是英文的,先贴上来,后面有空再解释
// Bi-linear interpolation
// https://en.wikipedia.org/wiki/Bilinear_interpolation
// IN : [channels height1 width1] cropped from a bigger [Height1 Width1] image
// OUT: [channels height2 width2] cropped from a bigger [Height2 Width2] image
template &typename Dtype, bool packed&
void caffe_cpu_interp2(const int channels,
const Dtype *data1, const int x1, const int y1, const int height1, const int width1, const int Height1, const int Width1,
Dtype *data2, const int x2, const int y2, const int height2, const int width2, const int Height2, const int Width2) {
// 检查是否合法
CHECK(x1 &= 0 && y1 &= 0 && height1 & 0 && width1 & 0 && x2 &= 0 && y2 &= 0 && height2 & 0 && width2 & 0);
CHECK(Width1 &= width1 + x1 && Height1 &= height1 + y1 && Width2 &= width2 + x2 && Height2 &= height2 + y2);
// 参数解释
// channels 输入数据的个数
// data1 输入数据指针
// x1 输入数据w偏移量
// y1 输入数据h偏移量
// height1 输入数据crop之后的高度
输入数据crop之后的宽度
// Height1 输入数据的原始高度
// Width1 输入数据的原始宽度
// data2 输出的数据指针
// x2 输出数据w偏移
// y2 输出数据h偏移
// height2 输出数据crop之后的高度
// width2 输出数据crop之后的宽度
// Height2 输出数据的原始高度
// Width2 输出数据的原始宽度
// special case: just copy
if (height1 == height2 && width1 == width2)
{// 输入和输出一样大小的
for (int h2 = 0; h2 & height2; ++h2)
const int h1 = h2;
for (int w2 = 0; w2 & width2; ++w2)
const int w1 = w2;
if (packed)
// what is packed?
const Dtype* pos1 = &data1[channels * ((y1 + h1) * Width1 + (x1 + w1))];
Dtype* pos2 = &data2[channels * ((y2 + h2) * Width2 + (x2 + w2))];
for (int c = 0; c & ++c)
pos2[0] = pos1[0];
// normal situation
const Dtype* pos1 = &data1[(y1 + h1) * Width1 + (x1 + w1)];
Dtype* pos2 = &data2[(y2 + h2) * Width2 + (x2 + w2)];
for (int c = 0; c & ++c)
pos2[0] = pos1[0];
pos1 += Width1 * Height1;
pos2 += Width2 * Height2;
// calculate height factor and width factor
// input / output
const float rheight = (height2 & 1) ? static_cast&float&(height1 - 1) / (height2 - 1) : 0.f;
const float rwidth = (width2 & 1) ? static_cast&float&(width1 - 1) / (width2 - 1) : 0.f;
for (int h2 = 0; h2 & height2; ++h2) {// calculate h1 and w1 according to h2 and w2
// calculate height in the input image according to h factor
const float h1r = rheight * h2;
// convert h1r to int
const int h1 = h1r;
// h1p indicates whether the pos is valid
const int h1p = (h1 & height1 - 1) ? 1 : 0;
// h0lambda and h1lambda indicate two residuals in wiki equation
const Dtype h1lambda = h1r - h1;
const Dtype h0lambda = Dtype(1.) - h1
for (int w2 = 0; w2 & width2; ++w2)
// calculate width in the input image according to w factor
const float w1r = rwidth * w2;
// convert w1r to int
const int w1 = w1r;
// w1p indicates whether the pos is valid
const int w1p = (w1 & width1 - 1) ? 1 : 0;
// w0lambda and w1lambda indicate two residuals in wiki equation
const Dtype w1lambda = w1r - w1;
const Dtype w0lambda = Dtype(1.) - w1
if (packed)
const Dtype* pos1 = &data1[channels * ((y1 + h1) * Width1 + (x1 + w1))];
Dtype* pos2 = &data2[channels * ((y2 + h2) * Width2 + (x2 + w2))];
for (int c = 0; c & ++c)
h0lambda * (w0lambda * pos1[0]
+ w1lambda * pos1[channels * w1p]) +
h1lambda * (w0lambda * pos1[channels * h1p * Width1] + w1lambda * pos1[channels * (h1p * Width1 + w1p)]);
const Dtype* pos1 = &data1[(y1 + h1) * Width1 + (x1 + w1)];
Dtype* pos2 = &data2[(y2 + h2) * Width2 + (x2 + w2)];
for (int c = 0; c & ++c) {// visit all channels
// calculate bi-linear interpolation according to wiki
h0lambda * (w0lambda * pos1[0]
+ w1lambda * pos1[w1p]) +
h1lambda * (w0lambda * pos1[h1p * Width1] + w1lambda * pos1[h1p * Width1 + w1p]);
pos1 += Width1 * Height1;
pos2 += Width2 * Height2;
// Backward (adjoint) operation 1 &- 2 (accumulates)
template &typename Dtype, bool packed&
void caffe_cpu_interp2_backward(const int channels,
Dtype *data1, const int x1, const int y1, const int height1, const int width1, const int Height1, const int Width1,
const Dtype *data2, const int x2, const int y2, const int height2, const int width2, const int Height2, const int Width2) {
// check parameters
CHECK(x1 &= 0 && y1 &= 0 && height1 & 0 && width1 & 0 && x2 &= 0 && y2 &= 0 && height2 & 0 && width2 & 0);
CHECK(Width1 &= width1 + x1 && Height1 &= height1 + y1 && Width2 &= width2 + x2 && Height2 &= height2 + y2);
// special case: same-size matching grids
if (height1 == height2 && width1 == width2)
for (int h2 = 0; h2 & height2; ++h2)
const int h1 = h2;
for (int w2 = 0; w2 & width2; ++w2)
const int w1 = w2;
if (packed)
Dtype* pos1 = &data1[channels * ((y1 + h1) * Width1 + (x1 + w1))];
const Dtype* pos2 = &data2[channels * ((y2 + h2) * Width2 + (x2 + w2))];
for (int c = 0; c & ++c)
pos1[0] += pos2[0];
Dtype* pos1 = &data1[(y1 + h1) * Width1 + (x1 + w1)];
const Dtype* pos2 = &data2[(y2 + h2) * Width2 + (x2 + w2)];
for (int c = 0; c & ++c)
// acumulate gradients
pos1[0] += pos2[0];
pos1 += Width1 * Height1;
pos2 += Width2 * Height2;
// calculate w/h factor
// input image / output image
const float rheight = (height2 & 1) ? static_cast&float&(height1 - 1) / (height2 - 1) : 0.f;
const float rwidth = (width2 & 1) ? static_cast&float&(width1 - 1) / (width2 - 1) : 0.f;
for (int h2 = 0; h2 & height2; ++h2)
const float h1r = rheight * h2;
const int h1 = h1r;
const int h1p = (h1 & height1 - 1) ? 1 : 0;
const Dtype h1lambda = h1r - h1;
const Dtype h0lambda = Dtype(1.) - h1
for (int w2 = 0; w2 & width2; ++w2)
const float w1r = rwidth * w2;
const int w1 = w1r;
const int w1p = (w1 & width1 - 1) ? 1 : 0;
const Dtype w1lambda = w1r - w1;
const Dtype w0lambda = Dtype(1.) - w1
if (packed)
Dtype* pos1 = &data1[channels * ((y1 + h1) * Width1 + (x1 + w1))];
const Dtype* pos2 = &data2[channels * ((y2 + h2) * Width2 + (x2 + w2))];
for (int c = 0; c & ++c)
pos1[0] += h0lambda * w0lambda * pos2[0];
pos1[channels * w1p] += h0lambda * w1lambda * pos2[0];
pos1[channels * h1p * Width1] += h1lambda * w0lambda * pos2[0];
pos1[channels * (h1p * Width1 + w1p)] += h1lambda * w1lambda * pos2[0];
Dtype* pos1 = &data1[(y1 + h1) * Width1 + (x1 + w1)];
const Dtype* pos2 = &data2[(y2 + h2) * Width2 + (x2 + w2)];
for (int c = 0; c & ++c)
// acumulate gradients from scaled pixels
pos1[0] += h0lambda * w0lambda * pos2[0];
pos1[w1p] += h0lambda * w1lambda * pos2[0];
pos1[h1p * Width1] += h1lambda * w0lambda * pos2[0];
pos1[h1p * Width1 + w1p] += h1lambda * w1lambda * pos2[0];
pos1 += Width1 * Height1;
pos2 += Width2 * Height2;
四、总结:
实际上这里实现的双线性插值还挺简单的,就是根据维基百科上面的实现来实现前传的,另外,前传的时候需要注意边界问题
此外反传则是将进行插值后之后的像素值反传到插值之前的位置,进行累加来实现的。
有不懂的可以留言
后来发现torch里面已经有人移植过去了,干脆帖进来
五、torch中的实现
/torch/nn/blob/master/SpatialUpSamplingBilinear.lua
require 'nn.THNN'
local SpatialUpSamplingBilinear, parent =
torch.class('nn.SpatialUpSamplingBilinear', 'nn.Module')
Applies a 2D bilinear up-sampling over an input image composed of several
input planes.
The Y and X dimensions are assumed to be the last 2 tensor dimensions.
instance, if the tensor is 4D, then dim 3 is the y dimension and dim 4 is the x.
scale_factor is assumed to be a positive integer.
= (width-1)*(scale_factor-1) + width
= (height-1)*(scale_factor-1) + height
Alternatively, owidth and oheight can be directly provided as input.
function SpatialUpSamplingBilinear:__init(params)
parent.__init(self)
self.owidth, self.oheight, self.scale_factor = nil, nil, nil
if torch.type(params) == 'table' then
self.owidth, self.oheight = params.owidth, params.oheight
self.scale_factor = params
if self.scale_factor & 1 then
error('scale_factor must be greater than 1')
if math.floor(self.scale_factor) ~= self.scale_factor then
error('scale_factor must be integer')
self.inputSize = torch.LongStorage(4)
self.outputSize = torch.LongStorage(4)
local function makeContiguous(self, input, gradOutput)
if not input:isContiguous() then
self._input = self._input or input.new()
self._input:resizeAs(input):copy(input)
input = self._input
if gradOutput then
if not gradOutput:isContiguous() then
self._gradOutput = self._gradOutput or gradOutput.new()
self._gradOutput:resizeAs(gradOutput):copy(gradOutput)
gradOutput = self._gradOutput
return input, gradOutput
function SpatialUpSamplingBilinear:setSize(input)
local xdim = input:dim()
local ydim = xdim - 1
for i = 1, input:dim() do
self.inputSize[i] = input:size(i)
self.outputSize[i] = input:size(i)
if self.scale_factor ~= nil then
self.outputSize[ydim] = self.outputSize[ydim] * self.scale_factor
self.outputSize[xdim] = self.outputSize[xdim] * self.scale_factor
self.outputSize[ydim] = self.oheight
self.outputSize[xdim] = self.owidth
function SpatialUpSamplingBilinear:updateOutput(input)
assert(input:dim() == 4 or input:dim()==3,
'SpatialUpSamplingBilinear only supports 3D or 4D tensors' )
input = makeContiguous(self, input)
local inputwas3D = false
if input:dim() == 3 then
input=input:view(-1, input:size(1), input:size(2), input:size(3))
inputwas3D = true
local xdim = input:dim()
local ydim = xdim - 1
self:setSize(input)
input.THNN.SpatialUpSamplingBilinear_updateOutput(
input:cdata(),
self.output:cdata(),
self.outputSize[ydim],
self.outputSize[xdim]
if inputwas3D then
input = input:squeeze(1)
self.output = self.output:squeeze(1)
return self.output
function SpatialUpSamplingBilinear:updateGradInput(input, gradOutput)
assert(input:dim() == 4 or input:dim()==3,
'SpatialUpSamplingBilinear only support 3D or 4D tensors' )
assert(input:dim() == gradOutput:dim(),
'Input and gradOutput should be of same dimension' )
input, gradOutput = makeContiguous(self, input, gradOutput)
local inputwas3D = false
if input:dim() == 3 then
input = input:view(-1, input:size(1), input:size(2), input:size(3))
gradOutput = gradOutput:view(-1, gradOutput:size(1), gradOutput:size(2),
gradOutput:size(3))
inputwas3D = true
local xdim = input:dim()
local ydim = xdim - 1
self.gradInput:resizeAs(input)
input.THNN.SpatialUpSamplingBilinear_updateGradInput(
gradOutput:cdata(),
self.gradInput:cdata(),
input:size(1),
input:size(2),
input:size(3),
input:size(4),
self.outputSize[ydim],
self.outputSize[xdim]
if inputwas3D then
input = input:squeeze(1)
gradOutput = gradOutput:squeeze(1)
self.gradInput = self.gradInput:squeeze(1)
return self.gradInput
function SpatialUpSamplingBilinear:__tostring__()
if self.scale_factor ~= nil then
s = string.format('%s(%d)', torch.type(self), self.scale_factor)
s = string.format('%s(%d, %d)',
torch.type(self), self.oheight, self.owidth)
底层的c实现
/torch/nn/master/lib/THNN/generic/SpatialUpSamplingBilinear.c
// Adapted from interp.cpp from Caffe util by Pauline Luc
// Originally developed by George Papandreou
#ifndef TH_GENERIC_FILE
#define TH_GENERIC_FILE &generic/SpatialUpSamplingBilinear.c&
static inline void THNN_(SpatialUpSamplingBilinear_shapeCheck)
(THTensor *input, THTensor *gradOutput,
int nBatch, int nChannels,
int inputHeight, int inputWidth,
int outputHeight, int outputWidth) {
THArgCheck(inputHeight & 0 && inputWidth & 0
&& outputHeight & 0 && outputWidth & 0, 2,
&input and output sizes should be greater than 0,&
& but got input (H: %d, W: %d) output (H: %d, W: %d)&,
inputHeight, inputWidth, outputHeight, outputWidth);
if (input != NULL) {
THNN_ARGCHECK(input-&nDimension == 4, 2, input,
&4D input tensor expected but got: %s&);
if (gradOutput != NULL) {
THNN_CHECK_DIM_SIZE(gradOutput, 4, 0, nBatch);
THNN_CHECK_DIM_SIZE(gradOutput, 4, 1, nChannels);
THNN_CHECK_DIM_SIZE(gradOutput, 4, 2, outputHeight);
THNN_CHECK_DIM_SIZE(gradOutput, 4, 3, outputWidth);
void THNN_(SpatialUpSamplingBilinear_updateOutput)(
THNNState *state,
THTensor *input,
THTensor *output,
int outputHeight,
int outputWidth){
int nbatch = THTensor_(size)(input, 0);
int channels = THTensor_(size)(input, 1);
int inputHeight = THTensor_(size)(input, 2);
int inputWidth = THTensor_(size)(input, 3);
THNN_(SpatialUpSamplingBilinear_shapeCheck)
(input, NULL,
nbatch, channels,
inputHeight, inputWidth,
outputHeight, outputWidth);
input = THTensor_(newContiguous)(input);
THTensor_(resize4d)(output,
THTensor_(size)(input, 0),
THTensor_(size)(input, 1),
outputHeight, outputWidth);
THTensor_(zero)(output);
real *idata = THTensor_(data)(input);
real *odata = THTensor_(data)(output);
channels = nbatch *
THAssert(inputHeight & 0 && inputWidth & 0 && outputHeight & 0 && outputWidth & 0);
// special case: just copy
if (inputHeight == outputHeight && inputWidth == outputWidth) {
for (int h2 = 0; h2 & outputH ++h2) {
const int h1 = h2;
for (int w2 = 0; w2 & outputW ++w2) {
const int w1 = w2;
const real* pos1 = &idata[h1 * inputWidth + w1];
real* pos2 = &odata[h2 * outputWidth + w2];
for (int c = 0; c & ++c) {
pos2[0] = pos1[0];
pos1 += inputWidth * inputH
pos2 += outputWidth * outputH
const float rheight =(outputHeight & 1) ? (float)(inputHeight - 1)/(outputHeight - 1) : 0.f;
const float rwidth = (outputWidth & 1) ? (float)(inputWidth - 1) / (outputWidth - 1) : 0.f;
for (int h2 = 0; h2 & outputH ++h2) {
const float h1r = rheight * h2;
const int h1 = h1r;
const int h1p = (h1 & inputHeight - 1) ? 1 : 0;
const real h1lambda = h1r - h1;
const real h0lambda = (real)1. - h1
for (int w2 = 0; w2 & outputW ++w2) {
const float w1r = rwidth * w2;
const int w1 = w1r;
const int w1p = (w1 & inputWidth - 1) ? 1 : 0;
const real w1lambda = w1r - w1;
const real w0lambda = (real)1. - w1
const real* pos1 = &idata[h1 * inputWidth + w1];
real* pos2 = &odata[h2 * outputWidth + w2];
for (int c = 0; c & ++c) {
pos2[0] = h0lambda * (w0lambda * pos1[0]+ w1lambda * pos1[w1p])
+ h1lambda * (w0lambda * pos1[h1p * inputWidth]
+ w1lambda * pos1[h1p * inputWidth + w1p]);
pos1 += inputWidth * inputH
pos2 += outputWidth * outputH
THTensor_(free)(input);
void THNN_(SpatialUpSamplingBilinear_updateGradInput)(
THNNState *state,
THTensor *gradOutput,
THTensor *gradInput,
int nbatch,
int channels,
int inputHeight,
int inputWidth,
int outputHeight,
int outputWidth){
THNN_(SpatialUpSamplingBilinear_shapeCheck)
(NULL, gradOutput,
nbatch, channels,
inputHeight, inputWidth,
outputHeight, outputWidth);
THTensor_(resize4d)(gradInput, nbatch, channels, inputHeight, inputWidth);
THTensor_(zero)(gradInput);
gradOutput = THTensor_(newContiguous)(gradOutput);
real *data1 = THTensor_(data)(gradInput);
real *data2 = THTensor_(data)(gradOutput);
channels = nbatch *
// special case: same-size matching grids
if (inputHeight == outputHeight && inputWidth == outputWidth) {
for (int h2 = 0; h2 & outputH ++h2) {
const int h1 = h2;
for (int w2 = 0; w2 & outputW ++w2) {
const int w1 = w2;
real* pos1 = &data1[h1 * inputWidth + w1];
const real* pos2 = &data2[h2 * outputWidth + w2];
for (int c = 0; c & ++c) {
pos1[0] += pos2[0];
pos1 += inputWidth * inputH
pos2 += outputWidth * outputH
const float rheight =(outputHeight & 1) ? (float)(inputHeight - 1)/(outputHeight - 1) : 0.f;
const float rwidth = (outputWidth & 1) ? (float)(inputWidth - 1)/(outputWidth - 1) : 0.f;
for (int h2 = 0; h2 & outputH ++h2) {
const float h1r = rheight * h2;
const int h1 = h1r;
const int h1p = (h1 & inputHeight - 1) ? 1 : 0;
const real h1lambda = h1r - h1;
const real h0lambda = (real)1. - h1
for (int w2 = 0; w2 & outputW ++w2) {
const float w1r = rwidth * w2;
const int w1 = w1r;
const int w1p = (w1 & inputWidth - 1) ? 1 : 0;
const real w1lambda = w1r - w1;
const real w0lambda = (real)1. - w1
real* pos1 = &data1[h1 * inputWidth + w1];
const real* pos2 = &data2[h2 * outputWidth + w2];
for (int c = 0; c & ++c) {
pos1[0] += h0lambda * w0lambda * pos2[0];
pos1[w1p] += h0lambda * w1lambda * pos2[0];
pos1[h1p * inputWidth] += h1lambda * w0lambda * pos2[0];
pos1[h1p * inputWidth + w1p] += h1lambda * w1lambda * pos2[0];
pos1 += inputWidth * inputH
pos2 += outputWidth * outputH
THTensor_(free)(gradOutput);
我的热门文章
即使是一小步也想与你分享

我要回帖

更多关于 caffe 卷积层反向传播 的文章

 

随机推荐