欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

BP神经网络 c++实现

程序员文章站 2022-04-27 20:40:07
BP神经网络 c++实现 BP神经网络是通过反向传播来逐渐修正层之间的权值和每个节点的阈值,可以通过学习率避免走入局部最优解。 #include #include...

BP神经网络 c++实现
BP神经网络是通过反向传播来逐渐修正层之间的权值和每个节点的阈值,可以通过学习率避免走入局部最优解。

#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

#define LAYER    3        //三层神经网络
#define NUM      10       //每层的最多节点数

#define A        30.0
#define B        10.0     //A和B是S型函数的参数
#define ITERS    10000     //最大训练次数
#define LR       0.0013   //学习率
#define ERROR    0.1    //单个样本允许的误差

struct Data {
    vector x;       //输入数据
    vector y;       //输出数据
};

class BP{

public:

    void GetData(const vector);
    void Train();
    vector ForeCast(const vector);

private:

    void InitNetWork();         //初始化网络
    void GetNums();             //获取输入、输出和隐含层节点数
    void ForwardTransfer();     //正向传播子过程
    void ReverseTransfer(int);  //逆向传播子过程
    void CalcDelta(int);        //计算w和b的调整量
    void UpdateNetWork();       //更新权值和阀值
    double GetError(int);         //计算单个样本的误差
    double GetAccu();             //计算所有样本的精度
    double Sigmoid(const double);   //计算Sigmoid的值

private:
    int in_num;                 //输入层节点数
    int ou_num;                 //输出层节点数
    int hd_num;                 //隐含层节点数

    vector data;          //输入输出数据

    double w[LAYER][NUM][NUM];    //BP网络的权值
    double b[LAYER][NUM];         //BP网络节点的阀值

    double x[LAYER][NUM];         //每个神经元的值经S型函数转化后的输出值,输入层就为原值
    double d[LAYER][NUM];         //记录delta学习规则中delta的值
};

//获取训练所有样本数据
void BP::GetData(const vector _data) {
    data = _data;
}

//开始进行训练
void BP::Train() {
    printf("Begin to train BP NetWork!\n");
    GetNums();
    InitNetWork();
    int num = data.size();

    for(int iter = 0; iter <= ITERS; iter++) {
        for(int cnt = 0; cnt < num; cnt++) {
            //第一层输入节点赋值
            for(int i = 0; i < in_num; i++)
                x[0][i] = data[cnt].x[i];

            while(1) {
                ForwardTransfer();
                if(GetError(cnt) < ERROR)    //如果误差比较小,则针对单个样本跳出循环
                    break;
                ReverseTransfer(cnt);
            }
        }
        printf("This is the %d th trainning NetWork !\n", iter);

        double accu = GetAccu();
        printf("All Samples Accuracy is %lf\n", accu);
    }
    printf("The BP NetWork train End!\n");
}

//根据训练好的网络来预测输出值
vector BP::ForeCast(const vector data) {
    int n = data.size();
    assert(n == in_num);
    for(int i = 0; i < in_num; i++)
        x[0][i] = data[i];

    ForwardTransfer();
    vector v;
    for(int i = 0; i < ou_num; i++)
        v.push_back(x[2][i]);
    return v;
}

//获取网络节点数
void BP::GetNums() {
    in_num = data[0].x.size();                         //获取输入层节点数
    ou_num = data[0].y.size();                         //获取输出层节点数
    hd_num = (int)sqrt((in_num + ou_num) * 1.0) + 5;   //获取隐含层节点数
    if(hd_num > NUM) hd_num = NUM;                     //隐含层数目不能超过最大设置
}

//初始化网络
void BP::InitNetWork() {
    memset(w, 0, sizeof(w));      //初始化权值和阈值为0,也可以初始化随机值
    memset(b, 0, sizeof(b));
}

//工作信号正向传递子过程
void BP::ForwardTransfer() {
    //计算隐含层各个节点的输出值
    for(int j = 0; j < hd_num; j++) {
        double t = 0;
        for(int i = 0; i < in_num; i++)
            t += w[1][i][j] * x[0][i];
        t += b[1][j];
        x[1][j] = Sigmoid(t);
    }

    //计算输出层各节点的输出值
    for(int j = 0; j < ou_num; j++) {
        double t = 0;
        for(int i = 0; i < hd_num; i++)
            t += w[2][i][j] * x[1][i];
        t += b[2][j];
        x[2][j] = Sigmoid(t);
    }
}

//计算单个样本的误差
double BP::GetError(int cnt) {
    double ans = 0;
    for(int i = 0; i < ou_num; i++)
        ans += 0.5 * (x[2][i] - data[cnt].y[i]) * (x[2][i] - data[cnt].y[i]);
    return ans;
}

//误差信号反向传递子过程
void BP::ReverseTransfer(int cnt) {
    CalcDelta(cnt);//计算调整量
    UpdateNetWork();//根据调整量调节权值和阈值
}

//计算所有样本的精度
double BP::GetAccu() {
    double ans = 0;
    int num = data.size();
    for(int i = 0; i < num; i++) {
        int m = data[i].x.size();
        for(int j = 0; j < m; j++)
            x[0][j] = data[i].x[j];
        ForwardTransfer();
        ans += fabs(x[2][0] - data[i].y[0]) <= 0.5 ? 1 : 0;
    }
    return ans / num;
}

//计算调整量
void BP::CalcDelta(int cnt) {
    //计算输出层的delta值
    for(int i = 0; i < ou_num; i++)
        d[2][i] = (x[2][i] - data[cnt].y[i]) * x[2][i] * (A - x[2][i]) / (A * B);
    //计算隐含层的delta值
    for(int i = 0; i < hd_num; i++) {
        double t = 0;
        for(int j = 0; j < ou_num; j++)
            t += w[2][i][j] * d[2][j];
        d[1][i] = t * x[1][i] * (A - x[1][i]) / (A * B);
    }
}

//根据计算出的调整量对BP网络进行调整
void BP::UpdateNetWork() {
    //隐含层和输出层之间权值和阈值调整
    for(int i = 0; i < hd_num; i++) {
        for(int j = 0; j < ou_num; j++)
            w[2][i][j] -= LR * d[2][j] * x[1][i];
    }
    for(int i = 0; i < ou_num; i++)
        b[2][i] -= LR * d[2][i];

    //输入层和隐含层之间权值和阈值调整
    for(int i = 0; i < in_num; i++)
        for(int j = 0; j < hd_num; j++)
            w[1][i][j] -= LR * d[1][j] * x[0][i];
    for(int i = 0; i < hd_num; i++)
        b[1][i] -= LR * d[1][i];
}

//计算Sigmoid激活函数的值
double BP::Sigmoid(const double x) {
    return A / (1 + exp(-x / B));
}

int main() {
    FILE *fpTrain = fopen("/Users/really/Desktop/student_train.csv", "r");
    FILE *fpTest = fopen("/Users/really/Desktop/student_test.csv", "r");

    vector data;
    double maxA = -1, minA = 1000, maxB = -1, minB = 1000;
    for(int i = 0; i < 200; i++) {
        double a, b, c;
        Data t;
        fscanf(fpTrain, "%lf,%lf,%lf", &a, &b, &c);
        t.x.push_back(a);  t.x.push_back(b);
        t.y.push_back(c);

        maxA = max(maxA, a);
        minA = min(minA, a);
        maxB = max(maxB, b);
        minB = min(minB, b);

        data.push_back(t);
    }

    for(int i = 0; i < 200; i++) {
        data[i].x[0] = (data[i].x[0] - minA) / (maxA - minA);
        data[i].x[1] = (data[i].x[1] - minB) / (maxB - minB);
    }

    fclose(fpTrain);

    BP *bp = new BP();
    bp->GetData(data);
    bp->Train();

    for(int i = 0; i < 60; i++) {
        vector in;
        double a, b;
        fscanf(fpTest, "%lf,%lf", &a, &b);
        a = (a - minA) / (maxA - minA);
        b = (b - minB) / (maxB - minB);
        in.push_back(a);  in.push_back(b);

        vector ou;
        ou = bp->ForeCast(in);

        if(ou[0] >= 0.5)
            ou[0] = 1;
        else
            ou[0] = 0;

        printf("%d\n", (int)ou[0]);
    }

    fclose(fpTest);

    return 0;
}