Installation & Setup
NeuNet is built with pure Python and NumPy. Here's how to get started:
Prerequisites
pip install numpy matplotlib plotly networkx pandas
Project Structure
NeuNet/
├── src/
│ ├── models/
│ │ └── neural_network.py # Core NeuralNetwork class
│ ├── layers/
│ │ ├── core.py # Dense layer implementation
│ │ ├── activations.py # Activation functions
│ │ ├── regularization.py # BatchNorm, Dropout
│ │ ├── losses.py # Loss functions
│ │ ├── optimisers.py # SGD, Adam optimizers
│ │ └── dataset.py # Data generation utilities
│ ├── utils/
│ │ ├── metrics.py # Performance metrics
│ │ ├── network_data.py # Network export utilities
│ │ └── Visualization.py # Network visualization
│ └── main.py # Example implementation
└── docs/ # Documentation
Basic Usage
Creating your first neural network with NeuNet is straightforward:
from src.models.neural_network import NeuralNetwork
from src.layers.core import Dense
from src.layers.activations import ReLU, Softmax
from src.layers.losses import CategoricalCrossentropy
# Create a new neural network
model = NeuralNetwork()
# Add layers
model.add(Dense(2, 64, learning_rate=0.01)) # Input: 2, Output: 64
model.add(ReLU()) # ReLU activation
model.add(Dense(64, 32, learning_rate=0.01)) # Hidden layer
model.add(ReLU()) # ReLU activation
model.add(Dense(32, 3, learning_rate=0.01)) # Output layer: 3 classes
model.add(Softmax()) # Softmax for classification
# Set loss function
model.set_loss(CategoricalCrossentropy())
# Train the model (X, Y are your data)
model.train(X, Y, epochs=100, batch_size=32)
# Make predictions
predictions = model.predict(X)
probabilities = model.predict_proba(X)
Network Architecture
The NeuralNetwork class is the core component that orchestrates the entire training process:
Key Features
- Layer Management: Add layers sequentially using the
add()
method - Forward Pass: Automatic propagation through all layers
- Backward Pass: Gradient computation and backpropagation
- Training Loop: Built-in batch processing and early stopping
- History Tracking: Loss and accuracy logging during training
Training Process
# The training method handles:
# 1. Data shuffling for each epoch
# 2. Batch processing
# 3. Forward and backward passes
# 4. Early stopping based on loss improvement
# 5. Learning rate decay
# 6. Performance logging
model.train(
X, Y, # Training data
epochs=500, # Maximum epochs
batch_size=32, # Batch size (0 for full batch)
patience=30, # Early stopping patience
verbose=True # Print training progress
)
Layer Types
Dense Layer
The fully connected layer is the core building block of the network:
from src.layers.core import Dense
# Create a dense layer
layer = Dense(
n_inputs=64, # Number of input features
n_neurons=32, # Number of output neurons
learning_rate=0.01, # Learning rate
decay_rate=0.02, # Learning rate decay
momentum=0.9, # Momentum for SGD
optimizer='adam' # 'adam' or None for SGD
)
Weight Initialization
The Dense layer uses He initialization for better gradient flow:
# He initialization for ReLU networks
self.weights = np.random.randn(n_inputs, n_neurons) * np.sqrt(2. / n_inputs)
self.biases = np.zeros((1, n_neurons))
Forward and Backward Pass
# Forward pass: y = Wx + b
output = np.dot(inputs, weights) + biases
# Backward pass: compute gradients
dW = np.dot(inputs.T, dvalues)
db = np.sum(dvalues, axis=0, keepdims=True)
dinputs = np.dot(dvalues, weights.T)
Activation Functions
NeuNet provides a complete set of activation functions with proper gradient computation:
ReLU (Recommended for Hidden Layers)
from src.layers.activations import ReLU
# ReLU: max(0, x)
relu = ReLU()
output = relu.forward(inputs) # Forward pass
gradients = relu.backward(dvalues) # Backward pass
Leaky ReLU
from src.layers.activations import LeakyReLU
# Leaky ReLU: max(αx, x) where α=0.01
leaky_relu = LeakyReLU(alpha=0.01)
Softmax (For Classification Output)
from src.layers.activations import Softmax
# Softmax: e^xi / Σe^xj (probability distribution)
softmax = Softmax()
# Includes numerical stability and proper Jacobian computation
Other Activations
from src.layers.activations import Tanh, Sigmoid
# Tanh: (-1, 1) range
tanh = Tanh()
# Sigmoid: (0, 1) range with numerical stability
sigmoid = Sigmoid()
Optimizers
NeuNet includes two sophisticated optimizers for efficient training:
SGD with Momentum
# SGD with momentum helps accelerate gradients in relevant directions
# and dampens oscillations
# Velocity update:
velocity = momentum * velocity - learning_rate * gradient
# Parameter update:
weights += velocity
Adam Optimizer (Recommended)
# Adam combines momentum and adaptive learning rates
# Includes bias correction for better early training
# Momentum estimates:
m = β₁ * m + (1 - β₁) * gradient
v = β₂ * v + (1 - β₂) * gradient²
# Bias correction:
m_corrected = m / (1 - β₁^t)
v_corrected = v / (1 - β₂^t)
# Parameter update:
weights -= learning_rate * m_corrected / (√v_corrected + ε)
Usage Example
# Use Adam optimizer (recommended for most cases)
model.add(Dense(64, 32, learning_rate=0.002, optimizer='adam'))
# Use SGD with momentum (more traditional approach)
model.add(Dense(64, 32, learning_rate=0.01, optimizer=None, momentum=0.9))
Regularization Techniques
Prevent overfitting with built-in regularization methods:
Batch Normalization
from src.layers.regularization import BatchNormalization
# Normalize inputs to have zero mean and unit variance
batch_norm = BatchNormalization(
epsilon=1e-5, # Small constant for numerical stability
momentum=0.9 # Running statistics momentum
)
# Handles training vs inference modes automatically
# Maintains running mean and variance for inference
Dropout
from src.layers.regularization import Dropout
# Randomly zero out neurons during training
dropout = Dropout(rate=0.1) # Drop 10% of neurons
# Automatically handles training vs inference:
# - Training: applies random masking with scaling
# - Inference: no dropout applied
L1/L2 Regularization
from src.layers.losses import CategoricalCrossentropy
# Add L2 regularization to loss function
loss = CategoricalCrossentropy(
regularization_l2=0.0001, # L2 penalty coefficient
regularization_l1=0.0 # L1 penalty coefficient
)
# Regularization is applied during gradient computation
# Helps prevent overfitting by penalizing large weights
Training Process
The training loop includes several advanced features:
Early Stopping
# Early stopping prevents overfitting
model.train(
X, Y,
epochs=1000, # Maximum epochs
patience=30, # Stop if no improvement for 30 epochs
batch_size=32
)
# Monitors validation loss and stops when:
# 1. Loss doesn't improve for 'patience' epochs
# 2. Minimum 100 epochs have passed
Learning Rate Decay
# Automatic exponential learning rate decay
# learning_rate = initial_lr * exp(-decay_rate * epoch)
layer = Dense(64, 32,
learning_rate=0.01, # Initial learning rate
decay_rate=0.02 # Decay coefficient
)
Batch Processing
# Efficient batch processing with data shuffling
model.train(
X, Y,
batch_size=32, # Process 32 samples at once
# batch_size=0 # Use full batch (all data)
)
# Each epoch:
# 1. Shuffles training data
# 2. Processes data in batches
# 3. Updates parameters after each batch
Training History
# Access training history
history = model.history
print(f"Loss history: {history['loss']}")
print(f"Accuracy history: {history['accuracy']}")
# Logged every 20 epochs during training
Model Evaluation
Comprehensive evaluation metrics are available:
Basic Metrics
from src.utils.metrics import calculate_accuracy
# Get predictions
predictions = model.predict(X_test) # Class predictions
probabilities = model.predict_proba(X_test) # Class probabilities
# Calculate accuracy
accuracy = calculate_accuracy(y_true, probabilities)
print(f"Accuracy: {accuracy:.4f}")
Advanced Metrics
from src.utils.metrics import confusion_matrix, precision_recall_f1
# Confusion matrix
cm = confusion_matrix(y_true, predictions, num_classes=3)
print("Confusion Matrix:")
print(cm)
# Precision, Recall, F1-score
precision, recall, f1 = precision_recall_f1(y_true, predictions, num_classes=3)
print(f"Precision: {precision}")
print(f"Recall: {recall}")
print(f"F1-score: {f1}")
Visualization
NeuNet includes powerful visualization tools:
Network Visualization
from src.utils.network_data import export_network
from src.utils.Visualization import network_visualization
# Export network structure (first 4 dense layers)
dense_layers = [layer for layer in model.layers if hasattr(layer, 'weights')]
export_network(*dense_layers[:4])
# Create interactive visualization
fig = network_visualization("src/utils/network_data.json")
# Saves as 'neural_network_visualization.html'
Dataset Visualization
from src.layers.dataset import create_data
# Create and visualize synthetic dataset
X, Y = create_data(samples=100, classes=3, plot=True)
# Automatically saves scatter plot as 'scatter_plot.png'
Complete Examples
Classification on Synthetic Data
import sys
import os
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from src.models.neural_network import NeuralNetwork
from src.layers.core import Dense
from src.layers.activations import ReLU, Softmax
from src.layers.regularization import BatchNormalization, Dropout
from src.layers.losses import CategoricalCrossentropy
from src.layers.dataset import create_data
from src.utils.metrics import calculate_accuracy
# Create synthetic dataset
X, Y = create_data(samples=100, classes=3, noise=0.2)
# Build the network
model = NeuralNetwork()
# Layer 1: Input -> 128 neurons
model.add(Dense(2, 128, learning_rate=0.002, optimizer='adam'))
model.add(BatchNormalization())
model.add(ReLU())
model.add(Dropout(0.1))
# Layer 2: 128 -> 64 neurons
model.add(Dense(128, 64, learning_rate=0.002, optimizer='adam'))
model.add(BatchNormalization())
model.add(ReLU())
model.add(Dropout(0.1))
# Layer 3: 64 -> 32 neurons
model.add(Dense(64, 32, learning_rate=0.002, optimizer='adam'))
model.add(BatchNormalization())
model.add(ReLU())
model.add(Dropout(0.1))
# Output layer: 32 -> 3 classes
model.add(Dense(32, 3, learning_rate=0.002, optimizer='adam'))
model.add(Softmax())
# Set loss function with L2 regularization
model.set_loss(CategoricalCrossentropy(regularization_l2=0.0001))
# Train the model
print("Training neural network...")
model.train(X, Y, epochs=500, batch_size=32, patience=30)
# Evaluate the model
predictions = model.predict_proba(X)
accuracy = calculate_accuracy(Y, predictions)
print(f"\nFinal Accuracy: {accuracy:.4f}")
# Export and visualize the network
from src.utils.network_data import export_network
dense_layers = [layer for layer in model.layers if hasattr(layer, 'weights')]
if len(dense_layers) >= 4:
export_network(*dense_layers[:4])
print("Network exported successfully!")
Custom Training Loop
# For more control, you can implement custom training
def custom_training_loop(model, X, Y, epochs=100):
for epoch in range(epochs):
# Forward pass
output = model.forward(X, training=True)
# Calculate loss
loss = model.loss_function.calculate(output, Y)
# Backward pass
loss_gradient = model.loss_function.backward(output, Y)
model.backward(loss_gradient, epoch)
# Log progress
if epoch % 20 == 0:
val_output = model.forward(X, training=False)
accuracy = calculate_accuracy(Y, val_output)
print(f"Epoch {epoch}, Loss: {loss:.6f}, Accuracy: {accuracy:.4f}")
# Use custom training
custom_training_loop(model, X, Y, epochs=200)