【caffe解读】 caffe从数学公式到代码实现5-caffe中的卷积

【WRF-CMAQ实战指南】从环境配置到测试案例运行全解析 本文详细解析了WRF-CMAQ从环境配置到测试案例运行的全过程,包括系统环境检查、依赖库安装、源码获取与项目初始化、关键配置技巧、编译实战与排错、测试案例运行验证等关键步骤。通过实战指南,帮助用户快速掌握WRF-CMAQ的安装和运行技巧,提升空气质量模拟的效率。 阅读详情

文章首发于微信公众号《与有三学AI》

[caffe解读] caffe从数学公式到代码实现5-caffe中的卷积

今天要讲的就是跟卷积相关的一些layer了

im2col_layer.cpp

base_conv_layer.cpp

conv_layer.cpp

deconv_layer.cpp

inner_product_layer.cpp

 

01 im2col_layer.cpp

这是caffe里面的重要操作,caffe为什么这么耗显存,跟这个有很大关系。im2col的目的,就是把要滑动卷积的图像,先一次性存起来,然后再进行矩阵乘操作。简单来说,它的输入是一个C*H*W的blob,经过im2col操作会变成K' x (H xW) 的矩阵,其中K' =C*kernel_r*kernel_r,kernel_r就是卷积核的大小,这里只看正方形的卷积核。

如果不用这样的操作,贾扬清有一个吐槽,对于输入大小为W*H,维度为D的blob,卷积核为M*K*K,那么如果利用for循环,会是这样的一个操作,6层for循环,计算效率是极其低下的。

for w in 1..W
for h in 1..H
  for x in 1..K
    for y in 1..K
      for m in 1..M
        for d in 1..D
          output(w, h, m) += input(w+x, h+y, d) *filter(m, x, y, d)
        end
      end
    end
  end
end
end

具体im2col是什么原理呢?先贴出贾扬清的回答,https://www.zhihu.com/question/28385679

上面说了,要把C*H*W的blob,变成K' x (H x W)或者 (H xW) xK' 的矩阵,把filters也复制成一个大矩阵,这样两者直接相乘就得到结果,下面看一个简单小例子。

借用网友一张图,虽然和caffe细节上不同,但是还是有助于理解。http://blog.youkuaiyun.com/mrhiuser/article/details/52672824

4*4的原始数据,进行stride=1的3*3操作,其中im2col的操作就是:

也就是说4*4的矩阵,经过了im2col后,变成了9*4的矩阵,卷积核可以做同样扩展,卷积操作就变成了两个矩阵相乘。

下面看im2col的代码;

template <typename Dtype>
void im2col_cpu(const Dtype* data_im, const intchannels,
  const int height, const int width, const int kernel_h,const int kernel_w,
  const int pad_h, const int pad_w,
  const int stride_h, const int stride_w,
  const int dilation_h, const int dilation_w,
Dtype* data_col) {
 //输入为data_im,kernel_h,kernel_w以及各类卷积参数,输出就是data_col。
 //out_put_h,out_put_w,是输出的图像尺寸。
const int output_h = (height + 2 * pad_h -
  (dilation_h * (kernel_h - 1) + 1)) / stride_h + 1;
const int output_w = (width + 2 * pad_w -
  (dilation_w * (kernel_w - 1) + 1)) / stride_w + 1;
const int channel_size = height * width;
//外层channel循环不管
for (int channel = channels; channel--; data_im +=channel_size) {
   //这是一个关于kernel_row和kernel_col的2层循环 
  for (int kernel_row = 0; kernel_row < kernel_h;kernel_row++) {
for (int kernel_col = 0; kernel_col < kernel_w;kernel_col++) {
      int input_row = -pad_h + kernel_row * dilation_h;
       //这是一个关于output_h和output_w的循环,这实际上就是上图例子中每一行的数据 
 for (int output_rows = output_h; output_rows;output_rows--) {
         //边界条件属特殊情况,可以细下推敲
        if (!is_a_ge_zero_and_a_lt_b(input_row, height)) {
for (int output_cols = output_w; output_cols;output_cols--) {
            *(data_col++) = 0;
          }
        } else {
          int input_col = -pad_w + kernel_col *dilation_w;
          for (int output_col = output_w; output_col;output_col--) {
if (is_a_ge_zero_and_a_lt_b(input_col, width)) {
//这就是核心的赋值语句,按照循环的顺序,我们可以知道是按照输出output_col*output_h的尺寸,一截一截地串接成了一个col。
              *(data_col++) = data_im[input_row * width+ input_col];
            } else {
              *(data_col++) = 0;
            }
            input_col += stride_w;
          }
        }
        input_row += stride_h;
      }
    }
  }
}
}

相关注释已经放在了上面,col2im的操作非常类似,可以自行看源码,这一段要自己写出来怕是需要调试一些时间。

有了上面的核心代码后,Forward只需要调用im2col,输入为bottom_data,输出为top_data,Backward只需要调用col2im,输入为top_diff,输出为bottom_diff即可,代码就不贴出了。

 

02 conv_layer.cpp,base_conv_layer.cpp

数学定义不用说,我们直接看代码,这次要两个一起看。由于conv_layer.cpp依赖于base_conv_layer.cpp,我们先来看看base_conv_layer.hpp中包含了什么东西,非常多。

base_conv_layer.hpp变量:

/// @brief The spatial dimensions of a filter kernel.
Blob<int> kernel_shape_;
/// @brief The spatial dimensions of the stride.
Blob<int> stride_;
/// @brief The spatial dimensions of the padding.
Blob<int> pad_;
/// @brief The spatial dimensions of the dilation.
Blob<int> dilation_;
/// @brief The spatial dimensions of the convolutioninput.
Blob<int> conv_input_shape_;
/// @brief The spatial dimensions of the col_buffer.
vector<int> col_buffer_shape_;
/// @brief The spatial dimensions of the output.
vector<int> output_shape_;
const vector<int>* bottom_shape_;

int num_spatial_axes_;
int bottom_dim_;
int top_dim_;

int channel_axis_;
int num_;
int channels_;
int group_;
int out_spatial_dim_;
int weight_offset_;
int num_output_;
bool bias_term_;
bool is_1x1_;
bool force_nd_im2col_;

int num_kernels_im2col_;
int num_kernels_col2im_;
int conv_out_channels_;
int conv_in_channels_;
int conv_out_spatial_dim_;
int kernel_dim_;
int col_offset_;
int output_offset_;

Blob<Dtype> col_buffer_;
Blob<Dtype> bias_multiplier_;
 

非常之多,因为卷积发展到现在,已经有很多的参数需要控制。无法一一解释了,stride_,pad_,dilation是和卷积步长有关参数,kernel_shape_是卷积核大小,conv_input_shape_是输入大小,output_shape是输出大小,其他都是以后遇到了再说,现在我们先绕过。更具体的解答,有一篇博客可以参考

http://blog.youkuaiyun.com/lanxuecc/article/details/53188738

下面直接看conv_layer.cpp。既然是卷积,输出的大小就取决于很多参数,所以先要计算输出的大小。

voidConvolutionLayer<Dtype>::compute_output_shape(){
const int* kernel_shape_data = this->kernel_shape_.cpu_data();
const int* stride_data = this->stride_.cpu_data();
const int* pad_data = this->pad_.cpu_data();
const int* dilation_data = this->dilation_.cpu_data();
this->output_shape_.clear();
for (int i = 0; i < this->num_spatial_axes_; ++i) {
  // i + 1 to skip channel axis
  const int input_dim = this->input_shape(i + 1);
  const int kernel_extent = dilation_data[i] *(kernel_shape_data[i] - 1) + 1;
  const int output_dim = (input_dim + 2 * pad_data[i]- kernel_extent)
      / stride_data[i] + 1;
  this->output_shape_.push_back(output_dim);
}
}

然后,在forward函数中,

template <typename Dtype>
void ConvolutionLayer<Dtype>::Forward_cpu(constvector<Blob<Dtype>*>& bottom, constvector<Blob<Dtype>*>& top) {
const Dtype* weight = this->blobs_[0]->cpu_data();
for (int i = 0; i < bottom.size(); ++i) {
  const Dtype* bottom_data = bottom[i]->cpu_data();
  Dtype* top_data = top[i]->mutable_cpu_data();
  for (int n = 0; n < this->num_; ++n) {
    this->forward_cpu_gemm(bottom_data + n * this->bottom_dim_, weight,
        top_data + n * this->top_dim_);
    if (this->bias_term_) {
      const Dtype* bias = this->blobs_[1]->cpu_data();
      this->forward_cpu_bias(top_data + n * this->top_dim_, bias);
    }
  }
}
}

我们知道卷积层的输入,是一个blob,输出是一个blob,从上面代码知道卷积核的权重存在了this->blobs_[0]->cpu_data()中, this->blobs_[1]->cpu_data()则是bias,当然不一定有值。外层循环大小为bottom.size(),可见其实可以有多个输入。

看看里面最核心的函数,this>forward_cpu_gemm。

输入input,输出col_buff,关于这个函数的解析,https://tangxman.github.io/2015/12/07/caffe-conv/解释地挺详细,我大概总结一下。

首先,按照调用顺序,对于3*3等正常的卷积,forward_cpu_gemm会调用conv_im2col_cpu函数(在base_conv_layer.hpp中),它的作用看名字就知道,将图像先转换为一个大矩阵,将卷积核也按列复制成大矩阵;

然后利用caffe_cpu_gemm计算矩阵相乘得到卷积后的结果。

template <typename Dtype>
voidBaseConvolutionLayer<Dtype>::forward_cpu_gemm(const Dtype* input,
  const Dtype* weights, Dtype* output, boolskip_im2col) {
const Dtype* col_buff = input;
if (!is_1x1_) {
  if (!skip_im2col) {
     // 如果没有1x1卷积,也没有skip_im2col    
     // 则使用conv_im2col_cpu对使用卷积核滑动过程中的每一个kernel大小的图像块    
     // 变成一个列向量,形成一个height=kernel_dim_的   
     // width = 卷积后图像heght*卷积后图像width   
    conv_im2col_cpu(input,col_buffer_.mutable_cpu_data());
  }
  col_buff = col_buffer_.cpu_data();
}
// 使用caffe的cpu_gemm来进行计算  
// 假设输入是20个feature map,输出是10个featuremap,group_=2
// 那么他就会把这个训练网络分解成两个10->5的网络,由于两个网络结构是
// 一模一样的,那么就可以利用多个GPU完成训练加快训练速度
for (int g = 0; g < group_; ++g) {
  caffe_cpu_gemm<Dtype>(CblasNoTrans,CblasNoTrans, conv_out_channels_ /
      group_, conv_out_spatial_dim_, kernel_dim_,
      (Dtype)1., weights + weight_offset_ * g, col_buff+ col_offset_ * g,
      (Dtype)0., output + output_offset_ * g);
  //weights <--- blobs_[0]->cpu_data()。类比全连接层,
  //weights为权重,col_buff相当与数据,矩阵相乘weights×col_buff. 
  //其中,weights的维度为(conv_out_channels_/group_) x kernel_dim_,
  //col_buff的维度为kernel_dim_ xconv_out_spatial_dim_, 
  //output的维度为(conv_out_channels_ /group_) xconv_out_spatial_dim_.
}
}

 

反向传播:

template <typename Dtype>
void ConvolutionLayer<Dtype>::Backward_cpu(constvector<Blob<Dtype>*>& top,
    const vector<bool>& propagate_down, constvector<Blob<Dtype>*>& bottom) {
const Dtype* weight = this->blobs_[0]->cpu_data();
Dtype* weight_diff = this->blobs_[0]->mutable_cpu_diff();
for (int i = 0; i < top.size(); ++i) {
  const Dtype* top_diff = top[i]->cpu_diff();
  const Dtype* bottom_data = bottom[i]->cpu_data();
  Dtype* bottom_diff = bottom[i]->mutable_cpu_diff();
  // Bias gradient, if necessary.
  if (this->bias_term_ && this->param_propagate_down_[1]) {
    Dtype* bias_diff = this->blobs_[1]->mutable_cpu_diff();
    for (int n = 0; n < this->num_; ++n) {
      this->backward_cpu_bias(bias_diff, top_diff + n *this->top_dim_);
    }
  }
  if (this->param_propagate_down_[0] ||propagate_down[i]) {
    for (int n = 0; n < this->num_; ++n) {
      // gradient w.r.t. weight. Note that we willaccumulate diffs.
      if (this->param_propagate_down_[0]) {
        this->weight_cpu_gemm(bottom_data + n *this->bottom_dim_,
            top_diff + n * this->top_dim_, weight_diff);
      }
      // gradient w.r.t. bottom data, if necessary.
      if (propagate_down[i]) {
        this->backward_cpu_gemm(top_diff + n * this->top_dim_, weight,
            bottom_diff + n * this->bottom_dim_);
      }
    }
  }
}

略去bias,从上面源码可以看出,有this->weight_cpu_gemm和this->backward_cpu_gemm两项。

this->backward_cpu_gemm是计算bottom_data的反向传播的,也就是feature map的反向传播。

template <typename Dtype>
voidBaseConvolutionLayer<Dtype>::backward_cpu_gemm(const Dtype* output,
  const Dtype* weights, Dtype* input) {
Dtype* col_buff = col_buffer_.mutable_cpu_data();
if (is_1x1_) {
  col_buff = input;
}
for (int g = 0; g < group_; ++g) {
  caffe_cpu_gemm<Dtype>(CblasTrans,CblasNoTrans, kernel_dim_ / group_,
      conv_out_spatial_dim_, conv_out_channels_ /group_,
      (Dtype)1., weights + weight_offset_ * g, output +output_offset_ * g,
      (Dtype)0., col_buff + col_offset_ * g);
}
if (!is_1x1_) {
  conv_col2im_cpu(col_buff, input);

}

weight_cpu_gemm是计算权重的反向传播的;

template <typename Dtype>
voidBaseConvolutionLayer<Dtype>::weight_cpu_gemm(const Dtype* input,
  const Dtype* output, Dtype* weights) {
const Dtype* col_buff = input;
if (!is_1x1_) {
  conv_im2col_cpu(input,col_buffer_.mutable_cpu_data());
  col_buff = col_buffer_.cpu_data();
}
for (int g = 0; g < group_; ++g) {
  caffe_cpu_gemm<Dtype>(CblasNoTrans,CblasTrans, conv_out_channels_ / group_,
      kernel_dim_, conv_out_spatial_dim_,
      (Dtype)1., output + output_offset_ * g, col_buff +col_offset_ * g,
      (Dtype)1., weights + weight_offset_ * g);
}
}

其中诸多细节,看不懂就再去看源码,一次看不懂就看多次。

 

03 deconv_layer.cpp

卷积,就是将下图转换为上图,一个输出像素,和9个输入像素有关。反卷积则反之,计算反卷积的时候,就是把上图输入的像素乘以卷积核,然后放在下图对应的输出各个位置,移动输入像素,最后把所有相同位置的输出相加。

template <typename Dtype>
voidDeconvolutionLayer<Dtype>::Forward_cpu(constvector<Blob<Dtype>*>& bottom,
    const vector<Blob<Dtype>*>& top) {
const Dtype* weight = this->blobs_[0]->cpu_data();
for (int i = 0; i < bottom.size(); ++i) {
  const Dtype* bottom_data = bottom[i]->cpu_data();
  Dtype* top_data = top[i]->mutable_cpu_data();
  for (int n = 0; n < this->num_; ++n) {
    this->backward_cpu_gemm(bottom_data + n *this->bottom_dim_, weight,
        top_data + n * this->top_dim_);
    if (this->bias_term_) {
      const Dtype* bias = this->blobs_[1]->cpu_data();
      this->forward_cpu_bias(top_data + n * this->top_dim_, bias);
    }
  }
}
}

forward直接调用了backward_cpu_gemm函数,反向的时候就直接调用forward函数,这里肯定是需要反复去理解的,一次不懂就多次。

template <typename Dtype>
voidDeconvolutionLayer<Dtype>::Backward_cpu(constvector<Blob<Dtype>*>& top,const vector<bool>&propagate_down, const vector<Blob<Dtype>*>&bottom) {
const Dtype* weight = this->blobs_[0]->cpu_data();
Dtype* weight_diff = this->blobs_[0]->mutable_cpu_diff();
for (int i = 0; i < top.size(); ++i) {
  const Dtype* top_diff = top[i]->cpu_diff();
  const Dtype* bottom_data = bottom[i]->cpu_data();
  Dtype* bottom_diff = bottom[i]->mutable_cpu_diff();
  // Bias gradient, if necessary.
  if (this->bias_term_ && this->param_propagate_down_[1]) {
    Dtype* bias_diff = this->blobs_[1]->mutable_cpu_diff();
    for (int n = 0; n < this->num_; ++n) {
      this->backward_cpu_bias(bias_diff, top_diff + n *this->top_dim_);
    }
  }
  if (this->param_propagate_down_[0] ||propagate_down[i]) {
    for (int n = 0; n < this->num_; ++n) {
      // Gradient w.r.t. weight. Note that we willaccumulate diffs.
      if (this->param_propagate_down_[0]) {
        this->weight_cpu_gemm(top_diff + n * this->top_dim_,
            bottom_data + n * this->bottom_dim_,weight_diff);
      }
      // Gradient w.r.t. bottom data, if necessary,reusing the column buffer
      // we might have just computed above.
      if (propagate_down[i]) {
        this->forward_cpu_gemm(top_diff + n * this->top_dim_, weight,
            bottom_diff + n * this->bottom_dim_,
            this->param_propagate_down_[0]);
      }
    }
  }

 

04 inner_product_layerfilter.hpp

既然卷积层已经读过了,现在该读一读全连接层了。

全连接层和卷积层的区别是什么?就是没有局部连接,每一个输出都跟所有输入有关,如果输入feature map是H*W,那么去卷积它的核也是这么大,得到的输出是一个1*1的值。

它在setup函数里面要做一些事情,其中最重要的就是设定weights的尺寸,下面就是关键代码。num_output是一个输出标量数,比如imagenet1000类,最终输出一个1000维的向量。

K是一个样本的大小,当axis=1,实际上就是把每一个输入样本压缩成一个数,C*H*W经过全连接变成1个数。

const int num_output = this->layer_param_.inner_product_param().num_output();
K_ = bottom[0]->count(axis);
  // 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_;
  }

所以,weight的大小就是N*K_。

有了这个之后,forward就跟conv_layer是一样的了。

好了,这一节虽然没有复杂的公式,但是很多东西够大家喝一壶了,得仔细推敲才能好好理解的。caffe_cpu_gemm是整节计算的核心,感兴趣的去看吧!

同时,在我的知乎专栏也会开始同步更新这个模块,欢迎来交流

https://zhuanlan.zhihu.com/c_151876233

注:部分图片来自网络

—END—

打一个小广告,我在gitchat开设了一些课程和chat,欢迎交流。

感谢各位看官的耐心阅读,不足之处希望多多指教。后续内容将会不定期奉上,欢迎大家关注有三公众号 有三AI!

 

 

100天精通Python(基础篇)——第1天:Python从起源到应用的全面解析(附上Python、PyCharm、VSCode下载安装详细教程) 阅读详情

相关推荐

LightGBM学习笔记

LightGB学习笔记1. lightgbm概述1.1 lightgbm介绍1.2 lightgbm和xgboost对比2 .直方图算法及改进2.1 直方图算法2.2 直方图算法改进2.2.1 GOSS算法3.树的生长策略4.系统设计5. 实战 lightgbm是基于boosting的学习框架,使用的是基于决策树的学习算法,具有更快的训练速度、更低的内存使用、更高的正确率、并且能够支持并行化学习,可以用来处理大规模数据,本文主要是针对学习视频https://www.bilibili.com/video/BV

一一xi的博客 3307

2018年深度学习技术栈演进:从PyTorch 1.0到BERT的工程化转折点

深度学习作为人工智能的核心技术,其发展经历了从理论探索到工程落地的关键转折。其核心原理是通过多层神经网络模拟人脑学习机制,从数据中自动提取特征表示。这一技术价值在于能够解决传统算法难以处理的复杂模式识别问题,如图像分类、自然语言处理和时间序列预测等。在应用场景上,深度学习已广泛应用于计算机视觉、语音识别、推荐系统等领域。2018年是深度学习技术栈演进的分水岭,PyTorch 1.0的发布统一了研发与生产工具链,确立了“动态开发、静态部署”的最佳实践;而BERT模型的提出则奠定了“预训练+微调”的范式,推动了

weixin_34310369的博客 429

结构力学程序算法理论基础(一)————虚功原理

1.约束 定义约束:限制质点或者质点系运动的条件称为约束,表示这些限制条件的数学方程称为约束方程。 (1)几何约束 限制质点或者质点系在空间的几何位置的条件称为几何约束.如单摆,其中质点M可绕固定点O在平面Oxy内摆动,摆长为l.这时摆杆对质点的限制条件是:质点M必须在以点O为圆心,以l为半径的圆周上运动.其约束方程为: 又质点M在固定的曲面上运动,那么曲面方程就是质点M的约束方程,即 上...

l93919861的博客 4512

[caffe解读] caffe从数学公式到代码实现5-caffe中的卷积

今天要讲的就是跟卷积相关的一些layer了im2col_layer.cppbase_conv_layer.cppconv_layer.cppdeconv_layer.cp...

hacker_long的专栏 516

Fluent温度超限调试:从能量方程到网格优化的实战指南

本文深入解析Fluent中温度超限问题的诊断与解决方法,从能量方程机制到网格优化策略,提供实战指南。通过分析温度与焓的换算关系,探讨典型场景如能量方程发散、局部网格质量差等,并给出五步调试法及高级命令应用,帮助工程师有效解决温度计算异常问题。

weixin_29045585的博客 531

【扣子AI开发】如何开发一个图像生成智能体?

【扣子AI开发】如何开发一个图像生成智能体,从创建到发布

tl437002770的博客 2428

caffe使用

目录 1.caffe源码深入学习5:超级详细的caffe卷积层代码解析 2.实用:使用caffe训练模型时solver.prototxt中的参数设置解析 1.caffe源码深入学习5:超级详细的caffe卷积层代码解析 https://blog.youkuaiyun.com/jiongnima/article/details/69055941 针对conv_layer.cpp 注意dilation...

sinat_31376397的博客 251

Smart PLC高速脉冲输入计米轮模块(编码器测速/计米详细讲解)

线缆行业单绞机PLC控制算法详细解读可以参看下面的文章链接:线缆行业单绞机控制算法(详细图解+代码)_RXXW_Dor的博客-优快云博客在了解单绞机之前需要大家对收放卷以及排线控制有一定的了解,不清楚的可以参看下面几篇博客,这里不再赘述,受水平和能力所限,文中难免出现错误和不足之处,诚恳的欢迎大家批评和指正。PLC张力控制(开环闭环算法分析)_RXXW_Dor的博客-优快云博客_plc闭环控制实例。SMART PLC时间间隔定时器应用(高速脉冲测频/测速)_RXXW_Dor的博客-优快云博客。

RXXW_Dor的博客 2821

caffe源码深入学习5:超级详细的caffe卷积层代码解析

caffe实现的卷积层是一个功能强大完整,同时也是一个相对复杂的层,涉及conv_layer.hpp,conv_layer.cpp,base_conv_layer.hpp和base_conv_layer.cpp,请读者朋友们仔细欣赏~

jiongnima的博客 1万+

2026华为OD机试(JavaScript)真题【E卷+A卷+B卷+C卷+D卷】目录+在线OJ

华为OD机试真题题库【E卷+A卷+B卷+C卷+D卷】目录,JavaScript实现。

MISAYAONE的博客 8万+

FastAPI 极简教程(偏实战,一篇搞定)

本文是一篇FastAPI实战教程,通过最少的概念讲解快速掌握FastAPI的核心功能。文章首先介绍FastAPI是基于Starlette和Pydantic的高性能Python API框架,具有自动生成文档、类型驱动校验、异步支持和依赖注入等优势。教程从安装环境开始,演示了如何创建最小项目并编写第一个"Hello World"接口。接着详细讲解了路由参数处理(Path/Query/Body)、Pydantic数据模型校验、响应模型控制、状态码设置等核心功能。在高级功能方面,重点介绍了错误处

AI天才研究院 821

Caffe源码精读 - 7 - Caffe Layers之deconv_layer(反卷积层)

Class_7 Caffe Layers之deconv_layer(反卷积层) 1. 概述 deconv_layer是反卷积层,在DL领域又叫转置卷积。 反卷积层到目前最大的用处就是实现上采样。典型的如yolov3和yolov4中,做feature map融合的时候,先采取1*1卷积降维,然后采用反卷积实现上采样。 deconv_layer也是和base_conv_layer有千丝万缕的联系,鉴于在讲conv_layer的时候已经讲过,在此不做赘述。 2. deconv_layer ...

tecsai的博客 665

Test-Time Adaptation Induces Stronger Accuracy and Agreement-on-the-Line

TTA不管是否提高了OOD的泛化性都增强了线性的趋势,TTA的初始目的是为了增强OOD的表现,一个人可能会觉得TTA使得ACL更强的原因是他消除了ID和OOD之间的差距,因此所有模型间ID vs OOD的准确率接近于y=x这条线,但是实际上这个解释不能够反映我们的经验发现,因为虽然TTA将分布之间的偏移简化为一种放缩的分布,但是这个放缩的因子幅值可能很大,也就是α<<1但是r>>1,这就会导致很偏离y=x这条线,而且理论分析也是,虽然ID和OOD的差距很大,但是线性关系还在。

m0_62673638的博客 149

caffe卷积层延伸:卷积核膨胀详细解析

在笔者的上一篇博客中,解析caffe的卷积层时,在conv_layer.cpp中有一个卷积核膨胀操作,在conv_layer.cpp的第17行有如下代码 const int kernel_extent = dilation_data[i] * (kernel_shape_data[i] - 1) + 1;    上面的代码描述了卷积核的膨胀操作,我们不妨来做个假设,卷积核为3*3的,膨胀系数为

jiongnima的博客 1万+

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

c++调用caffe ssd_【caffe教程5】caffe中的卷积

本文首发于公众号《有三AI》[caffe解读] caffe从数学公式到代码实现5-caffe中的卷积​mp.weixin.qq.com今天要讲的就是跟卷积相关的一些layer了im2col_layer.cppbase_conv_layer.cppconv_layer.cppdeconv_layer.cppinner_product_layer.cpp1 im2col_layer.cpp这是caff...

weixin_34800976的博客 259

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

【好专栏】深度学习框架不会用?12大主流深度学习框架快速入门,你值得拥有!...

欢迎来到【好专栏】,本次我们给大家推荐一个专栏,名为《12 大深度学习开源框架快速入门》,这个专栏为介绍了深度学习的 12 大主流框架,附带实践案例,能够让读者快速掌握深度学习框架的使用...

Leong Blog 516

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Caffe源码精读 - 3 - Caffe Layers之conv_layer(卷积层)

Caffe Layers之conv_layer(卷积层) 概述 卷积层是组成卷积神经网络的基础应用层,也是最常用的层部件。而卷积神经网路有事当前深度学习的根本。在一般算法的Backbone、neck和head基本都是由卷积层组成。 卷积操作 一般从数学角度讲,卷积分两个步骤,第一步做翻转,第二部乘积求和。 DL中的卷积操作是一种无翻转卷积,类似于相关操作。卷积的作用即是提取特征,通过一层层的卷积,使得特征被一步步浓缩。在卷积操作过程中,浅层卷积保留更多的描述信息,而深层卷积描述更多的抽象特..

tecsai的博客 1971

caffe源码解读

caffe源码解读Caffe组成结构Blob模块SyncedMemory类layer类输入层Data Layer网络层ReLULayer Caffe组成结构 caffe主要由下列4个类组成:Blob;layer;Net;Solver。主要功能如下: caffe.proto定义了多个message结构体,它包含了网络需要传递的参数。required是必须要定义的,optional是可选的(后面可加默...

renchenliang的博客 713

Caffe(6)--神经网络中卷积层、池化层输出特征图大小计算

在图像卷积和池化操作中有固定的kernel_size和stride,当stride &amp;amp;gt; 1时,边界上会有可能发生越界的问题。 Caffe中的卷积、池化后输出图像尺寸计算 (1)卷积 计算定义在conv_layer.cpp中的compute_output_shape()函数中 const int output_h = (height + 2 * pad_h - (dilatio...

对角巷 1万+

caffe代码阅读10:Caffe中卷积的实现细节(涉及到BaseConvolutionLayer、ConvolutionLayer、im2col等)-2016.4.3

本文介绍了Caffe中的卷积层的实现,以及卷积实现的时候所使用的im2col的实现,其中重点介绍了im2col的实现原理。

一亩半分地 1万+

Caffe源码(五):conv_layer 分析

目录目录 简单介绍 主要函数 compute_output_shape 函数 Forward_cpu 函数 Backward_cpu 函数简单介绍首先要明确的一点是:ConvolutionLayer 是 BaseConvolutionLayer的子类,BaseConvolutionLayer 是 Layer 的子类。ConvolutionLayer 除了继承了相应的成员变量和函数以外,自己的成员函数

seven_first的博客 9132

Caffe中DeconvolutionLayer的用法

写在前面:关于Deconvolution 与Transposed Convolution的字面上的区别,在此不再讨论,以下统称为Deconvolution,可参考http://blog.youkuaiyun.com/u013250416/article/details/78247818。在我的理解里面,Convolution是将大尺寸的feature map转换为小尺寸的feature map,而Deconv...

北望花村 9542

Caffe中卷积层的实现

作者:xg123321123出处:http://blog.youkuaiyun.com/xg123321123/article/details/53319080声明:版权所有,转载请联系作者并注明出处1 简述 使用im2col分别将featrue maps和filter转换成矩阵; 调用GEMM(GEneralized Matrix Multiplication)对两矩阵内积,这样一来卷积操作就被转化为了矩阵乘

时光杂货店 6014

caffe中的卷积层源码笔记

卷积层的实现在conv_layer.hpp和conv_layer.cpp,在于其中的ConvolutionLayer类,继承于BaseConvolutionLayer类。 卷积主要的处理过程是将图像bottom[i]->cpu_data()+j * this->bottom_dim_进行im2col处理,转化成一个新的矩阵col_buff,然后用权值矩阵weights∗*转化后的矩阵 得到

便纵有千种风情 1014

Caffe解读(一)

Caffe 基本架构 理解caffe的架构,包括blob,layer,net,solver,tools的关系等 源码文件结构 tools 保存的源码是用于生成二进制处理程序的,caffe在训练时实际是直接调用这些二进制文件 include Caffe的实现代码的头文件 src 实现Caffe的源文件 gtest: google test一个用于测试的库你make runtest时看见的很多

cookie_234的博客 714

caffe各层解析以及参数说明(慢慢整理,做个笔记)

Convolution层 layer { name: &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;conv1&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot; type: &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;a

mingo_敏 3413
上一篇: 【分享预告】细数GAN和图像分类的前世今生
下一篇: 【caffe解读】 caffe从数学公式到代码实现4-认识caffe自带的7大loss
言有三
言有三 领域专家: 人工智能技术领域 领域专家: 人工智能技术领域
博客等级 码龄15年 3635粉丝 976原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

言有三

三人行必有AI

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值