Простая нейросеть на С++
Простая нейросеть на С++
Этот код реализует простую нейронную сеть на C++ для решения задачи логической операции "И" с использованием алгоритма обратного распространения ошибки. Он демонстрирует основы нейронных сетей и обучает сеть с двумя входами, скрытым слоем из двух нейронов и одним выходом. Данную модель нейронной сети можно адаптировать для решения других задач с использованием разных данных и сетевых архитектур.
// Простая нейросеть на С++ #include <iostream> #include <vector> #include <cmath> #include <random> // Современные генераторы случайных чисел #include <iomanip> #include <format> // C++20 форматирование строк using namespace std; // Функция активации (сигмоида) double sigmoid(double x) { return 1.0 / (1.0 + exp(-x)); } // Производная сигмоиды double sigmoidDerivative(double x) { return x * (1.0 - x); } // Класс для нейронной сети class NeuralNetwork { private: vector<vector<double>> weightsInputHidden; // Веса между входным и скрытым слоями vector<vector<double>> weightsHiddenOutput; // Веса между скрытым и выходным слоями vector<double> hiddenLayer; // Выходы скрытого слоя vector<double> outputLayer; // Выходы сети double learningRate; mt19937 generator; // Генератор случайных чисел uniform_real_distribution<double> dist; // Распределение для случайных чисел // Инициализация весов случайными значениями void initializeWeights(vector<vector<double>>& weights, int rows, int cols) { weights.resize(rows, vector<double>(cols)); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { weights[i][j] = dist(generator); // Используем современный генератор случайных чисел } } } public: NeuralNetwork(int inputSize, int hiddenSize, int outputSize, double lr) : learningRate(lr), generator(random_device{}()), dist(-1.0, 1.0) { initializeWeights(weightsInputHidden, inputSize, hiddenSize); initializeWeights(weightsHiddenOutput, hiddenSize, outputSize); hiddenLayer.resize(hiddenSize, 0.0); outputLayer.resize(outputSize, 0.0); } // Прямое распространение vector<double> forward(const vector<double>& inputs) { // Скрытый слой for (size_t j = 0; j < hiddenLayer.size(); j++) { double sum = 0.0; for (size_t i = 0; i < inputs.size(); i++) { sum += inputs[i] * weightsInputHidden[i][j]; } hiddenLayer[j] = sigmoid(sum); } // Выходной слой for (size_t j = 0; j < outputLayer.size(); j++) { double sum = 0.0; for (size_t i = 0; i < hiddenLayer.size(); i++) { sum += hiddenLayer[i] * weightsHiddenOutput[i][j]; } outputLayer[j] = sigmoid(sum); } return outputLayer; } // Обратное распространение void backpropagation(const vector<double>& inputs, const vector<double>& targets) { vector<double> outputErrors(outputLayer.size()); vector<double> hiddenErrors(hiddenLayer.size()); // Ошибки выходного слоя for (size_t j = 0; j < outputLayer.size(); j++) { outputErrors[j] = targets[j] - outputLayer[j]; } // Обновление весов скрытый-выходной слой for (size_t i = 0; i < hiddenLayer.size(); i++) { for (size_t j = 0; j < outputLayer.size(); j++) { double delta = outputErrors[j] * sigmoidDerivative(outputLayer[j]); weightsHiddenOutput[i][j] += learningRate * delta * hiddenLayer[i]; } } // Ошибки скрытого слоя for (size_t i = 0; i < hiddenLayer.size(); i++) { double error = 0.0; for (size_t j = 0; j < outputLayer.size(); j++) { error += outputErrors[j] * weightsHiddenOutput[i][j]; } hiddenErrors[i] = error * sigmoidDerivative(hiddenLayer[i]); } // Обновление весов вход-скрытый слой for (size_t i = 0; i < inputs.size(); i++) { for (size_t j = 0; j < hiddenLayer.size(); j++) { weightsInputHidden[i][j] += learningRate * hiddenErrors[j] * inputs[i]; } } } // Тренировка сети void train(const vector<double>& inputs, const vector<double>& targets) { forward(inputs); backpropagation(inputs, targets); } }; int main() { // Создаем нейронную сеть с 2 входами, 2 нейронами в скрытом слое и 1 выходом NeuralNetwork nn(2, 2, 1, 0.1); // Тренировочные данные для операции "И" vector<vector<double>> trainingInputs = { {0, 0}, {0, 1}, {1, 0}, {1, 1} }; vector<vector<double>> trainingOutputs = { {0}, {0}, {0}, {1} }; // Тренируем сеть for (int epoch = 0; epoch < 10000; epoch++) { for (size_t i = 0; i < trainingInputs.size(); i++) { nn.train(trainingInputs[i], trainingOutputs[i]); } if (epoch % 1000 == 0) { cout << std::format("Epoch: {}\n", epoch); for (const auto& inputs : trainingInputs) { auto output = nn.forward(inputs); cout << std::format("Input: [{}, {}] => Output: {:.6f}\n", inputs[0], inputs[1], output[0]); } cout << endl; } } // Финальный тест cout << "Final Results:\n"; for (const auto& inputs : trainingInputs) { auto output = nn.forward(inputs); cout << std::format("Input: [{}, {}] => Output: {:.6f}\n", inputs[0], inputs[1], output[0]); } return 0; }
Входы: 2 нейрона (каждый принимает 0 или 1).
Скрытый слой: 2 нейрона с функцией активации сигмоида.
Выход: 1 нейрон, который генерирует результат (0 или 1).
Сигмоида используется для активации нейронов (значение от 0 до 1).
Обратное распространение (backpropagation) корректирует веса нейронной сети на основе ошибки между предсказанным и целевым значением.
Обучение происходит в 10,000 эпохах (итерациях). На каждом шаге обновляются веса с использованием градиентного спуска.
В сети используется операция "И", то есть, при входах 0 и 0, 0 и 1, 1 и 0 результат будет 0, а при входах 1 и 1 — результат будет 1.
Программа находит веса для корректного выполнения операции "И", выводя для каждого шага обучения текущие результаты. После завершения обучения выводится финальный результат работы нейронной сети для всех возможных входных комбинаций. Веса инициализируются случайными числами в диапазоне от -1 до 1. Используется метод градиентного спуска для обновления весов.