Files
stm32-cnn/PORTING/CNN/cnn.c
Qiea 27b41dcd56 精简项目
删除了不需要的文件,并整理了文件夹
2024-11-08 21:40:34 +08:00

632 lines
21 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"
u8 isrun;
void _cnn_run(){
isrun = 1;
}
/*调试用打印函数*/
/// @brief 打印图像矩阵
/// @param array 输入的图像
/// @param array_num 输入图像总的个数
/// @param elements_per_line 每一行的个数,个数到了自动换行
void PrintfArray(float *array, int array_num, int elements_per_line)
{
for (int i = 0; i < array_num; i++)
{
printf("%f ", array[i]); // 打印当前元素
// 每打印完指定数量的元素后换行
if ((i + 1) % elements_per_line == 0)
{
printf("\n");
}
}
// 如果最后一行元素不足,手动换行
if (array_num % elements_per_line != 0)
{
printf("\n");
}
}
void PrintfResArray(float *array, int array_num, int elements_per_line)
{
for (int i = 0; i < array_num; i++)
{
printf("%f ", array[i]); // 打印当前元素
// 每打印完指定数量的元素后换行
if ((i + 1) % elements_per_line == 0)
{
printf("\t");
}
}
}
/*二维数组指针*/
/// @brief 动态申请二维数组类型为elementSize
/// @param depth 二维数组深度即a[depth][]
/// @param num 长度即a[][num]
/// @param elementSize 二维数组类型
void **allocate2DArray(int depth, int num, size_t elementSize)
{
void **array = (void **)mymalloc(SRAMEX, depth * sizeof(void *));
for (int d = 0; d < depth; d++)
{
array[d] = mymalloc(SRAMEX, num * elementSize);
}
return array;
}
/// @brief 释放通过allocate2DArray申请的二维数组
/// @param array 二维数组
/// @param depth 深度
void free2DArray(float **array, int depth)
{
for (int d = 0; d < depth; d++)
{
myfree(SRAMEX, array[d]);
}
myfree(SRAMEX, array);
}
/*卷积相关函数*/
/// @brief 对输入的图像进行四周0填充
/// @param inputArray 输入的图像
/// @param input_size 输入的单行的元素个数例如100即一行有100个元素
/// @param outputArray 保存输出的图像
void Full(float *inputArray, int input_size, float *outputArray)
{
int i, j;
for (i = 0; i < ((input_size + 2) * (input_size + 2)); i++)
{
outputArray[i] = 0;
}
for (i = 0; i < input_size; i++)
{
for (j = 0; j < input_size; j++)
{
outputArray[(i + 1) * (input_size + 2) + (j + 1)] = inputArray[i * input_size + j];
}
}
}
/// @brief 对输入的图像进行池化选取kernel_size*kernel_size的范围的最大值步长为step
/// @param inputArray 输入的图像
/// @param input_size 输入图像的单行的元素个数
/// @param kernel_size 池化核的单行的元素个数
/// @param step 步进值
/// @param outputArray 保存输出的图像
void Pooling(float *inputArray, int input_size,
int kernel_size, unsigned int step,
float *outputArray)
{
int output_size = (input_size - kernel_size) / step + 1;
for (int i = 0; i < output_size; i++)
{
for (int j = 0; j < output_size; j++)
{
float max_value = 0;
for (int m = 0; m < kernel_size; m++)
{
for (int n = 0; n < kernel_size; n++)
{
int input_row = i * step + m;
int input_col = j * step + n;
int input_idx = input_row * input_size + input_col;
if (inputArray[input_idx] > max_value)
{
max_value = inputArray[input_idx];
}
}
}
int output_idx = i * output_size + j;
outputArray[output_idx] = max_value;
}
}
}
/// @brief 对输入图像进行卷积选取kernel_size*kernel_size的范围这里步长为1
/// @param inputArray 输入的图像
/// @param input_size 输入图像的单行的元素个数
/// @param kernel 输入的卷积核即kernel_size * kernel_size的卷积核参数
/// @param kernel_size 池化核的单行的元素个数
/// @param outputArray 保存输出的图像
void Convolution(float *inputArray, int input_size,
float *kernel, int kernel_size,
float *outputArray)
{
int i, j, m, n;
int half_k = kernel_size / 2;
int output_size = input_size - 2 * half_k;
for (i = half_k; i < input_size - half_k; i++)
{
for (j = half_k; j < input_size - half_k; j++)
{
float sum = 0;
for (m = 0; m < kernel_size; m++)
{
for (n = 0; n < kernel_size; n++)
{
int input_row = i + m - half_k;
int input_col = j + n - half_k;
int input_idx = input_row * input_size + input_col;
int kernel_idx = m * kernel_size + n;
sum += inputArray[input_idx] * kernel[kernel_idx];
}
}
int output_idx = (i - half_k) * output_size + (j - half_k);
outputArray[output_idx] = sum;
}
}
}
/// @brief 对二维数组做合并处理,对应位相加
/// @param inputArray 输入的图像
/// @param input_depth 输入图像的深度即a[input_depth][]
/// @param input_size 输入图像的单行的元素个数
/// @param outputArray 保存输出的图像
void Combine(float **inputArray, int input_depth, int input_size, float *outputArray)
{
int i, j, k;
int input_idx;
for (i = 0; i < input_size; i++)
{
for (j = 0; j < input_size; j++)
{
float sum = 0;
input_idx = i * input_size + j;
for (k = 0; k < input_depth; k++)
{
sum += inputArray[k][input_idx];
}
outputArray[i * input_size + j] = sum;
}
}
}
/// @brief 展开二维数组成一维数组
/// @param inputArray 输入的图像
/// @param input_depth 输入图像的深度即a[input_depth][]
/// @param input_size 输入图像的单行的元素个数
/// @param outputArray 保存输出的图像
void Flatten2D(float **inputArray, int input_depth, int input_size, float *outputArray)
{
int i, j, k;
for (k = 0; k < input_depth; k++)
{
for (i = 0; i < input_size; i++)
{
for (j = 0; j < input_size; j++)
{
int input_idx = i * input_size + j;
outputArray[k * input_size * input_size + input_idx] = inputArray[k][input_idx];
}
}
}
}
/// @brief 对输入图像的每一位添加偏置
/// @param inputArray 输入的图像
/// @param input_num 输入图像总的元素个数
/// @param bias 偏置核
/// @param outputArray 保存输出的图像
void AddBias(float *inputArray, int input_num, float bias, float *outputArray)
{
for (int i = 0; i < input_num; i++)
{
outputArray[i] = inputArray[i] + bias;
}
}
/// @brief 全连接层操作:对一个神经元做全连接
/// @param inputArray 输入的图像
/// @param input_num 输入图像总的元素个数
/// @param input_w 输入的权重核
/// @param input_b 输入的偏置核
/// @return 一个神经元的结果类型为float
float ConnectedLayer(float *inputArray, int input_num,
float *input_w, float input_b)
{
int i;
float sum = 0;
for (i = 0; i < input_num; i++)
{
sum += inputArray[i] * input_w[i];
}
sum = sum + input_b;
return sum;
}
/// @brief ReLU函数激活此函数是对一整个数组做激活若值大于等于0则保留值否则置0
/// @param inputArray 输入的图像
/// @param num 输入图像总的元素个数
/// @param outputArray 保存输出的矩阵
void ReLU1(float *inputArray, int num, float *outputArray)
{
for (int i = 0; i < num; i++) {
outputArray[i] = (inputArray[i] > 0) ? inputArray[i] : 0;
}
}
/// @brief ReLU函数激活此函数是对一个数做激活若值大于等于0则保留值否则置0
/// @param data 输入的数
/// @return 激活完的数类型为float
float ReLU2(float data)
{
if (data > 0) {
return data;
}
else {
return 0;
}
}
//求数值
/// @brief 对一个输入数组求平均值、最大值和标准差
/// @param arr 输入的数组
/// @param size 数组的长度
/// @param max_val 最大值保存
/// @param mean 平均值保存
/// @param std_dev 标准差保存
void calculate_statistics(float arr[], int size, float *max_val, float *mean, float *std_dev)
{
*max_val = fabs(arr[0]);
float sum = 0.0;
float sum_sq = 0.0;
for (int i = 0; i < size; i++) {
float abs_val = fabs(arr[i]);
if (abs_val > *max_val) {
*max_val = abs_val;
}
sum += abs_val;
sum_sq += abs_val * abs_val;
}
*mean = sum / size;
float variance = (sum_sq / size) - (*mean * *mean);
*std_dev = sqrt(variance);
}
//判断放电
/// @brief 判断是否放电
/// @param arr 输入数组
/// @param size 数组的长度
/// @param mean 平均值
/// @param std_dev 标准差
/// @return 如果超过阈值则返回1否则返回0
int check_threshold(float arr[], int size, float *mean, float *std_dev)
{
const float threshold = 20.0;
for (int i = 0; i < size; i++) {
float K = (arr[i] - *mean) / *std_dev;
if (K > threshold) {
return 1;
}
}
return 0;
}
/// @brief 生成100*100矩阵
/// @param get_data 输入的数组
/// @param Max_value 输入最大值
/// @param totalPoints 总的数组元素个数
/// @param CNN_data 输出的数组(二维)
void generateMatrix(float *get_data, float Max_value, int totalPoints, float CNN_data[100*100])
{
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 100; j++) {
CNN_data[i*100+j] = 0;
}
}
int pointsPerInterval = totalPoints / 100;
float amplitudeStep = Max_value / 100;
for (int i = 0; i < totalPoints; i++) {
float amplitudeValue = fabsf(get_data[i]);
if (amplitudeValue == 0.0f) {
continue;
}
int intervalIndex = i / pointsPerInterval;
if (intervalIndex >= 100) intervalIndex = 99;
int amplitudeIndex = 99 - (int)(amplitudeValue / amplitudeStep);
if (amplitudeIndex < 0) amplitudeIndex = 0;
CNN_data[amplitudeIndex*100+intervalIndex]++;
}
}
/// @brief 计算概率
/// @param input_array 输入数组
/// @param output_array 保存输出的数组
/// @param input_num 输入元素个数
/// @return 返回概率中最大值的索引(默认+1即如果是数组的a[0]最大则返回1
int calculate_probabilities(float *input_array, float *output_array, int input_num)
{
float sum = 0.0;
float temp[input_num];
for (int i = 0; i < input_num; i++)
{
temp[i] = exp(input_array[i]);
sum = sum + temp[i];
}
for (int j = 0; j < input_num; j++)
{
output_array[j] = temp[j] / sum;
}
int max_index = 0;
float max_value = output_array[0];
for (int k = 1; k < input_num; k++)
{
if (output_array[k] > max_value)
{
max_value = output_array[k];
max_index = k;
}
}
return max_index + 1;
}
/*这个程序仅是基本验证程序能跑,还无优化*/
/*原始数据个数
*目前不保证能否在数目不对的情况下计算,有可能程序出错。
*目前没法再没有提前给出元素个数的情况下计算。
*原始数据个数仅印象预处理的计算,后面的卷积程序用不到。
*/
/// @brief 完整的程序
/// @param data 输入的一维数组原始数据 --> data.array代替
/// @param size 原始数据个数 --> data.maxlength代替
void cnn_run()
{
float maxvalue = 0.0; //最大值
float meanvalue = 0.0; //平均值
float std_devvalue = 0.0; //标准差
int index = 0; //索引(忘了干啥的)
float *Matrix_data = (float*)mymalloc(SRAMEX, sizeof(float) * 100 * 100); //生成的100*100的矩阵保存在这
printf("CNN模型正在运行\r\nData数据集为%s\r\n",data.dname);
DEBUG_PRINTF("data[%d]: %f\r\n",0,data.array[0]);
DEBUG_PRINTF("data[%d]: %f\r\n",1,data.array[1]);
DEBUG_PRINTF("data[%d]: %f\r\n",2,data.array[2]);
DEBUG_PRINTF("data[%d]: %f\r\n",299,data.array[299]);
DEBUG_PRINTF("data[%d]: %f\r\n",300,data.array[300]);
DEBUG_PRINTF("data[%d]: %f\r\n",301,data.array[301]);
DEBUG_PRINTF("data[%d]: %f\r\n",1249997,data.array[1249997]);
DEBUG_PRINTF("data[%d]: %f\r\n",1249998,data.array[1249998]);
DEBUG_PRINTF("data[%d]: %f\r\n",1249999,data.array[1249999]);
/*计算数据*/
calculate_statistics(data.array, data.maxlength, &maxvalue, &meanvalue,&std_devvalue);
/*判断数据是否超过阈值*/
int x = check_threshold(data.array, data.maxlength, &meanvalue, &std_devvalue);
if (x == 1) {
printf("Start\r\n");
//pre 开始预处理
/*生成矩阵*/
generateMatrix(data.array, maxvalue, data.maxlength, Matrix_data);
float *CNN_data = (float*)mymalloc(SRAMEX, sizeof(float) * 100 * 100);
/*二维数组转一维数组,以便后续计算*/
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 100; j++) {
CNN_data[9999 - index++] = Matrix_data[i*100+j];
}
}
DEBUG_PRINTF("开始第一层!\r\n");
//1 第一层卷积
/*数组资源申请*/
float* Full_output1 = (float*)mymalloc(SRAMEX, sizeof(float) * 102 * 102);
/*填充矩阵*/
Full(CNN_data, 100, Full_output1);
float** Convolution_result1_before = (float**)allocate2DArray(32, 100 * 100, sizeof(float));
float** Convolution_result1_relu = (float**)allocate2DArray(32, 100 * 100, sizeof(float));
float** Convolution_result1 = (float**)allocate2DArray(32, 100 * 100, sizeof(float));
/*卷积操作*/
for (int i = 0; i < 32; i++) {
/*提取卷积核(还没优化)*/
float conv1_weight_new[9] = {
conv1_weight.array[i*9+0],conv1_weight.array[i*9+1],
conv1_weight.array[i*9+2],conv1_weight.array[i*9+3],
conv1_weight.array[i*9+4],conv1_weight.array[i*9+5],
conv1_weight.array[i*9+6],conv1_weight.array[i*9+7],
conv1_weight.array[i*9+8] };
Convolution(Full_output1, 102, conv1_weight_new, 3, Convolution_result1_before[i]);
}
/*添加偏置*/
for (int i = 0; i < 32; i++) {
AddBias(Convolution_result1_before[i], 100 * 100, conv1_bias.array[i], Convolution_result1_relu[i]);
}
/*做ReLU激活*/
for (int i = 0; i < 32; i++) {
ReLU1(Convolution_result1_relu[i], 100 * 100, Convolution_result1[i]);
}
/*池化操作*/
float ** Pooling_result1 = (float**)allocate2DArray(32, 50 * 50, sizeof(float));
for (int i = 0; i < 32; i++) {
Pooling(Convolution_result1[i], 100, 2, 2, Pooling_result1[i]);
}
DEBUG_PRINTF("第一层完毕!\r\n");
//2 第二层卷积
/*释放上一层的资源*/
myfree(SRAMEX, Full_output1);
free2DArray(Convolution_result1_relu,32);
free2DArray(Convolution_result1_before,32);
free2DArray(Convolution_result1,32);
/*填充操作*/
float** Full_output2 = (float**)allocate2DArray(32, 52 * 52, sizeof(float));
for (int i = 0; i < 32; i++) {
Full(Pooling_result1[i], 50, Full_output2[i]);
}
/*申请本层资源*/
float** Convolution_result_temp_2 = (float**)allocate2DArray(32, 50 * 50, sizeof(float));
float** Convolution_result2_before = (float**)allocate2DArray(64, 50 * 50, sizeof(float));
float** Convolution_result2_relu = (float**)allocate2DArray(64, 50 * 50, sizeof(float));
float** Convolution_result2 = (float**)allocate2DArray(64, 50 * 50, sizeof(float));
/*卷积操作*/
for (int i = 0; i < 64; i++) {
for (int j = 0; j < 32; j++) {
/*提取卷积核*/
float conv2_weight_new[9] = {
conv2_weight.array[i*32*9+9*j+0],conv2_weight.array[i*32*9+9*j+1],
conv2_weight.array[i*32*9+9*j+2],conv2_weight.array[i*32*9+9*j+3],
conv2_weight.array[i*32*9+9*j+4],conv2_weight.array[i*32*9+9*j+5],
conv2_weight.array[i*32*9+9*j+6],conv2_weight.array[i*32*9+9*j+7],
conv2_weight.array[i*32*9+9*j+8]
};
Convolution(Full_output2[j], 52, conv2_weight_new, 3, Convolution_result_temp_2[j]);
}
/*每一个子图像合并*/
Combine(Convolution_result_temp_2, 32, 50, Convolution_result2_before[i]);
}
/*添加偏置*/
for (int i = 0; i < 64; i++) {
AddBias(Convolution_result2_before[i], 50 * 50, conv2_bias.array[i], Convolution_result2_relu[i]);
}
/*数组ReLU激活*/
for (int i = 0; i < 64; i++) {
ReLU1(Convolution_result2_relu[i], 50 * 50, Convolution_result2[i]);
}
/*池化操作*/
float** Pooling_result2 = (float**)allocate2DArray(64, 25 * 25, sizeof(float));
for (int i = 0; i < 64; i++) {
Pooling(Convolution_result2[i], 50, 2, 2, Pooling_result2[i]);
}
DEBUG_PRINTF("第二层完毕!\r\n");
//3 第三层卷积
/*释放上一层的资源*/
free2DArray(Full_output2,32);
free2DArray(Pooling_result1,32);
free2DArray(Convolution_result_temp_2,32);
free2DArray(Convolution_result2_relu,64);
free2DArray(Convolution_result2_before,64);
free2DArray(Convolution_result2,64);
/*填充操作*/
float** Full_output3 = (float**)allocate2DArray(64, 27 * 27, sizeof(float));
for (int i = 0; i < 64; i++) {
Full(Pooling_result2[i], 25, Full_output3[i]);
}
/*申请本层资源*/
float** Convolution_result_temp_3 = (float**)allocate2DArray(64, 25 * 25, sizeof(float));
float** Convolution_result3_before = (float**)allocate2DArray(128, 25 * 25, sizeof(float));
float** Convolution_result3_relu = (float**)allocate2DArray(128, 25 * 25, sizeof(float));
float** Convolution_result3 = (float**)allocate2DArray(128, 25 * 25, sizeof(float));
/*卷积操作*/
for (int i = 0; i < 128; i++) {
for (int j = 0; j < 64; j++) {
float conv3_weight_new[9] = {
conv3_weight.array[i*64*9+9*j+0],conv3_weight.array[i*64*9+9*j+1],
conv3_weight.array[i*64*9+9*j+2],conv3_weight.array[i*64*9+9*j+3],
conv3_weight.array[i*64*9+9*j+4],conv3_weight.array[i*64*9+9*j+5],
conv3_weight.array[i*64*9+9*j+6],conv3_weight.array[i*64*9+9*j+7],
conv3_weight.array[i*64*9+9*j+8]
};
Convolution(Full_output3[j], 27, conv3_weight_new, 3, Convolution_result_temp_3[j]);
}
/*子图像合并*/
Combine(Convolution_result_temp_3, 64, 25, Convolution_result3_before[i]);
}
/*添加偏置*/
for (int i = 0; i < 128; i++) {
AddBias(Convolution_result3_before[i], 25 * 25, conv3_bias.array[i], Convolution_result3_relu[i]);
}
/*ReLU激活*/
for (int i = 0; i < 128; i++) {
ReLU1(Convolution_result3_relu[i], 25 * 25, Convolution_result3[i]);
}
/*池化操作*/
float** Pooling_result3 = (float**)allocate2DArray(128, 12 * 12, sizeof(float));
for (int i = 0; i < 128; i++) {
Pooling(Convolution_result3_before[i], 25, 2, 2, Pooling_result3[i]);
}
/*池化的输出展开成一维数组*/
float* xi = (float *)mymalloc(SRAMEX, sizeof(float) * 128 * 12 * 12);
Flatten2D(Pooling_result3, 128, 12, xi);
DEBUG_PRINTF("第三层完毕!\r\n");
//4 第一连接层
/*释放上一层的资源*/
free2DArray(Full_output3,64);
free2DArray(Pooling_result2,64);
free2DArray(Convolution_result_temp_3,64);
free2DArray(Convolution_result3_relu,128);
free2DArray(Convolution_result3_before,128);
free2DArray(Convolution_result3,128);
/*连接层操作*/
//float yi[128] = {0};
DEBUG_PRINTF("开始第四层!\r\n");
float* yi = (float *)mymalloc(SRAMEX, 128 * sizeof(float));
memset(yi, 0, 128 * sizeof(float));
for (int i = 0; i < 128; i++) {
float sum = 0;
//float fc1_weight_new[128*12*12];
float* fc1_weight_new = (float*)mymalloc(SRAMEX, sizeof(float) * 128 * 12 * 12);
/*提取卷积核*/
memcpy(fc1_weight_new,&fc1_weight.array[i*128*12*12],128*12*12 * sizeof(float));
sum = ConnectedLayer(xi, 128 * 12 * 12, fc1_weight_new, fc1_bias.array[i]);
/*单个数做ReLU激活*/
yi[i] = ReLU2(sum);
myfree(SRAMEX, fc1_weight_new);
}
DEBUG_PRINTF("第四层完毕!\r\n");
//5 最后连接层
/*释放上一层的资源*/
free2DArray(Pooling_result3,128);
/*连接层操作*/
float zi[7] = {0};
// float *zi = (float *)mymalloc(SRAMEX, 7 * sizeof(float));
// memset(zi, 0, 7 * sizeof(float));
for (int i = 0; i < 7; i++) {
//float fc2_weight_new[128];
float* fc2_weight_new = (float*)mymalloc(SRAMEX, sizeof(float) * 128);
/*提取卷积核*/
memcpy(fc2_weight_new,&fc2_weight.array[i*128],128 * sizeof(float));
/*单个数做ReLU激活*/
zi[i] = ConnectedLayer(yi, 128, fc2_weight_new, fc2_bias.array[i]);
myfree(SRAMEX, fc2_weight_new);
}
DEBUG_PRINTF("第五层完毕!\r\n");
//end 输出处理层
float result[7];
int max_probability_idx = calculate_probabilities(zi,result,7);
PrintfResArray(zi,7,7);
PrintfResArray(result,7,7);
printf("%f, Label %d\t", result[max_probability_idx - 1] * 100, max_probability_idx);
myfree(SRAMEX, xi);
myfree(SRAMEX, yi);
myfree(SRAMEX, CNN_data);
myfree(SRAMEX, Full_output1);
myfree(SRAMEX, Matrix_data);
}
else printf("No\r\n");/*如果没放电打印“No”*/
printf("maxvalue is:%f\t",maxvalue);
printf("meanvalue is:%f\t",meanvalue);
printf("std_devvalue is:%f\r\n",std_devvalue);
isrun = 0;
}