我写了一个简单的“教程”,您可以在下面查看。
这是感知器模型的简单实现。你可以把感知器想象成一个只有一个神经元的神经网络。您可以测试我用 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> 对象,因为数据可能非常大,并且通过值传递它会复制它(效率低下) )。