You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

432 lines
14 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#define INNODE 2 // 输入层神经元个数
#define HIDENODE 10 // 隐藏层神经元个数
#define OUTNODE 1 // 输出层神经元个数
//#define HIDENODE 12 // 隐藏层神经元个数
//#define HIDENODE 8 // 隐藏层神经元个数
/**
* 步长(学习率)
*/
double StudyRate = 1.6;
//double StudyRate = 1.0;
//double StudyRate = 0.8;
//double StudyRate = 0.6;
/**
* 允许最大误差
*/
double threshold = 1e-4;
/**
* 最大迭代次数
*/
int mostTimes = 1e6;
/**
* 训练集大小
*/
int trainSize = 0;
/**
* 测试集大小
*/
int testSize = 0;
/**
* 样本
*/
typedef struct Sample{
double out[30][OUTNODE]; // 输出
double in[30][INNODE]; // 输入
}Sample;
/**
* 神经元结点
*/
typedef struct Node{
double value; // 当前神经元结点输出的值
double bias; // 当前神经元结点偏偏置值
double bias_delta; // 当前神经元结点偏置值的修正值
double *weight; // 当前神经元结点向下一层结点传播的权值
double *weight_delta; // 当前神经元结点向下一层结点传播的权值的修正值
}Node;
/**
* 输入层
*/
Node inputLayer[INNODE];
/**
* 隐藏层
*/
Node hideLayer[HIDENODE];
/**
* 输出层
*/
Node outLayer[OUTNODE];
double Max(double a, double b){
return a > b ? a : b;
}
/**
* 激活函数sigmoid
* @param x 输入值
* @return 输出值
*/
double sigmoid(double x){
//请补全sigmod函数的计算结果
return 1/(1+exp(-x));
}
// double tanh(double x)
// {
// return 2/(1+exp(-2*x))-1;
// }
// double Relu(double x)
// {
// if (x<0)
// {
// return 0;
// }
// else
// {
// return x;
// }
// }
/**
* 读取训练集
* @param filename 文件名
* @return 训练集
*/
Sample * getTrainData(const char * filename){
Sample * result = (Sample*)malloc(sizeof (Sample));
FILE * file = fopen(filename, "r");
if(file != NULL){
int count = 0;
while (fscanf(file, "%lf %lf %lf", &result->in[count][0], &result->in[count][1], &result->out[count][0]) != EOF){
++count;
}
trainSize = count;
printf("%s The file has been successfully read!\n", filename);
fclose(file);
return result;
} else{
fclose(file);
printf("%s Encountered an error while opening the file!\n\a", filename);
return NULL;
}
}
/**
* 读取测试集
* @param filename 文件名
* @return 测试集
*/
Sample * getTestData(const char * filename){
/*在内存中分配足够的空间来存储一个Sample结构并将指向该内存块的指针存储在result变量中*/
Sample * result = (Sample*)malloc(sizeof (Sample));
FILE * file = fopen(filename, "r");//打开文件
if(file != NULL){
// 初始化一个整数变量count用于跟踪读取的数据行数
int count = 0;
/*利用while循环从测试集文件中逐行读取两个浮点数直到读取到文件末尾。
每次成功读取一行数据后递增count*/
while (fscanf(file, "%lf %lf", &result->in[count][0], &result->in[count][1]) != EOF){
++count;
}
//将最终的count的值存储在名为testSize的全局变量中以便后续使用
testSize = count;
printf("%s The file has been successfully read!\n", filename);
fclose(file);
//返回result
return result;
}else{
fclose(file);
printf("%s Encountered an error while opening the file!\n\a", filename);
return NULL;
}
}
/**
* 打印样本
* @param data 要打印的样本
* @param size 样本大小
*/
void printData(Sample * data, int size){
if (data == NULL)//?????????????????????????????????????????
{
printf("Sample is empty\n");
return;
}
else
{
for (int i = 0; i < size; i++)
{
printf("%lf %lf %lf\n", data->in[i][0], data->in[i][1], data->out[i][0]);
}
}
}
/**
* 初始化函数
*/
void init(){
// 设置时间戳为生成随机序列的种子
srand(time(NULL));
// 输入层的初始化
for (int i = 0; i < INNODE; ++i) {
inputLayer[i].weight = (double *)malloc(sizeof (double ) * HIDENODE);
inputLayer[i].weight_delta = (double *) malloc(sizeof (double ) * HIDENODE);
inputLayer[i].bias = 0.0;
inputLayer[i].bias_delta = 0.0;
}
// 输出层权值初始化
for (int i = 0; i < INNODE; ++i) {
for (int j = 0; j < HIDENODE; ++j) {
inputLayer[i].weight[j] = rand() % 10000 / (double )10000 * 2 - 1.0;
inputLayer[i].weight_delta[j] = 0.0;
}
}
// 初始化隐藏层结点
for (int i = 0; i < HIDENODE; ++i) {
/*为隐藏层节点 i 分配了一个 double 类型的数组,用于存储该节点向下一层节点传播的权重。
使用malloc 函数在堆内存中分配足够的空间,以存储 OUTNODE 个 double 类型的权重值。
*/
hideLayer[i].weight = (double*)malloc(OUTNODE * sizeof (double));
/*为隐藏层节点 i 分配了一个用于存储权重修正值的数组。这个数组将在神经网络的训练过程中用于存储权重的更新值。
使用malloc 函数在堆内存中分配足够的空间,以存储 OUTNODE 个 double 类型的权重值。
*/
hideLayer[i].weight_delta = (double *) malloc(sizeof (double ) * OUTNODE);
/*为隐藏层节点 i 初始化了一个随机的偏置值。
这个值通常是一个在 -1.0 到 1.0 之间的随机数,用于调整该节点的激活函数的阈值。
*/
hideLayer[i].bias = (((rand() % 10) - 4.5) / 4.5);
/*初始化了隐藏层节点 i 的偏置值修正值初始值为0.0。*/
hideLayer[i].bias_delta = 0.0;
}
// 初始化隐藏层权值
for (int i = 0; i < HIDENODE; ++i) {
for (int j = 0; j < OUTNODE; ++j) {
hideLayer[i].weight[j] = rand() % 10000 / (double )10000 * 2 - 1.0;
hideLayer[i].weight_delta[j] = 0.0;
}
}
for (int i = 0; i < OUTNODE; ++i) {
outLayer[i].bias = rand() % 10000 / (double )10000 * 2 - 1.0;
outLayer[i].bias_delta = 0.0;
}
}
/**
* 重置修正值
*/
void resetDelta(){
for (int i = 0; i < INNODE; ++i) {
for (int j = 0; j < HIDENODE; ++j) {
inputLayer[i].weight_delta[j] = 0.0;
}
}
for (int i = 0; i < HIDENODE; ++i) {
hideLayer[i].bias_delta = 0.0;
for (int j = 0; j < OUTNODE; ++j) {
hideLayer[i].weight_delta[j] = 0.0;
}
}
for (int i = 0; i < OUTNODE; ++i) {
outLayer[i].bias_delta = 0.0;
}
}
int main() {
// 初始化
init();
// 获取训练集
Sample * trainSample = getTrainData("TrainData.txt");
// printData(trainSample, trainSize);
for (int trainTime = 0; trainTime < mostTimes; ++trainTime) {
// 重置梯度信息
resetDelta();
// 当前训练最大误差
double error_max = 0.0;
// 开始训练累计bp
for (int currentTrainSample_Pos = 0; currentTrainSample_Pos < trainSize; ++currentTrainSample_Pos) {
// 输入自变量
for (int inputLayer_Pos = 0; inputLayer_Pos < INNODE; ++inputLayer_Pos) {
inputLayer[inputLayer_Pos].value = trainSample->in[currentTrainSample_Pos][inputLayer_Pos];
}
/** ----- 开始正向传播 ----- */
for (int hideLayer_Pos = 0; hideLayer_Pos < HIDENODE; ++hideLayer_Pos) {
double sum = 0.0;
for (int inputLayer_Pos = 0; inputLayer_Pos < INNODE; ++inputLayer_Pos) {
sum += inputLayer[inputLayer_Pos].value * inputLayer[inputLayer_Pos].weight[hideLayer_Pos];
}
sum -= hideLayer[hideLayer_Pos].bias;
hideLayer[hideLayer_Pos].value = sigmoid(sum);
}
for (int outLayer_Pos = 0; outLayer_Pos < OUTNODE ; ++outLayer_Pos) {
double sum = 0.0;
for (int hideLayer_Pos = 0; hideLayer_Pos < HIDENODE; ++hideLayer_Pos) {
/*计算每一个隐藏层节点的value和权值的乘积相加得到sum
*/
sum += hideLayer[hideLayer_Pos].value * hideLayer[hideLayer_Pos].weight[outLayer_Pos];
}
/*更新sum使sum减去偏置值;
*/
sum -= outLayer[outLayer_Pos].bias;
/*利用sigmod函数对得到的sum进行激活把激活后的结果赋值给对应的输出层节点value(outLayer[outLayer_Pos].value)。
*/
outLayer[outLayer_Pos].value = sigmoid(sum);
}
/** ----- 计算误差 ----- */
double error = 0.0;
for (int outLayer_Pos = 0; outLayer_Pos < OUTNODE; ++outLayer_Pos) {
double temp = fabs(outLayer[outLayer_Pos].value - trainSample->out[currentTrainSample_Pos][outLayer_Pos]);
// 损失函数
error += temp * temp / 2.0;
}
error_max = Max(error_max, error);
/** ----- 反向传播 ----- */
for (int outLayer_Pos = 0; outLayer_Pos < OUTNODE; ++outLayer_Pos) {
double bias_delta = -(trainSample->out[currentTrainSample_Pos][outLayer_Pos] - outLayer[outLayer_Pos].value)
* outLayer[outLayer_Pos].value * (1.0 - outLayer[outLayer_Pos].value);
outLayer[outLayer_Pos].bias_delta += bias_delta;
}
for (int hideLayer_Pos = 0; hideLayer_Pos < HIDENODE; ++hideLayer_Pos) {
for (int outLayer_Pos = 0; outLayer_Pos < OUTNODE; ++outLayer_Pos) {
double weight_delta = (trainSample->out[currentTrainSample_Pos][outLayer_Pos] - outLayer[outLayer_Pos].value)
* outLayer[outLayer_Pos].value * (1.0 - outLayer[outLayer_Pos].value)
* hideLayer[hideLayer_Pos].value;
hideLayer[hideLayer_Pos].weight_delta[outLayer_Pos] += weight_delta;
}
}
//
for (int hideLayer_Pos = 0; hideLayer_Pos < HIDENODE; ++hideLayer_Pos) {
double sum = 0.0;
for (int outLayer_Pos = 0; outLayer_Pos < OUTNODE; ++outLayer_Pos) {
sum += -(trainSample->out[currentTrainSample_Pos][outLayer_Pos] - outLayer[outLayer_Pos].value)
* outLayer[outLayer_Pos].value * (1.0 - outLayer[outLayer_Pos].value)
* hideLayer[hideLayer_Pos].weight[outLayer_Pos];
}
hideLayer[hideLayer_Pos].bias_delta += sum * hideLayer[hideLayer_Pos].value * (1.0 - hideLayer[hideLayer_Pos].value);
}
for (int inputLayer_Pos = 0; inputLayer_Pos < INNODE; ++inputLayer_Pos) {
for (int hideLayer_Pos = 0; hideLayer_Pos < HIDENODE; ++hideLayer_Pos) {
double sum = 0.0;
for (int outLayer_Pos = 0; outLayer_Pos < OUTNODE; ++outLayer_Pos) {
sum += (trainSample->out[currentTrainSample_Pos][outLayer_Pos] - outLayer[outLayer_Pos].value)
* outLayer[outLayer_Pos].value * (1.0 - outLayer[outLayer_Pos].value)
* hideLayer[hideLayer_Pos].weight[outLayer_Pos];
}
inputLayer[inputLayer_Pos].weight_delta[hideLayer_Pos] += sum * hideLayer[hideLayer_Pos].value * (1.0 - hideLayer[hideLayer_Pos].value)
* inputLayer[inputLayer_Pos].value;
}
}
}
// 判断误差是否达到允许误差范围
if(error_max < threshold){
printf("\a Training completed!Total training count:%d, maximum error is:%f\n", trainTime + 1, error_max);
break;
}
// 误差无法接受,开始修正
for (int inputLayer_Pos = 0; inputLayer_Pos < INNODE; ++inputLayer_Pos) {
for (int hideLayer_Pos = 0; hideLayer_Pos < HIDENODE; ++hideLayer_Pos) {
inputLayer[inputLayer_Pos].weight[hideLayer_Pos] += StudyRate
* inputLayer[inputLayer_Pos].weight_delta[hideLayer_Pos] /
(double) trainSize;
}
}
for (int hideLayer_Pos = 0; hideLayer_Pos < HIDENODE; ++hideLayer_Pos) {
hideLayer[hideLayer_Pos].bias += StudyRate
* hideLayer[hideLayer_Pos].bias_delta / (double )trainSize;
for (int outLayer_Pos = 0; outLayer_Pos < OUTNODE; ++outLayer_Pos) {
hideLayer[hideLayer_Pos].weight[outLayer_Pos] += StudyRate
* hideLayer[hideLayer_Pos].weight_delta[outLayer_Pos] / (double )trainSize;
}
}
for (int outLayer_Pos = 0; outLayer_Pos < OUTNODE; ++outLayer_Pos) {
outLayer[outLayer_Pos].bias += StudyRate
* outLayer[outLayer_Pos].bias_delta / (double )trainSize;
}
}
// 训练完成,读取测试集
Sample * testSample = getTestData("TestData.txt");
printf("The predicted results are as follows:\n");
for (int currentTestSample_Pos = 0; currentTestSample_Pos < testSize; ++currentTestSample_Pos) {
for (int inputLayer_Pos = 0; inputLayer_Pos < INNODE; ++inputLayer_Pos) {
inputLayer[inputLayer_Pos].value = testSample->in[currentTestSample_Pos][inputLayer_Pos];
}
for (int hideLayer_Pos = 0; hideLayer_Pos < HIDENODE; ++hideLayer_Pos) {
double sum = 0.0;
for (int inputLayer_Pos = 0; inputLayer_Pos < INNODE; ++inputLayer_Pos) {
sum += inputLayer[inputLayer_Pos].value * inputLayer[inputLayer_Pos].weight[hideLayer_Pos];
}
sum -= hideLayer[hideLayer_Pos].bias;
hideLayer[hideLayer_Pos].value = sigmoid(sum);
}
for (int outLayer_Pos = 0; outLayer_Pos < OUTNODE; ++outLayer_Pos) {
double sum = 0.0;
for (int hideLayer_Pos = 0; hideLayer_Pos < HIDENODE; ++hideLayer_Pos) {
sum += hideLayer[hideLayer_Pos].value * hideLayer[hideLayer_Pos].weight[outLayer_Pos];
}
sum -= outLayer[outLayer_Pos].bias;
outLayer[outLayer_Pos].value = sigmoid(sum);
}
for (int outLayer_Pos = 0; outLayer_Pos < OUTNODE; ++outLayer_Pos) {
testSample->out[currentTestSample_Pos][outLayer_Pos] = outLayer[outLayer_Pos].value;
}
}
printData(testSample, testSize);
return 0;
}