C语言bp神经网络,四分类任务

C语言实现bp神经网络

该程序是gpt生成的,我稍加改动,可以直接运行。

后续会上传到github上。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#define N 4    // 输入维度
#define M 4    // 输出维度
#define H 15     // 隐藏层1维度
#define H2 15    // 隐藏层2维度
#define ETA 0.002  // 学习率
#define THETA 0.01 // 误差容限
#define EPOCHS 70000 // 最大训练次数
#define TEST_IN_PATH                    "D:\\data_save\\c-bpnn-master\\dataset\\at_in_p.txt"
#define TEST_OUT_PATH                   "D:\\data_save\\c-bpnn-master\\dataset\\at_out_p.txt"
#define SAVE_PARAM_PATH                 "D:\\data_save\\c-bpnn-master\\bpnn_param.h"
#define BUFFER_SIZE 256
static FILE *in_file = NULL;
static FILE *out_file = NULL;

// 随机数生成器
double rand_double(double min, double max) {
    return min + (max - min) * ((double)rand() / RAND_MAX);
}

// sigmoid 函数
double sigmoid(double x) {
    return 1.0 / (1.0 + exp(-x));
}
// d_sigmoid 函数
double d_sigmoid(double x)
{
    return x * (1 - x);
}
// softmax 函数
void softmax(double *x, int n) {
    double sum = 0.0;
    int i;
    for (i = 0; i < n; i++) {
        sum += exp(x[i]);
    }
    for (i = 0; i < n; i++) {
        x[i] = exp(x[i]) / sum;
    }
}

// 前向传播
void forward(double *x, double **w1, double *b1, double **w2, double *b2,double *y, double *z) {
    int i, j;
    //w1[N][H],b1[20],w2[H][N],b2[4]
    for (i = 0; i < H; i++) {
        z[i] = b1[i];
        for (j = 0; j < N; j++) {
            z[i] += x[j] * w1[j][i];
        }
        y[i] = sigmoid(z[i]);
    }
    for (i = 0; i < M; i++) {
        double u = b2[i];
        for (j = 0; j < H; j++) {
            u += y[j] * w2[j][i];
        }
        z[H+i] = u;
    }
    softmax(&z[H], M);
}

// 反向传播,更新模型
void backward(double *x, double *t, double **w1, double *b1, double **w2, double *b2, double *y, double *z) {
    int i, j, k;
    double delta[M], delta2[H];
    for (i = 0; i < M; i++) {
        delta[i] = z[H+i] - t[i];
    }
    for (i = 0; i < H; i++) {
        double sum = 0.0;
        for (j = 0; j < M; j++) {
            sum += delta[j] * w2[i][j];
        }
        delta2[i] = y[i] * (1.0 - y[i]) * sum;
    }
    for (i = 0; i < H; i++) {
        for (j = 0; j < M; j++) {
            w2[i][j] -= ETA * delta[j] * y[i];
        }
    }
    for (i = 0; i < M; i++) {
        b2[i] -= ETA * delta[i];
    }
    for (i =0; i < H; i++) {
        for (j = 0; j < N; j++) {
            w1[j][i] -= ETA * delta2[i] * x[j];
        }
        b1[i] -= ETA * delta2[i];
    }
}
// 前向传播
void predict(double *x, double **w1, double *b1, double **w2, double *b2, double *y, double *z) {
    int i, j;
    for (i = 0; i < H; i++) {
        z[i] = b1[i];
        for (j = 0; j < N; j++) {
            z[i] += x[j] * w1[j][i];
        }
        y[i] = sigmoid(z[i]);
    }
    for (i = 0; i < M; i++) {
        double u = b2[i];
        for (j = 0; j < H; j++) {
            u += y[j] * w2[j][i];
        }
        z[i] = u;
    }
    softmax(z, M);
}
// 训练神经网络
void train(double **x, double **t, int n, double **w1, double *b1, double **w2, double *b2) {
    int i, j, k;
    double y[H], z[H+M];
    for (i = 0; i < EPOCHS; i++) {
        double error = 0.0;
        for (j = 0; j < n; j++) {
            forward(x[j], w1, b1, w2, b2, y, z);
            backward(x[j], t[j], w1, b1, w2, b2, y, z);
            for (k = 0; k < M; k++) {
                error += t[j][k] * log(z[H+k] + 1e-7);
            }
        }
        error /= -n;
        if (i % 100 == 0) {
            printf("Epoch: %d, Error: %f\n", i, error);
        }
        if (error < THETA) {
            printf("Converged!\n");
            break;
        }
    }
}
//保存参数到 SAVE_PARAM_PATH 文件中
void save_parameter(double **w1, double *b1, double **w2, double *b2) {
    FILE *out = NULL;
    out = fopen(SAVE_PARAM_PATH, "w+");
    if (out == NULL) {
        fprintf(stderr, "[BPNN] OPEN FILE %s FAILED.\n", SAVE_PARAM_PATH);
    }
    fprintf(out, "double w1[N][H]={");
    //w1[N][H],b1[20],w2[H][M],b2[4]
    for (size_t i = 0; i < N; i++) {
        fprintf(out, "{");
        for (size_t h = 0; h < H; h++)
        {
            fprintf(out, "%lf,", w1[i][h]);
        }
        fprintf(out, "},\n");
    }
    fprintf(out, "};\n");

    fprintf(out, "double b1[H]={");
    for (size_t h = 0; h < H; h++)
        fprintf(out, "%lf,", b1[h]);
    fprintf(out, "};\n");

    fprintf(out, "double w2[H][M]={");
    for (size_t h = 0; h < H; h++){
        fprintf(out, "{");
        for (size_t j = 0; j < M; j++)
        {
            fprintf(out, "%lf,", w2[h][j]);
        }
        fprintf(out, "},\n");
    }
    fprintf(out, "};\n");

    fprintf(out, "double b2[M]={");
    for (size_t j = 0; j < M; j++)
        fprintf(out, "%lf,", b2[j]);
    fprintf(out, "};\n");
    fclose(out);
}

数据集长这样,已经做归一化了:

0.2347,0.2671,0.1805,0.2383
0.2347,0.2671,0.1805,0.2383
0.2347,0.2671,0.1805,0.2383
0.2347,0.2671,0.1805,0.2383
0.2347,0.2671,0.1805,0.2383
0.2347,0.2671,0.1805,0.2383
0.2347,0.2671,0.1805,0.2383
0.2347,0.2671,0.1805,0.2383
0.2347,0.2671,0.1805,0.2383
0.2347,0.2671,0.1805,0.2383
0.2347,0.2671,0.1805,0.2383

标签数据长这样:

0,0,0,1
0,0,0,1
0,0,0,1
0,0,0,1
0,0,0,1
0,0,0,1
0,0,0,1
0,0,0,1
0,0,0,1
0,0,0,1
0,0,0,1

下面的main函数中,TEST_IN_PATH=数据集文件地址,TEST_OUT_PATH=标签数据地址。

int main()
{
    static char buffer[BUFFER_SIZE];
    int d_len=2000;
    int train_len=0,val_len=0;
    int i, j;
    double max=0;
    double tab_max=0;
    int tab_flag=0;
    int flag=0;
    //获取数据
    in_file = fopen(TEST_IN_PATH, "r");
    if (in_file == NULL) {
        fprintf(stderr, "open file %s failed.\n", TEST_IN_PATH);
        return 0;
    }
    out_file = fopen(TEST_OUT_PATH, "r");
    if (out_file == NULL) {
        fprintf(stderr, "open file %s failed.\n", TEST_OUT_PATH);
        return 0;
    }
    //x是数据
    //t是标签
    double **x = (double **) malloc(d_len * sizeof(double *));
    double **t = (double **) malloc(d_len * sizeof(double *));

    if (in_file && out_file) {
        for(int m=0;m<d_len;m++)
        {
            x[m]=(double *) malloc(N * sizeof(double));
            t[m]=(double *) malloc(M * sizeof(double));
            if (fgets(buffer, BUFFER_SIZE, in_file) != NULL) {
                char *token = strtok(buffer, ",");
                for (i = 0; i < N; i++) {
                    if (token == NULL) {
                        fprintf(stderr, "the format of input is not correct!\n");
                    }
                    x[m][i] = strtod(token, NULL);
                    token = strtok(NULL, ",");
                }
            }
            if (fgets(buffer, BUFFER_SIZE, out_file) != NULL) {
                char *token = strtok(buffer, ",");
                for (i = 0; i < M; i++) {
                    if (token == NULL) {
                        fprintf(stderr, "the format of out is not correct!\n");
                    }
                    t[m][i] = strtod(token, NULL);
                    token = strtok(NULL, ",");
                }
            }
            //printf("in:%f,%f,%f,%f out:%f,%f,%f,%f \n",x[m][0],x[m][1],x[m][2],x[m][3],t[m][0],t[m][1],t[m][2],t[m][3]);
        }
    }
// 初始化权重和偏置  w1[N][H],b1[20],w2[H][N],b2[4]
    double **w1 = (double **) malloc(N * sizeof(double *));
    for (i = 0; i < N; i++) {
        w1[i] = (double *) malloc(H * sizeof(double));
        for (j = 0; j < H; j++) {
            w1[i][j] = rand_double(-1.0, 1.0);
        }
    }
    double *b1 = (double *) malloc(H * sizeof(double));
    for (i = 0; i < H; i++) {
        b1[i] = rand_double(-1.0, 1.0);
    }
    double **w2 = (double **) malloc(H * sizeof(double *));
    for (i = 0; i < H; i++) {
        w2[i] = (double *) malloc(M * sizeof(double));
        for (j = 0; j < M; j++) {
            w2[i][j] = rand_double(-1.0, 1.0);
        }
    }
    double *b2 = (double *) malloc(M * sizeof(double));
    for (i = 0; i < M;i++) {
        b2[i] = rand_double(-1.0, 1.0);
    }
    train_len=(int)(d_len*0.8);
    val_len=(int)(d_len*0.2);
    //训练
    train(x, t, train_len, w1, b1, w2, b2);
    //保存训练参数
    save_parameter( w1, b1, w2, b2);

    double *ys      = (double *) malloc(H * sizeof(double ));
    double *zs      = (double *) malloc(M * sizeof(double ));
    int correct = 0;
    //测试准确率
    for (i=train_len;i<d_len;i++)
    {
        //预测
        predict(x[i], w1, b1, w2, b2,ys,zs);
        max=0;
        flag=0;
        tab_max=0;
        tab_flag=0;
        for (j = 0; j < M; j++) {
            if (t[i][j]>tab_max)
            {
                tab_max=t[i][j];
                tab_flag=j;
            }
            if (zs[j]>max)
            {
                max=zs[j];
                flag=j;
            }
        }
        //预测准确的
        if(flag==tab_flag)
        {
            correct++;
        }
    }
    double accuracy = (double) correct / (d_len-train_len);
    printf("Accuracy: %f\n", accuracy);
    //x:d_len*N
    //t:d_len*M
    //ys:H
    //zs:M
    // 释放内存
    for (i = 0; i < d_len; i++) {
        free(x[i]);
    }
    for (i = 0; i < d_len; i++) {
        free(t[i]);
    }
    free(x);
    free(t);
    for (i = 0; i < N; i++) {
        free(w1[i]);
    }
    free(w1);
    free(b1);
    for (i = 0; i < H; i++) {
        free(w2[i]);
    }
    free(w2);
    free(b2);
    free(ys);
    free(zs);
}

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值