const std = @import("std"); const Neuron = @import("neuron.zig").Neuron; const Tensor = @import("tensor.zig").Tensor; pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; const allocator = gpa.allocator(); defer _ = gpa.deinit(); // 1. Inizializziamo il neurone (2 input perché la porta AND ha 2 ingressi) var my_neuron = try Neuron.init(allocator, 2); defer my_neuron.deinit(); // 2. Prepariamo il Dataset (AND Gate) // Creiamo un tensore riutilizzabile per gli input var input_tensor = try Tensor.init(allocator, &[_]usize{2}); defer input_tensor.deinit(); // I 4 casi possibili (Training Data) const training_data = [_][2]f32{ .{ 0.0, 0.0 }, .{ 0.0, 1.0 }, .{ 1.0, 0.0 }, .{ 1.0, 1.0 }, }; // Le 4 risposte corrette (Labels) const targets = [_]f32{ 0.0, 0.0, 0.0, 1.0 }; const lr: f32 = 0.1; // Learning rate un po' più aggressivo std.debug.print("--- INIZIO TRAINING (AND GATE) ---\n", .{}); // 3. Ciclo di Training var epoch: usize = 0; while (epoch < 2000) : (epoch += 1) { // 2000 Epoche var total_error: f32 = 0.0; // Per ogni epoca, passiamo attraverso TUTTI gli esempi for (training_data, 0..) |data, index| { // Carichiamo i dati nel tensore input_tensor.data[0] = data[0]; input_tensor.data[1] = data[1]; // Train su questo specifico esempio const loss = my_neuron.train(input_tensor, targets[index], lr); total_error += loss; } // Stampiamo ogni 200 epoche if (epoch % 200 == 0) { std.debug.print("Epoca {d}: Errore Medio = {d:.6}\n", .{ epoch, total_error / 4.0 }); } } std.debug.print("\n--- TEST FINALE ---\n", .{}); // Verifichiamo cosa ha imparato for (training_data) |data| { input_tensor.data[0] = data[0]; input_tensor.data[1] = data[1]; const prediction = my_neuron.forward(input_tensor); // Arrotondiamo visivamente per capire se è 0 o 1 const result_bool: u8 = if (prediction > 0.5) 1 else 0; std.debug.print("Input: {d:.1}, {d:.1} -> Predizione: {d:.4} (Interpretato: {d})\n", .{ data[0], data[1], prediction, result_bool }); } }