Skip to content

Commit f08df37

Browse files
committed
feat: Add comprehensive model persistence system and cleanup examples
1 parent d1e820d commit f08df37

9 files changed

Lines changed: 528 additions & 292 deletions

File tree

.gitignore

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,32 @@
1-
# Rust build artifacts
1+
# Build artifacts
22
/target/
3-
**/*.rs.bk
4-
Cargo.lock
3+
/Cargo.lock
54

65
# IDE files
76
.vscode/
87
.idea/
8+
*.swp
9+
*.swo
910

10-
# OS generated files
11+
# Model files (temporary training outputs)
12+
models/
13+
*.model
14+
*.json
15+
16+
# OS files
1117
.DS_Store
18+
Thumbs.db
19+
20+
# OS generated files
1221
.DS_Store?
1322
._*
1423
.Spotlight-V100
1524
.Trashes
1625
ehthumbs.db
17-
Thumbs.db
1826

1927
# Temporary files
2028
*~
2129
*.tmp
22-
*.swp
23-
*.swo
2430

2531
# Coverage reports
2632
/target/debug/deps/*

Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ ndarray = "0.15"
1616
ndarray-rand = "0.14"
1717
rand = { version = "0.8", features = ["std"] }
1818
rand_distr = "0.4"
19+
serde = { version = "1.0", features = ["derive"] }
20+
serde_json = "1.0"
21+
bincode = "1.3"
22+
chrono = { version = "0.4", features = ["serde"] }
1923

2024
[[example]]
2125
name = "training_example"
@@ -38,5 +42,5 @@ name = "real_data_example"
3842
path = "examples/real_data_example.rs"
3943

4044
[[example]]
41-
name = "simple_text_generation"
42-
path = "examples/simple_text_generation.rs"
45+
name = "model_inspection"
46+
path = "examples/model_inspection.rs"

examples/model_inspection.rs

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
use rust_lstm::{LSTMNetwork, ModelMetadata, PersistentModel};
2+
use std::fs;
3+
4+
fn main() -> Result<(), Box<dyn std::error::Error>> {
5+
println!("🔍 LSTM Model Internal Structure Inspection");
6+
println!("============================================\n");
7+
8+
// Create a simple model for inspection
9+
println!("🏗️ Creating a simple LSTM model...");
10+
let input_size = 3; // e.g., word embeddings of size 3
11+
let hidden_size = 4; // 4 hidden units
12+
let num_layers = 2; // 2-layer network
13+
14+
let network = LSTMNetwork::new(input_size, hidden_size, num_layers);
15+
16+
// Save it to examine the structure
17+
let metadata = ModelMetadata {
18+
model_name: "Language Model Example".to_string(),
19+
version: "1.0.0".to_string(),
20+
created_at: chrono::Utc::now().to_rfc3339(),
21+
input_size,
22+
hidden_size,
23+
num_layers,
24+
total_epochs: 0,
25+
final_loss: None,
26+
description: Some("Example LSTM for language modeling inspection".to_string()),
27+
};
28+
29+
std::fs::create_dir_all("models")?;
30+
network.save("models/inspection_model.json", metadata.clone())?;
31+
32+
// Now inspect what's inside
33+
println!("📊 WHAT'S STORED IN AN LSTM MODEL:");
34+
println!("=================================\n");
35+
36+
inspect_architecture(&network);
37+
inspect_parameters(&network);
38+
inspect_file_contents()?;
39+
explain_llm_context();
40+
calculate_parameter_count(&network);
41+
42+
Ok(())
43+
}
44+
45+
fn inspect_architecture(network: &LSTMNetwork) {
46+
println!("🏗️ NETWORK ARCHITECTURE:");
47+
println!(" 📐 Input Size: {} (size of input vectors, e.g., word embeddings)", network.input_size);
48+
println!(" 🧠 Hidden Size: {} (memory capacity per layer)", network.hidden_size);
49+
println!(" 📚 Number of Layers: {} (depth of the network)", network.num_layers);
50+
println!(" 🔄 Total Cells: {} LSTM cells", network.num_layers);
51+
println!();
52+
}
53+
54+
fn inspect_parameters(network: &LSTMNetwork) {
55+
println!("⚙️ PARAMETERS STORED FOR EACH LSTM CELL:");
56+
println!("==========================================");
57+
58+
for (layer_idx, cell) in network.get_cells().iter().enumerate() {
59+
println!("📦 Layer {} LSTM Cell:", layer_idx);
60+
61+
// Each LSTM cell contains 4 gates: input, forget, cell, output
62+
println!(" 🔢 w_ih (Input→Hidden weights): {}×{} = {} parameters",
63+
cell.w_ih.shape()[0], cell.w_ih.shape()[1],
64+
cell.w_ih.shape()[0] * cell.w_ih.shape()[1]);
65+
println!(" • Controls how input affects all 4 gates (i,f,g,o)");
66+
67+
println!(" 🔄 w_hh (Hidden→Hidden weights): {}×{} = {} parameters",
68+
cell.w_hh.shape()[0], cell.w_hh.shape()[1],
69+
cell.w_hh.shape()[0] * cell.w_hh.shape()[1]);
70+
println!(" • Controls how previous hidden state affects gates");
71+
72+
println!(" ➕ b_ih (Input biases): {}×{} = {} parameters",
73+
cell.b_ih.shape()[0], cell.b_ih.shape()[1],
74+
cell.b_ih.shape()[0] * cell.b_ih.shape()[1]);
75+
println!(" • Bias terms for input transformations");
76+
77+
println!(" ➕ b_hh (Hidden biases): {}×{} = {} parameters",
78+
cell.b_hh.shape()[0], cell.b_hh.shape()[1],
79+
cell.b_hh.shape()[0] * cell.b_hh.shape()[1]);
80+
println!(" • Bias terms for hidden transformations");
81+
82+
println!(" 📏 Hidden Size: {}", cell.hidden_size);
83+
println!();
84+
}
85+
86+
println!("🧮 WHAT THESE PARAMETERS REPRESENT:");
87+
println!(" 🚪 4 Gates per cell (each gets 1/4 of the weights):");
88+
println!(" • Input Gate (i): Decides what new information to store");
89+
println!(" • Forget Gate (f): Decides what to discard from memory");
90+
println!(" • Cell Gate (g): Creates candidate values to add");
91+
println!(" • Output Gate (o): Decides what parts of memory to output");
92+
println!();
93+
}
94+
95+
fn inspect_file_contents() -> Result<(), Box<dyn std::error::Error>> {
96+
println!("📄 ACTUAL FILE CONTENTS:");
97+
println!("========================");
98+
99+
let file_size = fs::metadata("models/inspection_model.json")?.len();
100+
println!(" 📊 File size: {} bytes", file_size);
101+
102+
// Show a sample of the JSON structure
103+
let content = fs::read_to_string("models/inspection_model.json")?;
104+
let lines: Vec<&str> = content.lines().collect();
105+
106+
println!(" 📋 JSON Structure (first 30 lines):");
107+
for (i, line) in lines.iter().enumerate().take(30) {
108+
println!(" {}: {}", i + 1, line);
109+
}
110+
111+
if lines.len() > 30 {
112+
println!(" ... ({} more lines)", lines.len() - 30);
113+
}
114+
println!();
115+
116+
Ok(())
117+
}
118+
119+
fn explain_llm_context() {
120+
println!("🤖 FOR LARGE LANGUAGE MODELS (LLMs):");
121+
println!("=====================================");
122+
println!("If you trained an LSTM-based LLM, the model would store:");
123+
println!();
124+
125+
println!("📚 LEARNED LANGUAGE PATTERNS:");
126+
println!(" • Word relationships and dependencies");
127+
println!(" • Grammar and syntax patterns");
128+
println!(" • Semantic associations between concepts");
129+
println!(" • Long-term dependencies in text");
130+
println!();
131+
132+
println!("🧠 HOW PATTERNS ARE ENCODED:");
133+
println!(" • w_ih weights: How input words influence gates");
134+
println!(" - Input gate: Which words trigger memory updates");
135+
println!(" - Forget gate: Which contexts cause forgetting");
136+
println!(" - Cell gate: What new information words contribute");
137+
println!(" - Output gate: What words trigger specific outputs");
138+
println!();
139+
140+
println!(" • w_hh weights: How previous context influences current processing");
141+
println!(" - Encodes how past words affect current decisions");
142+
println!(" - Captures sequential dependencies and patterns");
143+
println!(" - Stores grammatical and syntactic relationships");
144+
println!();
145+
146+
println!(" • Biases: Default tendencies and adjustments");
147+
println!(" - Language-specific biases (e.g., word order preferences)");
148+
println!(" - Default gate behaviors for the language");
149+
println!();
150+
151+
println!("📖 EXAMPLE: Training on 'The cat sat on the mat':");
152+
println!(" • Weights learn that 'The' often precedes nouns");
153+
println!(" • 'cat' + 'sat' pattern gets encoded in w_hh");
154+
println!(" • 'on the' creates strong forward dependency");
155+
println!(" • Sentence boundaries trigger memory resets");
156+
println!();
157+
158+
println!("💾 METADATA ALSO STORED:");
159+
println!(" • Training information (epochs, loss, date)");
160+
println!(" • Architecture details (vocabulary size, embedding dim)");
161+
println!(" • Model version and description");
162+
println!(" • Performance metrics");
163+
println!();
164+
}
165+
166+
fn calculate_parameter_count(network: &LSTMNetwork) {
167+
println!("🔢 TOTAL PARAMETER COUNT:");
168+
println!("=========================");
169+
170+
let mut total_params = 0;
171+
172+
for (layer_idx, cell) in network.get_cells().iter().enumerate() {
173+
let w_ih_params = cell.w_ih.len();
174+
let w_hh_params = cell.w_hh.len();
175+
let b_ih_params = cell.b_ih.len();
176+
let b_hh_params = cell.b_hh.len();
177+
178+
let layer_params = w_ih_params + w_hh_params + b_ih_params + b_hh_params;
179+
total_params += layer_params;
180+
181+
println!(" Layer {}: {} parameters", layer_idx, layer_params);
182+
}
183+
184+
println!(" 🎯 Total: {} trainable parameters", total_params);
185+
println!(" 💾 Memory: ~{:.1} KB (f64 precision)", total_params as f64 * 8.0 / 1024.0);
186+
println!();
187+
188+
println!("📊 COMPARISON TO MODERN LLMs:");
189+
println!(" • GPT-3: ~175 billion parameters");
190+
println!(" • This LSTM: {} parameters", total_params);
191+
println!(" • Ratio: This model is {:.0}x smaller", 175_000_000_000.0 / total_params as f64);
192+
println!();
193+
194+
println!("💡 SCALING FOR LLMs:");
195+
println!(" For a production LSTM LLM you might use:");
196+
println!(" • Input size: 512-1024 (embedding dimension)");
197+
println!(" • Hidden size: 1024-4096 (memory capacity)");
198+
println!(" • Layers: 6-12 (depth for complex patterns)");
199+
println!(" • Vocabulary: 50,000-100,000 tokens");
200+
201+
// Calculate a realistic LLM size
202+
let llm_input = 512;
203+
let llm_hidden = 2048;
204+
let llm_layers = 8;
205+
206+
let llm_params_per_layer = (llm_input * llm_hidden * 4) + (llm_hidden * llm_hidden * 4) + (llm_hidden * 4 * 2);
207+
let llm_total_params = llm_params_per_layer * llm_layers;
208+
209+
println!(" Example LSTM LLM ({} layers, {} hidden): ~{:.1}M parameters",
210+
llm_layers, llm_hidden, llm_total_params as f64 / 1_000_000.0);
211+
}

examples/peephole.rs

Lines changed: 0 additions & 23 deletions
This file was deleted.

0 commit comments

Comments
 (0)