【矿大】中国矿业大学 信息安全管理与工程2024春季学期 L: 基于改进BP神经网络的信息安全风险评估

讨论BP神经网络训练过程中的权重调整和对特定测试用例的处理,强调训练的迭代性和一致性
摘要由CSDN通过智能技术生成

题目

样例输入

0.5 0.4 0.5 0.6 0.4 0.4

样例输出

0.62

测试用例

0.4 0.3 0.4 0.6 0.5 0.3
0.38
0.3 0.5 0.8 0.4 0.2 0.5
0.45
0.5 0.6 0.2 0.8 0.7 0.5
0.62
0.4 0.3 0.4 0.2 0.3 0.4
0.38
0.3 0.2 0.3 0.2 0.2 0.4
0.26
0.4 0.3 0.4 0.6 0.5 0.4
0.47
0.7 0.6 0.8 0.7 0.8 0.6
0.75
0.3 0.2 0.2 0.2 0.4 0.2
0.24

问题探讨

这个题目首先给人一种云里雾里的感觉,而且并没有解释清除什么是BP神经网络

我以前也手写过一个BP神经网络:Back_Propagation_Neural_Network

只需要稍微修改数值,即可符合题目要求,然而不论我怎么测试,都无法训练到题目里给的样例输入输出

以下给出几个训练出来的权重

案例

当迭代次数为11000次时,不能达到训练精度要求

隐藏层1,隐藏层节点数8

权重为

0.621287 0.621287 0.621287 0.621287 0.621287 0.621287 0.621287 0.621287
0.618209 0.618209 0.618209 0.618209 0.618209 0.618209 0.618209 0.618209
0.300551 0.300551 0.300551 0.300551 0.300551 0.300551 0.300551 0.300551
0.251638 0.251638 0.251638 0.251638 0.251638 0.251638 0.251638 0.251638
0.537282 0.537282 0.537282 0.537282 0.537282 0.537282 0.537282 0.537282
0.676881 0.676881 0.676881 0.676881 0.676881 0.676881 0.676881 0.676881

0.921417
0.921417
0.921417
0.921417
0.921417
0.921417
0.921417
0.921417

迭代次数为600000时,在320000次左右完成迭代

隐藏层1,隐藏层节点数8

权重为

0.712765 0.712765 0.712765 0.712765 0.712765 0.712765 0.712765 0.712765
0.467164 0.467164 0.467164 0.467164 0.467164 0.467164 0.467164 0.467164
0.473798 0.473798 0.473798 0.473798 0.473798 0.473798 0.473798 0.473798
-0.236541 -0.236541 -0.236541 -0.236541 -0.236541 -0.236541 -0.236541 -0.236541
1.5081 1.5081 1.5081 1.5081 1.5081 1.5081 1.5081 1.5081
1.73236 1.73236 1.73236 1.73236 1.73236 1.73236 1.73236 1.73236

0.954487
0.954487
0.954487
0.954487
0.954487
0.954487
0.954487
0.954487

迭代次数为600000

隐藏层1,隐藏层节点数6

权重为

0.767259 0.767259 0.767259 0.767259 0.767259 0.767259
0.481757 0.481757 0.481757 0.481757 0.481757 0.481757
0.497398 0.497398 0.497398 0.497398 0.497398 0.497398
-0.243614 -0.243614 -0.243614 -0.243614 -0.243614 -0.243614
1.56032 1.56032 1.56032 1.56032 1.56032 1.56032
1.79748 1.79748 1.79748 1.79748 1.79748 1.79748

1.18672
1.18672
1.18672
1.18672
1.18672
1.18672

这三组数据对于测试用例给出的结果都是0.46,可以得出该网络训练结果的一致性

具体思路就不多说了,BP神经网络属于案例比较多的一类

这题很遗憾没有做出来,不知道是不是题目的问题,欢迎探讨

代码

#include <vector>
#include <iostream>
#include <cmath>

namespace Utils
{
    static double sigmoid(double x)
    {
        return 1.0 / (1.0 + std::exp(-x));
    }
}

namespace Config
{
    const size_t INNODE = 6;    //输入层节点数
    const size_t HIDE0NODE = 8;  //隐藏层节点数,一般大于输入层
    const size_t OUTNODE = 1;   //输出层节点数

    const double rate = 0.8f; //步长
    const double threshold = 1e-4;    //误差允许值(误差进入此范围认为成功)
    const size_t mosttimes = 600000; //训练次数
}

using std::vector;

struct Sample
{
    //输入输出都不是固定数量
    std::vector<double> in;
    std::vector<double> out;

    Sample();

    Sample(const vector<double>& feature, const vector<double>& label);

    void display();

};

struct Node
{
    double value{};
    double bias{};  //偏置值
    double bias_delta{};    //偏置值修正

    std::vector<double> weight; //权值
    std::vector<double> weight_delta;   //权值修正值

    explicit Node(size_t nextLayerSize);
};

class Net
{
private:
    void grad_zero();

    void forward();

    double calculateLoss(const vector<double>& label);

    void backward(const vector<double>& label);

    void revise(size_t batch_size);

public:
    Net();

    bool train(const vector<Sample>& trainDataSet);

    Sample predict(const vector<double>& feature);

    vector<Sample> predict(const vector<Sample>& predictDataSet);

    //创建神经网络
    Node* inputLayer[Config::INNODE]{};
    Node* hiddenLayer[Config::HIDE0NODE]{};
    Node* outputLayer[Config::OUTNODE]{};
};

Net::Net()
{

    //遍历
    //初始化输入层
    for (size_t i = 0; i < Config::INNODE; i++)
    {
        inputLayer[i] = new Node(Config::HIDE0NODE);
        //对于输入层连接的每一个隐藏层节点都要设置权重
        //一开始是随机数,修正值是0.0
        for (size_t j = 0; j < Config::HIDE0NODE; j++)
        {
            inputLayer[i]->weight[j] = 0.5f;
            inputLayer[i]->weight_delta[j] = 0.0f;
        }
    }

    //初始化隐藏层0
    for (size_t i = 0; i < Config::HIDE0NODE; i++)
    {
        hiddenLayer[i] = new Node(Config::OUTNODE);
        //隐藏层的偏置值也要初始化
        hiddenLayer[i]->bias = 0.0f;
        //对于每一个与隐藏层链接的输出层都要设置权重
        for (size_t j = 0; j < Config::OUTNODE; j++)
        {
            hiddenLayer[i]->weight[j] = 0.5f;
            hiddenLayer[i]->weight_delta[j] = 0.0f;
        }
    }

    //初始化输出层
    for (size_t i = 0; i < Config::OUTNODE; i++)
    {
        outputLayer[i] = new Node(0);
        outputLayer[i]->bias = 0.0f;
        outputLayer[i]->bias_delta = 0.0f;
    }
}

void Net::grad_zero()
{
    //清空输入层偏置变化值
    for (size_t i = 0; i < Config::INNODE; i++)
    {
        inputLayer[i]->weight_delta.assign(inputLayer[i]->weight_delta.size(), 0.0f);
    }

    //清空隐藏层偏置变化值和权重变化值
    for (size_t i = 0; i < Config::HIDE0NODE; i++)
    {
        hiddenLayer[i]->bias_delta = 0.0f;
        hiddenLayer[i]->weight_delta.assign(hiddenLayer[i]->weight_delta.size(), 0.0f);
    }

    //清空输出层偏置值
    for (size_t i = 0; i < Config::OUTNODE; i++)
    {
        outputLayer[i]->bias_delta = 0.0f;
    }
}

void Net::forward()
{
    //正向传播
            //生成隐藏层的值
    for (size_t j = 0; j < Config::HIDE0NODE; j++)
    {
        double sum = 0.0f;
        for (size_t i = 0; i < Config::INNODE; i++)
        {
            sum += inputLayer[i]->value * inputLayer[i]->weight.at(j);
        }
        sum -= hiddenLayer[j]->bias;

        hiddenLayer[j]->value = Utils::sigmoid(sum);
    }

    //生成输出层的值
    for (size_t j = 0; j < Config::OUTNODE; j++)
    {
        double sum = 0.0f;
        for (size_t i = 0; i < Config::HIDE0NODE; i++)
        {
            sum += hiddenLayer[i]->value * hiddenLayer[i]->weight[j];
        }
        sum -= outputLayer[j]->bias;

        outputLayer[j]->value = Utils::sigmoid(sum);
    }
}

double Net::calculateLoss(const vector<double>& label)
{
    //计算误差
    double error = 0.0f;
    for (size_t i = 0; i < Config::OUTNODE; i++)
    {
        double tmp = std::fabs(outputLayer[i]->value - label.at(i));
        error += tmp * tmp / 2;
    }
    return error;
}

void Net::backward(const vector<double>& label)
{
    //反向传播
    //输出层节点的偏置值修正值
    for (size_t i = 0; i < Config::OUTNODE; i++)
    {
        double bias_delta = -(label.at(i) - outputLayer[i]->value) *
            outputLayer[i]->value * (1.0f - outputLayer[i]->value);
        outputLayer[i]->bias_delta += bias_delta;
    }

    //隐藏层到输出层的权值修正值
    for (size_t i = 0; i < Config::HIDE0NODE; i++)
    {
        for (size_t j = 0; j < Config::OUTNODE; j++)
        {
            double weight_delta = (label.at(j) - outputLayer[j]->value) *
                outputLayer[j]->value * (1.0f - outputLayer[j]->value) *
                hiddenLayer[i]->value;
            hiddenLayer[i]->weight_delta[j] += weight_delta;
        }
    }

    //隐藏层节点偏置值修正值
    for (size_t i = 0; i < Config::HIDE0NODE; i++)
    {
        double sum = 0.0f;
        for (size_t j = 0; j < Config::OUTNODE; j++)
        {
            sum += -(label.at(j) - outputLayer[j]->value) *
                outputLayer[j]->value * (1.0f - outputLayer[j]->value) *
                hiddenLayer[i]->weight.at(j);
        }
        hiddenLayer[i]->bias_delta += sum * hiddenLayer[i]->value * (1.0f - hiddenLayer[i]->value);
    }

    //输入层到隐藏层的权值修正值
    for (size_t i = 0; i < Config::INNODE; i++)
    {
        for (size_t j = 0; j < Config::HIDE0NODE; j++)
        {
            double sum = 0.0f;
            for (size_t k = 0; k < Config::OUTNODE; k++)
            {
                sum += (label.at(k) - outputLayer[k]->value) *
                    outputLayer[k]->value * (1.0f - outputLayer[k]->value) *
                    hiddenLayer[j]->weight.at(k);
            }
            inputLayer[i]->weight_delta[j] += sum *
                hiddenLayer[j]->value * (1.0f - hiddenLayer[j]->value) *
                inputLayer[i]->value;
        }
    }
}

void Net::revise(size_t batch_size)
{
    auto batch_size_double = (double)batch_size;

    //调整输入层到隐藏层的所有权值
    for (size_t i = 0; i < Config::INNODE; i++)
    {
        for (size_t j = 0; j < Config::HIDE0NODE; j++)
        {
            inputLayer[i]->weight[j] += Config::rate * inputLayer[i]->weight_delta.at(j) / batch_size_double;
        }
    }

    //调整所有隐藏层的偏置值和隐藏层到输出层的权值
    for (size_t i = 0; i < Config::HIDE0NODE; i++)
    {
        hiddenLayer[i]->bias += Config::rate *
            hiddenLayer[i]->bias_delta / batch_size_double;

        for (size_t j = 0; j < Config::OUTNODE; j++)
        {
            hiddenLayer[i]->weight[j] += Config::rate *
                hiddenLayer[i]->weight_delta.at(j) / batch_size_double;
        }
    }

    for (size_t i = 0; i < Config::OUTNODE; i++)
    {
        outputLayer[i]->bias += Config::rate *
            outputLayer[i]->bias_delta / batch_size_double;
    }
}

bool Net::train(const vector<Sample>& trainDataSet)
{
    for (size_t times = 0; times < Config::mosttimes; times++)
    {
        grad_zero();

        //误差最大值
        double error_max = 0.0f;

        //更新误差值(####)
        for (const Sample& trainSample : trainDataSet)
        {
            //输入
            for (size_t i = 0; i < Config::INNODE; i++)
            {
                inputLayer[i]->value = trainSample.in.at(i);
            }

            forward();

            double error = calculateLoss(trainSample.out);
            error_max = std::max(error_max, error);

            backward(trainSample.out);
        }

        if (error_max < Config::threshold)
        {
            std::cout << "训练成功!迭代次数:" << times + 1 << std::endl;
            std::cout << "最终误差:" << error_max << std::endl;
            return true;
        }
        else if (times % 5000 == 0)
        {
            std::cout << "迭代了:" << times << "次\n最大误差值为:" << error_max;
            //printf_s("#epoch %-7lu - max_loss: %lf\n", times, error_max);
        }
        revise(trainDataSet.size());
    }
    //printf("Failed within %lu epoch.", Config::max_epoch);
    std::cout << "最大迭代次数:" << Config::mosttimes << "次" << std::endl;

    return false;
}

Sample Net::predict(const vector<double>& feature)
{
    for (size_t i = 0; i < Config::INNODE; i++)
    {
        inputLayer[i]->value = feature.at(i);
    }

    forward();

    vector<double> label(Config::OUTNODE);

    for (size_t k = 0; k < Config::OUTNODE; ++k)
    {
        label[k] = outputLayer[k]->value;
    }
    Sample pred = Sample(feature, label);
    return pred;
}

vector<Sample> Net::predict(const vector<Sample>& predictDataSet)
{
    vector<Sample> predSet;

    for (auto& sample : predictDataSet) {
        Sample pred = predict(sample.in);
        predSet.push_back(pred);
    }

    return predSet;
}

Node::Node(size_t nextLayerSize)
{
    weight.resize(nextLayerSize);
    weight_delta.resize(nextLayerSize);
}

Sample::Sample() = default;

Sample::Sample(const vector<double>&feature, const vector<double>&label)
{
    this->in = feature;
    this->out = label;
}

void Sample::display()
{
    std::cout << "input : ";
    for (auto& x : in)
    {
        std::cout << x << " ";
    }
    puts("");
    printf("output: ");
    for (auto& y : out)
    {
        std::cout << y << " ";
    }
    puts("");
}

using std::cin;
using std::cout;
using std::endl;

int main(int argc, char* argv[]) {

    //创建网络对象
    Net net;

    //读取训练数据
    vector<Sample> trainDataSet;
    
    for (int i = 0; i < 8; i++)
    {
        vector<double> in;
        vector<double> out;
        for (int j = 0; j < 6; j++)
        {
            double tem_in;
            cin >> tem_in;
            in.push_back(tem_in);
        }
        double tem_out;
        cin >> tem_out;
        out.push_back(tem_out);
        Sample temS(in, out);
        trainDataSet.push_back(temS);
    }

    //训练神经网络
    net.train(trainDataSet);

    //练完了输出权重
    for (int i = 0; i < Config::INNODE; i++)
    {
        for (int j = 0; j < Config::HIDE0NODE; j++)
        {
            cout<<net.inputLayer[i]->weight[j]<<" ";
        }
        cout << endl;
    }
    cout << endl;
    for (int i = 0; i < Config::HIDE0NODE; i++)
    {
        for (int j = 0; j < Config::OUTNODE; j++)
        {
            cout << net.hiddenLayer[i]->weight[j] << " ";
        }
        cout << endl;
    }
    cout << endl;
    //用神经网络预计样本
    vector<Sample> testDataSet;
    Sample tem1;
    for (int i = 0; i < Config::INNODE; i++)
    {
        double tmp;
        cin >> tmp;
        tem1.in.push_back(tmp);
    }
    testDataSet.push_back(tem1);
    vector<Sample> predSet = net.predict(testDataSet);
    for (auto& pred : predSet) {
        pred.display();
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值