tiny_cnn源码阅读(4)-convolutional_layer

原创 2016年06月02日 22:39:45

convolutional_layer是用来计算卷积的。

connection_table

connecction_table是一个二维数组,(x,y)的值(true/false)表示x和y是否关联,即是用来判断和那几个feature_map连接的。在其实现中,内部数据结构为一个bool类型队列,函数is_connected判断是否关联

bool is_connected(cnn_size_t x, cnn_size_t y) const {//判断x,y位置的值即可。如果connection table为空,那么一直为真
        return is_empty() ? true : connected_[y * cols_ + x];
    }

index3d

这个数据结构只是记录图像尺寸,即宽-高-深度,实际上并不持有数据

template <typename T>
struct index3d {//用来保存图像的数据结构,实际上并不保存数据,宽-高-深度,共三个参数
    index3d(T width, T height, T depth) {
        reshape(width, height, depth);
    }

    index3d() : width_(0), height_(0), depth_(0) {}

    void reshape(T width, T height, T depth) {
        width_ = width;
        height_ = height;
        depth_ = depth;

        if ((long long) width * height * depth > std::numeric_limits<T>::max())
            throw nn_error(
            format_str("error while constructing layer: layer size too large for tiny-cnn\nWidthxHeightxChannels=%dx%dx%d >= max size of [%s](=%d)",
            width, height, depth, typeid(T).name(), std::numeric_limits<T>::max()));
    }

    T get_index(T x, T y, T channel) const {
        assert(x >= 0 && x < width_);
        assert(y >= 0 && y < height_);
        assert(channel >= 0 && channel < depth_);
        return (height_ * channel + y) * width_ + x; 
    }

    T area() const {
        return width_ * height_;
    }

    T size() const {
        return width_ * height_ * depth_;
    }

    T width_;
    T height_;
    T depth_;
};

convolutional_layer

convolutional_layer用来计算卷积,先看一下其成员变量:

const vec_t* prev_out_padded_[CNN_TASK_SIZE];//指针,pad_type_ == padding::same时,填充用
    vec_t* prev_out_buf_[CNN_TASK_SIZE];
    vec_t  prev_delta_padded_[CNN_TASK_SIZE];//用来填充prev_delta_
    vec_t  prev_delta2_padded_;//用来填充prev_delta2_

    connection_table tbl_;//connection_table
    index3d<cnn_size_t> in_;//输入数据大小
    index3d<cnn_size_t> in_padded_;//填充大小,用来内存对齐
    index3d<cnn_size_t> out_;//输出大小
    index3d<cnn_size_t> weight_;//权重大小
    padding pad_type_;//枚举变量,输出大小是原图像大小还是卷积后:原图像-卷积核+1
    size_t w_stride_;//stride表示窗口移动的大小。卷积时窗口每次滑动1,但是pooling时每次滑动的大小为卷积核大小
    size_t h_stride_;

pad_type_表示填充类型。卷积时,如果如果不填充,卷积核每次滑动距离w_stride_h_stride_都为1,那么卷积后图像:高=原高度-卷积核高+1;宽=原宽度-卷积核宽度+1。

卷积层重点是卷积,看一下卷积函数

 virtual const vec_t& forward_propagation(const vec_t& in_raw, size_t worker_index) override
    {
        copy_and_pad_input(in_raw, static_cast<int>(worker_index));

        vec_t &a = a_[worker_index]; // w*x,输出
        vec_t &out = output_[worker_index]; // output
        const vec_t &in = *(prev_out_padded_[worker_index]); // input

        std::fill(a.begin(), a.end(), float_t(0));

        for_i(parallelize_, out_.depth_, [&](int o) {
            for (cnn_size_t inc = 0; inc < in_.depth_; inc++) {
                if (!tbl_.is_connected(o, inc)) continue;//通过connection_table判断是否有关联,connection_table为空则全部关联

                const float_t *pw = &this->W_[weight_.get_index(0, 0, in_.depth_ * o + inc)];//权重
                const float_t *pi = &in[in_padded_.get_index(0, 0, inc)];//输入
                float_t *pa = &a[out_.get_index(0, 0, o)];//pa用来保存计算结果

                //计算输出的值
                for (cnn_size_t y = 0; y < out_.height_; y++) {
                    for (cnn_size_t x = 0; x < out_.width_; x++) {
                        const float_t * ppw = pw;//指向权重,下面ppi指向输入数据
                        const float_t * ppi = pi + (y * h_stride_) * in_padded_.width_ + x * w_stride_;
                        float_t sum = float_t(0);

                        //下面是计算卷积
                        // should be optimized for small kernel(3x3,5x5)
                        for (cnn_size_t wy = 0; wy < weight_.height_; wy++) {
                            for (cnn_size_t wx = 0; wx < weight_.width_; wx++) {
                                sum += *ppw++ * ppi[wy * in_padded_.width_ + wx];//卷积为:sum(权重x某一像素)
                            }
                        }
                        pa[y * out_.width_ + x] += sum;//结果保存到输出
                    }
                }
            }
            //bias不为空时还要加上bias
            if (!this->b_.empty()) {
                float_t *pa = &a[out_.get_index(0, 0, o)];
                float_t b = this->b_[o];
                std::for_each(pa, pa + out_.width_ * out_.height_, [&](float_t& f) { f += b; });
            }
        });

        for_i(parallelize_, out_size_, [&](int i) {
            out[i] = h_.f(a, i);
        });

        CNN_LOG_VECTOR(in_raw, "[pc]in");
        CNN_LOG_VECTOR(W_, "[pc]w");
        CNN_LOG_VECTOR(a, "[pc]a");
        CNN_LOG_VECTOR(out, "[pc]forward");

        return next_ ? next_->forward_propagation(out, worker_index) : out;//后面有网络,就再计算下一层
    }

上面变量中a_用来保存:wx+b;out用来保存:f(wx+b)
权重保存的W_中,输入数据为函数参数in_raw
卷积时,遍历输出的每个像素点:

//计算输出的值
                for (cnn_size_t y = 0; y < out_.height_; y++) {
                    for (cnn_size_t x = 0; x < out_.width_; x++) {

之后遍历卷积核,卷积某个区域,计算wx:

for (cnn_size_t wy = 0; wy < weight_.height_; wy++) {
                            for (cnn_size_t wx = 0; wx < weight_.width_; wx++) {

随后在加上偏置bias,计算最终结果

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

华为面试自我介绍

面试官您好,我叫*,来自南京航空航天大学,是即将17年毕业的硕士研究生,研究生期间主要做图像处理、机器视觉方面的工作,所以我的求职意向是媒体算法。我的本科也是在南京航空航天大学理学院读的,专业是信息与...

深度学习与计算机视觉系列(7)_神经网络数据预处理,正则化与损失函数

1. 引言 上一节我们讲完了各种激励函数的优缺点和选择,以及网络的大小以及正则化对神经网络的影响。这一节我们讲一讲输入数据预处理、正则化以及损失函数设定的一些事情。 2. 数据与网络的设定 前一节提到...

机器学习系列(4)_机器学习算法一览,应用建议与解决思路

我们先带着大家过一遍传统机器学习算法,基本思想和用途。把问题解决思路和方法应用建议提前到这里的想法也很简单,希望能提前给大家一些小建议,对于某些容易出错的地方也先给大家打个预防针,这样在理解后续相应机...

腾讯公司十周年号中奖查询电话是多少∷腾讯公司10周年号中奖查询电话是多少

腾讯公司 总 部 电 话《95013+2195+0586》抽奖电话《95013+2195+0586》活动热线《95013+2195+0586》非常6+1 电 话《95013+2195+0586》幸 运...

深度学习与计算机视觉系列(2)_图像分类与KNN

图像分类问题这是很久以前就引起关注的一类图像相关问题。 对于一张输入的图片,要判定它属于给定的一些标签/类别中的哪一个。看似很简单的一个问题,这么多年却一直是计算机视觉的一个核心问题。应用场景也非常...

深度学习与计算机视觉系列(1)_基础介绍

为了简单易读易懂,这个系列中绝大多数的代码都使用python完成。这里稍微介绍一下python和Numpy/Scipy(python中的科学计算包)的一些基础。 python是一种长得像伪代码,具备...

深度学习与计算机视觉系列(3)_线性SVM与SoftMax分类器

这个部分我们介绍一类新的分类器方法,而对其的改进和启发也能帮助我们自然而然地过渡到深度学习中的卷积神经网。有两个重要的概念: 得分函数/score function:将原始数据映射到每个类的打分的函...

机器学习系列(6)_从白富美相亲看特征预处理与选择(下)

初步划定特征的范围,获取特征 李雷早就想过这个问题了。长期的职业素养让他对任何事情都想用机器学习的方法去鼓捣。李雷的基本思路是这样的,我们尽可能观察螃蟹更多的特征,从中找出与“螃蟹满黄”最相关的特征...

cs231n-(1)图像分类和kNN

cs231n(1) 讲述图像分类的任务、难点;训练集、验证集、测试集区别;NN

cs231n-(9)迁移学习和Fine-tune网络

介绍适合fine-tune的场景,以及方法。
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)