lm.Tensor is the core data structure — an N-dimensional array with automatic gradient tracking.
lm.Tensor(data, requires_grad=False, device='cpu', name=None, persistent=False)| Parameter | Type | Description |
|---|---|---|
| data | list / ndarray / scalar | The underlying data |
| requires_grad | bool | Enable gradient computation. Default False. |
| device | str | 'cpu' (Raspberry Pi optimized) |
| name | str | Memory management identifier |
| persistent | bool | Prevent automatic LRU cleanup |
Factory Functions
import lowmind as lm lm.zeros(3, 4) # shape (3,4) filled with 0 lm.ones(2, 2) # shape (2,2) filled with 1 lm.randn(10, 10) # random normal lm.rand(5, 5) # random uniform [0,1] lm.arange(0, 10, 2) # [0, 2, 4, 6, 8] lm.from_numpy(arr) # wrap a numpy array
Arithmetic Operations
a + b # addition (with autograd) a - b # subtraction a * b # element-wise multiply a / b # element-wise divide a ** 2 # power a @ b # matrix multiply -a # negation
Reduction Methods
x.sum() # scalar sum x.sum(axis=0) # axis-wise sum x.sum(axis=1, keepdims=True) # keepdims x.mean() # scalar mean x.mean(axis=(2, 3)) # tuple axis (CNN global pooling) x.max(axis=1) # row-wise max x.min() # global min
Shape Operations
x.reshape(6, 4) # reshape x.flatten(start_dim=1) # flatten from dim 1 x.transpose((0, 2, 1)) # permute axes x.T # transpose last two dims x.squeeze(axis=1) # remove size-1 dims x.unsqueeze(axis=0) # add new dim x[0] # index (gradient flows through)
Utility Properties & Methods
t.shape # shape tuple t.ndim # number of dimensions t.size # total number of elements t.dtype # numpy dtype (float32) t.item() # extract Python scalar t.numpy() # get underlying numpy array t.detach() # new tensor without grad tracking t.copy() # full copy including grad t.zero_grad() # fill grad with zeros
# Compute dy/dx at x=3 for y = x² + 2x + 1 x = lm.Tensor(3.0, requires_grad=True) y = x**2 + 2*x + 1 y.backward() print(x.grad) # 8.0 (dy/dx = 2x+2 = 8) # Gradient clipping lm.clip_grad_norm(model.parameters(), max_norm=1.0) # Disable autograd for inference (v2.1) with lm.no_grad(): out = model(X_test) # As decorator @lm.no_grad def predict(model, x): return model(x)
# All layers subclass lm.Module class MyBlock(lm.Module): def __init__(self, in_f, out_f): super().__init__() self.fc = lm.Linear(in_f, out_f) self.bn = lm.BatchNorm1d(out_f) def forward(self, x): return self.bn(self.fc(x)).relu() model.parameters() # generator of all params model.named_parameters() # generator of (name, param) model.train() # set training mode model.eval() # set eval mode (disables Dropout) model.num_parameters() # total trainable param count model.summary() # print architecture table model.state_dict() # dict of weight arrays model.load_state_dict(sd) # restore from dict model.save('/path/model.lmz') # save (gzip) model.load('/path/model.lmz') # load
lm.Linear(in_features, out_features, bias=True)Fully-connected layer. Input: (N, in_features) → Output: (N, out_features)
lm.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, bias=True)2D convolution with im2col-based backward. Input: (N, C, H, W) → Output: (N, out_channels, H', W')
conv = lm.Conv2d(3, 32, kernel_size=3, padding=1) # Input (N, 3, H, W) → Output (N, 32, H, W)
lm.BatchNorm1d(num_features) / lm.BatchNorm2d(num_features)Batch normalization with learnable gamma/beta and running stats for eval mode.
lm.MaxPool2d(kernel_size, stride=None) / lm.AvgPool2d(kernel_size)Spatial pooling. Default stride equals kernel_size. Halves H and W when kernel_size=stride=2.
lm.Dropout(p=0.5)Randomly zeros elements with probability p during training. Disabled automatically in eval mode.
lm.Embedding(num_embeddings, embedding_dim)Learnable lookup table. Input: integer indices → Output: (len(indices), embedding_dim)
lm.LSTM(input_size, hidden_size, num_layers=1, dropout=0.0) v2.1Long Short-Term Memory with multi-layer and dropout support.
lm.GRU(input_size, hidden_size, num_layers=1, dropout=0.0) v2.1Gated Recurrent Unit. Lighter than LSTM with comparable performance for many tasks.
Can be used as layer classes in Sequential or called as methods on Tensors.
# As layers in Sequential model = lm.Sequential(lm.Linear(128, 64), lm.ReLU()) # As Tensor methods x.relu() x.sigmoid() x.tanh() x.leaky_relu(alpha=0.01) x.elu(alpha=1.0) x.gelu() x.softmax(axis=-1) # Layer classes lm.ReLU() lm.LeakyReLU(negative_slope=0.01) lm.ELU() lm.GELU() lm.Sigmoid() lm.Tanh() lm.Softmax(axis=-1) lm.LogSoftmax(axis=-1)
from collections import OrderedDict # Positional model = lm.Sequential( lm.Linear(784, 256), lm.ReLU(), lm.BatchNorm1d(256), lm.Dropout(0.3), lm.Linear(256, 10), ) # Named (OrderedDict) model = lm.Sequential(OrderedDict([ ('fc1', lm.Linear(784, 256)), ('relu', lm.ReLU()), ('fc2', lm.Linear(256, 10)), ])) print(model) # shows architecture model.num_parameters() # total trainable params
All loss functions return a scalar Tensor with requires_grad=True.
# Cross Entropy — logits (N,C), targets (N,) int loss = lm.cross_entropy_loss(logits, targets) loss = lm.cross_entropy_loss(logits, targets, reduction='sum') # Binary Cross Entropy loss = lm.binary_cross_entropy_loss(output, targets) loss = lm.binary_cross_entropy_loss(logits, targets, from_logits=True) # MSE — regression loss = lm.mse_loss(predictions, targets) # MAE — outlier-robust loss = lm.mae_loss(predictions, targets) # Huber (smooth L1) — quadratic near 0, linear for outliers loss = lm.huber_loss(predictions, targets, delta=1.0) # NLL — use after LogSoftmax log_probs = lm.LogSoftmax()(logits) loss = lm.nll_loss(log_probs, targets)
# Standard interface for all optimizers optimizer.zero_grad() # reset gradients loss.backward() # compute gradients optimizer.step() # update weights # SGD with Nesterov momentum opt = lm.SGD(model.parameters(), lr=0.01, momentum=0.9, weight_decay=1e-4, nesterov=True) # Adam opt = lm.Adam(model.parameters(), lr=1e-3, betas=(0.9, 0.999), amsgrad=False) # AdamW (decoupled weight decay) opt = lm.AdamW(model.parameters(), lr=1e-3, weight_decay=0.01) # RMSprop opt = lm.RMSprop(model.parameters(), lr=1e-3, alpha=0.99) # AdaGrad opt = lm.AdaGrad(model.parameters(), lr=0.01) # Parameter groups (v2.1) opt = lm.Adam([ {'params': backbone.parameters(), 'lr': 1e-4}, {'params': head.parameters(), 'lr': 1e-3}, ])
# StepLR — decay every N epochs scheduler = lm.StepLR(optimizer, step_size=10, gamma=0.5) # CosineAnnealingLR — smooth decay scheduler = lm.CosineAnnealingLR(optimizer, T_max=50, eta_min=1e-6) # ReduceLROnPlateau — reduce when stuck scheduler = lm.ReduceLROnPlateau( optimizer, mode='min', patience=5, factor=0.5) scheduler.step(val_loss) # pass the metric # MultiStepLR scheduler = lm.MultiStepLR(optimizer, milestones=[30, 60, 90], gamma=0.1) # LinearWarmupLR scheduler = lm.LinearWarmupLR(optimizer, warmup_steps=1000, target_lr=1e-3) # CyclicLR — call per batch scheduler = lm.CyclicLR(optimizer, base_lr=1e-4, max_lr=1e-1, step_size=2000, mode='triangular') # LR Finder (v2.1) — auto-detect optimal LR finder = lm.LRFinder(model, optimizer, loss_fn) best_lr = finder.find(train_loader)
# TensorDataset ds = lm.TensorDataset(X_train, y_train) X, y = ds[0] # first sample # Custom Dataset class MyDataset(lm.Dataset): def __len__(self): return len(self.X) def __getitem__(self, idx): return self.X[idx], self.y[idx] # DataLoader loader = lm.DataLoader(ds, batch_size=64, shuffle=True, drop_last=False) for X_b, y_b in loader: pass # train_test_split X_tr, X_val, y_tr, y_val = lm.train_test_split( X, y, test_size=0.2, shuffle=True, seed=42 ) # Data Transforms (v2.1) transform = lm.Compose([ lm.Normalize(mean=0.5, std=0.5), lm.RandomHorizontalFlip(p=0.5), lm.RandomCrop(size=28), lm.GaussianNoise(std=0.1), ])
# All metrics accept Tensors or numpy arrays # Classification lm.accuracy(predictions, targets) lm.top_k_accuracy(logits, targets, k=5) lm.precision(logits, targets, num_classes=10) lm.recall(logits, targets, num_classes=10) lm.f1_score(logits, targets, num_classes=10) lm.confusion_matrix(logits, targets) # → (C, C) ndarray # average='macro' (default), 'micro', or 'none' (per class) per_class_f1 = lm.f1_score(logits, targets, num_classes=10, average='none') # Regression lm.r2_score(predictions, targets) lm.mean_squared_error(predictions, targets) lm.mean_absolute_error(predictions, targets)
trainer = lm.Trainer( model=model, optimizer=lm.Adam(model.parameters(), lr=1e-3), loss_fn=lm.cross_entropy_loss, callbacks=[lm.EarlyStopping(patience=10)], clip_grad=1.0, # gradient norm clipping grad_accum_steps=4, # gradient accumulation (v2.1) verbose=1, # print every N epochs ) # Train history = trainer.fit(train_loader, val_loader, epochs=100) # → {'train_loss': [...], 'val_loss': [...], 'val_acc': [...]} # Evaluate val_loss, val_acc = trainer.evaluate(val_loader) # Inference preds = trainer.predict(X_test) # class indices probs = trainer.predict_proba(X_test) # softmax probs (v2.1)
# EarlyStopping lm.EarlyStopping(patience=10, min_delta=1e-4, mode='min', verbose=True) # ModelCheckpoint lm.ModelCheckpoint(filepath='/tmp/best.lmz', monitor='val_loss', mode='min', save_best_only=True, verbose=True) # LRSchedulerCallback sched = lm.ReduceLROnPlateau(optimizer, patience=5) lm.LRSchedulerCallback(sched, monitor='val_loss') # History history_cb = lm.History() # After training: history_cb.history['train_loss']
# MicroMLP — tabular/flat data model = lm.MicroMLP( input_size=784, hidden_sizes=[256, 128], output_size=10, dropout=0.3, ) # MicroCNN — small images (32×32) model = lm.MicroCNN( in_channels=3, # 3=RGB, 1=grayscale num_classes=10, input_size=32, dropout=0.2, ) # TinyResNet — residual connections model = lm.TinyResNet( in_channels=3, num_classes=10, input_size=32, base_filters=16, # use 8 for very constrained devices )
# Configure memory limit lm.configure_memory(max_mb=256) # System health monitor monitor = lm.SystemMonitor() monitor.print_status() # print CPU/RAM/temp score = monitor.health_score() # 0-100 stats = monitor.get_stats() # dict of all stats # Trace memory for a code block with lm.memory_trace("Forward Pass"): out = model(X) # Memory manager lm.memory_manager.optimize_for_inference() lm.memory_manager.free_unused() info = lm.memory_manager.get_memory_info() # Model Profiler (v2.1) profiler = lm.ModelProfiler(model) profiler.profile(input_shape=(1, 784)) # params, FLOPs, memory
import lowmind.init as init # Apply to individual parameter init.xavier_uniform_(layer.weight) init.xavier_normal_(layer.weight) init.kaiming_uniform_(layer.weight, mode='fan_in') init.kaiming_normal_(layer.weight) init.orthogonal_(layer.weight) init.normal_(layer.weight, mean=0, std=0.01) init.zeros_(layer.bias) # Apply to whole module recursively init.init_module(model, method='xavier_uniform')