Pulizia codice
This commit is contained in:
parent
ed8080e2d6
commit
aeb830bb0e
|
|
@ -6,7 +6,6 @@ pub const Sigmoid = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn derivative(y: f32) f32 {
|
pub fn derivative(y: f32) f32 {
|
||||||
// Nota: qui assumiamo che 'y' sia già il risultato della sigmoide
|
|
||||||
return y * (1.0 - y);
|
return y * (1.0 - y);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
15
src/main.zig
15
src/main.zig
|
|
@ -1,18 +1,16 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const World = @import("env.zig").World;
|
const World = @import("env.zig").World;
|
||||||
const env = @import("env.zig"); // Per accedere a costanti come GRID_SIZE
|
const env = @import("env.zig");
|
||||||
const Network = @import("modular_network.zig").Network;
|
const Network = @import("modular_network.zig").Network;
|
||||||
|
|
||||||
// --- IPERPARAMETRI ---
|
|
||||||
const GAMMA: f32 = 0.9;
|
const GAMMA: f32 = 0.9;
|
||||||
const LR: f32 = 0.005; // Basso perché output lineare
|
const LR: f32 = 0.005;
|
||||||
const EPSILON_START: f32 = 1.0;
|
const EPSILON_START: f32 = 1.0;
|
||||||
const EPSILON_END: f32 = 0.05;
|
const EPSILON_END: f32 = 0.05;
|
||||||
const DECAY_RATE: f32 = 0.0001; // Decadimento più lento dato che abbiamo tanti step
|
const DECAY_RATE: f32 = 0.0001;
|
||||||
|
|
||||||
// Helper per trovare max e argmax
|
|
||||||
fn maxVal(slice: []const f32) f32 {
|
fn maxVal(slice: []const f32) f32 {
|
||||||
var m: f32 = -1.0e20; // Numero molto basso
|
var m: f32 = -1.0e20;
|
||||||
for (slice) |v| if (v > m) {
|
for (slice) |v| if (v > m) {
|
||||||
m = v;
|
m = v;
|
||||||
};
|
};
|
||||||
|
|
@ -31,17 +29,14 @@ fn argmax(slice: []const f32) usize {
|
||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export aggiornato per Multi-Formica
|
|
||||||
fn exportAntJSON(world: *World, file_path: []const u8, episode: usize, epsilon: f32) !void {
|
fn exportAntJSON(world: *World, file_path: []const u8, episode: usize, epsilon: f32) !void {
|
||||||
const file = try std.fs.cwd().createFile(file_path, .{});
|
const file = try std.fs.cwd().createFile(file_path, .{});
|
||||||
defer file.close();
|
defer file.close();
|
||||||
|
|
||||||
// Buffer grande per contenere le coordinate di 20 formiche
|
|
||||||
var buffer: [65536]u8 = undefined;
|
var buffer: [65536]u8 = undefined;
|
||||||
var fba = std.heap.FixedBufferAllocator.init(&buffer);
|
var fba = std.heap.FixedBufferAllocator.init(&buffer);
|
||||||
const allocator = fba.allocator();
|
const allocator = fba.allocator();
|
||||||
|
|
||||||
// Costruiamo la lista delle formiche in JSON: [[x,y], [x,y], ...]
|
|
||||||
var ants_json = std.ArrayList(u8){};
|
var ants_json = std.ArrayList(u8){};
|
||||||
defer ants_json.deinit(allocator);
|
defer ants_json.deinit(allocator);
|
||||||
try ants_json.appendSlice(allocator, "[");
|
try ants_json.appendSlice(allocator, "[");
|
||||||
|
|
@ -52,8 +47,6 @@ fn exportAntJSON(world: *World, file_path: []const u8, episode: usize, epsilon:
|
||||||
}
|
}
|
||||||
try ants_json.appendSlice(allocator, "]");
|
try ants_json.appendSlice(allocator, "]");
|
||||||
|
|
||||||
// Scriviamo il JSON completo
|
|
||||||
// QUI RISOLVIAMO L'ERRORE: Usiamo 'epsilon' nella stringa
|
|
||||||
const json = try std.fmt.allocPrint(allocator, "{{\n \"grid_size\": {d},\n \"food\": [{d}, {d}],\n \"ants\": {s},\n \"episode\": {d},\n \"epsilon\": {d:.3}\n}}", .{ env.GRID_SIZE, world.food_x, world.food_y, ants_json.items, episode, epsilon });
|
const json = try std.fmt.allocPrint(allocator, "{{\n \"grid_size\": {d},\n \"food\": [{d}, {d}],\n \"ants\": {s},\n \"episode\": {d},\n \"epsilon\": {d:.3}\n}}", .{ env.GRID_SIZE, world.food_x, world.food_y, ants_json.items, episode, epsilon });
|
||||||
|
|
||||||
try file.writeAll(json);
|
try file.writeAll(json);
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,14 @@ pub const MnistData = struct {
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, img_path: []const u8, lbl_path: []const u8, max_items: usize) !MnistData {
|
pub fn init(allocator: Allocator, img_path: []const u8, lbl_path: []const u8, max_items: usize) !MnistData {
|
||||||
// --- 1. CARICAMENTO IMMAGINI ---
|
|
||||||
const img_file = try std.fs.cwd().openFile(img_path, .{});
|
const img_file = try std.fs.cwd().openFile(img_path, .{});
|
||||||
defer img_file.close();
|
defer img_file.close();
|
||||||
try img_file.seekTo(16);
|
try img_file.seekTo(16);
|
||||||
|
|
||||||
// --- 2. CARICAMENTO ETICHETTE ---
|
|
||||||
const lbl_file = try std.fs.cwd().openFile(lbl_path, .{});
|
const lbl_file = try std.fs.cwd().openFile(lbl_path, .{});
|
||||||
defer lbl_file.close();
|
defer lbl_file.close();
|
||||||
try lbl_file.seekTo(8);
|
try lbl_file.seekTo(8);
|
||||||
|
|
||||||
// --- 3. ALLOCAZIONE ---
|
|
||||||
var images: std.ArrayList([]f32) = .{};
|
var images: std.ArrayList([]f32) = .{};
|
||||||
defer images.deinit(allocator);
|
defer images.deinit(allocator);
|
||||||
var labels: std.ArrayList([]f32) = .{};
|
var labels: std.ArrayList([]f32) = .{};
|
||||||
|
|
@ -29,11 +26,9 @@ pub const MnistData = struct {
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
|
|
||||||
while (i < max_items) : (i += 1) {
|
while (i < max_items) : (i += 1) {
|
||||||
// Leggi direttamente dal file
|
|
||||||
const bytes_read = try img_file.read(&buffer);
|
const bytes_read = try img_file.read(&buffer);
|
||||||
if (bytes_read < img_size) break;
|
if (bytes_read < img_size) break;
|
||||||
|
|
||||||
// Leggi 1 byte per l'etichetta
|
|
||||||
const lbl_read = try lbl_file.read(&label_byte);
|
const lbl_read = try lbl_file.read(&label_byte);
|
||||||
if (lbl_read < 1) break;
|
if (lbl_read < 1) break;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ pub const Network = struct {
|
||||||
return current_input;
|
return current_input;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- QUESTA ERA LA FUNZIONE MANCANTE ---
|
|
||||||
pub fn printTopology(self: *Network) void {
|
pub fn printTopology(self: *Network) void {
|
||||||
std.debug.print("Architettura Rete: [Input]", .{});
|
std.debug.print("Architettura Rete: [Input]", .{});
|
||||||
for (self.layers.items) |layer| {
|
for (self.layers.items) |layer| {
|
||||||
|
|
@ -41,7 +40,6 @@ pub const Network = struct {
|
||||||
}
|
}
|
||||||
std.debug.print("\n", .{});
|
std.debug.print("\n", .{});
|
||||||
}
|
}
|
||||||
// ---------------------------------------
|
|
||||||
|
|
||||||
pub fn train(self: *Network, input: []const f32, target: []const f32, lr: f32) !f32 {
|
pub fn train(self: *Network, input: []const f32, target: []const f32, lr: f32) !f32 {
|
||||||
_ = self.forward(input);
|
_ = self.forward(input);
|
||||||
|
|
@ -49,9 +47,8 @@ pub const Network = struct {
|
||||||
const last_layer_idx = self.layers.items.len - 1;
|
const last_layer_idx = self.layers.items.len - 1;
|
||||||
const last_layer = &self.layers.items[last_layer_idx];
|
const last_layer = &self.layers.items[last_layer_idx];
|
||||||
|
|
||||||
// Questo è il buffer iniziale degli errori
|
|
||||||
var output_errors = try self.allocator.alloc(f32, last_layer.neurons_count);
|
var output_errors = try self.allocator.alloc(f32, last_layer.neurons_count);
|
||||||
defer self.allocator.free(output_errors); // Zig pulirà questo automaticamente alla fine
|
defer self.allocator.free(output_errors);
|
||||||
|
|
||||||
var total_loss: f32 = 0.0;
|
var total_loss: f32 = 0.0;
|
||||||
for (0..last_layer.neurons_count) |i| {
|
for (0..last_layer.neurons_count) |i| {
|
||||||
|
|
@ -68,23 +65,15 @@ pub const Network = struct {
|
||||||
var layer = &self.layers.items[i];
|
var layer = &self.layers.items[i];
|
||||||
const prev_input = if (i == 0) input else self.layers.items[i - 1].output;
|
const prev_input = if (i == 0) input else self.layers.items[i - 1].output;
|
||||||
|
|
||||||
// 1. Calcoliamo i nuovi gradienti (nuova allocazione in memoria)
|
|
||||||
const new_gradients = layer.backward(next_gradients, prev_input, lr);
|
const new_gradients = layer.backward(next_gradients, prev_input, lr);
|
||||||
|
|
||||||
// 2. CHECK ANTI-LEAK:
|
|
||||||
// Se il vecchio next_gradients NON è output_errors (che è protetto dal defer),
|
|
||||||
// allora è un buffer intermedio creato dal layer precedente. Dobbiamo distruggerlo.
|
|
||||||
if (next_gradients.ptr != output_errors.ptr) {
|
if (next_gradients.ptr != output_errors.ptr) {
|
||||||
self.allocator.free(next_gradients);
|
self.allocator.free(next_gradients);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Aggiorniamo il puntatore per il prossimo giro
|
|
||||||
next_gradients = new_gradients;
|
next_gradients = new_gradients;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Pulizia Finale:
|
|
||||||
// L'ultimo buffer creato dal primo layer (input layer) è rimasto in mano nostra.
|
|
||||||
// Dobbiamo liberare anche quello.
|
|
||||||
if (next_gradients.ptr != output_errors.ptr) {
|
if (next_gradients.ptr != output_errors.ptr) {
|
||||||
self.allocator.free(next_gradients);
|
self.allocator.free(next_gradients);
|
||||||
}
|
}
|
||||||
|
|
@ -92,27 +81,23 @@ pub const Network = struct {
|
||||||
return total_loss;
|
return total_loss;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- SALVATAGGIO BINARIO ---
|
|
||||||
pub fn save(self: *Network, file_path: []const u8) !void {
|
pub fn save(self: *Network, file_path: []const u8) !void {
|
||||||
const file = try std.fs.cwd().createFile(file_path, .{});
|
const file = try std.fs.cwd().createFile(file_path, .{});
|
||||||
defer file.close();
|
defer file.close();
|
||||||
|
|
||||||
const MagicNumber: u64 = 0xDEADBEEF;
|
const MagicNumber: u64 = 0xDEADBEEF;
|
||||||
|
|
||||||
// Scriviamo Header
|
|
||||||
try file.writeAll(std.mem.asBytes(&MagicNumber));
|
try file.writeAll(std.mem.asBytes(&MagicNumber));
|
||||||
|
|
||||||
const layer_count = @as(u64, self.layers.items.len);
|
const layer_count = @as(u64, self.layers.items.len);
|
||||||
try file.writeAll(std.mem.asBytes(&layer_count));
|
try file.writeAll(std.mem.asBytes(&layer_count));
|
||||||
|
|
||||||
// Scriviamo Layers
|
|
||||||
for (self.layers.items) |layer| {
|
for (self.layers.items) |layer| {
|
||||||
const inputs = @as(u64, layer.inputs_count);
|
const inputs = @as(u64, layer.inputs_count);
|
||||||
const neurons = @as(u64, layer.neurons_count);
|
const neurons = @as(u64, layer.neurons_count);
|
||||||
try file.writeAll(std.mem.asBytes(&inputs));
|
try file.writeAll(std.mem.asBytes(&inputs));
|
||||||
try file.writeAll(std.mem.asBytes(&neurons));
|
try file.writeAll(std.mem.asBytes(&neurons));
|
||||||
|
|
||||||
// Scriviamo Pesi e Bias
|
|
||||||
const weights_bytes = std.mem.sliceAsBytes(layer.weights);
|
const weights_bytes = std.mem.sliceAsBytes(layer.weights);
|
||||||
try file.writeAll(weights_bytes);
|
try file.writeAll(weights_bytes);
|
||||||
|
|
||||||
|
|
@ -121,7 +106,6 @@ pub const Network = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- CARICAMENTO BINARIO ---
|
|
||||||
pub fn load(allocator: Allocator, file_path: []const u8) !Network {
|
pub fn load(allocator: Allocator, file_path: []const u8) !Network {
|
||||||
const file = try std.fs.cwd().openFile(file_path, .{});
|
const file = try std.fs.cwd().openFile(file_path, .{});
|
||||||
defer file.close();
|
defer file.close();
|
||||||
|
|
@ -131,7 +115,6 @@ pub const Network = struct {
|
||||||
|
|
||||||
defer if (!success) net.deinit();
|
defer if (!success) net.deinit();
|
||||||
|
|
||||||
// Header Check
|
|
||||||
var magic: u64 = 0;
|
var magic: u64 = 0;
|
||||||
_ = try file.readAll(std.mem.asBytes(&magic));
|
_ = try file.readAll(std.mem.asBytes(&magic));
|
||||||
if (magic != 0xDEADBEEF) return error.InvalidNetworkFile;
|
if (magic != 0xDEADBEEF) return error.InvalidNetworkFile;
|
||||||
|
|
@ -139,7 +122,6 @@ pub const Network = struct {
|
||||||
var layer_count: u64 = 0;
|
var layer_count: u64 = 0;
|
||||||
_ = try file.readAll(std.mem.asBytes(&layer_count));
|
_ = try file.readAll(std.mem.asBytes(&layer_count));
|
||||||
|
|
||||||
// Layers Loop
|
|
||||||
var i: u64 = 0;
|
var i: u64 = 0;
|
||||||
while (i < layer_count) : (i += 1) {
|
while (i < layer_count) : (i += 1) {
|
||||||
var inputs: u64 = 0;
|
var inputs: u64 = 0;
|
||||||
|
|
@ -148,7 +130,6 @@ pub const Network = struct {
|
||||||
_ = try file.readAll(std.mem.asBytes(&inputs));
|
_ = try file.readAll(std.mem.asBytes(&inputs));
|
||||||
_ = try file.readAll(std.mem.asBytes(&neurons));
|
_ = try file.readAll(std.mem.asBytes(&neurons));
|
||||||
|
|
||||||
// SE è l'ultimo layer, niente sigmoide!
|
|
||||||
const is_last = (i == layer_count - 1);
|
const is_last = (i == layer_count - 1);
|
||||||
try net.addLayer(@intCast(inputs), @intCast(neurons), 0, !is_last);
|
try net.addLayer(@intCast(inputs), @intCast(neurons), 0, !is_last);
|
||||||
|
|
||||||
|
|
@ -165,8 +146,6 @@ pub const Network = struct {
|
||||||
return net;
|
return net;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- EXPORT JSON CON IMMAGINE INPUT ---
|
|
||||||
// Aggiungiamo il parametro 'input_snapshot' (può essere null se non vogliamo stampare nulla)
|
|
||||||
pub fn exportJSON(self: *Network, file_path: []const u8, epoch: usize, loss: f32, input_snapshot: ?[]const f32) !void {
|
pub fn exportJSON(self: *Network, file_path: []const u8, epoch: usize, loss: f32, input_snapshot: ?[]const f32) !void {
|
||||||
const file = try std.fs.cwd().createFile(file_path, .{});
|
const file = try std.fs.cwd().createFile(file_path, .{});
|
||||||
defer file.close();
|
defer file.close();
|
||||||
|
|
@ -181,11 +160,9 @@ pub const Network = struct {
|
||||||
|
|
||||||
try Utils.print(file, "{{\n \"epoch\": {d},\n \"loss\": {d:.6},\n", .{ epoch, loss });
|
try Utils.print(file, "{{\n \"epoch\": {d},\n \"loss\": {d:.6},\n", .{ epoch, loss });
|
||||||
|
|
||||||
// SEZIONE NUOVA: Stampiamo l'array dei pixel se presente
|
|
||||||
if (input_snapshot) |pixels| {
|
if (input_snapshot) |pixels| {
|
||||||
try file.writeAll(" \"input_pixels\": [");
|
try file.writeAll(" \"input_pixels\": [");
|
||||||
for (pixels, 0..) |p, idx| {
|
for (pixels, 0..) |p, idx| {
|
||||||
// Arrotondiamo a 2 decimali per risparmiare spazio
|
|
||||||
try Utils.print(file, "{d:.2}", .{p});
|
try Utils.print(file, "{d:.2}", .{p});
|
||||||
if (idx < pixels.len - 1) try file.writeAll(",");
|
if (idx < pixels.len - 1) try file.writeAll(",");
|
||||||
}
|
}
|
||||||
|
|
@ -197,7 +174,6 @@ pub const Network = struct {
|
||||||
for (self.layers.items, 0..) |layer, i| {
|
for (self.layers.items, 0..) |layer, i| {
|
||||||
try Utils.print(file, " {{\n \"layer_index\": {d},\n \"neurons\": {d},\n \"inputs\": {d},\n \"weights\": [", .{ i, layer.neurons_count, layer.inputs_count });
|
try Utils.print(file, " {{\n \"layer_index\": {d},\n \"neurons\": {d},\n \"inputs\": {d},\n \"weights\": [", .{ i, layer.neurons_count, layer.inputs_count });
|
||||||
|
|
||||||
// Salviamo solo un sottoinsieme dei pesi per non intasare il file
|
|
||||||
const max_w = if (layer.weights.len > 1000) 100 else layer.weights.len;
|
const max_w = if (layer.weights.len > 1000) 100 else layer.weights.len;
|
||||||
|
|
||||||
for (layer.weights[0..max_w], 0..) |w, w_idx| {
|
for (layer.weights[0..max_w], 0..) |w, w_idx| {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue