Hyunjun

simple_convnet_cpp 코드 추가

......@@ -26,5 +26,17 @@ test하는 부분만 골라내기 위해 python test 코드를 추가(test.py), simple_convnet
7. python test 폴더 추가
python test 폴더에는 test에 필요하지 않은 train 부분을 삭제함
8. make_img_py 추가
이미지를 불러와 (32,32,3)의 크기로 resize한 후 input.txt에 저장함
\ No newline at end of file
8. make_img.py 추가
이미지를 불러와 (32,32,3)의 크기로 resize한 후 input.txt에 저장함
9. simple_convnet_cpp 코드 추가
1) layers.hpp : Convolution, ReLu, Normalization, Pooling, DW_Conv등 각 layer가 구현
2) SimpleConvNet.hpp : 딥러닝 모델이 구현
3) input.txt : make_img.py코드로 만든 이미지를 (32,32,3)의 크기로 만들어 txt파일로 저장
4) pred.txt : 1개의 이미지만 넣으면 예측이 되지않아 dummy를 같이 읽어서 처리함. 출력은 되지않음
5) params.txt : 속도향상을 위해 params.pkl파일을 params.txt로 변환
6) main.cpp
*c++ 컴파일러 버전 11이상
*프로젝트 생성 시 sdl 검사 체크 해제
\ No newline at end of file
......
#ifndef LAYERS_
#define LAYERS_
#include<vector>
#include<cmath>
// 3D Matrix
struct Mat {
int dim, row, col;
std::vector< double > mat;
Mat(int dim, int row, int col, std::vector< double > v) : dim(dim), row(row), col(col), mat(v) {};
};
// Mat 일차원으로 계산 + dimension 변수
class Layer {
public:
virtual std::vector<Mat> forward(std::vector<Mat> &x) { return std::vector<Mat>(); }
};
// padding
class Convolution : public Layer {
private:
std::vector<Mat> W;
int stride, pad;
public:
Convolution() {};
~Convolution() {};
Convolution(std::vector<Mat> W, int stride=1, int pad=0) : W(W), stride(stride), pad(pad) {};
// Each image x conv with each w
virtual std::vector<Mat> forward(std::vector<Mat> &x) {
std::vector< Mat > out;
int n, nw;
for (n = 0; n < x.size(); n++) {
std::vector<double>rev;
for (nw = 0; nw < W.size(); nw++) {
auto e = Convolution_(x[n], W[nw]);
rev.insert(rev.end(), e.begin(), e.end());
}
int out_r = (x[n].row + 2 * pad - W[0].row) / stride + 1;
int out_c = (x[n].col + 2 * pad - W[0].col) / stride + 1;
out.push_back(Mat(nw, out_r, out_c, rev));
}
return out;
}
// Convolution x and W (both are 3-D Mat)
std::vector<double> Convolution_(const Mat& x,const Mat& w) {
std::vector<double> ret;
int ndim = x.dim - w.dim + 1;
for (int d = 0; d < x.dim - w.dim + 1; d++) {
for (int r = -pad; r < x.row - w.row + 1 + pad; r++) {
for (int c = -pad; c < x.col - w.col +1 +pad; c++) {
ret.push_back(Convolution_(x, w, d, r, c));
}
}
}
return ret;
}
double Convolution_(const Mat& x, const Mat& w, int d, int r,int c) {
double ret = 0, xx=0;
int ds = w.col * w.row, rs = w.col;
int dxs = x.col * x.row, rxs = x.col;
for (int dd = 0; dd < w.dim; dd++) {
for (int rr = 0; rr < w.row; rr++) {
for (int cc = 0; cc < w.col; cc++) {
if ((pad > 0) && (r + rr < 0 || c + cc < 0 || r + rr >= x.row || c + cc >= x.col))
xx = 0;
else
xx = x.mat[(d + dd)*(dxs)+(r + rr)*rxs + (c + cc)];
ret += xx * w.mat[dd*(ds)+rr*(rs)+cc];
}
}
}
return ret;
}
};
// Depthwise Conv
class DW_Convolution : public Layer {
private:
std::vector<Mat> W;
int stride, pad;
public:
DW_Convolution() {};
~DW_Convolution() {};
DW_Convolution(std::vector<Mat> W, int stride=1, int pad=0) : W(W), stride(stride), pad(pad) {};
virtual std::vector<Mat> forward(std::vector<Mat> &x) {
std::vector<Mat> out;
int n, d;
for (n = 0; n < x.size(); n++) {
// Each dimension Conv with each filter
std::vector<double> rev;
for (d = 0; d < x[n].dim; d++) {
std::vector<double> e = Convolution_(x[n], W[d], d);
rev.insert(rev.end(), e.begin(), e.end());
}
int out_r = (x[n].row + 2 * pad - W[0].row) / stride + 1;
int out_c = (x[n].col + 2 * pad - W[0].col) / stride + 1;
out.push_back(Mat(d, out_r, out_c, rev));
}
return out;
}
std::vector<double> Convolution_(const Mat& x, const Mat& w, int d) {
std::vector<double> out;
int dd = d * x.col * x.row;
for (int r = -pad; r < x.row - w.row + 1 + pad; r++) { // r+=stride
for (int c = -pad; c < x.col - w.col + 1 + pad; c++) {
out.push_back(Convolution_(x, w, dd, r, c));
}
}
return out;
}
double Convolution_(const Mat& x, const Mat& w, int dd, int r, int c) {
double ret = 0, xx=0;
for (int rr = 0; rr < w.row; rr++) {
for (int cc = 0; cc < w.col; cc++) {
if ((pad > 0) && (r + rr < 0 || c + cc < 0 || r + rr >= x.row || c + cc >= x.col))
xx = 0;
else
xx = x.mat[dd + (r + rr)*x.col + (c+cc)];
ret += xx * w.mat[rr*w.col + cc];
}
}
return ret;
}
};
// n개의 이미지 같은 위치의 Row, col 값을 Normalization
class LightNormalization : public Layer{
public:
virtual std::vector<Mat> forward(std::vector<Mat>& x) {
std::vector<Mat> out;
int dim = x[0].dim, row = x[0].row, col = x[0].col, nx = x.size();
int ds = row*col;
for (int d = 0; d < dim; d++) {
double mu = 0, var=0, std, tmp; // mu : mean of x img each dim
for (int r = 0; r < row; r++)
for (int c = 0; c < col; c++)
for (int n = 0; n < nx; n++)
mu += x[n].mat[d*ds + r*col + c];
mu = mu / (double)(row*col*nx);
for (int r = 0; r < row; r++)
for (int c = 0; c < col; c++)
for (int n = 0; n < nx; n++) {
tmp = x[n].mat[d*ds + r*col + c] - mu;
var += (tmp*tmp);
}
var = var / (double)(row*col*nx);
std = sqrt(var+10e-7);
for (int r = 0; r < row; r++)
for (int c = 0; c < col; c++)
for (int n = 0; n < nx; n++)
x[n].mat[d*ds + r*col + c] = (x[n].mat[d*ds + r*col + c] - mu) / std;
}
return x;
}
};
class Relu : public Layer {
public:
virtual std::vector<Mat> forward(std::vector<Mat> &x) {
int nx = x.size(), nm = x[0].dim * x[0].row * x[0].col;
for (int n = 0; n < nx; n++)
for (int i = 0; i < nm; i++)
if (x[n].mat[i] < 0)
x[n].mat[i] = 0;
return x;
}
};
class Pooling : public Layer{
private:
int pool_h, pool_w, stride, pad;
public:
Pooling() { pad = 0; };
~Pooling() {};
Pooling(int pool_h, int pool_w, int stride=1, int pad=0) :pool_h(pool_h), pool_w(pool_w), stride(stride), pad(pad) {};
virtual std::vector<Mat> forward(std::vector<Mat>& x) {
std::vector<Mat> out;
int n, d, nx = x.size();
for (n = 0; n < nx; n++) {
std::vector<double> rev;
for (d = 0; d < x[n].dim; d++) {
std::vector<double> e = MaxPooling_(x[n], d);
rev.insert(rev.end(), e.begin(), e.end());
}
int out_h = (x[n].row + 2 * pad - pool_h) / stride + 1;
int out_w = (x[n].col + 2 * pad - pool_w) / stride + 1;
out.push_back(Mat(d, out_h, out_w, rev));
}
return out;
}
// Pooling each image
std::vector<double> MaxPooling_(Mat& x, int d) {
std::vector<double> out;
int row = x.row, col = x.col;
int dd = d * col * row;
for (int r = -pad; r < row - pool_h + 1 + pad; r+=stride) {
for (int c = -pad; c < col - pool_w + 1 + pad; c+=stride) {
out.push_back(MaxPooling_(x, dd, r, c));
}
}
return out;
}
// Pooling pool_w * pool_h
double MaxPooling_(Mat& x, int dd, int r, int c) {
double ret = 0, xx = 0;
for (int rr = 0; rr < pool_h; rr++) {
for (int cc = 0; cc < pool_w; cc++) {
if ((pad > 0) && (r + rr < 0 || c + cc < 0 || r + rr >= x.row || c + cc >= x.col))
xx = 0;
else
xx = x.mat[dd + (r + rr)*x.col + (c + cc)];
if(ret < xx)
ret = xx;
}
}
return ret;
}
};
class Affine : public Layer{
private:
std::vector<Mat> W;
public:
Affine() {}
~Affine() {}
Affine(std::vector<Mat>& W) : W(W){}
virtual std::vector<Mat> forward(std::vector<Mat>& x) {
std::vector<Mat> out;
int nx = x.size();
for (int n = 0; n < nx; n++) {
Mat e = Dot_(x[n]);
out.push_back(e);
}
return out;
}
Mat Dot_(const Mat& x) {
int dim = W[0].dim, row = W[0].row, col = W[0].col, nw = W.size();
int size = dim*row*col;
std::vector<double> ret(col);
for (int c = 0; c < col; c++) {
for (int n = 0; n < nw; n++) {
ret[c] += W[n].mat[c] * x.mat[n];
}
}
return Mat(col, 1, 1, ret);
}
};
#endif
#ifndef SIMPLECONV_
#define SIMPLECONV_
#include"Layers.hpp"
#include<iostream>
#include<cstdio>
#include<string.h>
#include<stdlib.h>
struct input_dim {
int d1, d2, d3;
input_dim(int d1, int d2, int d3) :d1(d1), d2(d2), d3(d3) {};
};
struct conv_param {
int fn1, fn2, fn3;
int filtersize, pad, stride;
conv_param(int ftnum1, int ftnum2, int ftnum3, int ftsize, int pad, int stride) :fn1(ftnum1),
fn2(ftnum2), fn3(ftnum3), filtersize(ftsize), pad(pad), stride(stride) {};
};
class SimpleConvNet {
private:
std::vector< Layer* > layers;
std::vector<Mat> W[7]; // weights
std::vector<int> shape[7]; // shape of each weights
public:
SimpleConvNet() {}
~SimpleConvNet() {}
SimpleConvNet(input_dim id, conv_param cp, int hidden_size=512, int output_size=10, bool pretrained=true) {
if (pretrained)
load_trained("params.txt");
layers.push_back(new Convolution(W[0], 1, 1));
layers.push_back(new LightNormalization());
layers.push_back(new Relu());
layers.push_back(new Pooling(2, 2, 2));
layers.push_back(new Convolution(W[1], 1, 0));
layers.push_back(new LightNormalization());
layers.push_back(new Relu());
layers.push_back(new DW_Convolution(W[2], 1, 1));
layers.push_back(new LightNormalization());
layers.push_back(new Relu());
layers.push_back(new Pooling(2, 2, 2));
layers.push_back(new Convolution(W[3], 1, 0));
layers.push_back(new LightNormalization());
layers.push_back(new Relu());
layers.push_back(new DW_Convolution(W[4], 1, 1));
layers.push_back(new LightNormalization());
layers.push_back(new Relu());
layers.push_back(new Pooling(2, 2, 2));
layers.push_back(new Affine(W[5]));
layers.push_back(new LightNormalization());
layers.push_back(new Relu());
layers.push_back(new Affine(W[6]));
}
std::vector< Mat > predict(std::vector<Mat>& x) {
for (int i = 0; i < layers.size(); i++) {
x = layers[i]->forward(x);
}
return x;
}
double accuracy(std::vector< std::vector< unsigned char > > x, std::vector< int > ans, int batch_size=100) {
return 1.0;
}
std::vector<int> argmax(std::vector< Mat >& x) {
std::vector<int> pred;
for (int n = 0; n < x.size(); n++) {
int pid = 0, pos;
double pval = -1e9;
for (int i = 0; i < x[n].mat.size(); i++) {
if (pval < x[n].mat[i]) {
pval = x[n].mat[i];
pid = i;
}
}
pred.push_back(pid);
}
return pred;
}
void load_trained(const char* filename="params.txt") {
FILE *f = fopen(filename, "r");
if (f == NULL) {
printf("File not found\n");
exit(1);
}
char line[10] = { 0 };
int keynum;
while (fscanf(f, "%s", line)==1) {
char s[4][10] = { 0 };
keynum = line[1] - '0' - 1;
// get shape
fscanf(f, "%s", s[0]);
fscanf(f, "%s", s[1]);
if (s[1][strlen(s[1]) - 1] != '\"') {
fscanf(f, "%s", s[2]);
fscanf(f, "%s", s[3]);
}
// nw = number of weights : shape[0]
// size = input size of W[key]
int size = 1, nw=0;
for (int i = 0; i < 4; i++) {
int val = 0;
for (int j = 0; j < strlen(s[i]); j++) {
if ('0' <= s[i][j] && s[i][j] <= '9') {
val = 10 * val + (s[i][j] - '0');
}
}
if (val) {
shape[keynum].push_back(val);
size *= val;
if (nw == 0)
nw = val;
}
}
// Read data of W[key]
int fsize = size / nw;
double *mm = new double[fsize];
for (int i = 0; i < size; i++) {
fscanf(f, "%lf", &mm[i%fsize]);
if (i%fsize == fsize - 1) {
if(shape[keynum].size() == 2)
W[keynum].push_back(Mat(1, 1, shape[keynum][1], std::vector<double>(mm, mm + fsize)));
else if(shape[keynum].size() == 4)
W[keynum].push_back(Mat(shape[keynum][1], shape[keynum][2],
shape[keynum][3], std::vector<double>(mm, mm + fsize)));
}
}
}
printf("Trained weights loading done\n");
}
};
#endif
This diff is collapsed. Click to expand it.
#include"Layers.hpp"
#include"SimpleConvNet.hpp"
using namespace std;
int main() {
input_dim id = { 3, 32, 32 };
conv_param cp = { 32,32,64, 3,1,1 };
SimpleConvNet SCN(id, cp);
freopen("input.txt", "r", stdin);
vector<Mat> X;
int nx = 1, dim = 3, row = 32, col = 32;
double tmp;
for (int i = 0; i < nx; i++) {
vector<double> rev;
for (int d = 0; d < dim; d++) {
for (int r = 0; r < row; r++) {
for (int c = 0; c < col; c++) {
scanf("%lf", &tmp);
rev.push_back(tmp);
}
}
}
X.push_back(Mat(dim, row, col, rev));
}
freopen("pred.txt", "r", stdin);
nx = 2, dim = 3, row = 32, col = 32;
for (int i = 0; i < nx; i++) {
vector<double> rev;
for (int d = 0; d < dim; d++) {
for (int r = 0; r < row; r++) {
for (int c = 0; c < col; c++) {
scanf("%lf", &tmp);
rev.push_back(tmp);
}
}
}
X.push_back(Mat(dim, row, col, rev));
}
auto x = SCN.predict(X);
auto pred = SCN.argmax(x);
int num = 0, pd;
printf("predict : %d ", pred[0]);
return 0;
}
\ No newline at end of file
This diff could not be displayed because it is too large.
This diff is collapsed. Click to expand it.