tiny_cnn源码阅读(4)-convolutional_layer

  • connection_table
  • index3d
  • convolutional_layer

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 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 in_;//输入数据大小
    index3d in_padded_;//填充大小,用来内存对齐
    index3d out_;//输出大小
    index3d 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,计算最终结果

你可能感兴趣的:(tiny_cnn源码阅读)