Files
c-cnn/cnn.c

222 lines
8.7 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "cnn.h"
// 将原始矩阵复制到填充后的矩阵中央
float* expand(const float* old_matrix, int old_matrix_length, int layer){
float* new_matrix = (float *)malloc(sizeof(float)*layer*(old_matrix_length+2)*(old_matrix_length+2));
memset(new_matrix, 0, sizeof(float)*layer*(old_matrix_length+2)*(old_matrix_length+2));
for(int l=0; l < layer; l++){
for (int i = 0; i < old_matrix_length; i++) {
for (int j = 0; j < old_matrix_length; j++) {
new_matrix[(i + 1) * (old_matrix_length+2) + (j + 1) +
l * (old_matrix_length+2) * (old_matrix_length+2)]
= old_matrix[i * old_matrix_length + j +
l * (old_matrix_length) * (old_matrix_length)];
}
}
}
return new_matrix;
}
//model 模型名字
//input_matrix 输入图像
//input_matrix_length 输入图像的边长102
//c_rl 输出图像的边长100
//返回卷积的结果
float* convolution(Model model_w, Model model_b, const float* input_matrix, int input_matrix_length){
// 初始化卷积层参数
int _debug=0;
int im_l = input_matrix_length;
int cr_l = input_matrix_length - 2;
float conv_temp; // 临时变量,用于存储卷积计算的中间结果
//用于合并前的数组具有32*64*50*50(第二层)的大小
float* _conv_rlst = (float *) malloc(sizeof (float) * model_w.channel * model_w.num_kernels * (cr_l * cr_l));
memset(_conv_rlst, 0, sizeof (float) * model_w.channel * model_w.num_kernels * (cr_l * cr_l));
//子图合并后的数组
float* conv_rlst = (float *) malloc(sizeof (float) * model_w.num_kernels * (cr_l * cr_l));
memset(conv_rlst, 0, sizeof (float) * model_w.num_kernels * (cr_l * cr_l));
// 遍历30个卷积核假设有30个通道
for(int c=0; c < model_w.channel; c++){
for(int k=0; k < model_w.num_kernels; k++){
for(int row = 0; row < cr_l; row++) {
for (int col = 0; col < cr_l; col++) {
conv_temp = 0; // 每个输出像素初始化为0
// 进行3x3的卷积操作
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 3; y++) {
// 将输入图像的对应像素与卷积核权重相乘并累加到conv_temp
conv_temp += input_matrix[(c*im_l*im_l) + (row*(im_l)+col) + (x*(im_l)+y)]
* model_w.array[((c+k*model_w.channel)*3*3) + (x*3+y)];
}
}
_conv_rlst[(c*model_w.num_kernels*cr_l*cr_l) + (k*cr_l*cr_l) + (row*cr_l+col)] = conv_temp;
}
}
}
}
//合并子图
{
for(int k=0; k < model_w.num_kernels; k++) {
for (int row = 0; row < cr_l; row++) {
for (int col = 0; col < cr_l; col++) {
conv_temp = 0; // 每个输出像素初始化为0
for (int c = 0; c < model_w.channel; c++) {
conv_temp += _conv_rlst[(c * model_w.num_kernels * cr_l * cr_l) + (k * cr_l * cr_l) + (row * cr_l + col)];
}
// 加上对应卷积核的偏置
conv_temp += model_b.array[k];
// 激活函数ReLU将小于0的值设为0
if (conv_temp > 0)
conv_rlst[(k * (cr_l * cr_l)) + (row * cr_l) + (col)] = conv_temp; // 如果卷积结果大于0存入结果数组
else
conv_rlst[(k * (cr_l * cr_l)) + (row * cr_l) + (col)] = 0; // 否则存入0
}
}
}
}
return conv_rlst;
}
//num_kernels 卷积核的个数32
//area 池化的面积2*2
//input_matrix 输入图像
//input_matrix_length 输入图像的边长100
//输出图像的边长50
//返回池化的结果
float* pooling(Model model_w, const float* input_matrix, u8 input_matrix_length){
u8 im_l = input_matrix_length;
float pool_temp = 0; // 临时变量,用于存储池化操作的最大值
float* pool_rslt = (float *) malloc(sizeof (float)*model_w.num_kernels*im_l*im_l);
memset(pool_rslt, 0, sizeof (float)*model_w.num_kernels*im_l*im_l);
// 遍历30个通道与卷积核数量相同
for(u8 n=0; n<model_w.num_kernels; n++)
{
// 遍历输入图像的每一行步长为22x2的池化窗口
for(u8 row=0; row<im_l; row=row+2)
{
// 遍历输入图像的每一列步长为2
for(u8 col=0; col<im_l; col=col+2)
{
pool_temp = 0; // 每个池化区域的最大值初始化为0
// 进行2x2的最大池化操作
for(u8 x=0; x<2; x++)
{
for(u8 y=0; y<2; y++)
{
// 更新当前池化区域的最大值
if(pool_temp <= input_matrix[row*im_l+col+x*im_l+y+n*(im_l*im_l)])
pool_temp = input_matrix[row*im_l+col+x*im_l+y+n*(im_l*im_l)];
}
}
// 将最大值存入池化结果数组
pool_rslt[(row/2)*(im_l/2)+col/2+n*((im_l/2)*(im_l/2))] = pool_temp;
}
}
}
return pool_rslt;
}
void print_rslt(float* rslt, u8 input_matrix_length, u32 length){
int _tmp = 0;
printf("[0:0]");
for (int i = 0; i < length; i++) {
printf("%f ",rslt[i]);
if ((i + 1) % input_matrix_length == 0) {
printf("\n[%d:%d]",++_tmp,i+1);
}
}
printf("\r\n");
}
void cnn_run(){
//第一层填充102 * 102
float* expand_matrix_1 = expand(data.array, 100, 1);
float* conv_rlst_1 = convolution(conv1_weight,conv1_bias,expand_matrix_1, 102);
float* pool_rslt_1 = pooling(conv1_weight, conv_rlst_1, 100);
//第二层填充32 * 52 * 52
float* expand_matrix_2 = expand(pool_rslt_1, 50, 32);
float* conv_rlst_2 = convolution(conv2_weight,conv2_bias,expand_matrix_2, 52);
float* pool_rslt_2 = pooling(conv2_weight, conv_rlst_2, 50);
//第三层:填充 64 * 27 * 27
float* expand_matrix_3 = expand(pool_rslt_2, 25, 64);
float* conv_rlst_3 = convolution(conv3_weight,conv3_bias,expand_matrix_3, 27);
float* pool_rslt_3 = pooling(conv3_weight, conv_rlst_3, 25);
{
// 隐藏层参数地址
float *affine1_rslt = (float *) malloc(sizeof(float)*128);
memset(affine1_rslt, 0, sizeof(float)*128);
float affine1_temp; // 临时变量,用于存储全连接层的中间结果
// 遍历128个神经元假设隐藏层有128个神经元
for(u8 n=0; n<128; n++)
{
affine1_temp = 0; // 每个神经元的输出初始化为0
// 进行矩阵乘法,将池化层输出展平为一维向量后,与全连接层权重进行点积
for(int i=0; i<(128*12*12); i++)
{
affine1_temp = affine1_temp + pool_rslt_3[i] * fc1_weight.array[i+(128*12*12)*n];
}
// 加上对应神经元的偏置
affine1_temp = affine1_temp + fc1_bias.array[n];
// 激活函数ReLU将小于0的值设为0
if(affine1_temp > 0)
affine1_rslt[n] = affine1_temp; // 如果结果大于0存入结果数组
else
affine1_rslt[n] = 0; // 否则存入0
}
// print_rslt(affine1_rslt,1,128);
float affine2_temp; // 临时变量,用于存储输出层的中间结果
float affine2_rslt[7]; // 存储输出层的结果假设输出层有7个神经元
// 比较输出层的最大值
float temp = -100; // 用于存储最大值的临时变量,初始化为一个非常小的值
int predict_num; // 用于存储预测的数字(对应最大值的索引)
// 遍历10个输出神经元假设有10个类别
for(int n=0; n<7; n++)
{
affine2_temp = 0; // 当前神经元的输出初始化为0
// 进行矩阵乘法,将隐藏层的输出与输出层权重进行点积
for(int i=0; i<128; i++)
{
affine2_temp = affine2_temp + fc2_weight.array[i+128*n] * affine1_rslt[i];
}
// 加上对应神经元的偏置
affine2_temp = affine2_temp + fc2_weight.array[n];
affine2_rslt[n] = affine2_temp; // 存储输出层的结果
// 寻找最大值
if(temp <= affine2_rslt[n])
{
temp = affine2_rslt[n]; // 更新最大值
predict_num = n; // 记录最大值对应的类别索引
}
}
print_rslt(affine2_rslt,7,7);
printf("Label is:%d\r\n",predict_num+1);
}
}