A lightweight deep learning library for Python featuring a fast C backend, autograd, neural network layers, optimizers, and tensor operations.
Project description
mingrad
Motor de autograd minimalista escrito en C puro, con bindings nativos a Python vía Python.h (sin ctypes, sin pybind11, sin Cython, sin NumPy como dependencia). Piénsalo como una base tipo PyTorch pero mucho más simple, pensada para servir de punto de partida para entrenar tus propios modelos sin depender de un framework grande.
Incluye tensores N-dimensionales, autograd con grafo dinámico, capas Linear, Conv2D y Conv3D, activaciones, losses (MSE, CrossEntropy), y un optimizador SGD.
Por qué existe
Para tener una base propia, entendible de punta a punta, sobre la que entrenar modelos (incluyendo datos volumétricos/3D) sin la complejidad interna de PyTorch/TensorFlow. En benchmarks propios contra NumPy+BLAS, mingrad resultó consistentemente más rápido (ver Rendimiento) gracias a un diseño de acceso a memoria cache-friendly, sin necesidad de escribir SIMD a mano.
Instalación
Requiere un compilador de C (gcc/clang) y headers de desarrollo de Python (python3-dev / python3-devel según tu distro).
pip install mingrad
O desde el código fuente:
git clone <repo>
cd mingrad
pip install .
No hay dependencias de Python en tiempo de ejecución (ni NumPy, ni nada más).
Uso básico
Tensores y autograd
import mingrad as mg
mg.init(64 * 1024 * 1024) # reserva 64 MB de pool; llamar una sola vez, al principio
x = mg.Tensor([1.0, 2.0, 3.0, 4.0], shape=(2, 2), requires_grad=True)
y = mg.Tensor([5.0, 6.0, 7.0, 8.0], shape=(2, 2), requires_grad=True)
z = (x + y).sum()
z.backward()
print(x.grad) # [1.0, 1.0, 1.0, 1.0]
mg.shutdown()
Operaciones disponibles sobre Tensor: +, -, *, @ (matmul), .relu(), .sigmoid(), .tanh(), .softmax(), .sum(), .mean(), .mse_loss(target), .cross_entropy_loss(targets).
Entrenar un MLP
import mingrad as mg
mg.init(64 * 1024 * 1024)
# datos de ejemplo: X (lista plana, row-major), Y (índices de clase)
X, Y, N = load_my_dataset() # tu propio dataset
model = mg.Model()
model.add_linear(in_features=2, out_features=16).add_relu()
model.add_linear(in_features=16, out_features=3)
opt = mg.SGD(model.parameters(), lr=0.1)
for epoch in range(200):
x = mg.Tensor(X, shape=(N, 2))
logits = model(x)
loss = logits.cross_entropy_loss(Y)
model.zero_grad()
loss.backward()
opt.step()
if epoch % 20 == 0:
print(f"epoch {epoch}: loss={loss.data[0]:.4f}")
mg.shutdown()
Conv2D (imágenes)
model = mg.Model()
model.add_conv2d(in_channels=3, out_channels=16, kernel_size=3, stride=1, padding=1)
model.add_relu()
# input: (batch, in_channels, height, width), row-major
x = mg.Tensor(image_data, shape=(batch, 3, 32, 32))
out = model(x) # (batch, 16, 32, 32)
Conv3D (datos volumétricos)
model = mg.Model()
model.add_conv3d(in_channels=1, out_channels=8, kernel_size=3, stride=1, padding=1)
model.add_relu()
# input: (batch, in_channels, depth, height, width), row-major
x = mg.Tensor(volume_data, shape=(batch, 1, 16, 16, 16))
out = model(x) # (batch, 8, 16, 16, 16)
kernel_size, stride y padding aceptan un entero (mismo valor en todos los ejes) o una tupla explícita: kernel_size=(3, 3) para Conv2D, kernel_size=(3, 3, 3) para Conv3D.
Leer datos y gradientes
t.shape # tupla de ints
t.data # copia de los datos como list[float]
t.grad # copia del gradiente como list[float] (requiere requires_grad=True y backward() ya llamado)
.data y .grad devuelven copias en cada acceso, no vistas — no hay interoperabilidad directa con NumPy/arrays en esta versión (ver limitaciones).
Rendimiento
mingrad usa un pool de memoria global (bump allocator, sin malloc/free por operación) y patrones de acceso a memoria cache-friendly (acceso contiguo, evitando iterar por columnas), auto-vectorizado por el compilador con -O3. No usa SIMD manual ni multithreading.
Benchmarks propios (forward + backward, batch representativo), comparado contra NumPy/BLAS puro y contra nnetflow (motor de autograd en Python+NumPy):
| Escenario | mingrad vs referencia Python/NumPy |
|---|---|
| MLP denso (64→128→10), entrenamiento completo | ~7.8× más rápido |
| Conv2D (batch=8, 3×32×32→16 canales) | ~6.4× más rápido |
| Conv3D (batch=4, 2×16³→8 canales) | ~1.8× más rápido |
Los números exactos dependen de tu CPU y del tamaño de tus tensores; estos son solo un punto de referencia obtenido durante el desarrollo, no una garantía.
Sobre -march=native: por defecto, este paquete se compila con -O3 sin -march=native. Es una decisión deliberada, no un descuido: durante el desarrollo confirmamos que -march=native (con AVX-512) causaba una regresión de rendimiento de más de 4× específicamente en Conv3D en el hardware de prueba, además del riesgo general de que un binario compilado con -march=native puede crashear (SIGILL) si se ejecuta en una CPU distinta a la que compiló. Si sabés que tu caso de uso se beneficia y tu hardware de build/deploy es el mismo, podés recompilar vos mismo con esa flag — pero medí antes y después, no asumas que es una mejora.
Limitaciones
Léelas antes de usar esto en algo importante.
-
El pool de memoria no libera tensores individuales. No hay recolector de basura ni referencia-conteo por tensor: cada tensor que creás (incluyendo resultados intermedios de operaciones) ocupa espacio en el pool hasta que llamás
mg.reset_pool()omg.shutdown(). Un loop de entrenamiento largo sin resetear el pool eventualmente lo agota. -
Si el pool se queda sin memoria, el proceso aborta (
SIGABRT), no lanza una excepción Python. Esto es intencional en el motor en C (preferimos fallar ruidosamente a corromper memoria), pero significa que no podés capturarlo contry/except— tu programa Python entero termina. Dimensionámg.init(pool_bytes)con margen, y en producción considerá correr el entrenamiento en un subproceso si necesitás aislar este tipo de fallo. -
mg.reset_pool()invalida todos los handles existentes sin avisar, y usar un handle viejo después de un reset puede devolver datos de OTRO tensor en silencio, sin ningún error. Esto es una consecuencia de cómo se reciclan los índices de handles al hacer reset — no es un caso raro, lo reproducimos fácilmente durante el desarrollo. Nunca conserves una referencia a unTensor/Modelcreado antes de unreset_pool(). Si necesitás resetear memoria entre epochs, hacelo solo cuando no haya ningún tensor vivo que te importe (por ejemplo, recreando el modelo también), o simplemente usá un pool lo bastante grande y no resetees. -
No es thread-safe. El estado interno (tabla de handles, último error, el pool mismo) es completamente global sin ningún lock. Usar mingrad desde más de un thread de Python al mismo tiempo es una condición de carrera, aunque el GIL de Python evite corrupción de memoria a nivel de bytecode individual.
-
.datay.graddevuelven copias en listas Python planas, no arrays de NumPy ni vistas de memoria. Para tensores grandes esto tiene un costo notable de conversión en cada lectura. No hay soporte de__array_interface__ni buffer protocol en esta versión. -
Sin GPU. Todo corre en CPU.
-
Sin serialización de modelos. No hay
save()/load(); si necesitás persistir un modelo entrenado, tenés que extraermodel.parameters()(cada uno con.data) y guardarlos vos mismo (por ejemplo a JSON), y reconstruir el modelo + cargar los datos a mano al volver a levantarlo. -
Sin batching automático, sin DataLoader, sin manejo de datasets. Vos armás los batches y se los pasás como listas planas.
-
Modelsolo cubre arquitecturas secuenciales (una capa tras otra). No hay soporte para ramas, skip-connections (tipo ResNet), o cualquier grafo que no sea una cadena lineal. Para eso hay que operar directamente sobreTensory construir el grafo a mano con las operaciones sueltas (+,@, etc.). -
Compilación probada en Linux con gcc.
setup.pyintenta detectar Windows y usar flags de MSVC, pero no fue probado ahí. macOS con clang debería funcionar (misma familia de flags que gcc) pero tampoco fue verificado extensivamente. -
Sin validación exhaustiva de shapes en todos los casos límite. El motor de autograd fue validado contra gradiente numérico (diferencias finitas) para todas las operaciones sobre los casos de test incluidos, pero eso no cubre cada combinación posible de shapes, dtypes, o tamaños extremos.
Estructura del paquete
mingrad/
├── __init__.py # API Python ergonómica (Tensor, Model, SGD)
├── _mingrad # módulo de extensión compilado (.so)
└── csrc/ # código fuente en C (motor + bindings)
├── mingrad.h # API del motor en C puro
├── mingrad_api.h/c # capa de handles, para exponer a otros lenguajes
├── _mingrad.c # módulo de extensión Python (Python.h)
└── *.c # tensor, ops, losses, capas, conv2d/3d, optimizador
Si preferís usar el motor directamente desde C (sin Python), mingrad.h es la API completa; mingrad_api.h es una capa simplificada basada en handles enteros, pensada originalmente para bindings pero utilizable también desde C directamente.
Tests
El motor de autograd fue validado con tests de gradiente numérico (diferencias finitas contra el gradiente analítico) para cada operación, incluyendo Conv2D y Conv3D con distintos stride/padding. Estos tests están en tests/ y se compilan directo con gcc contra mingrad/csrc/, sin depender de Python. Los fuentes del motor no necesitan Python.h — solo _mingrad.c (el binding) lo requiere, así que se excluye de este build:
gcc -std=c11 -O2 -Imingrad/csrc \
mingrad/csrc/arena.c mingrad/csrc/shape.c mingrad/csrc/tensor.c mingrad/csrc/ops.c \
mingrad/csrc/losses.c mingrad/csrc/layers.c mingrad/csrc/optim.c \
mingrad/csrc/conv2d.c mingrad/csrc/conv3d.c \
tests/test_grad_check.c -o test_grad_check -lm
./test_grad_check
Licencia
Ver LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mingrad-0.1.0.tar.gz.
File metadata
- Download URL: mingrad-0.1.0.tar.gz
- Upload date:
- Size: 45.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: python-requests/2.34.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
87aad46a3804faa369cdf806dd9a1466745486113398a3b04fca1c3b6ca973d7
|
|
| MD5 |
cbf72ee892fea669b91147b06ae61f83
|
|
| BLAKE2b-256 |
254fd3e02310cd308a9d9f3722dd335d982adf635dffc7cf230d21f618920281
|
File details
Details for the file mingrad-0.1.0-cp314-cp314-android_24_arm64_v8a.whl.
File metadata
- Download URL: mingrad-0.1.0-cp314-cp314-android_24_arm64_v8a.whl
- Upload date:
- Size: 109.7 kB
- Tags: Android API level 24+ ARM64 v8a, CPython 3.14
- Uploaded using Trusted Publishing? No
- Uploaded via: python-requests/2.34.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1d203442b340e7f66a2a22381d74f53dc20ecdaca382c641bf1e0a3ca33f86d4
|
|
| MD5 |
bf73ca257841268f46cdff81566c9b43
|
|
| BLAKE2b-256 |
fc5d3eed4a42466dd4455f9a76291f5f3ae90f4ded6b34c113324c9f1c66dbca
|