Percettrone per la Simulazione di una Porta Logica AND
Idea
Questo codice è stato sviluppato come esercizio pratico basato su un video del canale YouTube @enkk. Nel video, Enkk spiega in modo semplice e intuitivo che cos’è un percettrone attraverso un esempio pratico e facilmente comprensibile e in questo post andiamo ad implementare il codice del percettrone.
Link utili:
Inutile dire quanto sia importante e utile seguire @Enkk se siete interessati allo studio dei Large Language Model (LLM) e dell’AI
L’Analogia del Cancello Automatico
L’esempio utilizzato nel video è particolarmente efficace: immaginiamo un sistema per un parcheggio dove un’automobile può entrare solo e soltanto se entrambi i cancelli (Cancello A e Cancello B) sono aperti contemporaneamente… OVVIAMENTE! ;-)
Schema logico del percettrone di esempio
Logica del Sistema (Esempio di @Enkk):
- Cancello A aperto = segnale 1
- Cancello B aperto = segnale 1
- Output 1 = L’automobile può entrare
- Output 0 = L’automobile non può entrare
Tabella della Verità (AND):
Cancello A | Cancello B | Risultato | Azione |
---|---|---|---|
0 (chiuso) | 0 (chiuso) | 0 | ❌ Auto non entra |
1 (aperto) | 0 (chiuso) | 0 | ❌ Auto non entra |
0 (chiuso) | 1 (aperto) | 0 | ❌ Auto non entra |
1 (aperto) | 1 (aperto) | 1 | ✅ Auto può entrare |
Connessione con la Logica Digitale
Questa analogia rappresenta perfettamente il comportamento di una porta logica AND, dove l’output è 1 solo quando entrambi gli input sono 1. In tutti gli altri casi, l’output è 0.
Perché Usare un Percettrone?
È importante sottolineare che implementare una semplice porta AND richiede poche righe di codice:
int porta_and(int a, int b) {
return a && b; // Semplicissimo!
}
…ancora più semplice:
#include <stdio.h>
int main(void){
int a,b;
a=0; // Assegnare valore 1 o 0
b=0; // Assegnare valore 1 o 0
if (a && b){
printf("Valore output: 1\n");
}
else{
printf("Valore output: 0\n");
}
}
Che cos’è un Percettrone?
Il percettrone è la forma più semplice e fondamentale di una rete neurale artificiale. E’ un singolo neurone artificiale che prende delle decisioni basandosi sui dati che riceve in input.
Struttura di Base
Un percettrone è composto da:
- Input: I dati che riceve (nel nostro caso, lo stato dei due cancelli)
- Pesi (Weights): Numeri che determinano l’importanza di ogni input (IMPORTANTISSIMO i pesi sono i PARAMETRI di un Large Language Model, quindi l’intelligenza)
- Bias: Un valore di soglia che aiuta nella decisione finale
- Funzione di Attivazione: Decide se “attivare” il neurone (output 1) o meno (output 0)
- Output: La decisione finale (1 o 0)
Perché il percettrone è l’elemento più piccolo di una Rete Neurale?
Il percettrone è come il “mattone” con cui si costruiscono reti neurali più complesse:
- Rete Neurale Semplice: 1 percettrone o pochi percettroni (ormai solo a scopo didattico)
- Rete Neurale Multistrato: Centinaia o migliaia di percettroni collegati insieme
- Deep Learning: Milioni di percettroni organizzati in layer (strati)
Proprio come per costruire una casa bisogna capire come funziona un singolo mattone, per comprendere le reti neurali ritengo sia utile prima padroneggiare il percettrone.
Perché è Importante Capire Come Funziona?
- Fondamento Teorico: Tutti i concetti avanzati (backpropagation, gradient descent, ecc.) derivano dai principi del percettrone
- Semplicità Didattica: È abbastanza semplice da capire completamente, ma contiene tutti i concetti chiave e permette di creare un’immagine mentale.
- Base Storica: È stato il primo modello di neurone artificiale.
- Comprensione Intuitiva: Una volta capito il percettrone, il salto concettuale verso reti più complesse è molto più naturale
Valore Didattico di Questo Esercizio
Tuttavia, l’utilizzo di un percettrone per questo esempio, può sembrare eccessivo ma rappresenta un eccellente esercizio didattico per diversi motivi:
- Problema Semplice: La porta AND è facile da visualizzare
- Risultato Verificabile: Possiamo controllare facilmente se funziona (Ho messo nel codice la stampa a video di tutti i valori …passo per passo)
- Concetti Completi: Include tutti gli elementi fondamentali (training, weights, bias, epochs)
- Ponte Concettuale: Collega la logica digitale di base al machine learning avanzato
1. Comprensione dei Fondamenti delle Reti Neurali
- Pesi (Weights): Come la rete assegna importanza agli input
- Bias: Il termine di soglia che influenza la decisione finale
- Funzione di Attivazione: Come vengono prodotti gli output binari
2. Processo di Apprendimento
- Epoche di Addestramento: Cicli ripetuti di apprendimento
- Dataset: Raccolta di esempi di input-output per l’addestramento
- Aggiornamento dei Pesi: Come la rete “impara” dai suoi errori IMPORTANTISSIMO
3. Algoritmo di Apprendimento
- Forward Pass: Calcolo della predizione
- Calcolo dell’Errore: Confronto con l’output desiderato
- Backward Pass: Aggiornamento dei parametri basato sull’errore
4. Concetti di Convergenza
- Come la rete raggiunge una soluzione ottimale
- Criteri di arresto dell’addestramento
- Valutazione delle prestazioni
Implementazione
NOTA BENE: Qui riporto la versione 1 dell’implementazione, probabilmente su github aggiornerò il codice mano a mano che mi vengono nuove idee. Quindi è importante avere come riferimento sempre il link al progetto github per la versione più aggiornata.
Link Github dell’implementazione
/*
* PERCEPTRON
* (AND Logic Gate Learning)
*
* Author: Vincenzo Argese
* Web: https://cr1s1um.github.io/
* Date: 2025-08-09
* Version: v1.0
* Idea: Youtuber @Enkk video “come funziona: le reti neurali (pt.1)” https://www.youtube.com/watch?v=2UdQQA65jcM
*
* Description:
* This program implements a simple perceptron (single artificial neuron) that learns
* to simulate an AND logic gate through supervised learning.
*
* The perceptron receives two binary inputs (0 or 1) and learns to output:
* - 1 only when both inputs are 1 (like an AND gate)
* - 0 in all other cases
*
* Training process:
* 1. Initialize weights and bias with small random values
* 2. For each training example, calculate prediction
* 3. Compare prediction with expected output
* 4. If wrong, adjust weights using perceptron learning rule
* 5. Repeat until all predictions are correct or max epochs reached
*
* This demonstrates fundamental neural network concepts: weights, bias,
* activation functions, training epochs, and supervised learning.
*/
#include <stdio.h>
#define EPOCHS 100 // Define constant: maximum number of training epochs
/* Forward declaration of activation function */
int activation_function(float sum);
int main(void)
{
/* Training Dataset */
int
x[4][2]={ // Input matrix: 4 examples with 2 features each
{0,0}, // First example: x1=0, x2=0 -> expected output: 0
{1,0}, // Second example: x1=1, x2=0 -> expected output: 0
{0,1}, // Third example: x1=0, x2=1 -> expected output: 0
{1,1} // Fourth example: x1=1, x2=1 -> expected output: 1
},
y[4]={0,0,0,1}; // Expected outputs: represents AND logic function
/* Weights and Bias initialization */
float
w1 = 0.1, // Weight for first input, initialized to 0.1
w2 = 0.1, // Weight for second input, initialized to 0.1
b = 0.1; // Bias term, initialized to 0.1
// Learning rate parameter
float learning_rate = 0.1; // Controls how much weights are adjusted during learning
/* Training Phase */
for (int epoch=0; epoch < EPOCHS; epoch++){ // Main training loop: up to 100 epochs
int errors = 0; // Counter for errors in current epoch
// Process all training examples
for(int i=0; i<4; i++){ // Iterate through all 4 examples in dataset
// DEBUG - Print current training state
printf("TRAINING EPOCH: %d\n", epoch); // Fixed typo: epoch instead of ephoc
printf("Current errors: %d\n", errors); // Print current error count
printf("Input X1: %d - X2: %d\n", x[i][0], x[i][1]); // Print current inputs
printf("Expected output Y: %d\n", y[i]); // Print expected output
printf("w1: %.1f - w2: %.1f - Bias: %.1f\n", w1, w2, b); // Print current weights and bias
printf("Learning Rate: %.1f\n", learning_rate); // Print learning rate
// Forward pass: calculate weighted sum
float weighted_sum = (x[i][0]*w1 + x[i][1]*w2) + b; // Linear combination: x1*w1 + x2*w2 + bias
int predicted_output = activation_function(weighted_sum); // Apply activation function
// DEBUG - Print forward pass results
printf("Weighted sum: %.1f\n", weighted_sum); // Print the weighted sum
printf("Predicted output: %d\n", predicted_output); // Print network's prediction
// Calculate prediction error
int error = y[i] - predicted_output; // Error = expected - predicted
// DEBUG - Print error information
printf("Prediction error: %d\n", error); // Print the error
printf("---------------------------------------------\n"); // Separator line
// Update weights if there's an error (perceptron learning rule)
if (error != 0){ // Only update if prediction is wrong
// Apply perceptron learning rule
w1 += learning_rate * error * x[i][0]; // Update w1: w1 = w1 + η * error * x1
w2 += learning_rate * error * x[i][1]; // Update w2: w2 = w2 + η * error * x2
b += learning_rate * error; // Update bias: b = b + η * error
errors++; // Increment error counter
}
}
// Check convergence: stop if no errors occurred
if(errors == 0){ // Perfect classification achieved
printf("TRAINING COMPLETED - epoch: %d\n", epoch); // Print completion message
break; // Exit training loop early
}
}
// Print final results
printf("\n\n"); // Print empty lines for readability
printf("=== FINAL TRAINED PARAMETERS ===\n"); // Print results header
printf("Final w1: %.1f - w2: %.1f - Bias: %.1f\n", w1, w2, b); // Print final weights and bias
// Test the trained perceptron
printf("\n=== TESTING TRAINED PERCEPTRON ===\n");
for(int i=0; i<4; i++){
float test_sum = (x[i][0]*w1 + x[i][1]*w2) + b;
int test_output = activation_function(test_sum);
printf("Input (%d,%d) -> Output: %d (Expected: %d)\n",
x[i][0], x[i][1], test_output, y[i]);
}
return 0;
}
/*
* Activation Function
*
* Purpose: Converts continuous weighted sum to binary output
* Parameter: float sum - the weighted sum from perceptron
* Returns: 1 if sum > 0, otherwise returns 0
*
* This implements a threshold activation function commonly used in perceptrons
*/
int activation_function(float sum){
return (sum > 0) ? 1 : 0; // Ternary operator: if sum > 0 return 1, else return 0
}
Conclusione
Una porta AND può essere implementata in modo molto più semplice ma questo esercizio fornisce una base solida per comprendere i meccanismi fondamentali che stanno alla base delle reti neurali più complesse. È un ponte ideale tra la logica digitale di base e i concetti avanzati del machine learning.
Ogni elemento che sembra “eccessivo” in questo contesto (pesi, bias, epoche di training) diventa essenziale quando si passa a problemi più complessi dove le soluzioni tradizionali non sono più sufficienti.