使用C++编写卷积神经网络(一)

    xiaoxiao2022-07-13  152

    鉴于用python写神经网络、卷积神经网络的话,不利于框架直接用numpy等来完成,速度实在是慢的吓人,所以改用c++来写。c++的话什么操作基本都要自己定义。由于卷积神经网络主要针对图像类,故最好定义一个适用于图像类的矩阵类,我这边使用了网上一个开源的用c++写好的卷积神经网络,使用了里面定义好的矩阵类,如下。这个类个人觉得非常好用,故写成博客,收藏一波,方便以后使用时候查找。

    首先导入需要的模块:

    #include <math.h> #include <string.h> #include <string> #include <cstdlib> #include <random> #include <algorithm>

    然后定义一个矩阵类,主要用于图像处理方面: 里面包括了矩阵的各种属性,宽、高、通道数,大小,元素个数等,以及对矩阵的各种操作,包括padding、翻转等,还有矩阵之间的各种运算,+、-、*、+=等。。。

    namespace mojo { enum pad_type { zero = 0, edge = 1, median_edge = 2 }; // matrix class --------------------------------------------------- // should use opencv if available // class matrix { int _size; int _capacity; float *_x_mem; void delete_x() { delete[] _x_mem; x = NULL; _x_mem = NULL; } // 4 extra for alignment and 4 for 3 padding for SSE //float *new_x(const int size) { _x_mem = new float[size + 4+3]; x = (float *)(((uintptr_t)_x_mem + 16) & ~(uintptr_t)0x0F); return x; } // avx mem aligment float *new_x(const int size) { _x_mem = new float[size + 8 + 7]; x = (float *)(((uintptr_t)_x_mem + 32) & ~(uintptr_t)0x1F); return x; } public: std::string _name; int cols, rows, chans; int chan_stride; int chan_aligned; float *x; // size must be divisible by 8 for AVX virtual int calc_chan_stride(int w, int h) { if (chan_aligned) { int s = w*h; const int remainder = s % 8; if (remainder > 0) s += 8 - remainder; return s; } else return w*h; } matrix( ): cols(0), rows(0), chans(0), _size(0), _capacity(0), chan_stride(0), x(NULL), chan_aligned(0)/*, empty_chan(NULL)*/{} matrix( int _w, int _h, int _c=1, const float *data=NULL, int align_chan=0): cols(_w), rows(_h), chans(_c) { chan_aligned = align_chan; chan_stride = calc_chan_stride(cols, rows); _size= chan_stride*chans; _capacity=_size; x = new_x(_size); if(data!=NULL) memcpy(x,data,_size*sizeof(float)); } // copy constructor - deep copy matrix( const matrix &m) : cols(m.cols), rows(m.rows), chan_aligned(m.chan_aligned), chans(m.chans), chan_stride(m.chan_stride), _size(m._size), _capacity(m._size) {x = new_x(_size); memcpy(x,m.x,sizeof(float)*_size); /*empty_chan = new unsigned char[chans]; memcpy(empty_chan, m.empty_chan, chans);*/} // { v=m.v; x=(float*)v.data();} // copy and pad constructor matrix( const matrix &m, int pad_cols, int pad_rows, mojo::pad_type padding= mojo::zero, int threads=1) : cols(m.cols), rows(m.rows), chans(m.chans), chan_aligned(m.chan_aligned), chan_stride(m.chan_stride), _size(m._size), _capacity(m._size) { x = new_x(_size); memcpy(x, m.x, sizeof(float)*_size); *this = pad(pad_cols, pad_rows, padding, threads); } ~matrix() { if (x) delete_x(); } matrix get_chans(int start_channel, int num_chans=1) const { return matrix(cols,rows,num_chans,&x[start_channel*chan_stride]); } // if edge_pad==0, then the padded area is just 0. // if edge_pad==1 it fills with edge pixel colors // if edge_pad==2 it fills with median edge pixel color matrix pad(int dx, int dy, mojo::pad_type edge_pad = mojo::zero, int threads=1) const { return pad(dx, dy, dx, dy, edge_pad, threads); } matrix pad(int dx, int dy, int dx_right, int dy_bottom, mojo::pad_type edge_pad = mojo::zero, int threads=1) const { matrix v(cols+dx+dx_right,rows+dy+dy_bottom,chans);//,NULL,this->chan_aligned); v.fill(0); //float *new_x = new float[chans*w*h]; #pragma omp parallel for num_threads(threads) for(int k=0; k<chans; k++) { const int v_chan_offset=k*v.chan_stride; const int chan_offset=k*chan_stride; // find median color of perimeter float median = 0.f; if (edge_pad == mojo::median_edge) { int perimeter = 2 * (cols + rows - 2); std::vector<float> d(perimeter); for (int i = 0; i < cols; i++) { d[i] = x[i+ chan_offset]; d[i + cols] = x[i + cols*(rows - 1)+ chan_offset]; } for (int i = 1; i < (rows - 1); i++) { d[i + cols * 2] = x[cols*i+ chan_offset]; // file from back so i dont need to cal index d[perimeter - i] = x[cols - 1 + cols*i+ chan_offset]; } std::nth_element(d.begin(), d.begin() + perimeter / 2, d.end()); median = d[perimeter / 2]; //for (int i = 0; i < v.rows*v.cols; i++) v.x[v_chan_offset + i] = solid_fill; } for(int j=0; j<rows; j++) { memcpy(&v.x[dx+(j+dy)*v.cols+v_chan_offset], &x[j*cols+chan_offset], sizeof(float)*cols); if(edge_pad== mojo::edge) { // do left/right side for(int i=0; i<dx; i++) v.x[i+(j+dy)*v.cols+v_chan_offset]=x[0+j*cols+chan_offset]; for (int i = 0; i<dx_right; i++) v.x[i + dx + cols + (j + dy)*v.cols + v_chan_offset] = x[(cols - 1) + j*cols + chan_offset]; } else if (edge_pad == mojo::median_edge) { for (int i = 0; i < dx; i++) v.x[i + (j + dy)*v.cols + v_chan_offset] = median; for (int i = 0; i < dx_right; i++) v.x[i + dx + cols + (j + dy)*v.cols + v_chan_offset] = median; } } // top bottom pad if(edge_pad== mojo::edge) { for(int j=0; j<dy; j++) memcpy(&v.x[(j)*v.cols+v_chan_offset],&v.x[(dy)*v.cols+v_chan_offset], sizeof(float)*v.cols); for (int j = 0; j<dy_bottom; j++) memcpy(&v.x[(j + dy + rows)*v.cols + v_chan_offset], &v.x[(rows - 1 + dy)*v.cols + v_chan_offset], sizeof(float)*v.cols); } if (edge_pad == mojo::median_edge) { for (int j = 0; j<dy; j++) for (int i = 0; i<v.cols; i++) v.x[i + j*v.cols + v_chan_offset] = median; for (int j = 0; j<dy_bottom; j++) for (int i = 0; i<v.cols; i++) v.x[i + (j + dy + rows)*v.cols + v_chan_offset] = median; } } return v; } matrix crop(int dx, int dy, int w, int h, int threads=1) const { matrix v(w,h,chans); #pragma omp parallel for num_threads(threads) for(int k=0; k<chans; k++) { for(int j=0; j<h; j++) { memcpy(&v.x[j*w+k*v.chan_stride], &x[dx+(j+dy)*cols+k*chan_stride], sizeof(float)*w); } } return v; } mojo::matrix shift(int dx, int dy, mojo::pad_type edge_pad=mojo::zero) { int orig_cols=cols; int orig_rows=rows; int off_x=abs(dx); int off_y=abs(dy); mojo::matrix shifted= pad(off_x, off_y, edge_pad); return shifted.crop(off_x-dx, off_y-dy,orig_cols,orig_rows); } mojo::matrix flip_cols() { mojo::matrix v(cols,rows,chans); for(int k=0; k<chans; k++) for(int j=0; j<rows; j++) for(int i=0; i<cols; i++) v.x[i+j*cols+k*chan_stride]=x[(cols-i-1)+j*cols+k*chan_stride]; return v; } mojo::matrix flip_rows() { mojo::matrix v(cols, rows, chans); for (int k = 0; k<chans; k++) for (int j = 0; j<rows; j++) memcpy(&v.x[(rows-1-j)*cols + k*chan_stride],&x[j*cols + k*chan_stride], cols*sizeof(float)); return v; } void clip(float min, float max) { int s = chan_stride*chans; for (int i = 0; i < s; i++) { if (x[i] < min) x[i] = min; if (x[i] > max) x[i]=max; } } void min_max(float *min, float *max, int *min_i=NULL, int *max_i=NULL) { int s = rows*cols; int mini = 0; int maxi = 0; for (int c = 0; c < chans; c++) { const int t = chan_stride*c; for (int i = t; i < t+s; i++) { if (x[i] < x[mini]) mini = i; if (x[i] > x[maxi]) maxi = i; } } *min = x[mini]; *max = x[maxi]; if (min_i) *min_i = mini; if (max_i) *max_i = maxi; } float mean() { const int s = rows*cols; int cnt = 0;// channel*s; float average = 0; for (int c = 0; c < chans; c++) { const int t = chan_stride*c; for (int i = 0; i < s; i++) average += x[i + t]; } average = average / (float)(s*chans); return average; } float remove_mean(int channel) { int s = rows*cols; int offset = channel*chan_stride; float average=0; for(int i=0; i<s; i++) average+=x[i+offset]; average= average/(float)s; for(int i=0; i<s; i++) x[i+offset]-=average; return average; } float remove_mean() { float m=mean(); int s = chan_stride*chans; //int offset = channel*s; for(int i=0; i<s; i++) x[i]-=m; return m; } void fill(float val) { for(int i=0; i<_size; i++) x[i]=val; } void fill_random_uniform(float range) { std::mt19937 gen(0); std::uniform_real_distribution<float> dst(-range, range); for (int i = 0; i<_size; i++) x[i] = dst(gen); } void fill_random_normal(float std) { std::mt19937 gen(0); std::normal_distribution<float> dst(0, std); for (int i = 0; i<_size; i++) x[i] = dst(gen); } // deep copy inline matrix& operator =(const matrix &m) { resize(m.cols, m.rows, m.chans, m.chan_aligned); memcpy(x,m.x,sizeof(float)*_size); // memcpy(empty_chan, m.empty_chan, chans); return *this; } int size() const {return _size;} void resize(int _w, int _h, int _c, int align_chans=0) { chan_aligned = align_chans; int new_stride = calc_chan_stride(_w,_h); int s = new_stride*_c; if(s>_capacity) { if(_capacity>0) delete_x(); _size = s; _capacity=_size; x = new_x(_size); } cols = _w; rows = _h; chans = _c; _size = s; chan_stride = new_stride; } // dot vector to 2d mat inline matrix dot_1dx2d(const matrix &m_2d) const { mojo::matrix v(m_2d.rows, 1, 1); for(int j=0; j<m_2d.rows; j++) v.x[j]=dot(x,&m_2d.x[j*m_2d.cols],_size); return v; } // += inline matrix& operator+=(const matrix &m2){ for(int i = 0; i < _size; i++) x[i] += m2.x[i]; return *this; } // -= inline matrix& operator-=(const matrix &m2) { for (int i = 0; i < _size; i++) x[i] -= m2.x[i]; return *this; } // *= float inline matrix operator *=(const float v) { for (int i = 0; i < _size; i++) x[i] = x[i] * v; return *this; } inline matrix operator *=(const matrix &v) { for (int i = 0; i < _size; i++) x[i] = x[i] * v.x[i]; return *this; } inline matrix operator *(const matrix &v) { matrix T(cols, rows, chans); for (int i = 0; i < _size; i++) T.x[i] = x[i] * v.x[i]; return T; } // * float inline matrix operator *(const float v) { matrix T(cols, rows, chans); for (int i = 0; i < _size; i++) T.x[i] = x[i] * v; return T; } // + float inline matrix operator +(const float v) { matrix T(cols, rows, chans); for (int i = 0; i < _size; i++) T.x[i] = x[i] + v; return T; } // + inline matrix operator +(matrix m2) { matrix T(cols,rows,chans); for(int i = 0; i < _size; i++) T.x[i] = x[i] + m2.x[i]; return T; } // - inline matrix operator -(matrix m2) { matrix T(cols, rows, chans); for (int i = 0; i < _size; i++) T.x[i] = x[i] - m2.x[i]; return T; } }; }// namespace
    最新回复(0)