首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在C++中实现一个简单的神经网络从头开始工作

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

Stack Overflow用户
提问于 2010-01-07 08:23:29
回答 5查看 87.1K关注 0票数 38

我一直试图得到一个简单的双异或神经网络工作,我有问题,得到反向传播,以训练一个真正简单的前馈神经网络。

在获取神经网络方面,我一直在尝试遵循的指导,但充其量我的程序学习速度极慢。

据我所知,神经网络:

  1. 值是通过从输入到该神经元的所有输入之和中提取sigmoid函数的结果来计算的。然后,使用每个神经元的权重将其传送到下一层。
  2. 在运行结束时,计算输出神经元的误差,然后使用权值,通过简单地乘以值,然后在每个神经元处求和,将误差反传回来。
  3. 当计算所有的误差时,权值由δ=连接的权重*乙状结肠的导数(神经元重量的值将达到)*神经元的值调整为*神经元的误差*神经元输出误差的量到*β(学习速率的某个常数)。

是我目前正在努力工作的代码。我还有很多其他的尝试,但我尝试的主要反向传播功能是在Net.cpp中的line293。

EN

回答 5

Stack Overflow用户

发布于 2010-01-07 08:26:25

看看实现神经网络的15个步骤,它会让你开始的。

票数 25
EN

Stack Overflow用户

发布于 2016-09-29 19:32:14

我写了一个简单的“教程”,你可以在下面查看。

它是感知器模型的一个简单实现。你可以把感知器想象成只有一个神经元的神经网络。有诅咒代码,你可以测试,我用C++写的。我一步一步地检查代码,这样你就不会有任何问题了。

虽然感知器并不是一个真正的“神经网络”,但是如果你想要开始,并且可能帮助你更好地理解一个完整的神经网络是如何工作的,这真的是很有帮助的。

希望这能帮上忙!干杯!^_^

在本例中,我将介绍在C++中感知器模型的实现,以便您能够更好地了解它是如何工作的。

首先,写下我们想要做的事情的简单算法是一个很好的实践。

算法:

  1. 为权重设置一个向量,并将其初始化为0(不要忘记添加偏置项)
  2. 一直调整权重,直到我们得到0的误差或低的错误计数。
  3. 根据看不见的数据进行预测。

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

  • 我们需要一个函数来计算网络的输入(e.i *x * wT*乘以输入时间和权重)
  • 一种阶跃函数,因此我们可以得到对1或-1的预测。
  • 以及一个为权重找到理想值的函数。

所以,不费吹灰之力,让我们直接进入它。

让我们从创建感知器类开始简单:

代码语言:javascript
复制
class perceptron
{
public:

private:

};

现在,让我们添加我们需要的函数。

代码语言:javascript
复制
class perceptron
{
public:
    perceptron(float eta,int epochs);
    float netInput(vector<float> X);
    int predict(vector<float> X);
    void fit(vector< vector<float> > X, vector<float> y);
private:

};

注意fit函数如何将vector< float >的向量作为参数。这是因为我们的训练数据集是一个输入矩阵。从本质上说,我们可以想象矩阵是两个向量,x将矩阵叠加在另一个矩阵的上面,矩阵的每一列都是一个特征。

最后,让我们添加类需要的值。例如向量w保持权重,历元数,表示我们将在训练数据集上进行的传球次数。以及常数eta,它是我们将每一个权重更新乘以的学习速率,以便通过拨打这个值使训练过程更快,或者如果eta太高,我们可以将其向下拨以获得理想的结果(对于大多数感知器的应用,我建议将eta值设为0.1 )。

代码语言:javascript
复制
class perceptron
{
public:
    perceptron(float eta,int epochs);
    float netInput(vector<float> X);
    int predict(vector<float> X);
    void fit(vector< vector<float> > X, vector<float> y);
private:
    float m_eta;
    int m_epochs;
    vector < float > m_w;
};

现在我们的课准备好了。现在是编写每个函数的时候了。

我们将从构造函数开始(感知器(float,int历元);)

代码语言:javascript
复制
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 ) ; )。请记住,all 预测函数所做的是接受净输入,如果netInput大于0和-1,则返回值为1。

代码语言:javascript
复制
int perceptron::predict(vector<float> X)
{
    return netInput(X) > 0 ? 1 : -1; //Step Function
}

注意,我们使用内联if语句来简化我们的生活。下面是内联if语句的工作方式:

条件?if_true :要不然

到目前一切尚好。让我们继续实现netInput函数( float netInput(向量X); )

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

*x * wT*

换句话说,它将输入向量x的每个元素乘以权值w的向量的对应元素,然后取其和并增加偏差。

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

*偏差=1* w0*

代码语言:javascript
复制
float perceptron::netInput(vector<float> 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函数,它修改了权重。

代码语言:javascript
复制
void perceptron::fit(vector< vector<float> > X, vector<float> 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个功能,我们现在有一个工作感知器类,我们可以使用它来进行预测!

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

以下是代码:

类标题:

代码语言:javascript
复制
class perceptron
{
public:
    perceptron(float eta,int epochs);
    float netInput(vector<float> X);
    int predict(vector<float> X);
    void fit(vector< vector<float> > X, vector<float> 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文件:

代码语言:javascript
复制
perceptron::perceptron(float eta, int epochs)
{
    m_epochs = epochs;
    m_eta = eta;
}

void perceptron::fit(vector< vector<float> > X, vector<float> 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<float> 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<float> 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:

代码语言:javascript
复制
#include <iostream>
#include <vector>
#include <algorithm>
#include <fstream>
#include <string>
#include <math.h> 

#include "MachineLearning.h"

using namespace std;
using namespace MachineLearning;

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

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

    vector<float> 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<float> getIrisy()
{
    vector<float> 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<float> > 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<float> > 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<float> &X并通常通过引用传递vector/vector<vector>对象是很重要的,因为数据可能非常大,通过值传递数据将产生一个副本(这是效率低下的)。

票数 21
EN

Stack Overflow用户

发布于 2010-01-07 09:38:35

对我来说,听起来你是在苦苦挣扎,上面你所描述的并不完全符合我对它的理解,而且你的描述有点模糊。

计算输出误差项,作为预测值与实际值的差值乘以传递函数的导数。这是一个错误的值,然后你向后传播。乙状结肠的导数非常简单地计算为y(1-y),其中y是你的输出值。网上有很多证据证明这一点。

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

代码语言:javascript
复制
total_error = sum(output_errors * weights)
node_error = sigmoid_derivative(node_output) * total_error

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

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

代码语言:javascript
复制
weight_change = outer_error * inner_output_value

学习率很重要,因为输入数据中的每个模式/行/观察都会计算权重变化。您希望对每一行的权重变化进行适当调整,这样权重就不会被任何一行不适当地更改,这样所有行都会对权重产生影响。学习率给你这个,你通过乘以它来调整重量的变化。

代码语言:javascript
复制
weight_change = outer_error * inner_output_value * learning_rate

记住这些不同时期(迭代)之间的变化并将其中的一小部分添加到更改中也是正常的。添加的分数被称为动量,被认为是用来加速你通过误差面的区域,在那里没有太多的变化,在有细节的地方减缓你的速度。

代码语言:javascript
复制
weight_change = (outer_error*inner_output_value*learning_rate) + (last_change*momentum)

随着训练的进行,有调整学习速度和动量的算法。

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

代码语言:javascript
复制
new_weight = old_weight + weight_change

我看了你的代码,但不是纠正它和张贴,我认为这是更好的描述回来的道具,以便你可以自己编码。如果你理解它,你也可以根据你的情况来调整它。

祝你好运。

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2019056

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档