当前位置:  开发笔记 > 编程语言 > 正文

让一个简单的神经网络在C++中从头开始工作

如何解决《让一个简单的神经网络在C++中从头开始工作》经验,为你挑选了3个好方法。

我一直试图让一个简单的双XOR神经网络工作,我遇到了反向传播训练一个非常简单的前馈神经网络的问题.
我一直在尝试遵循本指南来获得神经网络,但最多只能制作以极慢的速度学习的程序.

据我了解神经网络:

    通过从该神经元的所有输入的总和中获取sigmoid函数的结果来计算值.然后使用每个神经元的重量将其送入下一层

    在运行结束时,计算输出神经元的误差,然后使用权重,通过简单地将值乘以然后在每个神经元处求和,将误差反馈回传播.

    当计算所有误差时,权重由delta =连接的权重*sigmoid的导数(神经元权重的值将要)*连接的神经元的值*神经元的误差*输出误差的量来调整.神经元进入*beta(学习率有些常数)

这是我目前正在努力工作的代码.我有很多其他的尝试有点混合,但我试图工作的主要反向传播功能是在Net.cpp的第293行



1> Gregory Pako..:

看看实施神经网络的15个步骤,它应该让你入门.


我们总是欢迎指向潜在解决方案的链接,但请[在链接周围添加上下文](// meta.stackoverflow.com/a/8259/169503),以便您的同行用户能够了解它是什么以及它为何存在.如果目标站点无法访问或永久脱机,请始终引用重要链接的最相关部分.考虑到只是一个链接到外部网站_可能是[为什么以及如何删除某些答案?](// stackoverflow.com/help/deleted-answers).

2> Panos..:

我写了一个简单的"教程",您可以在下面查看.

它是感知器模型的简单实现.你可以将感知器想象成只有一个神经元的神经网络.你可以测试我用C++编写的诅咒代码.我一步一步地完成代码,所以你不应该有任何问题.

虽然感知器并不是真正的"神经网络",但如果你想要开始并且可能有助于你更好地理解完整的神经网络是如何工作的,那么它真的很有帮助.

希望有所帮助!干杯! ^ _ ^



在这个例子中,我将介绍C++中感知器模型的实现,以便您可以更好地了解它的工作原理.

首先,首先写下一个我们想要做的简单算法是一个好习惯.

算法:

    制作权重的向量并将其初始化为0(不要忘记添加偏差项)

    继续调整权重,直到我们得到0错误或错误计数低.

    对看不见的数据做出预测.

编写了一个超级简单的算法后,我们现在编写一些我们需要的函数.

我们需要一个函数来计算网络的输入(ei *x*wT*乘以输入时间的权重)

阶梯函数,以便我们得到1或-1的预测

并且找到权重的理想值的函数.

所以没有进一步的努力让我们进入它.

让我们通过创建一个感知器类来开始:

class perceptron
{
public:

private:

};

现在让我们添加我们需要的功能.

class perceptron
{
public:
    perceptron(float eta,int epochs);
    float netInput(vector X);
    int predict(vector X);
    void fit(vector< vector > X, vector y);
private:

};

注意函数拟合如何将向量的向量作为参数.那是因为我们的训练数据集是一个输入矩阵.基本上我们可以想象矩阵作为几个向量x将一个叠加在另一个上面,并且该矩阵的每列都是一个特征.

最后,让我们添加我们的类需要具有的值.例如用于保持权重的向量w,表示我们将在训练数据集上进行的遍数的时期数.并且常数eta是学习速率,我们将乘以每个权重更新,以便通过拨打此值来使训练过程更快或如果eta过高,我们可以将其调低以获得理想的结果(对于大多数应用程序)我会建议感知器的eta值为0.1).

class perceptron
{
public:
    perceptron(float eta,int epochs);
    float netInput(vector X);
    int predict(vector X);
    void fit(vector< vector > X, vector y);
private:
    float m_eta;
    int m_epochs;
    vector < float > m_w;
};

现在我们的课程设置.是时候编写每个函数了.

我们将从构造函数开始(perceptron(float eta,int epochs);)

perceptron::perceptron(float eta, int epochs)
{
    m_epochs = epochs; // We set the private variable m_epochs to the user selected value
    m_eta = eta; // We do the same thing for eta
}

正如您所看到的,我们将要做的事情非常简单.那么让我们继续讨论另一个简单的函数.预测函数(int预测(向量X);).请记住,所有预测函数的作用是获取净输入,如果netInput大于0 则返回值1,其他为-1.

int perceptron::predict(vector X)
{
    return netInput(X) > 0 ? 1 : -1; //Step Function
}

请注意,我们使用内联if语句来使我们的生活更轻松.以下是内联if语句的工作原理:

条件?if_true:else

到现在为止还挺好.让我们继续实现netInput函数(float netInput(vector X);)

netInput执行以下操作; 将输入向量乘以权重向量的转置

*x*wT*

换句话说,它将输入向量x的每个元素乘以权重w的向量的对应元素,然后获取它们的和并加上偏差.

*(x1*w1 + x2*w2 + ... + xn*wn)+偏差*

*bias = 1*w0*

float perceptron::netInput(vector X)
{
    // Sum(Vector of weights * Input vector) + bias
    float probabilities = m_w[0]; // In this example I am adding the perceptron first
    for (int i = 0; i < X.size(); i++)
    {
        probabilities += X[i] * m_w[i + 1]; // Notice that for the weights I am counting
        // from the 2nd element since w0 is the bias and I already added it first.
    }
    return probabilities;
}

好吧,所以我们现在已经完成了很多工作,我们需要做的是编写修改权重的fit函数.

void perceptron::fit(vector< vector > X, vector y)
{
    for (int i = 0; i < X[0].size() + 1; i++) // X[0].size() + 1 -> I am using +1 to add the bias term
    {
        m_w.push_back(0); // Setting each weight to 0 and making the size of the vector
        // The same as the number of features (X[0].size()) + 1 for the bias term
    }
    for (int i = 0; i < m_epochs; i++) // Iterating through each epoch
    {
        for (int j = 0; j < X.size(); j++) // Iterating though each vector in our training Matrix
        {
            float update = m_eta * (y[j] - predict(X[j])); //we calculate the change for the weights
            for (int w = 1; w < m_w.size(); w++){ m_w[w] += update * X[j][w - 1]; } // we update each weight by the update * the training sample
            m_w[0] = update; // We update the Bias term and setting it equal to the update
        }
    }
}

所以这基本上就是这样.只有3个函数,我们现在有一个工作感知器类,我们可以使用它来进行预测!

如果您想复制粘贴代码并尝试它.这是整个类(我添加了一些额外的功能,例如打印权重向量和每个时期的错误,以及添加导入/导出权重的选项.)

这是代码:

类标题:

class perceptron
{
public:
    perceptron(float eta,int epochs);
    float netInput(vector X);
    int predict(vector X);
    void fit(vector< vector > X, vector y);
    void printErrors();
    void exportWeights(string filename);
    void importWeights(string filename);
    void printWeights();
private:
    float m_eta;
    int m_epochs;
    vector < float > m_w;
    vector < float > m_errors;
};

类.cpp文件的功能:

perceptron::perceptron(float eta, int epochs)
{
    m_epochs = epochs;
    m_eta = eta;
}

void perceptron::fit(vector< vector > X, vector y)
{
    for (int i = 0; i < X[0].size() + 1; i++) // X[0].size() + 1 -> I am using +1 to add the bias term
    {
        m_w.push_back(0);
    }
    for (int i = 0; i < m_epochs; i++)
    {
        int errors = 0;
        for (int j = 0; j < X.size(); j++)
        {
            float update = m_eta * (y[j] - predict(X[j]));
            for (int w = 1; w < m_w.size(); w++){ m_w[w] += update * X[j][w - 1]; }
            m_w[0] = update;
            errors += update != 0 ? 1 : 0;
        }
        m_errors.push_back(errors);
    }
}

float perceptron::netInput(vector X)
{
    // Sum(Vector of weights * Input vector) + bias
    float probabilities = m_w[0];
    for (int i = 0; i < X.size(); i++)
    {
        probabilities += X[i] * m_w[i + 1];
    }
    return probabilities;
}

int perceptron::predict(vector X)
{
    return netInput(X) > 0 ? 1 : -1; //Step Function
}

void perceptron::printErrors()
{
    printVector(m_errors);
}

void perceptron::exportWeights(string filename)
{
    ofstream outFile;
    outFile.open(filename);

    for (int i = 0; i < m_w.size(); i++)
    {
        outFile << m_w[i] << endl;
    }

    outFile.close();
}

void perceptron::importWeights(string filename)
{
    ifstream inFile;
    inFile.open(filename);

    for (int i = 0; i < m_w.size(); i++)
    {
        inFile >> m_w[i];
    }
}

void perceptron::printWeights()
{
    cout << "weights: ";
    for (int i = 0; i < m_w.size(); i++)
    {
        cout << m_w[i] << " ";
    }
    cout << endl;
}

此外,如果您想尝试一个示例,这是我做的一个例子:

main.cpp中:

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

#include "MachineLearning.h"

using namespace std;
using namespace MachineLearning;

vector< vector > getIrisX();
vector getIrisy();

int main()
{
    vector< vector > X = getIrisX();
    vector y = getIrisy();
    vector test1;
    test1.push_back(5.0);
    test1.push_back(3.3);
    test1.push_back(1.4);
    test1.push_back(0.2);

    vector test2;
    test2.push_back(6.0);
    test2.push_back(2.2);
    test2.push_back(5.0);
    test2.push_back(1.5);
    //printVector(X);
    //for (int i = 0; i < y.size(); i++){ cout << y[i] << " "; }cout << endl;

    perceptron clf(0.1, 14);
    clf.fit(X, y);
    clf.printErrors();
    cout << "Now Predicting: 5.0,3.3,1.4,0.2(CorrectClass=-1,Iris-setosa) -> " << clf.predict(test1) << endl;
    cout << "Now Predicting: 6.0,2.2,5.0,1.5(CorrectClass=1,Iris-virginica) -> " << clf.predict(test2) << endl;

    system("PAUSE");
    return 0;
}

vector getIrisy()
{
    vector y;

    ifstream inFile;
    inFile.open("y.data");
    string sampleClass;
    for (int i = 0; i < 100; i++)
    {
        inFile >> sampleClass;
        if (sampleClass == "Iris-setosa")
        {
            y.push_back(-1);
        }
        else
        {
            y.push_back(1);
        }
    }

    return y;
}

vector< vector > getIrisX()
{
    ifstream af;
    ifstream bf;
    ifstream cf;
    ifstream df;
    af.open("a.data");
    bf.open("b.data");
    cf.open("c.data");
    df.open("d.data");

    vector< vector > X;

    for (int i = 0; i < 100; i++)
    {
        char scrap;
        int scrapN;
        af >> scrapN;
        bf >> scrapN;
        cf >> scrapN;
        df >> scrapN;

        af >> scrap;
        bf >> scrap;
        cf >> scrap;
        df >> scrap;
        float a, b, c, d;
        af >> a;
        bf >> b;
        cf >> c;
        df >> d;
        X.push_back(vector < float > {a, b, c, d});
    }

    af.close();
    bf.close();
    cf.close();
    df.close();

    return X;
}

我导入虹膜数据集的方式并不是很理想,但我只想要一些有用的东西.

数据文件可以在这里找到.

我希望你发现这有用!

注意:上面的代码仅作为示例.正如juzzlin所指出的那样,使用const vector &X和通常通过引用传递vector/ vector对象是很重要的,因为数据可能非常大并且按值传递它会产生它的副本(这是低效的).


这个答案主要基于一篇文档.请将相关部分复制到您的答案中.

3> Simon..:

对我来说听起来就像你正在努力使用backprop,你上面描述的内容并不完全符合我理解它的工作原理,你的描述有点含糊不清.

您计算输出误差项以反向传播为预测值与实际值之间的差异乘以传递函数的导数.然后是您向后传播的错误值.sigmoid的导数非常简单地计算为y(1-y),其中y是您的输出值.网上有很多可用的证明.

对于内层上的节点,将该输出误差乘以两个节点之间的权重,并将所有这些乘积相加作为外层传播到内层节点的总误差.然后将与内部节点相关联的误差乘以应用于原始输出值的传递函数的导数.这是一些伪代码:

total_error = sum(output_errors * weights)
node_error = sigmoid_derivative(node_output) * total_error

然后,该错误以相同的方式向后传播通过输入层权重.

使用这些误差项和节点的输出值来调整权重

weight_change = outer_error * inner_output_value

学习率很重要,因为计算输入数据中每个模式/行/观察的权重变化.您希望调整每行的权重更改,以便权重不会被任何单行进行过度更改,因此所有行都会对权重产生影响.学习率为您提供,并通过乘以它来调整体重变化

weight_change = outer_error * inner_output_value * learning_rate

在纪元(迭代)之间记住这些变化并将其中一小部分添加到变化中也是正常的.添加的分数称为动量,并且应该通过误差表面的区域加速,在那里没有太大的变化,并且在有细节的地方减速.

weight_change = (outer_error*inner_output_value*learning_rate) + (last_change*momentum)

随着训练的进行,存在用于调整学习速率和动量的算法.

然后通过添加更改来更新权重

new_weight = old_weight + weight_change

我查看了你的代码,但不是纠正它并发布我认为最好为你描述支持你所以你可以自己编写代码.如果您了解它,您也可以根据自己的情况调整它.

HTH,祝你好运.

推荐阅读
勤奋的瞌睡猪_715
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有