Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
i-evi committed Oct 4, 2020
1 parent 1de2a85 commit 33febca
Show file tree
Hide file tree
Showing 11 changed files with 207 additions and 232 deletions.
File renamed without changes.
File renamed without changes.
6 changes: 3 additions & 3 deletions makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ DFLAG += # -g -fsanitize=address -fno-omit-frame-pointer
CFLAG += # -std=c89
CFLAG += -Wall # -Wpedantic

OFLAG += # -O3
OFLAG += -O3

# Enable OpenMP
OFLAG += -DENABLE_OPENMP -fopenmp
Expand Down Expand Up @@ -93,8 +93,8 @@ VPATH = $(OBJS_PATH)
ALL_O += \
catcoon.o cc_tensor.o cc_dtype.o cc_tsrmgr.o cc_fmap2d.o cc_pool2d.o \
cc_array.o cc_basic.o cc_actfn.o cc_fullycon.o cc_pad2d.o cc_cpufn.o \
cc_conv2d.o cc_dsc2d.o cc_normfn.o cc_image.o util_rbt.o util_list.o \
util_log.o util_vec.o util_image.o global_fn_cfg.o
cc_conv2d.o cc_normfn.o cc_image.o util_rbt.o util_list.o util_log.o \
util_vec.o util_image.o global_fn_cfg.o

CATCOON_A = libcatcoon.a

Expand Down
58 changes: 32 additions & 26 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

* Catcoon 本体是使用 C 语言进行开发的,方便移植到各种平台,因此 bind 到脚本的话会首先选择 Lua,将在未来提供 Lua 支持。

* Catcoon 建议兼容 ISO C89 标准。考虑到性能,不必完全遵循 C89,但需要注明。
* Catcoon 建议兼容 ISO C89 标准。

* 编码规范不做强制,建议参考 [Linux kernel coding style](https://www.kernel.org/doc/html/v4.10/process/coding-style.html),现有的代码几乎遵守了此规范。

Expand All @@ -29,19 +29,19 @@ Catcoon 使用 tensor 进行计算。神经网络的计算过程中,经常产
编译时定义 `AUTO_TSRMGR` 打开 tensor 管理器,取消这个预定义,即可以禁用自动 tensor 管理,参考 `makefile`。通常,加载一个模型之后,网络可能会持续处理多个样本,因此内存管理器使很多中间过程中产生的 tensor 驻留在内存中,这样做能够减少系统内存管理开销并且提高性能。但是在某些资源极端匮乏的平台上可能会显得有些奢侈,如果有需要,可以在编译时禁用 tensor 管理器,手动管理内存。
在启动了自动 tensor 管理的情况下任何创建新 tensor 的操作都会自动向 `cc_tsrmgr` 注册新创建的 tensor。但是 `name` 是 `NULL` 的 tensor 不会被自动注册
在启动了自动 tensor 管理的情况下除了 `name` 是 `NULL` 的 tensor 任何创建新 tensor 的操作都会自动向 `cc_tsrmgr` 注册新创建的 tensor。
## 张量(Tensor, cc_tensor)
`cc_tensor.h` 定义了 `cc_tensor_t` 和一些基本操作。
```c
typedef struct {
list_t *container;
struct list *container;
const char *name;
unsigned char *data;
const cc_dtype *dtype;
const cc_int32 *shape;
const cc_int32 *dtype;
} cc_tensor_t;
```

Expand All @@ -60,36 +60,36 @@ typedef struct {
使用 `cc_create_tensor`/`cc_free_tensor` 创建/释放一个 tensor:

```c
cc_tensor_t *cc_create_tensor(cc_int32 *shape, cc_int32 dtype, const char *name);
cc_tensor_t *cc_create(cc_int32 *shape, cc_int32 dtype, const char *name);

void cc_free_tensor(cc_tensor_t *tensor);
void cc_free(cc_tensor_t *tensor);
```
一个简单的例子(`demo/simple.c`):
```c
...
cc_tensor_t *tensor;
cc_int32 shape[] = {3, 3, 3, 0};
tensor = cc_create_tensor(shape, CC_FLOAT32, "tensor0");
cc_print_tensor_property(tensor);
cc_free_tensor(tensor);
tensor = cc_create(shape, CC_FLOAT32, "tensor0");
cc_property(tensor);
cc_free(tensor);
...
```
`cc_print_tensor_property` 输出 tensor 的基本属性:
`cc_property` 打印 tensor 的基本属性:

```bash
[00000003]: tensor: "tensor0", dtype: "cc_float32", shape: [3, 3, 3]
```
如果没有使用 `cc_tsrmgr`,需要调用 `cc_free_tensor` 手动释放资源。特别注意, `cc_free_tensor` 是手动管理内存的方式,如果使用 `cc_tsrmgr`,不推荐使用 `cc_free_tensor`,在 `cc_tsrmgr` 启动的情况下,几乎所有过程中创建的 tensor 都会被纳入 `cc_tsrmgr`,如非必要,不应该手动释放 tensor,可能造成空悬指针。
如果没有使用 `cc_tsrmgr`,需要调用 `cc_free` 手动释放资源。特别注意, `cc_free` 是手动管理内存的方式,如果使用 `cc_tsrmgr`,不推荐使用 `cc_free`,在 `cc_tsrmgr` 启动的情况下,几乎所有过程中创建的 tensor 都会被纳入 `cc_tsrmgr`,如非必要,不应该手动释放 tensor,可能造成空悬指针。

### 保存/加载 Tensor (cc_save_tensor/cc_load_tensor)
### 保存/加载 Tensor (cc_save/cc_load)

你可以把一个 tensor 保存到文件中,也可以通过一个文件恢复一个 tensor。在保存 tensor 时,你可以使用任意的文件名,tensor 的名字也被记录到文件中。加载 tensor 时,如果启用了自动 tensor 管理器,而且 tensor 管理器中已经存在了和加载的 tensor 同名的 tensor,已经在 tensor 管理器中的 tensor 会被加载的 tensor 覆盖。

```c
cc_tensor_t *cc_load_tensor(const char *filename);
cc_tensor_t *cc_load(const char *filename);

void cc_save_tensor(cc_tensor_t *tensor, const char *filename);
void cc_save(cc_tensor_t *tensor, const char *filename);
```
在保存和加载 tensor 时,都不需要指定 tensor 的形状和名字,`cc_load_bin` 是一种从文件创建 tensor 特殊方法,在某些特殊场合使用:
Expand All @@ -106,21 +106,21 @@ cc_tensor_t *cc_load_bin(const char *filename,
`cc_image` 提供了 tensor 和图像之间的转换功能。

```c
cc_tensor_t *cc_image2tensor(utim_image_t *img, const char *name);
cc_tensor_t *cc_image2tensor(UTIM_IMG *img, const char *name);

utim_image_t *cc_tensor2image(cc_tensor_t *tensor);
UTIM_IMG *cc_tensor2image(cc_tensor_t *tensor);
```
图像操作的支持在 `util_image.h` 中定义。包含有一些常用功能:
**读取/保存图像**支持 `bmp`, `jpg`, `png`, `tga` 格式的图像文件。 其中 `jpg`, `png`, `tga` 文件的支持是通过 [stb](https://github.com/nothings/stb) 实现的,我不确定 [stb](https://github.com/nothings/stb) 在某些编译器或者硬件平台的兼容性如何,可以在 `makefile` 中禁用 [stb](https://github.com/nothings/stb),如果 [stb](https://github.com/nothings/stb) 被禁用,只支持 `bmp` 图像文件的读写。
```c
utim_image_t *utim_read(const char *filename);
UTIM_IMG *utim_read(const char *filename);
int utim_write(const char *filename, utim_image_t *img);
int utim_write(const char *filename, UTIM_IMG *img);
int utim_write_ctrl(const char *filename, utim_image_t *img, int comp, int quality);
int utim_write_ctrl(const char *filename, UTIM_IMG *img, int comp, int quality);
```

**基本的图像预处理**包括灰度,缩放,简单的 2D 图形功能,参考 `util_image.h`
Expand All @@ -136,21 +136,27 @@ cc_tensor_t *cc_conv2d(cc_tensor_t *inp, cc_tensor_t *kernel,
cc_tensor_t *bias, cc_int32 s, cc_int32 p, cc_int32 off, const char *name);
```
2D 卷积是逐通道进行的,在 `src/cc_conv2d.c` 用于实现某层 feature map 卷积的函数指针声明如下:
2D 卷积是逐通道进行的,在 `src/cc_conv2d.c` 中用于对某个通道执行卷积操作的函数指针声明如下:
```c
extern void (*_conv2d)(void *inp, void *oup, cc_int32 x, cc_int32 y, cc_int32 oup_x,
cc_int32 oup_y, cc_int32 sx, cc_int32 sy, void *filter, cc_int32 fw, cc_dtype dt);
extern fn_conv2d _conv2d;
```

函数指针 `_conv2d` 的值在全局功能配置 `global_fn_cfg.h` 中设置:
`fn_conv2d` `global_fn_cfg.h` 中定义:

```c
void (*_conv2d)(void *inp, void *oup, cc_int32 x, cc_int32 y,
cc_int32 oup_x, cc_int32 oup_y, cc_int32 sx, cc_int32 sy, void *filter, cc_int32 fw, cc_dtype dt) = cc_cpu_conv2d;
typedef void (*fn_conv2d)(const void *inp, void *oup, cc_int32 x,cc_int32 y,
cc_int32 oup_x, cc_int32 oup_y, cc_int32 sx, cc_int32 sy, const void *filter,
cc_int32 fw, cc_dtype dt);
```
这样,实际上卷积计算是 `cc_cpu_conv2d` 完成的。如果你想用其他版本的卷积实现(例如 GPU/FPGA),在 `global_fn_cfg.h``_conv2d` 指向你的实现就行了。
函数指针 `_conv2d` 的值在全局功能配置 `global_fn_cfg.c` 中的设置可以是:
```c
fn_conv2d _conv2d = cc_cpu_conv2d;
```

这样,实际上卷积计算是 `cc_cpu_conv2d` 完成的。如果你想用其他版本的卷积实现(例如 GPU/FPGA),在 `global_fn_cfg.c``_conv2d` 指向你的实现就行了。

## 实现一个完整的 CNN 网络

Expand Down
1 change: 0 additions & 1 deletion src/catcoon.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include "cc_assert.h"
#include "cc_basic.h"
#include "cc_conv2d.h"
#include "cc_dsc2d.h"
#include "cc_fmap2d.h"
#include "cc_fullycon.h"
#include "cc_image.h"
Expand Down
155 changes: 155 additions & 0 deletions src/cc_conv2d.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "global_fn_cfg.h"
extern fn_conv2d _conv2d;
extern fn_array_add_ew _array_add_ew;
extern fn_array_mul_by _array_mul_by;

cc_int32 cc_conv2d_shape_calc(
cc_int32 i, cc_int32 k, cc_int32 s, cc_int32 p)
Expand Down Expand Up @@ -136,3 +137,157 @@ cc_tensor_t *cc_conv2d(const cc_tensor_t *inp,
#endif
return oup;
}

cc_tensor_t *cc_dw_conv2d(cc_tensor_t *inp,
const cc_tensor_t *kernel, const cc_tensor_t *bias,
cc_int32 s, cc_int32 p, cc_int32 off, const char *name)
{
cc_tensor_t *inp_pad, *oup = NULL;
cc_int32 o_ch_size, p_ch_mem_size, o_ch_mem_size, k_ch_mem_size, i;
cc_int32 shape[CC_CNN2D_SHAPE] = {0};
char pad_name[CC_CONV2D_PAD_NAME_LEN];
#ifdef ENABLE_CC_ASSERT
cc_assert_zero(cc_dimension(inp) - CC_CNN2D_DIM);
cc_assert_zero(cc_dimension(kernel) - CC_CONV2D_KERNEL_DIM);
cc_assert_zero(*inp->dtype - *kernel->dtype);
cc_assert_zero(inp->shape[CC_CNN2D_SHAPE_C]
- kernel->shape[CC_CONV2D_KERNEL_O]);
#endif
if (p) {
sprintf(pad_name, "%s%s",
inp->name, CC_CONV2D_PAD_NAME_SURFFIX);
inp_pad = cc_pad2d(inp, p, off, pad_name);
}
else
inp_pad = inp;
#ifdef AUTO_TSRMGR
oup = cc_tsrmgr_get(name);
#endif
if (!oup) {
shape[CC_CNN2D_SHAPE_C] = kernel->shape[CC_CONV2D_KERNEL_O];
shape[CC_CNN2D_SHAPE_H] = cc_conv2d_shape_calc(
inp->shape[CC_CNN2D_SHAPE_H],
kernel->shape[CC_CONV2D_KERNEL_H], s, p);
shape[CC_CNN2D_SHAPE_W] = cc_conv2d_shape_calc(
inp->shape[CC_CNN2D_SHAPE_W],
kernel->shape[CC_CONV2D_KERNEL_W], s, p);
oup = cc_create(shape, *inp->dtype, name);
}
o_ch_size = oup->shape[CC_CNN2D_SHAPE_W] *
oup->shape[CC_CNN2D_SHAPE_H];
o_ch_mem_size = o_ch_size * cc_dtype_size(*oup->dtype);
p_ch_mem_size = inp_pad->shape[CC_CNN2D_SHAPE_W] *
inp_pad->shape[CC_CNN2D_SHAPE_H] *
cc_dtype_size(*inp->dtype);
k_ch_mem_size = kernel->shape[CC_CONV2D_KERNEL_W] *
kernel->shape[CC_CONV2D_KERNEL_H] *
cc_dtype_size(*kernel->dtype);
#ifdef AUTO_TSRMGR
memset(oup->data, 0,
list_getlen(oup->container, CC_TENSOR_DATA));
#endif
#ifdef ENABLE_OPENMP
#pragma omp parallel for private(i)
#endif
for (i = 0; i < kernel->shape[CC_CONV2D_KERNEL_O]; ++i) {
_conv2d((inp_pad->data + i * p_ch_mem_size),
oup->data + i * o_ch_mem_size,
inp_pad->shape[CC_CNN2D_SHAPE_W],
inp_pad->shape[CC_CNN2D_SHAPE_H],
oup->shape[CC_CNN2D_SHAPE_W],
oup->shape[CC_CNN2D_SHAPE_H], s, s,
kernel->data + (k_ch_mem_size * i),
kernel->shape[CC_CONV2D_KERNEL_W],
*kernel->dtype);
}
if (!bias){
#ifndef AUTO_TSRMGR
if (p)
cc_free_tensor(inp_pad);
#endif
return oup;
} else {
oup = cc_fmap2d_bias(oup, bias, oup->name);
}
#ifndef AUTO_TSRMGR
if (p)
cc_free_tensor(inp_pad);
#endif
return oup;
}

cc_tensor_t *cc_pw_conv2d(cc_tensor_t *inp, const cc_tensor_t *kernel,
const cc_tensor_t *bias, const char *name)
{
cc_uint8 *omp_out_buf = NULL;
cc_tensor_t *oup = NULL;
cc_int32 o_ch_size, o_ch_mem_size,
k_ch_mem_size, k_mem_size, num_omp_threads, i, j;
cc_int32 shape[CC_CNN2D_SHAPE] = {0};
#ifdef ENABLE_CC_ASSERT
cc_assert_zero(cc_dimension(inp) - CC_CNN2D_DIM);
cc_assert_zero(cc_dimension(kernel) - CC_CONV2D_KERNEL_DIM);
cc_assert_zero(*inp->dtype - *kernel->dtype);
cc_assert_zero(inp->shape[CC_CNN2D_SHAPE_C]
- kernel->shape[CC_CONV2D_KERNEL_I]);
#endif
#ifdef AUTO_TSRMGR
oup = cc_tsrmgr_get(name);
#endif
if (!oup) {
shape[CC_CNN2D_SHAPE_C] = kernel->shape[CC_CONV2D_KERNEL_O];
shape[CC_CNN2D_SHAPE_H] = inp->shape[CC_CNN2D_SHAPE_H];
shape[CC_CNN2D_SHAPE_W] = inp->shape[CC_CNN2D_SHAPE_W];
oup = cc_create(shape, *inp->dtype, name);
}
o_ch_size = oup->shape[CC_CNN2D_SHAPE_W] *
oup->shape[CC_CNN2D_SHAPE_H];
o_ch_mem_size = o_ch_size * cc_dtype_size(*oup->dtype);
k_ch_mem_size = kernel->shape[CC_CONV2D_KERNEL_W] *
kernel->shape[CC_CONV2D_KERNEL_H] *
cc_dtype_size(*kernel->dtype);
k_mem_size = k_ch_mem_size * kernel->shape[CC_CONV2D_KERNEL_I];
num_omp_threads = 1;
#ifdef ENABLE_OPENMP
num_omp_threads = omp_get_max_threads();
#endif
cc_assert_alloc(omp_out_buf =
(cc_uint8*)malloc(o_ch_mem_size * num_omp_threads));
#ifdef AUTO_TSRMGR
memset(oup->data, 0,
list_getlen(oup->container, CC_TENSOR_DATA));
#endif
#ifdef ENABLE_OPENMP
#pragma omp parallel for private(i, j)
#endif
for (i = 0; i < kernel->shape[CC_CONV2D_KERNEL_O]; ++i) {
for (j = 0; j < kernel->shape[CC_CONV2D_KERNEL_I]; ++j)
{
#ifdef ENABLE_OPENMP
_array_mul_by(
omp_out_buf + omp_get_thread_num() * o_ch_mem_size,
o_ch_size, inp->data + o_ch_mem_size * j,
kernel->data + k_mem_size * i + k_ch_mem_size * j,
*oup->dtype);
_array_add_ew(oup->data + o_ch_mem_size * i,
o_ch_size, oup->data + o_ch_mem_size * i,
omp_out_buf + omp_get_thread_num() * o_ch_mem_size,
*oup->dtype);
#else
_array_mul_by(omp_out_buf, o_ch_size,
inp->data + o_ch_mem_size * j,
kernel->data + k_mem_size * i + k_ch_mem_size * j,
*oup->dtype);
_array_add_ew(oup->data + o_ch_mem_size * i, o_ch_size,
oup->data + o_ch_mem_size * i, omp_out_buf,
*oup->dtype);
#endif
}
}
free(omp_out_buf);
if (!bias)
return oup;
else
oup = cc_fmap2d_bias(oup, bias, oup->name);
return oup;
}
9 changes: 9 additions & 0 deletions src/cc_conv2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ cc_tensor_t *cc_conv2d(const cc_tensor_t *inp,
const cc_tensor_t *kernel, const cc_tensor_t *bias,
cc_int32 s, cc_int32 p, cc_int32 off, const char *name);

/* Depth-wise convolution 2d */
cc_tensor_t *cc_dw_conv2d(cc_tensor_t *inp,
const cc_tensor_t *kernel, const cc_tensor_t *bias,
cc_int32 s, cc_int32 p, cc_int32 off, const char *name);

/* Point-wise convolution 2d */
cc_tensor_t *cc_pw_conv2d(cc_tensor_t *inp, const cc_tensor_t *kernel,
const cc_tensor_t *bias, const char *name);

#ifdef __cplusplus
}
#endif
Expand Down
Loading

0 comments on commit 33febca

Please sign in to comment.