для тех кому реально захочется разобраться, легкую и полезную задачу придумать мне сложно… особенно что бы сделать материал для ее обучения легко…
#include <EEPROM.h>
// Структура нейросети для 256 клавиш
#define INPUT_NODES 32 // 16 строк + 16 столбцов
#define HIDDEN_NODES 64 // Больше нейронов для сложной задачи
#define OUTPUT_NODES 8 // 8 бит = 256 комбинаций (0-255)
// Параметры обучения
#define LEARNING_RATE 0.3
#define MOMENTUM 0.6
// ============================================================
// ==== ГЕНЕРАЦИЯ ОБУЧАЮЩЕЙ ВЫБОРКИ НА 256 КЛАВИШ ===========
// ============================================================
// Для клавиатуры 16x16:
// Вход: 16 строк + 16 столбцов (32 бита)
// Выход: 8-битный код клавиши (0-255)
#define TOTAL_KEYS 256
// Функция генерации примера для клавиши номер key (0-255)
void generateKeyExample(int key, float* input, float* target) {
int row = key / 16;// строка 0-15
int col = key % 16;// столбец 0-15
// Очищаем вход
for (int i = 0; i < 32; i++) { input[i] = 0; }
// Устанавливаем биты строки и столбца
input[row] = 1; // строка row активна
input[16 + col] = 1; // столбец col активен
// Заполняем target - 8-битный код клавиши
for (int i = 0; i < 8; i++) {
target[i] = (key >> (7 - i)) & 1; // Старший бит первый
}
}
// Для хранения весов
float hiddenWeights[INPUT_NODES][HIDDEN_NODES];
float outputWeights[HIDDEN_NODES][OUTPUT_NODES];
float hiddenBias[HIDDEN_NODES];
float outputBias[OUTPUT_NODES];
// Для момента
float hiddenDeltaWeights[INPUT_NODES][HIDDEN_NODES];
float outputDeltaWeights[HIDDEN_NODES][OUTPUT_NODES];
float hiddenDeltaBias[HIDDEN_NODES];
float outputDeltaBias[OUTPUT_NODES];
bool isTraining = false;
float inputs[INPUT_NODES];
float targets[OUTPUT_NODES];
// ============================================================
// ==== НЕЙРОСЕТЕВЫЕ ФУНКЦИИ =================================
// ============================================================
float sigmoid(float x) {
if (x < -15.0) return 0.0;
if (x > 15.0) return 1.0;
return 1.0 / (1.0 + exp(-x));
}
float sigmoidDerivative(float x) { return x * (1.0 - x); }
void forwardPass(float* hiddenOutput, float* finalOutput) {
// Скрытый слой
for (int j = 0; j < HIDDEN_NODES; j++) { float sum = hiddenBias[j];
for (int i = 0; i < INPUT_NODES; i++) {
sum += inputs[i] * hiddenWeights[i][j]; }
hiddenOutput[j] = sigmoid(sum); }
// Выходной слой
for (int k = 0; k < OUTPUT_NODES; k++) {
float sum = outputBias[k];
for (int j = 0; j < HIDDEN_NODES; j++) {
sum += hiddenOutput[j] * outputWeights[j][k]; }
finalOutput[k] = sigmoid(sum);
}
}
void trainEpoch(int key) {
// Генерируем пример для клавиши key
generateKeyExample(key, inputs, targets);
float hiddenOutput[HIDDEN_NODES];
float finalOutput[OUTPUT_NODES];
forwardPass(hiddenOutput, finalOutput);
// Ошибка выходного слоя
float outputDelta[OUTPUT_NODES];
for (int k = 0; k < OUTPUT_NODES; k++) {
float error = targets[k] - finalOutput[k];
outputDelta[k] = error * sigmoidDerivative(finalOutput[k]);
}
// Ошибка скрытого слоя
float hiddenDelta[HIDDEN_NODES];
for (int j = 0; j < HIDDEN_NODES; j++) {
float error = 0;
for (int k = 0; k < OUTPUT_NODES; k++) {
error += outputDelta[k] * outputWeights[j][k]; }
hiddenDelta[j] = error * sigmoidDerivative(hiddenOutput[j]);
}
// Обновление весов выходного слоя
for (int j = 0; j < HIDDEN_NODES; j++) {
for (int k = 0; k < OUTPUT_NODES; k++) {
float delta = LEARNING_RATE * outputDelta[k] * hiddenOutput[j];
outputDeltaWeights[j][k] = MOMENTUM * outputDeltaWeights[j][k] + delta;
outputWeights[j][k] += outputDeltaWeights[j][k];
}
}
// Обновление bias выходного слоя
for (int k = 0; k < OUTPUT_NODES; k++) {
float delta = LEARNING_RATE * outputDelta[k];
outputDeltaBias[k] = MOMENTUM * outputDeltaBias[k] + delta;
outputBias[k] += outputDeltaBias[k];
}
// Обновление весов скрытого слоя
for (int i = 0; i < INPUT_NODES; i++) {
for (int j = 0; j < HIDDEN_NODES; j++) {
float delta = LEARNING_RATE * hiddenDelta[j] * inputs[i];
hiddenDeltaWeights[i][j] = MOMENTUM * hiddenDeltaWeights[i][j] + delta;
hiddenWeights[i][j] += hiddenDeltaWeights[i][j];
}
}
// Обновление bias скрытого слоя
for (int j = 0; j < HIDDEN_NODES; j++) {
float delta = LEARNING_RATE * hiddenDelta[j];
hiddenDeltaBias[j] = MOMENTUM * hiddenDeltaBias[j] + delta;
hiddenBias[j] += hiddenDeltaBias[j];
}
}
// ============================================================
// ==== ИНИЦИАЛИЗАЦИЯ ========================================
// ============================================================
void initializeWeights() {
randomSeed(analogRead(0));
// Xavier инициализация
float scale = sqrt(2.0 / INPUT_NODES);
for (int i = 0; i < INPUT_NODES; i++) {
for (int j = 0; j < HIDDEN_NODES; j++) {
hiddenWeights[i][j] = (random(-1000, 1001) / 1000.0) * scale;
hiddenDeltaWeights[i][j] = 0;
}
}
for (int j = 0; j < HIDDEN_NODES; j++) {
hiddenBias[j] = 0; hiddenDeltaBias[j] = 0; }
scale = sqrt(2.0 / HIDDEN_NODES);
for (int j = 0; j < HIDDEN_NODES; j++) {
for (int k = 0; k < OUTPUT_NODES; k++) {
outputWeights[j][k] = (random(-1000, 1001) / 1000.0) * scale;
outputDeltaWeights[j][k] = 0;
}
}
for (int k = 0; k < OUTPUT_NODES; k++) {
outputBias[k] = 0;outputDeltaBias[k] = 0; }
}
// ============================================================
// ==== ТЕСТИРОВАНИЕ =========================================
// ============================================================
void testAllKeys() {
Serial.println(F("=== ТЕСТИРОВАНИЕ ВСЕХ 256 КЛАВИШ ==="));
int correct = 0;
for (int key = 0; key < 256; key++) {
generateKeyExample(key, inputs, targets);
float hidden[HIDDEN_NODES];
float output[OUTPUT_NODES];
forwardPass(hidden, output);
// Преобразуем выход в число
int predicted = 0;
for (int k = 0; k < 8; k++) {
if (output[k] > 0.5) {
predicted |= (1 << (7 - k));
}
}
if (predicted == key) { correct++;
} else if (key % 50 == 0) { // Показываем только каждую 50-ю ошибку
Serial.print(F("Клавиша ")); Serial.print(key);
Serial.print(F(": предсказано ")); Serial.print(predicted);
Serial.print(F(", биты: "));
for (int k = 0; k < 8; k++) {
Serial.print(output[k], 2); Serial.print(F(" "));
} Serial.println(); }
}
float accuracy = (float)correct / 256 * 100;
Serial.print(F("✅ ТОЧНОСТЬ: "));
Serial.print(accuracy, 2);
Serial.println(F("%"));
if (accuracy == 100) {
Serial.println(F("ПОЛНЫЙ УСПЕХ! Нейросеть выучила все 256 комбинаций!"));
}
}
void testRandomKeys(int count) {
Serial.println(F("=== ТЕСТ СЛУЧАЙНЫХ КЛАВИШ ==="));
for (int t = 0; t < count; t++) {
int key = random(0, 256);
generateKeyExample(key, inputs, targets);
float hidden[HIDDEN_NODES];
float output[OUTPUT_NODES];
forwardPass(hidden, output);
// Преобразуем выход в число
int predicted = 0;
for (int k = 0; k < 8; k++) {
if (output[k] > 0.5) {
predicted |= (1 << (7 - k));
}
}
Serial.print(F("Клавиша ")); Serial.print(key);
Serial.print(F(" (строка ")); Serial.print(key / 16);
Serial.print(F(", столбец ")); Serial.print(key % 16);
Serial.print(F(") → предсказано ")); Serial.print(predicted);
if (predicted == key) { Serial.println(F(" ✓"));
} else { Serial.print(F(" ✗ ["));
for (int k = 0; k < 8; k++) {
Serial.print(output[k], 2);
if (k < 7) Serial.print(F(","));
} Serial.println(F("]"));
}
}
}
// ============================================================
// ==== ЭКСПОРТ ВЕСОВ ========================================
// ============================================================
void printWeightsForExport() {
Serial.println(F("\n// ==== ВЕСА ДЛЯ ДЕКОДЕРА 256 КЛАВИШ ===="));
Serial.println(F("// Нейросеть: 32 входа → 64 скрытых → 8 выходов\n"));
Serial.println(F("float hiddenWeights[INPUT_NODES][HIDDEN_NODES] = {"));
for (int i = 0; i < INPUT_NODES; i++) { Serial.print(F(" {"));
for (int j = 0; j < HIDDEN_NODES; j++) {
Serial.print(hiddenWeights[i][j], 6);
if (j < HIDDEN_NODES - 1) Serial.print(F(", "));
} Serial.println(F("},"));
} Serial.println(F("};"));
Serial.println(F("float hiddenBias[HIDDEN_NODES] = {"));
Serial.print(F(" "));
for (int j = 0; j < HIDDEN_NODES; j++) {
Serial.print(hiddenBias[j], 6);
if (j < HIDDEN_NODES - 1) Serial.print(F(", "));
} Serial.println(F("\n};"));
Serial.println(F("\nfloat outputWeights[HIDDEN_NODES][OUTPUT_NODES] = {"));
for (int j = 0; j < HIDDEN_NODES; j++) {
Serial.print(F(" {"));
for (int k = 0; k < OUTPUT_NODES; k++) {
Serial.print(outputWeights[j][k], 6);
if (k < OUTPUT_NODES - 1) Serial.print(F(", "));
} Serial.println(F("},"));
} Serial.println(F("};"));
Serial.println(F("\nfloat outputBias[OUTPUT_NODES] = {"));
Serial.print(F(" "));
for (int k = 0; k < OUTPUT_NODES; k++) {
Serial.print(outputBias[k], 6);
if (k < OUTPUT_NODES - 1) Serial.print(F(", "));
}Serial.println(F("\n};"));
}
// ============================================================
// ==== SETUP И LOOP =========================================
// ============================================================
void setup() {
Serial.begin(9600);
randomSeed(analogRead(0));
Serial.println(F("=========================================="));
Serial.println(F("НЕЙРОСЕТЬ-ДЕКОДЕР ДЛЯ 256 КЛАВИШ"));
Serial.println(F("=========================================="));
Serial.println(F("Вход: 16 строк + 16 столбцов (32 бита)"));
Serial.println(F("Выход: 8-битный код клавиши (0-255)"));
Serial.println(F("=========================================="));
Serial.println(F("Команды:"));
Serial.println(F("1 - Начать обучение"));
Serial.println(F("2 - Тест всех 256 клавиш"));
Serial.println(F("3 - Тест 10 случайных клавиш"));
Serial.println(F("4 - Ручной ввод"));
Serial.println(F("5 - Сбросить веса"));
Serial.println(F("7 - Экспортировать веса"));
Serial.println(F("=========================================="));
initializeWeights();
}
void loop() {
if (Serial.available() > 0) {
char command = Serial.read();
while (Serial.available() > 0) Serial.read();
switch (command) {
case '1': isTraining = true;
Serial.println(F("Обучение начато")); break;
case '2': testAllKeys(); break;
case '3': testRandomKeys(10); break;
case '4': {
Serial.println(F("Введите номер строки (0-15) и столбца (0-15):"));
Serial.println(F("Пример: 5,3 для строки 5, столбца 3"));
String input = Serial.readStringUntil('\n');
input.replace(',', ' ');
int row, col;
if (sscanf(input.c_str(), "%d %d", &row, &col) == 2) {
if (row >= 0 && row < 16 && col >= 0 && col < 16) {
// Очищаем вход
for (int i = 0; i < 32; i++) inputs[i] = 0;
// Устанавливаем биты
inputs[row] = 1; inputs[16 + col] = 1;
float hidden[HIDDEN_NODES];
float output[OUTPUT_NODES];
forwardPass(hidden, output);
// Преобразуем выход в число
int key = 0;
for (int k = 0; k < 8; k++) {
if (output[k] > 0.5) { key |= (1 << (7 - k)); }
}
Serial.print(F("Строка ")); Serial.print(row);
Serial.print(F(", столбец ")); Serial.print(col);
Serial.print(F(" → клавиша ")); Serial.println(key);
Serial.print(F("Вероятности битов: "));
for (int k = 0; k < 8; k++) {
Serial.print(output[k], 3); Serial.print(F(" "));
} Serial.println();
}
}
break;
}
case '5':
initializeWeights();
Serial.println(F("Веса сброшены"));
break;
case '7': printWeightsForExport(); break;
}
}
if (isTraining) {
static int epoch = 0; static float totalError = 0;
// Обучаем на всех 256 клавишах
for (int e = 0; e < 10; e++) { // 10 эпох за цикл
for (int key = 0; key < 256; key++) {
trainEpoch(key);
// Считаем ошибку для мониторинга
float hidden[HIDDEN_NODES];
float output[OUTPUT_NODES];
forwardPass(hidden, output);
float target[OUTPUT_NODES];
generateKeyExample(key, inputs, target);
float error = 0;
for (int k = 0; k < 8; k++) {
error += pow(target[k] - output[k], 2); }
totalError += error; }
}
epoch += 10;
float avgError = totalError / (256 * 10);
Serial.print(F("Эпоха ")); Serial.print(epoch);
Serial.print(F(" | Ошибка: ")); Serial.print(avgError, 6);
// Проверяем точность
int correct = 0;
for (int key = 0; key < 256; key += 16) { // Проверяем каждую 16-ю для скорости
generateKeyExample(key, inputs, targets);
float hidden[HIDDEN_NODES];
float output[OUTPUT_NODES];
forwardPass(hidden, output);
int predicted = 0;
for (int k = 0; k < 8; k++) {
if (output[k] > 0.5) {
predicted |= (1 << (7 - k));
}
}
if (predicted == key) correct++;
}
float accuracy = (float)correct / 16 * 100;
Serial.print(F(" | Точность: "));
Serial.print(accuracy, 1);
Serial.println(F("%"));
totalError = 0;
// Останавливаемся при 100%
if (accuracy >= 99.9) {
isTraining = false;
Serial.println(F("✅ ОБУЧЕНИЕ ЗАВЕРШЕНО! Точность 100%"));
testAllKeys();
}
}
}
однако тут не показана база для обучения… она много весить будет, так что для наглядности как она работает вот второй код , вначале показаны входные 8 слоев через запятую, а в конце выход, эта база вопрос - ответ, многократно проходит через нейросеть, и она меняет свои веса, если сеть обучится на 100% она сможет правильно угадывать…
#include <EEPROM.h>
// Структура нейросети
#define INPUT_NODES 8
#define HIDDEN_NODES 16
#define OUTPUT_NODES 1
// Параметры обучения
#define LEARNING_RATE 0.3
#define MOMENTUM 0.8
// Адреса EEPROM
#define EEPROM_START_HIDDEN 0
#define EEPROM_MAGIC_ADDR 1020
// Пины
#define LED_PIN 13
// ============================================================
// ==== ВАША ПОЛНАЯ ВЫБОРКА ДЛЯ ОБУЧЕНИЯ (255 примеров) ======
// ============================================================
// Первые 8 чисел - вход, последнее - правильный выход (0 или 1)
const byte trainingData[255][9] PROGMEM = {
{0,0,0,0,0,0,0,1,1}, // 1 - нечетное
{0,0,0,0,0,0,1,0,0}, // 2 - четное
{0,0,0,0,0,0,1,1,1}, // 3 - нечетное
{0,0,0,0,0,1,0,0,0}, // 4 - четное
{0,0,0,0,0,1,0,1,1}, // 5 - нечетное
{0,0,0,0,0,1,1,0,0}, // 6 - четное
{0,0,0,0,0,1,1,1,1}, // 7 - нечетное
{0,0,0,0,1,0,0,0,0}, // 8 - четное
{0,0,0,0,1,0,0,1,1}, // 9 - нечетное
{0,0,0,0,1,0,1,0,0}, // 10 - четное
{0,0,0,0,1,0,1,1,1}, // 11 - нечетное
{0,0,0,0,1,1,0,0,0}, // 12 - четное
{0,0,0,0,1,1,0,1,1}, // 13 - нечетное
{0,0,0,0,1,1,1,0,0}, // 14 - четное
{0,0,0,0,1,1,1,1,1}, // 15 - нечетное
{0,0,0,1,0,0,0,0,0}, // 16 - четное
{0,0,0,1,0,0,0,1,1}, // 17 - нечетное
{0,0,0,1,0,0,1,0,0}, // 18 - четное
{0,0,0,1,0,0,1,1,1}, // 19 - нечетное
{0,0,0,1,0,1,0,0,0}, // 20 - четное
{0,0,0,1,0,1,0,1,1}, // 21 - нечетное
{0,0,0,1,0,1,1,0,0}, // 22 - четное
{0,0,0,1,0,1,1,1,1}, // 23 - нечетное
{0,0,0,1,1,0,0,0,0}, // 24 - четное
{0,0,0,1,1,0,0,1,1}, // 25 - нечетное
{0,0,0,1,1,0,1,0,0}, // 26 - четное
{0,0,0,1,1,0,1,1,1}, // 27 - нечетное
{0,0,0,1,1,1,0,0,0}, // 28 - четное
{0,0,0,1,1,1,0,1,1}, // 29 - нечетное
{0,0,0,1,1,1,1,0,0}, // 30 - четное
{0,0,0,1,1,1,1,1,1}, // 31 - нечетное
{0,0,1,0,0,0,0,0,0}, // 32 - четное
{0,0,1,0,0,0,0,1,1}, // 33 - нечетное
{0,0,1,0,0,0,1,0,0}, // 34 - четное
{0,0,1,0,0,0,1,1,1}, // 35 - нечетное
{0,0,1,0,0,1,0,0,0}, // 36 - четное
{0,0,1,0,0,1,0,1,1}, // 37 - нечетное
{0,0,1,0,0,1,1,0,0}, // 38 - четное
{0,0,1,0,0,1,1,1,1}, // 39 - нечетное
{0,0,1,0,1,0,0,0,0}, // 40 - четное
{0,0,1,0,1,0,0,1,1}, // 41 - нечетное
{0,0,1,0,1,0,1,0,0}, // 42 - четное
{0,0,1,0,1,0,1,1,1}, // 43 - нечетное
{0,0,1,0,1,1,0,0,0}, // 44 - четное
{0,0,1,0,1,1,0,1,1}, // 45 - нечетное
{0,0,1,0,1,1,1,0,0}, // 46 - четное
{0,0,1,0,1,1,1,1,1}, // 47 - нечетное
{0,0,1,1,0,0,0,0,0}, // 48 - четное
{0,0,1,1,0,0,0,1,1}, // 49 - нечетное
{0,0,1,1,0,0,1,0,0}, // 50 - четное
{0,0,1,1,0,0,1,1,1}, // 51 - нечетное
{0,0,1,1,0,1,0,0,0}, // 52 - четное
{0,0,1,1,0,1,0,1,1}, // 53 - нечетное
{0,0,1,1,0,1,1,0,0}, // 54 - четное
{0,0,1,1,0,1,1,1,1}, // 55 - нечетное
{0,0,1,1,1,0,0,0,0}, // 56 - четное
{0,0,1,1,1,0,0,1,1}, // 57 - нечетное
{0,0,1,1,1,0,1,0,0}, // 58 - четное
{0,0,1,1,1,0,1,1,1}, // 59 - нечетное
{0,0,1,1,1,1,0,0,0}, // 60 - четное
{0,0,1,1,1,1,0,1,1}, // 61 - нечетное
{0,0,1,1,1,1,1,0,0}, // 62 - четное
{0,0,1,1,1,1,1,1,1}, // 63 - нечетное
{0,1,0,0,0,0,0,0,0}, // 64 - четное
{0,1,0,0,0,0,0,1,1}, // 65 - нечетное
{0,1,0,0,0,0,1,0,0}, // 66 - четное
{0,1,0,0,0,0,1,1,1}, // 67 - нечетное
{0,1,0,0,0,1,0,0,0}, // 68 - четное
{0,1,0,0,0,1,0,1,1}, // 69 - нечетное
{0,1,0,0,0,1,1,0,0}, // 70 - четное
{0,1,0,0,0,1,1,1,1}, // 71 - нечетное
{0,1,0,0,1,0,0,0,0}, // 72 - четное
{0,1,0,0,1,0,0,1,1}, // 73 - нечетное
{0,1,0,0,1,0,1,0,0}, // 74 - четное
{0,1,0,0,1,0,1,1,1}, // 75 - нечетное
{0,1,0,0,1,1,0,0,0}, // 76 - четное
{0,1,0,0,1,1,0,1,1}, // 77 - нечетное
{0,1,0,0,1,1,1,0,0}, // 78 - четное
{0,1,0,0,1,1,1,1,1}, // 79 - нечетное
{0,1,0,1,0,0,0,0,0}, // 80 - четное
{0,1,0,1,0,0,0,1,1}, // 81 - нечетное
{0,1,0,1,0,0,1,0,0}, // 82 - четное
{0,1,0,1,0,0,1,1,1}, // 83 - нечетное
{0,1,0,1,0,1,0,0,0}, // 84 - четное
{0,1,0,1,0,1,0,1,1}, // 85 - нечетное
{0,1,0,1,0,1,1,0,0}, // 86 - четное
{0,1,0,1,0,1,1,1,1}, // 87 - нечетное
{0,1,0,1,1,0,0,0,0}, // 88 - четное
{0,1,0,1,1,0,0,1,1}, // 89 - нечетное
{0,1,0,1,1,0,1,0,0}, // 90 - четное
{0,1,0,1,1,0,1,1,1}, // 91 - нечетное
{0,1,0,1,1,1,0,0,0}, // 92 - четное
{0,1,0,1,1,1,0,1,1}, // 93 - нечетное
{0,1,0,1,1,1,1,0,0}, // 94 - четное
{0,1,0,1,1,1,1,1,1}, // 95 - нечетное
{0,1,1,0,0,0,0,0,0}, // 96 - четное
{0,1,1,0,0,0,0,1,1}, // 97 - нечетное
{0,1,1,0,0,0,1,0,0}, // 98 - четное
{0,1,1,0,0,0,1,1,1}, // 99 - нечетное
{0,1,1,0,0,1,0,0,0}, // 100 - четное
{0,1,1,0,0,1,0,1,1}, // 101 - нечетное
{0,1,1,0,0,1,1,0,0}, // 102 - четное
{0,1,1,0,0,1,1,1,1}, // 103 - нечетное
{0,1,1,0,1,0,0,0,0}, // 104 - четное
{0,1,1,0,1,0,0,1,1}, // 105 - нечетное
{0,1,1,0,1,0,1,0,0}, // 106 - четное
{0,1,1,0,1,0,1,1,1}, // 107 - нечетное
{0,1,1,0,1,1,0,0,0}, // 108 - четное
{0,1,1,0,1,1,0,1,1}, // 109 - нечетное
{0,1,1,0,1,1,1,0,0}, // 110 - четное
{0,1,1,0,1,1,1,1,1}, // 111 - нечетное
{0,1,1,1,0,0,0,0,0}, // 112 - четное
{0,1,1,1,0,0,0,1,1}, // 113 - нечетное
{0,1,1,1,0,0,1,0,0}, // 114 - четное
{0,1,1,1,0,0,1,1,1}, // 115 - нечетное
{0,1,1,1,0,1,0,0,0}, // 116 - четное
{0,1,1,1,0,1,0,1,1}, // 117 - нечетное
{0,1,1,1,0,1,1,0,0}, // 118 - четное
{0,1,1,1,0,1,1,1,1}, // 119 - нечетное
{0,1,1,1,1,0,0,0,0}, // 120 - четное
{0,1,1,1,1,0,0,1,1}, // 121 - нечетное
{0,1,1,1,1,0,1,0,0}, // 122 - четное
{0,1,1,1,1,0,1,1,1}, // 123 - нечетное
{0,1,1,1,1,1,0,0,0}, // 124 - четное
{0,1,1,1,1,1,0,1,1}, // 125 - нечетное
{0,1,1,1,1,1,1,0,0}, // 126 - четное
{0,1,1,1,1,1,1,1,1}, // 127 - нечетное
{1,0,0,0,0,0,0,0,0}, // 128 - четное
{1,0,0,0,0,0,0,1,1}, // 129 - нечетное
{1,0,0,0,0,0,1,0,0}, // 130 - четное
{1,0,0,0,0,0,1,1,1}, // 131 - нечетное
{1,0,0,0,0,1,0,0,0}, // 132 - четное
{1,0,0,0,0,1,0,1,1}, // 133 - нечетное
{1,0,0,0,0,1,1,0,0}, // 134 - четное
{1,0,0,0,0,1,1,1,1}, // 135 - нечетное
{1,0,0,0,1,0,0,0,0}, // 136 - четное
{1,0,0,0,1,0,0,1,1}, // 137 - нечетное
{1,0,0,0,1,0,1,0,0}, // 138 - четное
{1,0,0,0,1,0,1,1,1}, // 139 - нечетное
{1,0,0,0,1,1,0,0,0}, // 140 - четное
{1,0,0,0,1,1,0,1,1}, // 141 - нечетное
{1,0,0,0,1,1,1,0,0}, // 142 - четное
{1,0,0,0,1,1,1,1,1}, // 143 - нечетное
{1,0,0,1,0,0,0,0,0}, // 144 - четное
{1,0,0,1,0,0,0,1,1}, // 145 - нечетное
{1,0,0,1,0,0,1,0,0}, // 146 - четное
{1,0,0,1,0,0,1,1,1}, // 147 - нечетное
{1,0,0,1,0,1,0,0,0}, // 148 - четное
{1,0,0,1,0,1,0,1,1}, // 149 - нечетное
{1,0,0,1,0,1,1,0,0}, // 150 - четное
{1,0,0,1,0,1,1,1,1}, // 151 - нечетное
{1,0,0,1,1,0,0,0,0}, // 152 - четное
{1,0,0,1,1,0,0,1,1}, // 153 - нечетное
{1,0,0,1,1,0,1,0,0}, // 154 - четное
{1,0,0,1,1,0,1,1,1}, // 155 - нечетное
{1,0,0,1,1,1,0,0,0}, // 156 - четное
{1,0,0,1,1,1,0,1,1}, // 157 - нечетное
{1,0,0,1,1,1,1,0,0}, // 158 - четное
{1,0,0,1,1,1,1,1,1}, // 159 - нечетное
{1,0,1,0,0,0,0,0,0}, // 160 - четное
{1,0,1,0,0,0,0,1,1}, // 161 - нечетное
{1,0,1,0,0,0,1,0,0}, // 162 - четное
{1,0,1,0,0,0,1,1,1}, // 163 - нечетное
{1,0,1,0,0,1,0,0,0}, // 164 - четное
{1,0,1,0,0,1,0,1,1}, // 165 - нечетное
{1,0,1,0,0,1,1,0,0}, // 166 - четное
{1,0,1,0,0,1,1,1,1}, // 167 - нечетное
{1,0,1,0,1,0,0,0,0}, // 168 - четное
{1,0,1,0,1,0,0,1,1}, // 169 - нечетное
{1,0,1,0,1,0,1,0,0}, // 170 - четное
{1,0,1,0,1,0,1,1,1}, // 171 - нечетное
{1,0,1,0,1,1,0,0,0}, // 172 - четное
{1,0,1,0,1,1,0,1,1}, // 173 - нечетное
{1,0,1,0,1,1,1,0,0}, // 174 - четное
{1,0,1,0,1,1,1,1,1}, // 175 - нечетное
{1,0,1,1,0,0,0,0,0}, // 176 - четное
{1,0,1,1,0,0,0,1,1}, // 177 - нечетное
{1,0,1,1,0,0,1,0,0}, // 178 - четное
{1,0,1,1,0,0,1,1,1}, // 179 - нечетное
{1,0,1,1,0,1,0,0,0}, // 180 - четное
{1,0,1,1,0,1,0,1,1}, // 181 - нечетное
{1,0,1,1,0,1,1,0,0}, // 182 - четное
{1,0,1,1,0,1,1,1,1}, // 183 - нечетное
{1,0,1,1,1,0,0,0,0}, // 184 - четное
{1,0,1,1,1,0,0,1,1}, // 185 - нечетное
{1,0,1,1,1,0,1,0,0}, // 186 - четное
{1,0,1,1,1,0,1,1,1}, // 187 - нечетное
{1,0,1,1,1,1,0,0,0}, // 188 - четное
{1,0,1,1,1,1,0,1,1}, // 189 - нечетное
{1,0,1,1,1,1,1,0,0}, // 190 - четное
{1,0,1,1,1,1,1,1,1}, // 191 - нечетное
{1,1,0,0,0,0,0,0,0}, // 192 - четное
{1,1,0,0,0,0,0,1,1}, // 193 - нечетное
{1,1,0,0,0,0,1,0,0}, // 194 - четное
{1,1,0,0,0,0,1,1,1}, // 195 - нечетное
{1,1,0,0,0,1,0,0,0}, // 196 - четное
{1,1,0,0,0,1,0,1,1}, // 197 - нечетное
{1,1,0,0,0,1,1,0,0}, // 198 - четное
{1,1,0,0,0,1,1,1,1}, // 199 - нечетное
{1,1,0,0,1,0,0,0,0}, // 200 - четное
{1,1,0,0,1,0,0,1,1}, // 201 - нечетное
{1,1,0,0,1,0,1,0,0}, // 202 - четное
{1,1,0,0,1,0,1,1,1}, // 203 - нечетное
{1,1,0,0,1,1,0,0,0}, // 204 - четное
{1,1,0,0,1,1,0,1,1}, // 205 - нечетное
{1,1,0,0,1,1,1,0,0}, // 206 - четное
{1,1,0,0,1,1,1,1,1}, // 207 - нечетное
{1,1,0,1,0,0,0,0,0}, // 208 - четное
{1,1,0,1,0,0,0,1,1}, // 209 - нечетное
{1,1,0,1,0,0,1,0,0}, // 210 - четное
{1,1,0,1,0,0,1,1,1}, // 211 - нечетное
{1,1,0,1,0,1,0,0,0}, // 212 - четное
{1,1,0,1,0,1,0,1,1}, // 213 - нечетное
{1,1,0,1,0,1,1,0,0}, // 214 - четное
{1,1,0,1,0,1,1,1,1}, // 215 - нечетное
{1,1,0,1,1,0,0,0,0}, // 216 - четное
{1,1,0,1,1,0,0,1,1}, // 217 - нечетное
{1,1,0,1,1,0,1,0,0}, // 218 - четное
{1,1,0,1,1,0,1,1,1}, // 219 - нечетное
{1,1,0,1,1,1,0,0,0}, // 220 - четное
{1,1,0,1,1,1,0,1,1}, // 221 - нечетное
{1,1,0,1,1,1,1,0,0}, // 222 - четное
{1,1,0,1,1,1,1,1,1}, // 223 - нечетное
{1,1,1,0,0,0,0,0,0}, // 224 - четное
{1,1,1,0,0,0,0,1,1}, // 225 - нечетное
{1,1,1,0,0,0,1,0,0}, // 226 - четное
{1,1,1,0,0,0,1,1,1}, // 227 - нечетное
{1,1,1,0,0,1,0,0,0}, // 228 - четное
{1,1,1,0,0,1,0,1,1}, // 229 - нечетное
{1,1,1,0,0,1,1,0,0}, // 230 - четное
{1,1,1,0,0,1,1,1,1}, // 231 - нечетное
{1,1,1,0,1,0,0,0,0}, // 232 - четное
{1,1,1,0,1,0,0,1,1}, // 233 - нечетное
{1,1,1,0,1,0,1,0,0}, // 234 - четное
{1,1,1,0,1,0,1,1,1}, // 235 - нечетное
{1,1,1,0,1,1,0,0,0}, // 236 - четное
{1,1,1,0,1,1,0,1,1}, // 237 - нечетное
{1,1,1,0,1,1,1,0,0}, // 238 - четное
{1,1,1,0,1,1,1,1,1}, // 239 - нечетное
{1,1,1,1,0,0,0,0,0}, // 240 - четное
{1,1,1,1,0,0,0,1,1}, // 241 - нечетное
{1,1,1,1,0,0,1,0,0}, // 242 - четное
{1,1,1,1,0,0,1,1,1}, // 243 - нечетное
{1,1,1,1,0,1,0,0,0}, // 244 - четное
{1,1,1,1,0,1,0,1,1}, // 245 - нечетное
{1,1,1,1,0,1,1,0,0}, // 246 - четное
{1,1,1,1,0,1,1,1,1}, // 247 - нечетное
{1,1,1,1,1,0,0,0,0}, // 248 - четное
{1,1,1,1,1,0,0,1,1}, // 249 - нечетное
{1,1,1,1,1,0,1,0,0}, // 250 - четное
{1,1,1,1,1,0,1,1,1}, // 251 - нечетное
{1,1,1,1,1,1,0,0,0}, // 252 - четное
{1,1,1,1,1,1,0,1,1}, // 253 - нечетное
{1,1,1,1,1,1,1,0,0}, // 254 - четное
{1,1,1,1,1,1,1,1,1} // 255 - нечетное
};
// ============================================================
// Веса сети
float hiddenWeights[INPUT_NODES][HIDDEN_NODES];
float outputWeights[HIDDEN_NODES][OUTPUT_NODES];
float hiddenBias[HIDDEN_NODES];
float outputBias[OUTPUT_NODES];
// Для момента
float hiddenDeltaWeights[INPUT_NODES][HIDDEN_NODES];
float outputDeltaWeights[HIDDEN_NODES][OUTPUT_NODES];
float hiddenDeltaBias[HIDDEN_NODES];
float outputDeltaBias[OUTPUT_NODES];
bool isTraining = false;
bool stopTraining = false;
unsigned long lastSaveTime = 0;
const unsigned long saveInterval = 600000;
float inputs[INPUT_NODES];
void getExample(int index, float* input, float* target) {
byte example[9];
for (int i = 0; i < 9; i++) {
example[i] = pgm_read_byte(&trainingData[index][i]);
}
for (int i = 0; i < INPUT_NODES; i++) {
input[i] = example[i];
}
*target = example[8];
}
float sigmoid(float x) {
if (x < -10.0) return 0.0;
if (x > 10.0) return 1.0;
return 1.0 / (1.0 + exp(-x));
}
float sigmoidDerivative(float x) {
return x * (1.0 - x);
}
void forwardPass(float* hiddenOutput, float* finalOutput) {
for (int j = 0; j < HIDDEN_NODES; j++) {
float sum = hiddenBias[j];
for (int i = 0; i < INPUT_NODES; i++) {
sum += inputs[i] * hiddenWeights[i][j];
}
hiddenOutput[j] = sigmoid(sum);
}
float sum = outputBias[0];
for (int j = 0; j < HIDDEN_NODES; j++) {
sum += hiddenOutput[j] * outputWeights[j][0];
}
finalOutput[0] = sigmoid(sum);
}
void trainEpoch(int exampleIndex) {
float target;
getExample(exampleIndex, inputs, &target);
float hiddenOutput[HIDDEN_NODES];
float finalOutput[OUTPUT_NODES];
forwardPass(hiddenOutput, finalOutput);
float outputError = target - finalOutput[0];
float outputDelta = outputError * sigmoidDerivative(finalOutput[0]);
float hiddenDelta[HIDDEN_NODES];
for (int j = 0; j < HIDDEN_NODES; j++) {
float hiddenError = outputDelta * outputWeights[j][0];
hiddenDelta[j] = hiddenError * sigmoidDerivative(hiddenOutput[j]);
}
for (int j = 0; j < HIDDEN_NODES; j++) {
float delta = LEARNING_RATE * outputDelta * hiddenOutput[j];
outputDeltaWeights[j][0] = MOMENTUM * outputDeltaWeights[j][0] + delta;
outputWeights[j][0] += outputDeltaWeights[j][0];
}
outputDeltaBias[0] = MOMENTUM * outputDeltaBias[0] + LEARNING_RATE * outputDelta;
outputBias[0] += outputDeltaBias[0];
for (int i = 0; i < INPUT_NODES; i++) {
for (int j = 0; j < HIDDEN_NODES; j++) {
float delta = LEARNING_RATE * hiddenDelta[j] * inputs[i];
hiddenDeltaWeights[i][j] = MOMENTUM * hiddenDeltaWeights[i][j] + delta;
hiddenWeights[i][j] += hiddenDeltaWeights[i][j];
}
}
for (int j = 0; j < HIDDEN_NODES; j++) {
float delta = LEARNING_RATE * hiddenDelta[j];
hiddenDeltaBias[j] = MOMENTUM * hiddenDeltaBias[j] + delta;
hiddenBias[j] += hiddenDeltaBias[j];
}
}
void initializeWeights() {
for (int i = 0; i < INPUT_NODES; i++) {
for (int j = 0; j < HIDDEN_NODES; j++) {
hiddenWeights[i][j] = random(-1000, 1001) / 1000.0;
hiddenDeltaWeights[i][j] = 0;
}
}
for (int j = 0; j < HIDDEN_NODES; j++) {
hiddenBias[j] = random(-1000, 1001) / 1000.0;
hiddenDeltaBias[j] = 0;
}
for (int j = 0; j < HIDDEN_NODES; j++) {
outputWeights[j][0] = random(-1000, 1001) / 1000.0;
outputDeltaWeights[j][0] = 0;
}
outputBias[0] = random(-1000, 1001) / 1000.0;
outputDeltaBias[0] = 0;
}
void printWeightsForExport() {
Serial.println(F("\n=== ВЕСА ДЛЯ ВСТАВКИ В КОД ===\n"));
Serial.println(F("float hiddenWeights[INPUT_NODES][HIDDEN_NODES] = {"));
for (int i = 0; i < INPUT_NODES; i++) {
Serial.print(F(" {"));
for (int j = 0; j < HIDDEN_NODES; j++) {
Serial.print(hiddenWeights[i][j], 6);
if (j < HIDDEN_NODES - 1) Serial.print(F(", "));
}
Serial.println(F("},"));
}
Serial.println(F("};"));
Serial.println(F("\nfloat hiddenBias[HIDDEN_NODES] = {"));
Serial.print(F(" "));
for (int j = 0; j < HIDDEN_NODES; j++) {
Serial.print(hiddenBias[j], 6);
if (j < HIDDEN_NODES - 1) Serial.print(F(", "));
}
Serial.println(F("\n};"));
Serial.println(F("\nfloat outputWeights[HIDDEN_NODES][OUTPUT_NODES] = {"));
for (int j = 0; j < HIDDEN_NODES; j++) {
Serial.print(F(" {"));
Serial.print(outputWeights[j][0], 6);
Serial.println(F("},"));
}
Serial.println(F("};"));
Serial.println(F("\nfloat outputBias[OUTPUT_NODES] = {"));
Serial.print(F(" "));
Serial.print(outputBias[0], 6);
Serial.println(F("\n};"));
}
void setup() {
Serial.begin(9600);
pinMode(LED_PIN, OUTPUT);
randomSeed(analogRead(0));
Serial.println(F("=== НЕЙРОСЕТЬ С ВАШЕЙ ВЫБОРКОЙ ==="));
Serial.println(F("Команды:"));
Serial.println(F("1 - Начать обучение"));
Serial.println(F("2 - Проверить число (введите 8 бит)"));
Serial.println(F("3 - Остановить обучение"));
Serial.println(F("5 - Сбросить веса случайно"));
Serial.println(F("7 - Экспортировать веса"));
initializeWeights();
}
void loop() {
if (Serial.available() > 0) {
char command = Serial.read();
while (Serial.available() > 0) Serial.read();
switch (command) {
case '1':
isTraining = true;
stopTraining = false;
Serial.println(F("Обучение начато"));
break;
case '2':
Serial.println(F("Введите 8 бит (0 или 1) через запятую:"));
waitForInput();
break;
case '3':
isTraining = false;
stopTraining = true;
Serial.println(F("Обучение остановлено"));
break;
case '5':
initializeWeights();
Serial.println(F("Веса сброшены случайно"));
break;
case '7':
printWeightsForExport();
break;
}
}
if (isTraining && !stopTraining) {
static int epoch = 0;
static float totalError = 0;
static int correct = 0;
for (int e = 0; e < 10; e++) { // 10 эпох за цикл
for (int ex = 0; ex < 255; ex++) {
trainEpoch(ex);
float target;
getExample(ex, inputs, &target);
float hidden[HIDDEN_NODES];
float out[1];
forwardPass(hidden, out);
totalError += pow(target - out[0], 2);
if ((out[0] > 0.5 && target > 0.5) || (out[0] <= 0.5 && target <= 0.5)) {
correct++;
}
}
}
epoch += 10;
float avgError = totalError / (255 * 10);
float accuracy = (float)correct / (255 * 10) * 100;
Serial.print(F("Эпоха "));
Serial.print(epoch);
Serial.print(F(" | Ошибка: "));
Serial.print(avgError, 6);
Serial.print(F(" | Точность: "));
Serial.print(accuracy, 2);
Serial.println(F("%"));
totalError = 0;
correct = 0;
}
}
void waitForInput() {
String s = Serial.readStringUntil('\n');
s.replace(',', ' ');
float in[8];
int idx = 0;
int pos = 0;
while (pos < s.length() && idx < 8) {
int next = s.indexOf(' ', pos);
if (next == -1) next = s.length();
String val = s.substring(pos, next);
val.trim();
if (val.length() > 0) {
in[idx++] = val.toInt();
}
pos = next + 1;
}
if (idx == 8) {
for (int i = 0; i < 8; i++) inputs[i] = in[i];
float hidden[HIDDEN_NODES];
float out[1];
forwardPass(hidden, out);
Serial.print(F("Результат: "));
Serial.println(out[0], 6);
Serial.print(F("Это "));
Serial.println(out[0] > 0.5 ? F("НЕЧЕТНОЕ (1)") : F("ЧЕТНОЕ (0)"));
}
}
тестировал на esp32, если у вас возникли вопросы, ответ ¯\_(ツ)_/¯ но может кто другой вникнет, и будет создавать что то свое, заодно и объяснит!
сеть универсальная, есть так же и другие сети, для распознавания речи, или изображений… более сложные, и с обучением на пк или сайте!
https://circuitdigest.com/microcontrollers-projects/esp32-offline-voice-recognition-using-edge-impulse
https://circuitdigest.com/microcontroller-projects/esp32-cam-face-recognition-using-edge-impulse
и куча других, однако у меня просто показывает универсальную, и скорее знакомит с тем что такое нейросеть, однако в сети есть и другие, более сложные, и даже обученные!