🧬 Π§Π°ΡΡ‚ΡŒ 36: НСйроморфныС вычислСния Π½Π° ATmega328P

НСйроморфныС вычислСния ΠΈΠΌΠΈΡ‚ΠΈΡ€ΡƒΡŽΡ‚ Ρ€Π°Π±ΠΎΡ‚Ρƒ ΠΌΠΎΠ·Π³Π° β€” с использованиСм ΡΠΏΠ°ΠΉΠΊΠΎΠ² (ΠΈΠΌΠΏΡƒΠ»ΡŒΡΠΎΠ²) вмСсто чисСл. ΠœΡ‹ создадим ΡΠ΅Ρ‚ΡŒ ΠΈΠ· 8 Π½Π΅ΠΉΡ€ΠΎΠ½ΠΎΠ², соСдинСнных Ρ‡Π΅Ρ€Π΅Π· синапсы с измСняСмым вСсом.

РСализация спайковой Π½Π΅ΠΉΡ€ΠΎΠ½Π½ΠΎΠΉ сСти:

cpp

#include <avr/io.h>
#include <avr/interrupt.h>

#define NEURON_COUNT 8
#define SYNAPSE_DELAY 5  // Π—Π°Π΄Π΅Ρ€ΠΆΠΊΠ° распространСния спайка

// НСйрон
struct Neuron {
    int16_t potential;       // ΠœΠ΅ΠΌΠ±Ρ€Π°Π½Π½Ρ‹ΠΉ ΠΏΠΎΡ‚Π΅Π½Ρ†ΠΈΠ°Π»
    uint8_t threshold;       // ΠŸΠΎΡ€ΠΎΠ³ возбуТдСния (ΠΎΠ±Ρ‹Ρ‡Π½ΠΎ 100)
    uint8_t refractory;      // Π Π΅Ρ„Ρ€Π°ΠΊΡ‚Π΅Ρ€Π½Ρ‹ΠΉ ΠΏΠ΅Ρ€ΠΈΠΎΠ΄ (счСтчик)
    uint8_t fired;           // Π€Π»Π°Π³ срабатывания
};

// Бинапс
struct Synapse {
    uint8_t source;          // НСйрон-источник
    uint8_t target;          // НСйрон-Ρ†Π΅Π»ΡŒ
    int8_t weight;           // ВСс (-128..127)
};

Neuron neurons[NEURON_COUNT];
Synapse synapses[NEURON_COUNT * NEURON_COUNT];
uint8_t synapseCount = 0;

// Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ Π½Π΅ΠΉΡ€ΠΎΠ½Π½ΠΎΠΉ сСти
void initNetwork() {
    for (uint8_t i = 0; i < NEURON_COUNT; i++) {
        neurons[i].potential = 0;
        neurons[i].threshold = 100;
        neurons[i].refractory = 0;
        neurons[i].fired = 0;
    }
}

// Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ синапса
void addSynapse(uint8_t source, uint8_t target, int8_t weight) {
    synapses[synapseCount].source = source;
    synapses[synapseCount].target = target;
    synapses[synapseCount].weight = weight;
    synapseCount++;
}

// Π‘ΠΏΠ°ΠΉΠΊ-зависимая ΠΏΠ»Π°ΡΡ‚ΠΈΡ‡Π½ΠΎΡΡ‚ΡŒ (STDP)
void stdpLearning(uint8_t source, uint8_t target, uint32_t timeDiff) {
    // Находим синапс
    for (uint8_t i = 0; i < synapseCount; i++) {
        if (synapses[i].source == source && synapses[i].target == target) {
            // Если спайк ΠΏΡ€ΠΈΡˆΠ΅Π» Π΄ΠΎ возбуТдСния - усиливаСм
            if (timeDiff < 1000) {
                if (synapses[i].weight < 127) synapses[i].weight++;
            } else {
                // Если послС - ослабляСм
                if (synapses[i].weight > -128) synapses[i].weight--;
            }
            break;
        }
    }
}

// ΠžΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° спайка
void processSpike(uint8_t neuronIdx, uint32_t currentTime) {
    // ΠžΡ‚ΠΏΡ€Π°Π²Π»ΡΠ΅ΠΌ спайк всСм связанным Π½Π΅ΠΉΡ€ΠΎΠ½Π°ΠΌ
    for (uint8_t i = 0; i < synapseCount; i++) {
        if (synapses[i].source == neuronIdx) {
            uint8_t target = synapses[i].target;
            
            // ДобавляСм ΠΊ ΠΏΠΎΡ‚Π΅Π½Ρ†ΠΈΠ°Π»Ρƒ Ρ†Π΅Π»Π΅Π²ΠΎΠ³ΠΎ Π½Π΅ΠΉΡ€ΠΎΠ½Π° (с ΡƒΡ‡Π΅Ρ‚ΠΎΠΌ вСса)
            neurons[target].potential += synapses[i].weight;
            
            // STDP ΠΎΠ±ΡƒΡ‡Π΅Π½ΠΈΠ΅ (Ссли Π½Π΅ΠΉΡ€ΠΎΠ½ сработал)
            if (neurons[target].fired) {
                stdpLearning(neuronIdx, target, currentTime - lastSpikeTime[target]);
            }
        }
    }
}

// Основной Ρ†ΠΈΠΊΠ» ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ (вызываСтся ΠΊΠ°ΠΆΠ΄Ρ‹Π΅ 1 мс)
uint32_t time = 0;
uint32_t lastSpikeTime[NEURON_COUNT];

void processNetwork() {
    time++;
    
    for (uint8_t i = 0; i < NEURON_COUNT; i++) {
        // ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅ΠΌ Ρ€Π΅Ρ„Ρ€Π°ΠΊΡ‚Π΅Ρ€Π½ΠΎΡΡ‚ΡŒ
        if (neurons[i].refractory > 0) {
            neurons[i].refractory--;
            continue;
        }
        
        // ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅ΠΌ ΠΏΠΎΡ€ΠΎΠ³
        if (neurons[i].potential >= neurons[i].threshold) {
            // Π‘ΠΏΠ°ΠΉΠΊ!
            neurons[i].fired = 1;
            neurons[i].potential = 0;
            neurons[i].refractory = 5;  // 5 мс рСфрактСрности
            lastSpikeTime[i] = time;
            
            processSpike(i, time);
        } else {
            neurons[i].fired = 0;
            // ΠŸΠΎΡΡ‚Π΅ΠΏΠ΅Π½Π½ΠΎΠ΅ угасаниС ΠΏΠΎΡ‚Π΅Π½Ρ†ΠΈΠ°Π»Π°
            neurons[i].potential = (neurons[i].potential * 15) / 16;
        }
    }
}

// Π’Ρ…ΠΎΠ΄Π½ΠΎΠΉ стимул (ΠΎΡ‚ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠ²)
void stimulateNetwork(uint8_t* input) {
    for (uint8_t i = 0; i < NEURON_COUNT; i++) {
        if (input[i]) {
            neurons[i].potential += 20;  // ДобавляСм стимул
        }
    }
}

// Π§Ρ‚Π΅Π½ΠΈΠ΅ Π²Ρ‹Ρ…ΠΎΠ΄Π° (ΡΡ€Π°Π±ΠΎΡ‚Π°Π²ΡˆΠΈΠ΅ Π½Π΅ΠΉΡ€ΠΎΠ½Ρ‹)
void readOutput(uint8_t* output) {
    for (uint8_t i = 0; i < NEURON_COUNT; i++) {
        output[i] = neurons[i].fired;
    }
}

// ΠŸΡ€ΠΈΠΌΠ΅Ρ€: распознаваниС ΠΏΠ°Ρ‚Ρ‚Π΅Ρ€Π½ΠΎΠ²
void setup() {
    Serial.begin(115200);
    initNetwork();
    
    // Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ случайныС связи (50% Π²Π΅Ρ€ΠΎΡΡ‚Π½ΠΎΡΡ‚ΡŒ)
    for (uint8_t i = 0; i < NEURON_COUNT; i++) {
        for (uint8_t j = 0; j < NEURON_COUNT; j++) {
            if (i != j && (rand() % 2)) {
                addSynapse(i, j, rand() % 20 - 10);
            }
        }
    }
    
    // ΠžΠ±ΡƒΡ‡Π°Π΅ΠΌ ΡΠ΅Ρ‚ΡŒ Π½Π° ΠΏΠ°Ρ‚Ρ‚Π΅Ρ€Π½Π΅ (00110011)
    uint8_t pattern[] = {0, 0, 1, 1, 0, 0, 1, 1};
    for (uint8_t epoch = 0; epoch < 100; epoch++) {
        stimulateNetwork(pattern);
        for (uint8_t i = 0; i < 100; i++) {
            processNetwork();
        }
    }
}

void loop() {
    // ВСстируСм ΡΠ΅Ρ‚ΡŒ
    uint8_t testInput[] = {0, 1, 1, 0, 0, 0, 1, 1};
    uint8_t output[NEURON_COUNT];
    
    stimulateNetwork(testInput);
    for (uint8_t i = 0; i < 100; i++) {
        processNetwork();
    }
    readOutput(output);
    
    Serial.print("Распознанный ΠΏΠ°Ρ‚Ρ‚Π΅Ρ€Π½: ");
    for (uint8_t i = 0; i < NEURON_COUNT; i++) {
        Serial.print(output[i]);
    }
    Serial.println();
    
    delay(1000);
}

Π’Π°ΠΌ Ρ‚Π°ΠΊΠΆΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΠΎΠ½Ρ€Π°Π²ΠΈΡ‚ΡŒΡΡ

About the Author: user1

Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ

Π’Π°Ρˆ адрСс email Π½Π΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΎΠΏΡƒΠ±Π»ΠΈΠΊΠΎΠ²Π°Π½. ΠžΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ поля ΠΏΠΎΠΌΠ΅Ρ‡Π΅Π½Ρ‹ *