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.

15 KiB

#include <stdio.h> #include <math.h> #include <stdlib.h> #include <time.h>

#define INNODE 2 // 输入层神经元个数 #define HIDENODE 10 // 隐藏层神经元个数 #define OUTNODE 1 // 输出层神经元个数

/**

  • 步长(学习率) */ double StudyRate = 1.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 relu(double x) { return(x>0) ?x :0; } double elu(double x,double alpha) { return(x>0||x==0) ?x :alpha(exp(x)-1); } double tanh(double x) {//其实math库中也有函数 但再写一个 return 2/(1+exp(-2x))-1; } double identity(double x) { return x; } double binarystep(double x) { return(x>0||x==0) ?1 :0; } double arctan(double x) { return atan(x);//直接用math库中的函数 } double prelu(double x,double alpha) { return(x>0||x==0) ?x :alphax; } double softplus(double x) { //x较大时 值约为x本身 计算exp(x)反而会导致溢出 较小时同理 if(x>15) { return x; } else if(x<-15) { return exp(x); } return log(1+exp(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("The file has been successfully read!\n"); 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){ int count=0; // 初始化一个整数变量count用于跟踪读取的数据行数 while(fscanf(file,"%lf %lf",&result->in[count][0]),&result->in[count][1]!=NULL) ++count; /利用while循环从测试集文件中逐行读取两个浮点数直到读取到文件末尾。 每次成功读取一行数据后递增count/ testSize=count; //将最终的count的值存储在名为testSize的全局变量中以便后续使用

    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!"); return; } int i; for(i=0;i<size;i++) { printf("%3d: Input1: %5lf, Input2: %5lf, Output: %5lf\n",i+1,data->in[i][0],data->in[i][1],data->out[i][0]); }

}

/**

  • 初始化函数 */ void init(){ // 设置时间戳为生成随机序列的种子 srand(time(NULL));

    // 输入层的初始化 int i,j; for (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 (i = 0; i < INNODE; ++i) { for (j = 0; j < HIDENODE; ++j) { inputLayer[i].weight[j] = rand() % 10000 / (double )10000 * 2 - 1.0; inputLayer[i].weight_delta[j] = 0.0; } }

    // 初始化隐藏层结点 for (i = 0; i < HIDENODE; ++i) { /*为隐藏层节点 i 分配了一个 double 类型的数组,用于存储该节点向下一层节点传播的权重。 使用malloc 函数在堆内存中分配足够的空间,以存储 OUTNODE 个 double 类型的权重值。 / hideLayer[i].weight =(double)malloc(sizeof(double)*OUTNODE); /*为隐藏层节点 i 分配了一个用于存储权重修正值的数组。这个数组将在神经网络的训练过程中用于存储权重的更新值。 使用malloc 函数在堆内存中分配足够的空间,以存储 OUTNODE 个 double 类型的权重值。 / hideLayer[i].weight_delta =(double)malloc(sizeof(double)*OUTNODE); /*为隐藏层节点 i 初始化了一个随机的偏置值。 这个值通常是一个在 -1.0 到 1.0 之间的随机数,用于调整该节点的激活函数的阈值。 / hideLayer[i].bias =rand()%10000/(double)100002-1.0; /初始化了隐藏层节点 i 的偏置值修正值初始值为0.0。/ hideLayer[i].bias_delta = 0.0; }

    // 初始化隐藏层权值 for (i = 0; i < HIDENODE; ++i) { for (j = 0; j < OUTNODE; ++j) { hideLayer[i].weight[j] = rand() % 10000 / (double )10000 * 2 - 1.0; hideLayer[i].weight_delta[j] = 0.0; } }

    for (i = 0; i < OUTNODE; ++i) { outLayer[i].bias = rand() % 10000 / (double )10000 * 2 - 1.0; outLayer[i].bias_delta = 0.0; } }

/**

  • 重置修正值 */ void resetDelta() { int i,j; for ( i = 0; i < INNODE; ++i) { for ( j = 0; j < HIDENODE; ++j) { inputLayer[i].weight_delta[j] = 0.0; } }

    for ( i = 0; i < HIDENODE; ++i) { hideLayer[i].bias_delta = 0.0; for ( j = 0; j < OUTNODE; ++j) { hideLayer[i].weight_delta[j] = 0.0; } }

    for ( i = 0; i < OUTNODE; ++i) { outLayer[i].bias_delta = 0.0; } }

int main() { // 初始化 init(); // 获取训练集 Sample * trainSample = getTrainData("TrainData.txt"); printData(trainSample, trainSize); int trainTime,currentTrainSample_Pos,inputLayer_Pos,hideLayer_Pos,outLayer_Pos,currentTestSample_Pos; for ( trainTime = 0; trainTime < mostTimes; ++trainTime) { // 重置梯度信息 resetDelta();

    // 当前训练最大误差
    double error_max = 0.0;

    // 开始训练累计bp
    for ( currentTrainSample_Pos = 0; currentTrainSample_Pos < trainSize; ++currentTrainSample_Pos) {

        // 输入自变量
        for ( inputLayer_Pos = 0; inputLayer_Pos < INNODE; ++inputLayer_Pos) {
            inputLayer[inputLayer_Pos].value = trainSample->in[currentTrainSample_Pos][inputLayer_Pos];
        }

        /** ----- 开始正向传播 ----- */
        for ( hideLayer_Pos = 0; hideLayer_Pos < HIDENODE; ++hideLayer_Pos) {
            double sum = 0.0;
            for ( 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 ( outLayer_Pos = 0;  outLayer_Pos < OUTNODE ; ++outLayer_Pos) {
            double sum = 0.0;
            for ( 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 ( 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 ( 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 (hideLayer_Pos = 0; hideLayer_Pos < HIDENODE; ++hideLayer_Pos) {
            for ( 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 (hideLayer_Pos = 0; hideLayer_Pos < HIDENODE; ++hideLayer_Pos) {
            double sum = 0.0;
            for ( 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 ( inputLayer_Pos = 0; inputLayer_Pos < INNODE; ++inputLayer_Pos) {
            for ( hideLayer_Pos = 0; hideLayer_Pos < HIDENODE; ++hideLayer_Pos) {
                double sum  = 0.0;
                for ( 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 ( inputLayer_Pos = 0; inputLayer_Pos < INNODE; ++inputLayer_Pos) {
        for ( 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 ( hideLayer_Pos = 0; hideLayer_Pos < HIDENODE; ++hideLayer_Pos) {
        hideLayer[hideLayer_Pos].bias += StudyRate
                * hideLayer[hideLayer_Pos].bias_delta / (double )trainSize;
        for ( 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 ( 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 ( currentTestSample_Pos = 0; currentTestSample_Pos < testSize; ++currentTestSample_Pos) {
    for ( inputLayer_Pos = 0; inputLayer_Pos < INNODE; ++inputLayer_Pos) {
        inputLayer[inputLayer_Pos].value = testSample->in[currentTestSample_Pos][inputLayer_Pos];
    }

    for ( hideLayer_Pos = 0; hideLayer_Pos < HIDENODE; ++hideLayer_Pos) {
        double sum = 0.0;
        for ( 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 ( outLayer_Pos = 0; outLayer_Pos < OUTNODE; ++outLayer_Pos) 
	{
        double sum = 0.0;
        for ( 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 ( outLayer_Pos = 0; outLayer_Pos < OUTNODE; ++outLayer_Pos) {
        testSample->out[currentTestSample_Pos][outLayer_Pos] = outLayer[outLayer_Pos].value;
    }
}

printData(testSample, testSize);

return 0;

}