让一个简单的神经网络在 C++ 中从头开始工作
Posted
技术标签:
【中文标题】让一个简单的神经网络在 C++ 中从头开始工作【英文标题】:Getting a simple Neural Network to work from scratch in C++ 【发布时间】:2011-01-02 09:46:33 【问题描述】:我一直在尝试让一个简单的双 XOR 神经网络工作,但在通过反向传播来训练一个非常简单的前馈神经网络时遇到了问题。 我一直在尝试遵循this 指南来获取神经网络,但充其量只能制作学习速度极慢的程序。
据我了解神经网络:
-
通过从该神经元的所有输入之和中获取 sigmoid 函数的结果来计算值。然后使用每个神经元的权重将其馈送到下一层
在运行结束时计算输出神经元的误差,然后使用权重,通过简单地将值相乘然后在每个神经元上求和来反向传播误差
当计算所有错误时,权重由 delta = 连接权重 * sigmoid 的导数(神经元权重的值)* 连接到的神经元值 * 神经元的误差 * 的数量神经元的输出误差达到 * beta(学习率的一些常数)
This 是我目前正在尝试使用的一堆代码。我还有很多其他尝试,但我尝试使用的主要反向传播功能位于 Net.cpp 的第 293 行
【问题讨论】:
@Martin C++ 没有错。 C/C++ 感知器:sourceforge.net/projects/ccperceptron 【参考方案1】:比如预测和拟合正弦函数的简单函数逼近网络怎么样?另外,我认为,在实施过程中避免上课是轻松获得基础知识的必要条件。让我们考虑一个单一的隐藏层网络。
//Had a lot of trouble with shuffle
#include <iostream>
#include<vector>
#include <list>
#include <cstdlib>
#include <math.h>
#define PI 3.141592653589793238463
#define N
#define epsilon 0.1
#define epoch 2000
using namespace std;
// Just for GNU Plot issues
extern "C" FILE *popen(const char *command, const char *mode);
// Defining activation functions
//double sigmoid(double x) return 1.0f / (1.0f + exp(-x));
//double dsigmoid(double x) return x * (1.0f - x);
double tanh(double x) return (exp(x)-exp(-x))/(exp(x)+exp(-x)) ;
double dtanh(double x) return 1.0f - x*x ;
double lin(double x) return x;
double dlin(double x) return 1.0f;
double init_weight() return (2*rand()/RAND_MAX -1);
double MAXX = -9999999999999999; //maximum value of input example
// Network Configuration
static const int numInputs = 1;
static const int numHiddenNodes = 7;
static const int numOutputs = 1;
// Learning Rate
const double lr = 0.05f;
double hiddenLayer[numHiddenNodes];
double outputLayer[numOutputs];
double hiddenLayerBias[numHiddenNodes];
double outputLayerBias[numOutputs];
double hiddenWeights[numInputs][numHiddenNodes];
double outputWeights[numHiddenNodes][numOutputs];
static const int numTrainingSets = 50;
double training_inputs[numTrainingSets][numInputs];
double training_outputs[numTrainingSets][numOutputs];
// Shuffling the data with each epoch
void shuffle(int *array, size_t n)
if (n > 1) //If no. of training examples > 1
size_t i;
for (i = 0; i < n - 1; i++)
size_t j = i + rand() / (RAND_MAX / (n - i) + 1);
int t = array[j];
array[j] = array[i];
array[i] = t;
// Forward Propagation. Only used after training is done.
void predict(double test_sample[])
for (int j=0; j<numHiddenNodes; j++)
double activation=hiddenLayerBias[j];
for (int k=0; k<numInputs; k++)
activation+=test_sample[k]*hiddenWeights[k][j];
hiddenLayer[j] = tanh(activation);
for (int j=0; j<numOutputs; j++)
double activation=outputLayerBias[j];
for (int k=0; k<numHiddenNodes; k++)
activation+=hiddenLayer[k]*outputWeights[k][j];
outputLayer[j] = lin(activation);
//std::cout<<outputLayer[0]<<"\n";
//return outputLayer[0];
//std::cout << "Input:" << training_inputs[i][0] << " " << training_inputs[i][1] << " Output:" << outputLayer[0] << " Expected Output: " << training_outputs[i][0] << "\n";
int main(int argc, const char * argv[])
///TRAINING DATA GENERATION
for (int i = 0; i < numTrainingSets; i++)
double p = (2*PI*(double)i/numTrainingSets);
training_inputs[i][0] = (p);
training_outputs[i][0] = sin(p);
///FINDING NORMALIZING FACTOR
for(int m=0; m<numInputs; ++m)
if(MAXX < training_inputs[i][m])
MAXX = training_inputs[i][m];
for(int m=0; m<numOutputs; ++m)
if(MAXX < training_outputs[i][m])
MAXX = training_outputs[i][m];
///NORMALIZING
for (int i = 0; i < numTrainingSets; i++)
for(int m=0; m<numInputs; ++m)
training_inputs[i][m] /= 1.0f*MAXX;
for(int m=0; m<numOutputs; ++m)
training_outputs[i][m] /= 1.0f*MAXX;
cout<<"In: "<<training_inputs[i][0]<<" out: "<<training_outputs[i][0]<<endl;
///WEIGHT & BIAS INITIALIZATION
for (int i=0; i<numInputs; i++)
for (int j=0; j<numHiddenNodes; j++)
hiddenWeights[i][j] = init_weight();
for (int i=0; i<numHiddenNodes; i++)
hiddenLayerBias[i] = init_weight();
for (int j=0; j<numOutputs; j++)
outputWeights[i][j] = init_weight();
for (int i=0; i<numOutputs; i++)
//outputLayerBias[i] = init_weight();
outputLayerBias[i] = 0;
///FOR INDEX SHUFFLING
int trainingSetOrder[numTrainingSets];
for(int j=0; j<numInputs; ++j)
trainingSetOrder[j] = j;
///TRAINING
//std::cout<<"start train\n";
vector<double> performance, epo; ///STORE MSE, EPOCH
for (int n=0; n < epoch; n++)
double MSE = 0;
shuffle(trainingSetOrder,numTrainingSets);
std::cout<<"epoch :"<<n<<"\n";
for (int i=0; i<numTrainingSets; i++)
//int i = trainingSetOrder[x];
int x=i;
//std::cout<<"Training Set :"<<x<<"\n";
/// Forward pass
for (int j=0; j<numHiddenNodes; j++)
double activation=hiddenLayerBias[j];
//std::cout<<"Training Set :"<<x<<"\n";
for (int k=0; k<numInputs; k++)
activation+=training_inputs[x][k]*hiddenWeights[k][j];
hiddenLayer[j] = tanh(activation);
for (int j=0; j<numOutputs; j++)
double activation=outputLayerBias[j];
for (int k=0; k<numHiddenNodes; k++)
activation+=hiddenLayer[k]*outputWeights[k][j];
outputLayer[j] = lin(activation);
//std::cout << "Input:" << training_inputs[x][0] << " " << " Output:" << outputLayer[0] << " Expected Output: " << training_outputs[x][0] << "\n";
for(int k=0; k<numOutputs; ++k)
MSE += (1.0f/numOutputs)*pow( training_outputs[x][k] - outputLayer[k], 2);
/// Backprop
/// For V
double deltaOutput[numOutputs];
for (int j=0; j<numOutputs; j++)
double errorOutput = (training_outputs[i][j]-outputLayer[j]);
deltaOutput[j] = errorOutput*dlin(outputLayer[j]);
/// For W
double deltaHidden[numHiddenNodes];
for (int j=0; j<numHiddenNodes; j++)
double errorHidden = 0.0f;
for(int k=0; k<numOutputs; k++)
errorHidden+=deltaOutput[k]*outputWeights[j][k];
deltaHidden[j] = errorHidden*dtanh(hiddenLayer[j]);
///Updation
/// For V and b
for (int j=0; j<numOutputs; j++)
//b
outputLayerBias[j] += deltaOutput[j]*lr;
for (int k=0; k<numHiddenNodes; k++)
outputWeights[k][j]+= hiddenLayer[k]*deltaOutput[j]*lr;
/// For W and c
for (int j=0; j<numHiddenNodes; j++)
//c
hiddenLayerBias[j] += deltaHidden[j]*lr;
//W
for(int k=0; k<numInputs; k++)
hiddenWeights[k][j]+=training_inputs[i][k]*deltaHidden[j]*lr;
//Averaging the MSE
MSE /= 1.0f*numTrainingSets;
//cout<< " MSE: "<< MSE<<endl;
///Steps to PLOT PERFORMANCE PER EPOCH
performance.push_back(MSE*100);
epo.push_back(n);
// Print weights
std::cout << "Final Hidden Weights\n[ ";
for (int j=0; j<numHiddenNodes; j++)
std::cout << "[ ";
for(int k=0; k<numInputs; k++)
std::cout << hiddenWeights[k][j] << " ";
std::cout << "] ";
std::cout << "]\n";
std::cout << "Final Hidden Biases\n[ ";
for (int j=0; j<numHiddenNodes; j++)
std::cout << hiddenLayerBias[j] << " ";
std::cout << "]\n";
std::cout << "Final Output Weights";
for (int j=0; j<numOutputs; j++)
std::cout << "[ ";
for (int k=0; k<numHiddenNodes; k++)
std::cout << outputWeights[k][j] << " ";
std::cout << "]\n";
std::cout << "Final Output Biases\n[ ";
for (int j=0; j<numOutputs; j++)
std::cout << outputLayerBias[j] << " ";
std::cout << "]\n";
/* This part is just for plotting the results.
This requires installing GNU Plot. You can also comment it out.
*/
//Plot the results
vector<float> x;
vector<float> y1, y2;
//double test_input[1000][numInputs];
int numTestSets = numTrainingSets;
for (float i = 0; i < numTestSets; i=i+0.25)
double p = (2*PI*(double)i/numTestSets);
x.push_back(p);
y1.push_back(sin(p));
double test_input[1];
test_input[0] = p/MAXX;
predict(test_input);
y2.push_back(outputLayer[0]*MAXX);
FILE * gp = popen("gnuplot", "w");
fprintf(gp, "set terminal wxt size 600,400 \n");
fprintf(gp, "set grid \n");
fprintf(gp, "set title '%s' \n", "f(x) = x sin (x)");
fprintf(gp, "set style line 1 lt 3 pt 7 ps 0.1 lc rgb 'green' lw 1 \n");
fprintf(gp, "set style line 2 lt 3 pt 7 ps 0.1 lc rgb 'red' lw 1 \n");
fprintf(gp, "plot '-' w p ls 1, '-' w p ls 2 \n");
///Exact f(x) = sin(x) -> Green Graph
for (int k = 0; k < x.size(); k++)
fprintf(gp, "%f %f \n", x[k], y1[k]);
fprintf(gp, "e\n");
///Neural Network Approximate f(x) = xsin(x) -> Red Graph
for (int k = 0; k < x.size(); k++)
fprintf(gp, "%f %f \n", x[k], y2[k]);
fprintf(gp, "e\n");
fflush(gp);
///FILE POINTER FOR SECOND PLOT (PERFORMANCE GRAPH)
FILE * gp1 = popen("gnuplot", "w");
fprintf(gp1, "set terminal wxt size 600,400 \n");
fprintf(gp1, "set grid \n");
fprintf(gp1, "set title '%s' \n", "Performance");
fprintf(gp1, "set style line 1 lt 3 pt 7 ps 0.1 lc rgb 'green' lw 1 \n");
fprintf(gp1, "set style line 2 lt 3 pt 7 ps 0.1 lc rgb 'red' lw 1 \n");
fprintf(gp1, "plot '-' w p ls 1 \n");
for (int k = 0; k < epo.size(); k++)
fprintf(gp1, "%f %f \n", epo[k], performance[k]);
fprintf(gp1, "e\n");
fflush(gp1);
system("pause");
//_pclose(gp);
return 0;
我也一直在尝试学习简单(浅层)神经网络,同时避免使用任何高级工具。我试图通过repository 保持我的一些学习。
【讨论】:
【参考方案2】:我写了一个简单的“教程”,您可以在下面查看。
这是感知器模型的简单实现。你可以把感知器想象成一个只有一个神经元的神经网络。您可以测试我用 C++ 编写的诅咒代码。我一步一步地浏览了代码,所以你应该没有任何问题。
虽然感知器并不是真正的“神经网络”,但如果您想开始使用它确实很有帮助,并且可能会帮助您更好地了解完整的神经网络的工作原理。
希望对您有所帮助! 干杯! ^_^
在此示例中,我将介绍 C++ 中感知器模型的实现,以便您更好地了解它的工作原理。
首先,写下我们想要做的简单算法是一个好习惯。
算法:
-
为权重创建一个向量并将其初始化为 0(不要忘记添加偏置项)
继续调整权重,直到我们得到 0 个错误或错误计数较低。
对看不见的数据进行预测。
已经编写了一个超级简单的算法,现在让我们编写一些我们需要的函数。
我们需要一个函数来计算网络的输入(例如 *x * wT* 将输入乘以权重) 一个阶跃函数,以便我们得到 1 或 -1 的预测 还有一个找到理想权重值的函数。所以事不宜迟,让我们开始吧。
让我们从创建一个感知器类开始:
class perceptron
public:
private:
;
现在让我们添加我们需要的函数。
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 如何将向量 的向量作为参数。那是因为我们的训练数据集是一个输入矩阵。本质上,我们可以将矩阵想象为一对向量 x 将一个向量堆叠在另一个向量之上,并且该矩阵的每一列都是一个特征。
最后让我们添加我们的类需要具有的值。比如向量 w 来保存权重,epochs 的数量表示我们通过的次数将对训练数据集进行处理。以及常数 eta,这是我们将乘以每次权重更新的学习率,以便通过调高该值或如果 eta 太高了,我们可以将其调低以获得理想的结果(对于感知器的大多数应用,我建议使用 eta 0.1 的值)。
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;
;
现在我们的班级集。是时候编写每个函数了。
我们将从构造函数开始(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 predict(vector X);)。请记住,所有 predict 函数所做的是获取净输入并在 netInput 时返回值 1大于 0 和 -1 否则。
int perceptron::predict(vector<float> X)
return netInput(X) > 0 ? 1 : -1; //Step Function
请注意,我们使用内联 if 语句使我们的生活更轻松。以下是内联 if 语句的工作原理:
条件? if_true : 否则
到目前为止一切顺利。让我们继续实现 netInput 函数(float netInput(vector X);)
netInput 执行以下操作; 将输入向量乘以权重向量的转置
*x * wT*
换句话说,它将输入向量 x 的每个元素乘以权重向量的对应元素 w 然后将它们的总和加上偏差。
*(x1 * w1 + x2 * w2 + ... + xn * wn) + 偏差*
*偏差 = 1 * w0*
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 函数。
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 个函数,我们现在有一个可以工作的感知器类,我们可以用它来进行预测!
如果您想复制粘贴代码并尝试一下。这是整个类(我添加了一些额外的功能,例如打印权重向量和每个时期的错误,并添加了导入/导出权重的选项。)
代码如下:
类头:
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文件的功能:
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:
#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;
我导入 iris 数据集的方式并不理想,但我只是想要一些可行的方法。
数据文件可以在here.找到
希望对您有所帮助!
注意:上面的代码仅作为示例。正如 juzzlin 所指出的,重要的是您使用 const vector<float> &X
并且通常通过引用传递 vector
/vector<vector>
对象,因为数据可能非常大,并且通过值传递它会复制它(效率低下) )。
【讨论】:
这个答案主要基于一个文档。请将相关部分复制到您的答案中。 请使用例如const vector<float> & X
而不仅仅是 vector<float> X
。现在,值总是在每次函数调用时被复制。
@juzzlin 没错。感谢您的提醒。我在最后添加了一条注释。
获取 printvector 未在此范围内声明【参考方案3】:
在我看来,你正在为反向传播而苦苦挣扎,而你上面描述的内容与我理解它的工作方式不太相符,而且你的描述有点模棱两可。
您计算要反向传播的输出误差项,即预测值与实际值之间的差异乘以传递函数的导数。然后您向后传播的就是那个错误值。 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
我查看了您的代码,但与其更正并发布我认为最好为您描述 back prop 以便您可以自己编写代码的内容。如果您理解它,您也可以根据自己的情况对其进行调整。
HTH,祝你好运。
【讨论】:
我尝试根据您的建议修复我的代码,(我还没有获得动力)但我仍然遇到反向传播系统的问题。 (至少对我来说)看起来你告诉我做同样的事情,但只是将移动数字捆绑到错误值中。我觉得我错过了一些小而重要的东西,这导致我的反向传播不起作用。 我已经尝试了几次来弄清楚你的代码在做什么,但已经放弃了。我认为您计算的 total_error 值可能是错误的,因为您为一个图层执行此操作并调用 DSigmoid 两次。我建议您在纸上或 Excel 中使用 2 行输入数据进行一次迭代,这样您就知道整个事情是如何工作的。然后让你的网络吐出它的权重,这样你就可以将它与你的计算进行比较。到那个阶段,你应该明白 a) 应该发生什么,b) 你的代码有什么问题。 我试图实现你描述的算法......但它不是很学习。介意看看吗? ***.com/questions/37973950/… 很好的解释:)【参考方案4】:看看15 Steps to implement a Neural Network,它应该可以帮助您入门。
【讨论】:
始终欢迎提供指向潜在解决方案的链接,但请add context around the link,以便您的其他用户知道它是什么以及为什么存在。始终引用重要链接中最相关的部分,以防目标站点无法访问或永久离线。考虑到仅仅是指向外部站点的链接是Why and how are some answers deleted? 的一个可能原因。 您的链接已失效。 这是存档中的链接:web.archive.org/web/20170813072242/http://code-spot.co.za/2009/…【参考方案5】:这个开源代码怎么样。它定义了一个简单的 1 个隐藏层网络(2 个输入,2 个隐藏,1 个输出)并解决了 XOR 问题:
http://www.sylbarth.com/mlp.php
【讨论】:
以上是关于让一个简单的神经网络在 C++ 中从头开始工作的主要内容,如果未能解决你的问题,请参考以下文章