Я написал простое «Учебное пособие», которое вы можете проверить ниже.
Это простая реализация модели персептрона. Вы можете представить персептрон как нейронную сеть с одним нейроном. Существует проклятый код, который вы можете проверить, который я написал на 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 для удержания весов, число эпох , которое указывает количество проходов, которые мы сделаем над учебный набор данных. И константа эта , которая является скоростью обучения, на которую мы будем умножать каждое обновление веса, чтобы ускорить процедуру тренировки, набрав это значение или, если Эта слишком высока, мы можем набрать его, чтобы получить идеальный результат (для большинства применений персептрона я бы предложил Эта значение 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;
};
Теперь с нашим набором классов. Пришло время написать каждую из функций.
Начнем с конструктора ( персептрон (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); ). Помните, что все, что делает функция предсказывать , берет чистый ввод и возвращает значение 1, если netInput больше 0 и -1 в остальном.
int perceptron::predict(vector<float> X)
{
return netInput(X) > 0 ? 1 : -1; //Step Function
}
Обратите внимание, что мы использовали встроенное выражение if, чтобы сделать нашу жизнь проще. Вот как работает встроенный оператор if:
состояние? if_true: else
Пока все хорошо. Давайте перейдем к реализации функции netInput ( float netInput (вектор 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, не совсем идеален, но я просто хотел что-то, что сработало.
Файлы данных можно найти здесь .
Я надеюсь, что вы нашли это полезным!
Примечание: приведенный выше код приведен только в качестве примера. Как отметил juzzlin, важно использовать const vector<float> &X
и вообще передавать объекты vector
/ vector<vector>
по ссылке, потому что данные могут быть очень большими, а передача их по значению сделает их копию (что неэффективно ).