可能存在一些语法错误,误差计算似乎有问题
setting.h
#ifndef SETTING_H
#define SETTING_H
#include "time.h"
#include <stdio.h>
#define IPNNUM 784
#define HDNNUM 100//取自hdnnum=(ipnnum*opmmum)^0.5,约数。
//若取log 2 ipnnum,hdnnum=10(约数)隐含层太少,准确率低
#define OPNNUM 10
#define TrainImgNum 60000
#define TestImgNum 10000
typedef struct
{
unsigned char tag;
double data[IPNNUM];
double label[OPNNUM];
} ImgData;//单个图像信息存储结构体
typedef struct
{
ImgData mImgData[TrainImgNum];
}GetTrainImgs;//存储所有训练图像数据
typedef struct
{
ImgData mImgData[TestImgNum];
}GetTestImgs;//存储所有测试图像数据
typedef struct innode
{
double value;
double W[HDNNUM]; //权重的值
}innode;
typedef struct hidenode
{
double value;
double W[OPNNUM]; //权重的值
}hidenode;
typedef struct outnode
{
double value;
}outnode;
typedef struct layer
{
innode inlayer[IPNNUM];
hidenode hidlayer[HDNNUM];
outnode outlayer[OPNNUM];
double learnrate;
double k1;//隐含层偏置项
double k2;//输出层偏置项
double target[OPNNUM];//应该叫tend,半途发现写错了,懒得改了
double realout[OPNNUM];
}layer;//三层神经网络结构体
#endif //
net.h
#ifndef NET_H
#define NET_H
#include "setting.h"
#include <math.h>
void InitInNode(innode* node, int num);
void InitHideNode(hidenode* node, int num);
void InitNet(layer* layer);
double sigmoid(double z);
double Getloss(layer* layer);
void ForwardPropagation(layer* layer, ImgData* imgdata);
void BackPropagation(layer* layer, ImgData* imgdata);
void PrintResult(int traintimes, layer* layer);
#endif
getImg.h
#ifndef GETIMG_H
#define GETIMG_H
#include "setting.h"
void GetTrainImgData(GetTrainImgs* img, const char* datapath, const char* labelpath);
void GetTestImgData(GetTestImgs* img, const char* datapath, const char* labelpath);
#endif
net.c
#include "setting.h"
#include <stdlib.h>
#include <math.h>
double learnrate = 0.10;
void InitInNode(innode *node,int num)
{
srand((unsigned)time(NULL));
for (int i = 0; i < num; i++)
{
node->W[i] = rand() % 100 / (double)100 * 0.1;
if(rand()%2==0)
{
node->W[i] = -node->W[i];
}
}
}
void InitHideNode(hidenode* node, int num)
{
srand((unsigned)time(NULL));
for (int i = 0; i < num; i++)
{
node->W[i] = rand() % 100 / (double)100 * 0.1;
if (rand() % 2 == 0)
{
node->W[i] = -node->W[i];
}
}
}
void InitNet(layer* layer)
{
srand((unsigned)time(NULL));
layer->k1 = rand() % 100 / (double)100;
layer->k2 = rand() % 100 / (double)100;
//初始化偏置项
for (int i = 0; i < IPNNUM; i++)
{
InitInNode(&layer->inlayer[i], HDNNUM);
}
for (int i = 0; i < HDNNUM; i++)
{
InitHideNode(&layer->hidlayer[i], OPNNUM);
}
}
double sigmoid(double z)
{
return 1 / (1 + exp(-z));
}//激活函数,可以选择sigmoid或者tanh
double Getloss(layer* layer)
{
double alloss = 0;//积累值
for (int i = 0; i < OPNNUM; i++)
{
alloss += (layer->target[i] - layer->realout[i]) * (layer->target[i] - layer->realout[i]);//报错
}
return alloss/OPNNUM;
}
//均方误差,计算正确率时可用
void ForwardPropagation(layer* layer, ImgData* imgdata)
{
//printf("the tag is %d\n", imgdata->tag);
for (int i = 0; i < IPNNUM; i++)//将输入赋给输入层
{
layer->inlayer[i].value = imgdata->data[i];//将图像数据赋给输入层
}
for (int i = 0; i < HDNNUM; i++)//计算隐含层的值
{
double z = 0;
for (int j = 0; j < IPNNUM; j++)
{
z += layer->inlayer[j].value * layer->inlayer[j].W[i];
//第j个输入层的值乘以第j个输入层到第i个隐含层的权重
}
z+=layer->k1;//加上偏置项
layer->hidlayer[i].value = sigmoid(z);//激活
}
for (int i = 0; i < OPNNUM; i++)
{
double z = 0;
for (int j = 0; j < HDNNUM; j++)
{
z += layer->hidlayer[j].value * layer->hidlayer[j].W[i];
}
z += layer->k2;
layer->outlayer[i].value = sigmoid(z);
layer->realout[i] = layer->outlayer[i].value;
}
}
//参数:标签,网络,输入
void BackPropagation(layer* layer,ImgData* imgdata)//传进来的T参数是标签
{
for (int i = 0; i < OPNNUM; i++)
{
layer->target[i] = imgdata->label[i];//将标签赋给target(target实际应该是tend期待输出值)
}
for (int innum = 0; innum < IPNNUM; innum++)//更新输入层权重,先改1-->all,而后2-->all
{
for (int hidnum = 0; hidnum < HDNNUM; hidnum++)
{//怀疑与输入层权重的赋值有关,learnrate???
double y = layer->hidlayer[hidnum].value;//隐含层的输出
double LOSS = 0;//E对Y的偏导数
for (int outnum = 0; outnum < OPNNUM; outnum++)
{
LOSS += (layer->realout[outnum] - layer->target[outnum])//O-T
* layer->realout[outnum]//O
* (1 - layer->realout[outnum])//1-O
* layer->hidlayer[hidnum].W[outnum];//W
}
layer->inlayer[innum].W[hidnum]-= learnrate * LOSS * y * (1 - y) * layer->inlayer[innum].value;
}
}
for(int hidnum=0;hidnum< HDNNUM;hidnum++)//更新隐含层的权重
{
for(int outnum = 0; outnum < OPNNUM; outnum++)
{
layer->hidlayer[hidnum].W[outnum] -= (layer->realout[outnum] - layer->target[outnum])
* layer->realout[outnum]
* (1 - layer->realout[outnum])
* layer->hidlayer[hidnum].value*learnrate;
}
}
}
void PrintResult(int traintimes, layer *layer)//如能修改成把输入的标签也打印出来就好了
{
double loss = Getloss(layer);//target-->tend
printf("第%d次训练,误差为%f\n", traintimes, loss);
for (int i = 0; i < OPNNUM; i++)
{
printf("输出%d为:%f\n", i + 1, layer->realout[i]);
}
}
getImg.c
#include "setting.h"
/*
读取训练数据集
参数:图像信息存储结构体,数据集路径,标签集路径 (const表示这个变量的值不能被修改
返回值:无
功能:读取训练数据集,将数据集的图像数据和标签数据存入ImgData结构体数组中
*/
void GetTrainImgData(GetTrainImgs* img, const char* datapath, const char* labelpath)
{
unsigned char cache[4];//缓存区
FILE* f;
fopen_s(&f, datapath, "rb");//二进制方式打开文件,并将信息放到f这个结构体指针里
if (f == NULL)
{
printf("open file error!\n");
return;
}//检查文件是否打开成功
fread_s(cache, 4, 1, 4, f);//读取校验位
//fread_s(读取到哪里,读取数据总字节数,一个数据几字节,读几个数据,在哪里读);
fread_s(cache, 4, 1, 4, f);//读取数据集图像个数
int imgnum = (cache[0] << 24) + (cache[1] << 16) + (cache[2] << 8) + cache[3];
//换算,原本unsigned char类型的字节是无符号8位二进制整数,范围是0~255,这里换算成十进制整型数
if (imgnum != TrainImgNum)
{
printf("error!");
return;
}
fread_s(cache, 4, 1, 4, f);//读行数
int imglength = (cache[0] << 24) + (cache[1] << 16) + (cache[2] << 8) + cache[3];
fread_s(cache, 4, 1, 4, f);//读列数
int imgwidth = (cache[0] << 24) + (cache[1] << 16) + (cache[2] << 8) + cache[3];
for (int i = 0; i < imgnum; i++)
{
for (int j = 0; j < IPNNUM; j++)
{
unsigned char pixel;
fread_s(&pixel, 1, 1, 1, f);
img->mImgData[i].data[j] = (double)pixel / (double)255.0 * 0.99 + 0.01;
}
}
fclose(f);//关闭文件
fopen_s(&f, labelpath, "rb");
if (f == NULL)
{
printf("open file error!\n");
return;
}
fread_s(cache, 4, 1, 4, f);
fread_s(cache, 4, 1, 4, f);
imgnum = (cache[0] << 24) + (cache[1] << 16) + (cache[2] << 8) + cache[3];
if (imgnum != TrainImgNum )
{
printf("error!");
return;
}
for (int i = 0; i < imgnum; i++)
{
fread(&img->mImgData[i].tag, 1, 1, f);
for (int j = 0; j < OPNNUM; j++)
{
img->mImgData[i].label[j] = 0.01;
}
img->mImgData[i].label[img->mImgData[i].tag] = 0.99;
}
fclose(f);
}
//malloc函数分配多图像存储空间必报错,无法写入参数,猜测是因为imgnum过大无法正常分配动态内存。
void GetTestImgData(GetTestImgs* img, const char* datapath, const char* labelpath)
{
unsigned char cache[4];
FILE* f;
fopen_s(&f, datapath, "rb");
if (f == NULL)
{
printf("open file error!\n");
return;
}//检查文件是否打开成功
fread_s(cache, 4, 1, 4, f);//读取校验位
fread_s(cache, 4, 1, 4, f);//读取数据集图像个数
int imgnum = (cache[0] << 24) + (cache[1] << 16) + (cache[2] << 8) + cache[3];
if (imgnum != TestImgNum)
{
printf("error!");
return;
}
fread_s(cache, 4, 1, 4, f);//读行数
int imglength = (cache[0] << 24) + (cache[1] << 16) + (cache[2] << 8) + cache[3];
fread_s(cache, 4, 1, 4, f);//读列数
int imgwidth = (cache[0] << 24) + (cache[1] << 16) + (cache[2] << 8) + cache[3];
for (int i = 0; i < imgnum; i++)
{
for (int j = 0; j < IPNNUM; j++)
{
unsigned char pixel;
fread_s(&pixel, 1, 1, 1, f);
img->mImgData[i].data[j] = (double)pixel / (double)255.0 * 0.99 + 0.01;
}
}
fclose(f);//关闭文件
fopen_s(&f, labelpath, "rb");
if (f == NULL)
{
printf("open file error!\n");
return;
}
fread_s(cache, 4, 1, 4, f);
fread_s(cache, 4, 1, 4, f);
imgnum = (cache[0] << 24) + (cache[1] << 16) + (cache[2] << 8) + cache[3];
if (imgnum != TestImgNum)
{
printf("error!");
return;
}
for (int i = 0; i < imgnum; i++)
{
fread(&img->mImgData[i].tag, 1, 1, f);
for (int j = 0; j < OPNNUM; j++)
{
img->mImgData[i].label[j] = 0.01;
}
img->mImgData[i].label[img->mImgData[i].tag] = 0.99;//跟tag一样的label置0.99
}
fclose(f);
}
main.c
#include "setting.h"
#include "net.h"
#include "getImg.h"
#include <stdlib.h>
GetTrainImgs TrainImgs;
GetTestImgs TestImgs;
layer TheLayer;
void RightRate(int time,layer* layer, GetTestImgs* getImg)
{
int rightnum = 0;
double loss = 0;
for (int i = 0; i < 10000; i++)
{
ForwardPropagation(layer, &getImg->mImgData[i]);
double value = -100;
int gettag = -100;
for (int j = 0; j < OPNNUM; j++)
{
if (layer->outlayer[j].value > value)//找到输出的最大值
{
value = layer->outlayer[j].value;
gettag = j;
}
}
if (gettag == getImg->mImgData[i].tag)//如果最大值的下标和标签相同,说明识别正确
{
rightnum++;
}
}
PrintResult(time+1, layer);
//printf("第%d轮: ", time + 1);
if (rightnum != 0)
printf("正确率为:%f\n", rightnum / 10000.0);
else
printf("正确率为:未定义(rightnum 为 0)\n");
}
void main()
{
srand((unsigned)time(NULL));
InitNet(&TheLayer);
/*printf("wi是%lf,wo是%lf\n", TheLayer.inlayer[1].W[1], TheLayer.hidlayer[1].W[2]);
测试权重是否被初始化好*/
GetTrainImgData(&TrainImgs, "train-images.idx3-ubyte", "train-labels.idx1-ubyte");
/*for (int i = 0; i < 10; i++)
{
printf("%d\n", TrainImgs.mImgData[i].tag);
for(int j=0;j<10;j++)
printf("%lf\n", TrainImgs.mImgData[i].label[j]);
}*/
GetTestImgData( &TestImgs, "t10k-images.idx3-ubyte", "t10k-labels.idx1-ubyte");
/*for (int i = 0; i < 10; i++)
printf("%d\n", TestImgs.mImgData[i].tag);
测试数据是否被读取好,结果是数据被读取好了*/
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 60000; j++)//每次处理一张图像
{
//printf("the tag is -- %d\n", TrainImgs.mImgData[j].tag);
//数据有成功传进ForwardPropagation函数
ForwardPropagation(&TheLayer, &TrainImgs.mImgData[j]);//传入第j张图像的数据
BackPropagation( &TheLayer, &TrainImgs.mImgData[j]);
//测试用,可查看权重变化特征
/*if (j % 1000 == 0)
{
printf("第%d张图像,wi是%lf,wo是%lf\n", j + 1, TheLayer.inlayer[1].W[2], TheLayer.hidlayer[1].W[2]);
}*/
}
RightRate(i, &TheLayer, &TestImgs);
}
printf("已经结束嘞!/n");
}
参考:深度学习3—用三层全连接神经网络训练MNIST手写数字字符集