- 一、分析输入输出
- 二、分析神经网络层数
- 三、分析神经元数量
- 四、分析参数数量
- 五、分析传递过程连接方式
- 六、代码框架
- 七、完整代码实现:
关于参数的获取:已经在上篇博客中提出,请参照相关链接请点击
一、分析输入输出1、手写体输入为28x28的黑白图片,所以输入为784个x
2、输出为识别0-9的数字的概率,所以有10个输出
3、输入只能是-1~1的小数,主要是防止计算溢出
如果只是一层,输入784,输出10,中间能记录的神经元只能是10个,很难达到识别0-9这10个数字,所以2层比较合适
三、分析神经元数量隐藏层神经元数量没有特定的计算方法,主要是靠经验测试,当然设置过多会导致参数太多训练比较困难,太少会达不到识别效果
1、第一层:第一层的神经元没有绝对,这里给64,主要是方便后面上fpga使用
2、第二层:因为一个神经元只有一个输出,而输出的数量是0-9的数字概率,所以第二层神经元为10
四、分析参数数量1、第一层:因为输入层28x28图片,所以一个神经元有784个w,1个b,64个神经元对应64x784个w,64个b
2、第二层:第一层的64个神经元对应64个输出,第二层输入则为64,所以第二层一个神经元有64个w,1个b,64个神经元对应64x10个w,10个b
五、分析传递过程连接方式1、第一层:单神经元计算,每个像素点w0x0+w1x1…w783x783+b经过激活函数输出a1,然后拓展给第一层所有神经元
2、第二层:第一层输出a0,a1…a63,单神经元计算,每个像素点w0a0+w1a1…w63a63+b经过激活函数输出a2,然后拓展给第二层所有神经元
注意:推理一般是为了拿到推理结果,不关心概率,所以为了节省运行时间,我们把最后一层激活函数省略
六、代码框架代码框架:
输入 图片数组,第一层权重,第一层偏置,第二层权重,第二层偏置
输出 推理结果
实现逻辑:
第一层神经网络计算
第二层神经网络计算
查询最大概率的结果输出
//全连接推理
//传入图片大小img 2828
//第一层的权重参数78464 偏置参数64个 输出64个
//第二层的权重参数64*10 偏置参数10
//找到最大值结果,并返回0-9
int my_predict(float *img, float *w1, float *b1, float *w2, float *b2)
{
//第一层64个神经元 x 28x28 w 784*64个 b 64个 输出 64个输出
//w1x1+w2x2 ... wnxn+b
//第二层 10个特征0-9 10个神经元 输入连接第一层的输出 64 w 64x10个 b 10个 输出10个
//w1x1+w2x2 ... wnxn+b
//查询那个特征值的概率最大,返回
}
七、完整代码实现:
#include#include //导入图片 #include "input_0.h" #include "input_1.h" #include "input_2.h" #include "input_3.h" #include "input_4.h" #include "input_5.h" #include "input_6.h" #include "input_7.h" #include "input_8.h" #include "input_9.h" #include //导入权重w和偏置b #include "layer1_weight.h" #include "layer1_bais.h" #include "layer2_weight.h" #include "layer2_bais.h" //输入图片 28*28 //layer1 神经元 64个 w:784*64 b:64 //layer2 神经元 10 w:64*10 b:10 //计算概率最大的值并返回 int predict(float *img,float *w1,float *b1,float *w2,float *b2) { int i,j; float y; float a1[64],a2[10]; int ret; //第一层计算 //多个神经元计算 for(i=0;i<64;i++) { //单个神经元计算 //y=w0*x0+w1*x1+w2*x2+...+w783*x783 + b y = 0.0; for(j=0;j<784;j++) { y = y + w1[j*64+i]*img[j]; } //加偏置 y = y + b1[i]; //加激活 relu 将线性转化为非线性 y = y > 0?y:0; a1[i] = y; //将单个神经元的值保存下来 // y = 0.0; } //y = 0.0; //第二层计算 //多个神经元计算 for(i=0;i<10;i++) { y = 0.0; for(j=0;j<64;j++) { //单点计算 //y=w0*x0+w1*x1+w2*x2+...+w63*x63 + b y = y + w2[i+10*j]*a1[j]; } //加偏置 y = y + b2[i]; a2[i] = y; } y = 0.0; //计算概率最大值 for(i=0;i<10;i++) { if(a2[i] > y) { y = a2[i]; ret = i; } } return ret; } void full_connect_test() { int ret; float *imgx[10]={ input_0, input_1, input_2, input_3, input_4, input_5, input_6, input_7, input_8, input_9 }; double run_time; LARGE_INTEGER time_start; //开始时间 LARGE_INTEGER time_over; //结束时间 double dqFreq; //计时器频率 LARGE_INTEGER f; //计时器频率 QueryPerformanceFrequency(&f); dqFreq=(double)f.QuadPart; for(int i=0;i<10;i++) { QueryPerformanceCounter(&time_start); //计时开始 ret = predict(imgx[i],layer1_weight,layer1_bais,layer2_weight,layer2_bais); QueryPerformanceCounter(&time_over); //计时结束 run_time=1000000*(time_over.QuadPart-time_start.QuadPart)/dqFreq; //乘以1000000把单位由秒化为微秒,精度为1000 000/(cpu主频)微秒 printf("nrun_time:%fusn",run_time); //clock_t start = clock(); //ret = predict(imgx[i],layer1_weight,layer1_bais,layer2_weight,layer2_bais); //clock_t end = clock(); //double runtime = (double)(end - start) / CLOCKS_PER_SEC; //printf("runtime:%f s ",runtime); printf("input is %d ,predict:%dn",i,ret); } } int main() { full_connect_test(); return 0; }
运行结果如下:



