High-performance neural network library with Keras-like API
Project description
GRNexus Python - Biblioteca de Redes Neuronales
Una biblioteca de redes neuronales de alto rendimiento con núcleo en C y API estilo Keras
📑 Tabla de Contenidos
- Características
- Instalación
- Inicio Rápido
- Arquitectura
- Guía Paso a Paso
- API Completa
- Ejemplos Prácticos
- Tests Interactivos
- Troubleshooting
✨ Características
- 🚀 Núcleo en C: Implementación de alto rendimiento con operaciones optimizadas
- 🎯 API Keras-like: Familiar y fácil de usar para desarrolladores de ML
- 🧩 Modular: Arquitectura flexible con capas desacopladas
- 💾 Persistencia: Guardar y cargar modelos en formato
.nexus - 🎨 50+ Funciones de Activación: Desde ReLU hasta Snake, Mish, GELU, etc.
- 🔧 Múltiples Optimizers: SGD, Momentum, Adam, RMSprop
- 📊 Regularización: Dropout, BatchNorm para mejor generalización
- 🌐 Cross-platform: Compatible con Windows, macOS y Linux
📦 Instalación
Instalación desde PyPI (Recomendado)
pip install grnexus
Instalación desde Código Fuente
Para desarrolladores que quieran contribuir o usar la última versión:
# Clonar el repositorio
git clone https://github.com/grcodedigitalsolutions/GRNexus.git
cd GRNexus/python
# Instalar en modo desarrollo
pip install -e .
# O instalar dependencias opcionales para tests
pip install flet numpy
Requisitos
- Python 3.8 o superior
- Sin dependencias externas (100% Python puro)
- Flet (opcional, solo para tests interactivos)
- NumPy (opcional, para operaciones matriciales avanzadas)
Verificar Instalación
from grnexus import NeuralNetwork
# Crear un modelo simple
model = NeuralNetwork()
print(f"✅ GRNexus v{grnexus.__version__} instalado correctamente!")
🚀 Inicio Rápido
from grnexus import NeuralNetwork, DenseLayer, ReLU, DropoutLayer
# 1. Crear modelo
model = NeuralNetwork()
# 2. Agregar capas
model.add(DenseLayer(128, input_dim=784, activation=ReLU()))
model.add(DropoutLayer(0.2))
model.add(DenseLayer(64, activation=ReLU()))
model.add(DenseLayer(10)) # Capa de salida
# 3. Compilar
model.compile(
optimizer='adam',
loss='cross_entropy',
learning_rate=0.001
)
# 4. Entrenar
history = model.train(
x_train, # Datos de entrenamiento
y_train, # Etiquetas (one-hot encoded)
epochs=10,
batch_size=32
)
# 5. Predecir
predictions = model.predict(x_test)
# 6. Guardar modelo
model.save('mi_modelo.nexus')
# Cargar modelo
model_cargado = NeuralNetwork.load('mi_modelo.nexus')
🏗️ Arquitectura
Estructura del Proyecto
GRNexus/python/
├── grnexus.py # Clase principal GRNexus
├── lib/
│ ├── grnexus_layers.py # Implementación de capas
│ ├── grnexus_activations.py # Funciones de activación
│ └── grnexus_optimizers.py # Optimizers
├── test/
│ ├── test_digitos.py # Test de reconocimiento de dígitos
│ ├── test_sentiment.py # Test de análisis de sentimientos
│ ├── test_iris.py # Test de clasificación Iris
│ ├── test_housing.py # Test de predicción de precios
│ └── test_suite.py # Launcher de todos los tests
└── README.md # Este archivo
Flujo de Datos
Input → DenseLayer → Activation → Dropout → ... → Softmax → Output
↓ ↑
└──────────── Backpropagation (gradientes) ─────────────────┘
🧠 Cómo Funciona una Red Neuronal
📖 Explicación Básica para Principiantes (Click para expandir)
¿Qué es una Neurona Artificial?
Imagina una neurona como una pequeña calculadora que:
- Recibe varios números de entrada
- Los multiplica por "pesos" (importancia de cada entrada)
- Los suma todos
- Aplica una función de activación (decide si "activarse" o no)
- Produce un número de salida
Ejemplo simple:
Entradas: [2, 3, 1]
Pesos: [0.5, 0.3, 0.2]
Cálculo: (2 × 0.5) + (3 × 0.3) + (1 × 0.2) = 1.0 + 0.9 + 0.2 = 2.1
Activación ReLU: max(0, 2.1) = 2.1
Salida: 2.1
¿Qué son los Pesos y Bias?
-
Pesos: Son como "perillas de volumen" que controlan qué tan importante es cada entrada
- Al inicio son aleatorios
- Durante el entrenamiento se ajustan automáticamente
- Pesos grandes = entrada muy importante
- Pesos pequeños = entrada poco importante
-
Bias: Es como un "ajuste fino" adicional
- Permite a la neurona activarse incluso sin entradas
- Ayuda a que la red sea más flexible
¿Cómo Aprende la Red?
El aprendizaje es un proceso de prueba y error automatizado:
1. PREDICCIÓN (Forward Pass):
Entrada → Capa 1 → Capa 2 → ... → Salida
2. COMPARACIÓN:
¿Qué predijo la red? vs ¿Cuál era la respuesta correcta?
Error = |Predicción - Respuesta Correcta|
3. AJUSTE (Backward Pass / Backpropagation):
- La red calcula: "¿Qué pesos causaron este error?"
- Ajusta los pesos en la dirección que reduce el error
- Usa el "learning rate" para controlar qué tan grandes son los ajustes
4. REPETIR:
- Hace esto miles de veces con muchos ejemplos
- Gradualmente los pesos mejoran
- El error disminuye
- ¡La red aprende!
¿Qué hace la Función de Activación?
Las funciones de activación añaden no-linealidad (capacidad de aprender patrones complejos):
- Sin activación: La red solo puede aprender líneas rectas (muy limitado)
- Con activación: La red puede aprender curvas, formas complejas, patrones
Analogía:
- Sin activación = Solo puedes dibujar con una regla
- Con activación = Puedes dibujar cualquier forma libremente
Funciones comunes:
ReLU: Si el número es negativo → 0, si es positivo → déjalo igualSigmoid: Convierte cualquier número a rango 0-1Softmax: Convierte números en probabilidades que suman 1
Ejemplo Completo: Reconocimiento de Dígitos
PASO 1: PREPARAR DATOS
Imagen 28×28 → Aplanar → Vector de 784 números
[pixel1, pixel2, ..., pixel784]
PASO 2: FORWARD PASS (Predicción)
784 números → Capa 1 (128 neuronas con ReLU)
→ Capa 2 (64 neuronas con ReLU)
→ Capa 3 (10 neuronas, una por dígito)
→ Softmax (convierte a probabilidades)
→ [0.01, 0.02, 0.85, 0.03, ...] ← "85% seguro que es un 2"
PASO 3: CALCULAR ERROR
Predicción: [0.01, 0.02, 0.85, 0.03, 0.01, 0.02, 0.01, 0.03, 0.01, 0.01]
Correcto: [0, 0, 1, 0, 0, 0, 0, 0, 0, 0 ] (es un 2)
Error: Categorical Cross-Entropy = 0.16 (bastante bajo, buena predicción)
PASO 4: BACKWARD PASS (Aprendizaje)
- Calcular gradientes: "¿Qué pesos causaron el error?"
- Actualizar pesos: peso_nuevo = peso_viejo - (learning_rate × gradiente)
- Los pesos se ajustan para que la próxima vez prediga mejor
PASO 5: REPETIR
- Entrenar con miles de imágenes
- Cada vez los pesos mejoran un poquito
- Después de muchas épocas, ¡la red reconoce dígitos con 95%+ precisión!
Conceptos Clave
| Concepto | Explicación Simple | Analogía |
|---|---|---|
| Neurona | Calculadora que suma entradas ponderadas | Una persona votando (cada voto tiene diferente peso) |
| Peso | Importancia de una conexión | Volumen de un canal de audio |
| Bias | Ajuste fino adicional | Temperatura base de un termostato |
| Activación | Función que decide si la neurona "se activa" | Interruptor que decide si pasa la señal |
| Forward Pass | Calcular la predicción | Resolver un problema de matemáticas |
| Loss | Qué tan equivocada está la predicción | Distancia entre tu respuesta y la correcta |
| Backward Pass | Ajustar pesos para reducir error | Aprender de tus errores |
| Epoch | Una pasada completa por todos los datos | Leer un libro completo una vez |
| Batch | Grupo pequeño de ejemplos | Estudiar un capítulo del libro |
| Learning Rate | Qué tan grandes son los ajustes | Tamaño de los pasos cuando caminas |
Flujo Detallado de Entrenamiento
graph TD
A[Datos de Entrada] --> B[Normalización]
B --> C[Forward Pass]
C --> D[Capa Dense 1]
D --> E[Activación ReLU]
E --> F[Dropout]
F --> G[Capa Dense 2]
G --> H[Activación]
H --> I[Capa de Salida]
I --> J[Softmax/Output]
J --> K[Calcular Loss]
K --> L[Backward Pass]
L --> M[Calcular Gradientes]
M --> N[Actualizar Pesos]
N --> O{¿Más Epochs?}
O -->|Sí| C
O -->|No| P[Modelo Entrenado]
Proceso de Predicción vs Entrenamiento
| Fase | Forward Pass | Backward Pass | Actualizar Pesos |
|---|---|---|---|
| Entrenamiento | ✅ Sí | ✅ Sí | ✅ Sí |
| Predicción | ✅ Sí | ❌ No | ❌ No |
Durante Entrenamiento:
- Forward: Calcular predicción
- Comparar con etiqueta real
- Backward: Calcular gradientes
- Actualizar pesos con optimizer
Durante Predicción:
- Forward: Calcular predicción
- Retornar resultado
- ¡Listo!
📚 Guía Paso a Paso
1. Crear un Modelo
from grnexus import GRNexus
# Especificar la dimensión de entrada
model = GRNexus(input_dim=784)
Parámetros:
input_dim(int): Número de características de entrada
¿Cómo determinar input_dim?
- Para imágenes 28x28:
input_dim = 28 * 28 = 784 - Para texto vectorizado:
input_dim = tamaño_vocabulario - Para datos tabulares:
input_dim = número_de_columnas
2. Agregar Capas
2.1 Capa Densa (Dense Layer)
model.add_dense(
units=128, # Número de neuronas
activation='ReLU', # Función de activación
use_bias=True, # Usar bias
weight_init='he' # Inicialización de pesos
)
Parámetros:
units(int): Número de neuronas en la capaactivation(str, opcional): Nombre de la función de activación- Opciones:
'ReLU','Sigmoid','Tanh','Swish','GELU','Mish', etc.
- Opciones:
use_bias(bool): Si usar bias (default:True)weight_init(str): Método de inicialización'xavier': Para Sigmoid/Tanh'he': Para ReLU y variantes (recomendado)'normal': Distribución normal'uniform': Distribución uniforme
2.2 Capa de Dropout
model.add_dropout(rate=0.2) # Desactivar 20% de neuronas durante entrenamiento
Parámetros:
rate(float): Proporción de neuronas a desactivar (0.0 - 1.0)
¿Cuándo usar Dropout?
- Para prevenir overfitting
- Típicamente después de capas densas
- Valores comunes: 0.2 - 0.5
2.3 Capa de Batch Normalization
model.add_batch_norm(
momentum=0.99,
epsilon=1e-5
)
Parámetros:
momentum(float): Momentum para actualizar media y varianza móvilesepsilon(float): Valor pequeño para estabilidad numérica
2.4 Capa de Activación
model.add_activation('ReLU')
¿Cuándo usar?
- Cuando necesitas aplicar una activación sin una capa densa
- Para arquitecturas más complejas
2.5 Capa Softmax
model.add_softmax() # Para clasificación multiclase
Importante:
- Solo usar en la capa de salida
- Para problemas de clasificación
- Convierte logits en probabilidades que suman 1.0
3. Compilar
model.compile(
optimizer='adam', # Optimizer a usar
loss='categorical_crossentropy', # Función de pérdida
learning_rate=0.01, # Tasa de aprendizaje
metrics=['accuracy'] # Métricas a monitorear
)
Parámetros:
optimizer(str): Algoritmo de optimización'sgd': Stochastic Gradient Descent'momentum': SGD con Momentum'adam': Adaptive Moment Estimation (recomendado)'rmsprop': RMSprop
loss(str): Función de pérdida'categorical_crossentropy': Para clasificación multiclase'mse': Mean Squared Error (para regresión)
learning_rate(float): Tasa de aprendizaje- Valores típicos: 0.001 - 0.1
- Valores más bajos: aprendizaje más estable pero lento
- Valores más altos: aprendizaje más rápido pero puede divergir
metrics(list, opcional): Métricas a calcular
🎯 Guía de Learning Rate:
| Optimizer | LR Recomendado |
|---|---|
| SGD | 0.01 - 0.1 |
| Momentum | 0.01 - 0.05 |
| Adam | 0.001 - 0.01 |
| RMSprop | 0.001 - 0.01 |
4. Entrenar
4.1 Método fit() (Recomendado)
history = model.fit(
x_train, # Datos de entrenamiento
y_train, # Etiquetas
epochs=10, # Número de épocas
batch_size=32, # Tamaño del batch
validation_data=(x_val, y_val), # Datos de validación (opcional)
verbose=1 # Nivel de detalle (0, 1, 2)
)
Parámetros:
x_train(list): Datos de entrada (lista de listas)- Ejemplo:
[[0.1, 0.2, ...], [0.3, 0.4, ...], ...]
- Ejemplo:
y_train(list): Etiquetas (one-hot encoded para clasificación)- Ejemplo:
[[1, 0, 0], [0, 1, 0], ...]para 3 clases
- Ejemplo:
epochs(int): Número de veces que pasar por todo el datasetbatch_size(int): Número de muestras por actualización de pesosvalidation_data(tuple, opcional): (x_val, y_val) para validaciónverbose(int): 0=silencioso, 1=barra de progreso, 2=una línea por época
Retorna:
history(dict): Historial de entrenamiento con pérdidas por época
4.2 Método train_on_batch() (Para control fino)
for epoch in range(epochs):
for i in range(0, len(x_train), batch_size):
x_batch = x_train[i:i+batch_size]
y_batch = y_train[i:i+batch_size]
loss = model.train_on_batch(x_batch, y_batch)
print(f"Batch loss: {loss}")
5. Predecir
5.1 Predicción Simple
# Predecir una muestra
prediction = model.predict(x_sample) # x_sample es una lista de números
# Predecir múltiples muestras
predictions = model.predict(x_test) # x_test es lista de listas
5.2 Obtener Clase Predicha
# Para clasificación
prediction = model.predict(x_sample)
predicted_class = prediction.index(max(prediction)) # Índice de máxima probabilidad
confidence = max(prediction) # Confianza de la predicción
print(f"Clase predicha: {predicted_class}")
print(f"Confianza: {confidence:.2%}")
6. Guardar/Cargar
6.1 Guardar Modelo
model.save('mi_modelo.nexus')
Formato .nexus:
- Guarda arquitectura completa del modelo
- Guarda todos los pesos y biases
- Guarda configuración del optimizer
- Compatible entre plataformas
6.2 Cargar Modelo
from grnexus import GRNexus
modelo_cargado = GRNexus.load('mi_modelo.nexus')
# Usar inmediatamente sin recompilar
predicciones = modelo_cargado.predict(x_test)
📊 Entendiendo los Datos
Preparación de Datos para Entrenamiento
Los datos son el combustible de las redes neuronales. Aquí aprenderás cómo prepararlos correctamente.
1. Formato de Entrada
Las redes neuronales solo entienden números. Debes convertir tus datos a listas de números.
Ejemplos por tipo de dato:
# IMÁGENES (28x28 píxeles)
imagen_2d = [
[0.0, 0.1, 0.2, ...], # Fila 1
[0.3, 0.4, 0.5, ...], # Fila 2
...
]
# Aplanar a 1D
imagen_1d = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, ...] # 784 números
# TEXTO
texto = "This movie is great"
# Convertir a bag-of-words o embeddings
vector_texto = [0.0, 0.2, 0.0, 0.5, ...] # Cada posición = una palabra
# DATOS TABULARES
casa = {
'habitaciones': 3,
'baños': 2,
'metros': 120,
'año': 2010,
'distancia': 5
}
# Convertir a lista
vector_casa = [3, 2, 120, 2010, 5]
2. Normalización de Datos
¿Por qué normalizar?
- Las redes aprenden mejor cuando todos los valores están en rangos similares
- Evita que características con valores grandes dominen el aprendizaje
- Acelera la convergencia
Métodos de normalización:
# MIN-MAX NORMALIZATION (escala a [0, 1])
def normalizar_min_max(valor, min_val, max_val):
return (valor - min_val) / (max_val - min_val)
# Ejemplo:
edad = 25 # rango: 18-80
edad_norm = (25 - 18) / (80 - 18) = 0.113
# Z-SCORE NORMALIZATION (media=0, std=1)
def normalizar_z_score(valor, media, std):
return (valor - media) / std
# PARA IMÁGENES (ya están en [0, 255])
pixel = 128
pixel_norm = pixel / 255.0 # → 0.502
Ejemplo completo:
# Datos sin normalizar
datos_crudos = [
[3, 2, 120, 2010, 5], # habitaciones, baños, m², año, km
[4, 3, 180, 2015, 3],
[2, 1, 80, 1990, 10]
]
# Encontrar min y max de cada característica
mins = [2, 1, 70, 1985, 1]
maxs = [6, 5, 320, 2024, 15]
# Normalizar
datos_normalizados = []
for muestra in datos_crudos:
muestra_norm = []
for i, valor in enumerate(muestra):
norm = (valor - mins[i]) / (maxs[i] - mins[i])
muestra_norm.append(norm)
datos_normalizados.append(muestra_norm)
# Resultado:
# [[0.25, 0.25, 0.2, 0.64, 0.29],
# [0.5, 0.5, 0.44, 0.77, 0.14],
# [0.0, 0.0, 0.04, 0.13, 0.64]]
3. One-Hot Encoding
Para clasificación, las etiquetas deben estar en formato one-hot.
¿Qué es One-Hot Encoding?
Convertir una categoría en un vector donde:
- Todas las posiciones son 0
- Excepto la posición de la categoría correcta que es 1
# EJEMPLO: Clasificación de dígitos (0-9)
digito = 3
# One-hot encoding:
one_hot = [0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
# 0 1 2 3 4 5 6 7 8 9
# ↑
# posición 3 = 1
# Función para crear one-hot
def crear_one_hot(etiqueta, num_clases):
vector = [0.0] * num_clases
vector[etiqueta] = 1.0
return vector
# Ejemplos:
crear_one_hot(0, 10) # → [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
crear_one_hot(5, 10) # → [0, 0, 0, 0, 0, 1, 0, 0, 0, 0]
crear_one_hot(9, 10) # → [0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
Para múltiples muestras:
# Etiquetas originales
etiquetas = [3, 7, 2, 9, 1]
# Convertir a one-hot
y_train = []
for etiqueta in etiquetas:
one_hot = [0.0] * 10
one_hot[etiqueta] = 1.0
y_train.append(one_hot)
# Resultado:
# [[0,0,0,1,0,0,0,0,0,0], # 3
# [0,0,0,0,0,0,0,1,0,0], # 7
# [0,0,1,0,0,0,0,0,0,0], # 2
# [0,0,0,0,0,0,0,0,0,1], # 9
# [0,1,0,0,0,0,0,0,0,0]] # 1
4. Formato de Entrada/Salida
Para Clasificación:
# ENTRADA: Lista de listas (cada muestra es una lista)
x_train = [
[0.1, 0.2, 0.3, ...], # Muestra 1
[0.4, 0.5, 0.6, ...], # Muestra 2
[0.7, 0.8, 0.9, ...] # Muestra 3
]
# SALIDA: One-hot encoded
y_train = [
[1, 0, 0], # Clase 0
[0, 1, 0], # Clase 1
[1, 0, 0] # Clase 0
]
# ENTRENAR
model.fit(x_train, y_train, epochs=10, batch_size=32)
Para Regresión:
# ENTRADA: Igual que clasificación
x_train = [
[3, 2, 120, 2010, 5], # Casa 1
[4, 3, 180, 2015, 3], # Casa 2
[2, 1, 80, 1990, 10] # Casa 3
]
# SALIDA: Valores continuos (NO one-hot)
y_train = [
[250], # Precio casa 1: $250k
[380], # Precio casa 2: $380k
[150] # Precio casa 3: $150k
]
# ENTRENAR
model.fit(x_train, y_train, epochs=100, batch_size=8)
5. Validación de Datos
Antes de entrenar, verifica que tus datos estén correctos:
# Verificar dimensiones
print(f"Muestras de entrenamiento: {len(x_train)}")
print(f"Dimensión de entrada: {len(x_train[0])}")
print(f"Dimensión de salida: {len(y_train[0])}")
# Verificar que coincidan
assert len(x_train) == len(y_train), "¡x_train y y_train deben tener mismo tamaño!"
# Verificar que input_dim sea correcto
assert len(x_train[0]) == model.input_dim, f"¡input_dim debe ser {len(x_train[0])}!"
# Verificar rangos (después de normalizar)
import statistics
muestra = x_train[0]
print(f"Min: {min(muestra)}, Max: {max(muestra)}")
# Idealmente debe estar en [0, 1] o [-1, 1]
# Verificar one-hot (para clasificación)
for y in y_train:
assert sum(y) == 1.0, "¡One-hot debe sumar 1!"
assert max(y) == 1.0, "¡One-hot debe tener un 1!"
6. División Train/Test
Siempre separa tus datos en entrenamiento y prueba:
import random
# Mezclar datos
combined = list(zip(x_data, y_data))
random.shuffle(combined)
x_data, y_data = zip(*combined)
# Dividir 80% train, 20% test
split_idx = int(0.8 * len(x_data))
x_train = list(x_data[:split_idx])
y_train = list(y_data[:split_idx])
x_test = list(x_data[split_idx:])
y_test = list(y_data[split_idx:])
print(f"Train: {len(x_train)} muestras")
print(f"Test: {len(x_test)} muestras")
# Entrenar solo con train
model.fit(x_train, y_train, epochs=50)
# Evaluar con test
for x, y in zip(x_test, y_test):
pred = model.predict(x)
# Comparar pred con y
🔧 API Completa
Clase GRNexus
Constructor
GRNexus(input_dim: int)
Métodos de Construcción
| Método | Descripción |
|---|---|
add_dense(units, activation=None, use_bias=True, weight_init='xavier') |
Agregar capa densa |
add_activation(activation) |
Agregar capa de activación |
add_dropout(rate) |
Agregar dropout |
add_batch_norm(momentum=0.99, epsilon=1e-5) |
Agregar batch normalization |
add_softmax() |
Agregar softmax (salida) |
Métodos de Configuración
| Método | Descripción |
|---|---|
compile(optimizer, loss, learning_rate, metrics=None) |
Compilar modelo |
Métodos de Entrenamiento
| Método | Descripción |
|---|---|
fit(x_train, y_train, epochs, batch_size, validation_data=None, verbose=1) |
Entrenar modelo |
train_on_batch(x_batch, y_batch) |
Entrenar en un batch |
Métodos de Predicción
| Método | Descripción |
|---|---|
predict(x, batch_size=32) |
Hacer predicciones |
forward(x, training=False) |
Forward pass (bajo nivel) |
Métodos de Persistencia
| Método | Descripción |
|---|---|
save(filename) |
Guardar modelo |
load(filename) (estático) |
Cargar modelo |
Capas Disponibles
DenseLayer
DenseLayer(units, input_dim, activation=None, use_bias=True, weight_init='xavier')
Capa completamente conectada.
DropoutLayer
DropoutLayer(rate)
Desactiva aleatoriamente neuronas durante entrenamiento.
BatchNormLayer
BatchNormLayer(input_dim, momentum=0.99, epsilon=1e-5)
Normaliza activaciones para acelerar entrenamiento.
ActivationLayer
ActivationLayer(activation)
Aplica función de activación no lineal.
SoftmaxLayer
SoftmaxLayer()
Convierte logits en probabilidades (suma = 1).
Funciones de Activación
Básicas
Linear: f(x) = xStep: Función escalónSigmoid: f(x) = 1 / (1 + e^(-x))Tanh: f(x) = tanh(x)
ReLU y Variantes
ReLU: f(x) = max(0, x)LeakyReLU: f(x) = max(αx, x)PReLU: ReLU paramétricoELU: Exponential Linear UnitSELU: Scaled ELUReLU6: min(max(0, x), 6)
Modernas
Swish: f(x) = x · sigmoid(x)Mish: f(x) = x · tanh(softplus(x))GELU: Gaussian Error Linear UnitLiSHT: Linearly Scaled Hyperbolic Tangent
Especializadas
Softplus: f(x) = log(1 + e^x)Softsign: f(x) = x / (1 + |x|)HardSigmoid: Aproximación rápida de sigmoidHardSwish: Aproximación rápida de swishHardTanh: Versión acotada de tanh
Exóticas
Snake: Función periódicaISRU: Inverse Square Root UnitFReLU: Funnel ReLU- Y más de 40 adicionales...
Ver lista completa en: lib/grnexus_activations.py
Optimizers
SGD (Stochastic Gradient Descent)
model.compile(optimizer='sgd', learning_rate=0.01)
El optimizer más básico. Actualiza pesos en dirección opuesta al gradiente.
Momentum
model.compile(optimizer='momentum', learning_rate=0.01)
SGD con momento. Acelera convergencia y reduce oscilaciones.
Adam (Recomendado)
model.compile(optimizer='adam', learning_rate=0.001)
Adaptive Moment Estimation. Combina momentum con tasas de aprendizaje adaptativas.
RMSprop
model.compile(optimizer='rmsprop', learning_rate=0.001)
Root Mean Square Propagation. Bueno para RNNs.
Funciones de Pérdida
Categorical Cross-Entropy
model.compile(loss='categorical_crossentropy')
Para clasificación multiclase con one-hot encoding.
MSE (Mean Squared Error)
model.compile(loss='mse')
Para problemas de regresión.
💡 Ejemplos Prácticos
Ejemplo 1: Clasificación de Dígitos (MNIST-like)
from grnexus import GRNexus
# ============================================
# PASO 1: CREAR EL MODELO
# ============================================
# input_dim=784 porque las imágenes son 28x28 píxeles
# 28 × 28 = 784 valores de entrada
model = GRNexus(input_dim=784)
# ============================================
# PASO 2: CONSTRUIR LA ARQUITECTURA
# ============================================
# Primera capa oculta: 784 → 128 neuronas
# - ReLU: Función de activación que elimina valores negativos
# - he: Inicialización de pesos óptima para ReLU
model.add_dense(128, activation='ReLU', weight_init='he')
# Dropout: Desactiva aleatoriamente 20% de neuronas durante entrenamiento
# - Previene overfitting (memorización de datos)
# - Solo activo durante entrenamiento, no durante predicción
model.add_dropout(0.2)
# Segunda capa oculta: 128 → 64 neuronas
model.add_dense(64, activation='ReLU')
model.add_dropout(0.2)
# Capa de salida: 64 → 10 neuronas (una por cada dígito 0-9)
model.add_dense(10)
# Softmax: Convierte las 10 salidas en probabilidades que suman 1.0
# Ejemplo: [0.01, 0.02, 0.85, 0.03, ...] → "85% seguro que es un 2"
model.add_softmax()
# ============================================
# PASO 3: COMPILAR EL MODELO
# ============================================
model.compile(
optimizer='adam', # Adam: optimizer adaptativo, generalmente el mejor
loss='categorical_crossentropy', # Para clasificación multiclase
learning_rate=0.01 # Qué tan grandes son los ajustes de pesos
)
# ============================================
# PASO 4: PREPARAR DATOS
# ============================================
# Ejemplo de cómo preparar datos (debes tener tus propios datos)
x_train = [] # Lista de imágenes aplanadas (cada una con 784 valores)
y_train = [] # Lista de etiquetas one-hot encoded
# Ejemplo: Agregar una imagen del dígito "3"
imagen_3 = [0.0] * 784 # Imagen de 28x28 aplanada
# ... (aquí irían los valores reales de los píxeles)
etiqueta_3 = [0, 0, 0, 1, 0, 0, 0, 0, 0, 0] # One-hot para el dígito 3
x_train.append(imagen_3)
y_train.append(etiqueta_3)
# ... agregar más ejemplos ...
# ============================================
# PASO 5: ENTRENAR
# ============================================
print("Iniciando entrenamiento...")
history = model.fit(
x_train, y_train,
epochs=10, # Número de veces que ver todo el dataset
batch_size=32, # Procesar 32 imágenes a la vez
verbose=1 # Mostrar progreso
)
# ============================================
# PASO 6: HACER PREDICCIONES
# ============================================
# Predecir una imagen de prueba
test_image = [...] # Imagen de prueba 784D
# Obtener probabilidades para cada dígito
prediction = model.predict(test_image)
# Resultado: [0.01, 0.02, 0.85, 0.03, 0.01, 0.02, 0.01, 0.03, 0.01, 0.01]
# Encontrar el dígito con mayor probabilidad
digit = prediction.index(max(prediction))
confidence = max(prediction)
print(f"Dígito predicho: {digit}")
print(f"Confianza: {confidence:.1%}")
# Salida: "Dígito predicho: 2, Confianza: 85.0%"
# ============================================
# PASO 7: GUARDAR MODELO
# ============================================
model.save('mnist_model.nexus')
print("Modelo guardado exitosamente")
# ============================================
# PASO 8: CARGAR Y USAR MODELO GUARDADO
# ============================================
# En otra sesión, puedes cargar el modelo entrenado
loaded_model = GRNexus.load('mnist_model.nexus')
new_prediction = loaded_model.predict(test_image)
Resultados Esperados:
- Precisión en entrenamiento: ~90-95% después de 10 épocas
- Loss final: ~0.2-0.3
- Tiempo de entrenamiento: Depende del tamaño del dataset
Ejemplo 2: Análisis de Sentimientos
from grnexus import GRNexus
# ============================================
# PREPARAR DATOS DE TEXTO
# ============================================
# Construir vocabulario (palabras únicas)
reviews = [
"This movie is amazing and wonderful",
"Terrible film, waste of time",
"Great acting and beautiful scenes"
]
# Crear diccionario de palabras
vocab = {}
for review in reviews:
words = review.lower().split()
for word in words:
if word not in vocab:
vocab[word] = len(vocab)
vocab_size = len(vocab)
print(f"Vocabulario: {vocab_size} palabras")
# ============================================
# CONVERTIR TEXTO A VECTORES (BAG-OF-WORDS)
# ============================================
def text_to_vector(text, vocab):
"""
Convierte texto en vector numérico
Cada posición representa una palabra del vocabulario
El valor es la frecuencia de esa palabra
"""
vector = [0.0] * len(vocab)
words = text.lower().split()
# Contar ocurrencias
for word in words:
if word in vocab:
vector[vocab[word]] += 1.0
# Normalizar (convertir conteos a frecuencias)
total = sum(vector)
if total > 0:
vector = [v / total for v in vector]
return vector
# ============================================
# CREAR MODELO
# ============================================
model = GRNexus(input_dim=vocab_size)
# Arquitectura para análisis de sentimientos
model.add_dense(64, activation='ReLU')
model.add_dropout(0.3) # Dropout más alto para texto
model.add_dense(32, activation='Swish') # Swish: activación moderna
model.add_dense(2) # 2 clases: Positivo/Negativo
model.add_softmax()
model.compile(
optimizer='adam',
loss='categorical_crossentropy',
learning_rate=0.005 # Learning rate más bajo para mejor convergencia
)
# ============================================
# PREPARAR DATASET
# ============================================
x_train = [
text_to_vector("This movie is amazing", vocab),
text_to_vector("Terrible waste of time", vocab),
# ... más ejemplos ...
]
y_train = [
[0, 1], # Positivo
[1, 0], # Negativo
# ... más etiquetas ...
]
# ============================================
# ENTRENAR
# ============================================
model.fit(x_train, y_train, epochs=20, batch_size=16)
# ============================================
# PREDECIR SENTIMIENTO
# ============================================
new_review = "This film is absolutely fantastic"
vector = text_to_vector(new_review, vocab)
prediction = model.predict(vector)
sentiment = "Positivo" if prediction[1] > prediction[0] else "Negativo"
confidence = max(prediction)
print(f"Sentimiento: {sentiment} ({confidence:.1%} confianza)")
Resultados Esperados:
- Precisión: ~80-90% con dataset pequeño
- El modelo aprende asociaciones palabra-sentimiento
- Palabras como "amazing", "great" → Positivo
- Palabras como "terrible", "waste" → Negativo
Ejemplo 3: Clasificación Multiclase (Iris)
from grnexus import GRNexus
# ============================================
# DATASET IRIS
# ============================================
# 4 características: largo sépalo, ancho sépalo, largo pétalo, ancho pétalo
# 3 especies: Setosa (0), Versicolor (1), Virginica (2)
# Datos de ejemplo (valores reales del dataset Iris)
iris_data = [
[5.1, 3.5, 1.4, 0.2, 0], # Setosa
[7.0, 3.2, 4.7, 1.4, 1], # Versicolor
[6.3, 3.3, 6.0, 2.5, 2], # Virginica
# ... más muestras ...
]
# ============================================
# NORMALIZAR DATOS
# ============================================
def normalize_iris(data):
"""Normaliza características al rango [0, 1]"""
# Valores min/max del dataset Iris
mins = [4.3, 2.0, 1.0, 0.1]
maxs = [7.9, 4.4, 6.9, 2.5]
normalized = []
for i, val in enumerate(data[:4]): # Solo las 4 características
norm = (val - mins[i]) / (maxs[i] - mins[i])
normalized.append(norm)
return normalized
# ============================================
# PREPARAR DATOS
# ============================================
x_train = [normalize_iris(sample) for sample in iris_data]
y_train = []
for sample in iris_data:
species = sample[4] # Última columna es la especie
one_hot = [0.0, 0.0, 0.0]
one_hot[species] = 1.0
y_train.append(one_hot)
# ============================================
# CREAR MODELO
# ============================================
model = GRNexus(input_dim=4) # 4 características
# Arquitectura pequeña (dataset pequeño)
model.add_dense(16, activation='ReLU')
model.add_dense(8, activation='Swish')
model.add_dense(3) # 3 especies
model.add_softmax()
model.compile(
optimizer='adam',
loss='categorical_crossentropy',
learning_rate=0.01
)
# ============================================
# ENTRENAR
# ============================================
model.fit(x_train, y_train, epochs=100, batch_size=16)
# ============================================
# PREDECIR ESPECIE
# ============================================
# Nueva flor con medidas: [6.0, 3.0, 4.5, 1.5]
new_flower = normalize_iris([6.0, 3.0, 4.5, 1.5, 0])
prediction = model.predict(new_flower)
species_names = ["Setosa", "Versicolor", "Virginica"]
predicted_species = prediction.index(max(prediction))
print(f"Especie predicha: {species_names[predicted_species]}")
print(f"Probabilidades:")
for i, name in enumerate(species_names):
print(f" {name}: {prediction[i]:.1%}")
Resultados Esperados:
- Precisión: ~95-98% (dataset Iris es muy separable)
- El modelo aprende patrones en las medidas de las flores
- Setosa es fácilmente distinguible (pétalos pequeños)
- Versicolor y Virginica son más similares
Ejemplo 4: Regresión (Predicción de Precios)
from grnexus import GRNexus
# ============================================
# DATOS DE CASAS
# ============================================
# [habitaciones, baños, m², año, distancia_km, precio_miles]
housing_data = [
[3, 2, 120, 2010, 5, 250],
[4, 3, 180, 2015, 3, 380],
[2, 1, 80, 1990, 10, 150],
# ... más casas ...
]
# ============================================
# NORMALIZAR ENTRADA Y SALIDA
# ============================================
def normalize_features(features):
"""Normaliza las 5 características"""
mins = [2, 1, 70, 1985, 1]
maxs = [6, 5, 320, 2024, 15]
return [(f - mins[i]) / (maxs[i] - mins[i])
for i, f in enumerate(features)]
def normalize_price(price):
"""Normaliza precio a [0, 1]"""
return (price - 100) / (750 - 100)
def denormalize_price(norm_price):
"""Convierte precio normalizado a valor real"""
return norm_price * (750 - 100) + 100
# ============================================
# PREPARAR DATOS
# ============================================
x_train = [normalize_features(house[:5]) for house in housing_data]
y_train = [[normalize_price(house[5])] for house in housing_data]
# ============================================
# CREAR MODELO (REGRESIÓN)
# ============================================
model = GRNexus(input_dim=5)
# Arquitectura para regresión
model.add_dense(32, activation='ReLU')
model.add_dropout(0.2)
model.add_dense(16, activation='Swish')
model.add_dense(8, activation='ReLU')
model.add_dense(1) # UNA sola neurona de salida (precio)
# NO usar Softmax en regresión
model.compile(
optimizer='adam',
loss='categorical_crossentropy', # Usaremos MSE manualmente
learning_rate=0.001 # Learning rate bajo para regresión
)
# ============================================
# ENTRENAR
# ============================================
print("Entrenando modelo de regresión...")
for epoch in range(150):
total_loss = 0
for i in range(0, len(x_train), 4):
batch_x = x_train[i:i+4]
batch_y = y_train[i:i+4]
loss = model.train_on_batch(batch_x, batch_y)
total_loss += loss
if (epoch + 1) % 30 == 0:
avg_loss = total_loss / (len(x_train) / 4)
print(f"Epoch {epoch+1}/150 - loss: {avg_loss:.4f}")
# ============================================
# PREDECIR PRECIO
# ============================================
# Casa nueva: 3 hab, 2 baños, 140m², año 2012, 6km del centro
new_house = normalize_features([3, 2, 140, 2012, 6])
prediction_norm = model.predict(new_house)
# Desnormalizar para obtener precio real
predicted_price = denormalize_price(prediction_norm[0])
print(f"\nCasa: 3 hab, 2 baños, 140m², 2012, 6km centro")
print(f"Precio predicho: ${predicted_price:.0f}k")
# ============================================
# EVALUAR MODELO
# ============================================
errors = []
for house in housing_data:
features = normalize_features(house[:5])
real_price = house[5]
pred_norm = model.predict(features)
pred_price = denormalize_price(pred_norm[0])
error = abs(real_price - pred_price)
errors.append(error)
avg_error = sum(errors) / len(errors)
print(f"\nError promedio: ${avg_error:.1f}k")
Resultados Esperados:
- Error promedio: $20-40k (depende del dataset)
- El modelo aprende relaciones:
- Más habitaciones → Precio más alto
- Más metros cuadrados → Precio más alto
- Más cerca del centro → Precio más alto
- Más nuevo → Precio más alto
🎮 Tests Interactivos
GRNexus incluye 5 aplicaciones interactivas con Flet para probar las capacidades de la biblioteca:
1. test_digitos.py - Reconocimiento de Dígitos
python3 test/test_digitos.py
- Dibuja dígitos con el mouse
- Entrena la red en tiempo real
- Visualiza probabilidades por clase
- Guarda/carga modelos
2. test_sentiment.py - Análisis de Sentimientos
python3 test/test_sentiment.py
- Analiza reseñas de películas
- 30 reseñas reales de IMDb
- Clasifica como positivo/negativo
- Bag-of-words + red neuronal
3. test_iris.py - Clasificación Iris
python3 test/test_iris.py
- Dataset clásico de ML
- 90 muestras de 3 especies de flores
- Sliders para ajustar características
- Visualización de probabilidades
4. test_housing.py - Predicción de Precios
python3 test/test_housing.py
- Regresión con datos reales
- 42 casas con precios
- Sliders para habitaciones, m², etc.
- Compara precio real vs predicho
5. test_suite.py - Launcher Maestro
python3 test/test_suite.py
- Ejecuta todos los tests desde una interfaz
- Descripción de cada test
- Ejecutar individual o todos juntos
🔍 Troubleshooting
Problema: "ModuleNotFoundError: No module named 'grnexus'"
Solución:
import sys
import os
sys.path.insert(0, os.path.abspath('path/to/GRNexus/python'))
Problema: "TypeError: layer.backward() takes 2 positional arguments but 3 were given"
Causa: Código desactualizado de GRNexus v1.0
Solución: Actualizar a GRNexus v2.0. El método backward ya no acepta learning_rate.
Problema: Pérdida (loss) no disminuye
Posibles causas:
- Learning rate muy alto → Reducir a 0.001 o menos
- Learning rate muy bajo → Aumentar a 0.01
- Datos no normalizados → Normalizar a rango [0, 1] o [-1, 1]
- Arquitectura inadecuada → Agregar más capas/neuronas
- Inicialización incorrecta → Usar 'he' para ReLU, 'xavier' para Sigmoid/Tanh
Problema: Overfitting (buena pérdida en entrenamiento, mala en validación)
Soluciones:
- Agregar Dropout:
model.add_dropout(0.2) - Reducir tamaño del modelo (menos neuronas/capas)
- Aumentar tamaño del dataset
- Agregar regularización
Problema: Predicciones siempre retornan la misma clase
Causas:
- Clases desbalanceadas en el dataset
- Learning rate muy alto provocando divergencia
- Pesos no inicializados correctamente
Soluciones:
- Balancear clases en el dataset
- Reducir learning rate
- Verificar que
compile()fue llamado antes de entrenar
Problema: "IndexError: list index out of range" durante entrenamiento
Causa: Dimensiones incorrectas en los datos
Solución:
- Verificar que
x_traintenga dimensióninput_dim - Verificar que
y_trainsea one-hot encoded connum_classeselementos - Ejemplo: Si 3 clases, usar
[[1,0,0], [0,1,0], [0,0,1]]
📊 Mejores Prácticas
1. Preparación de Datos
# Normalizar datos
def normalize(data):
min_val = min(data)
max_val = max(data)
return [(x - min_val) / (max_val - min_val) for x in data]
# One-hot encoding
def one_hot(label, num_classes):
encoding = [0.0] * num_classes
encoding[label] = 1.0
return encoding
2. Arquitectura
- Clasificación simple: 1-2 capas ocultas
- Clasificación compleja: 3-5 capas con Dropout
- Regresión: 2-4 capas sin Softmax final
3. Hiperparámetros
| Tamaño Dataset | Batch Size | Learning Rate | Epochs |
|---|---|---|---|
| < 1000 | 16-32 | 0.01 | 50-100 |
| 1000-10000 | 32-64 | 0.01-0.001 | 20-50 |
| > 10000 | 64-128 | 0.001 | 10-30 |
4. Monitoreo
history = model.fit(x_train, y_train, epochs=10, verbose=1)
# Graficar pérdida por época
import matplotlib.pyplot as plt
plt.plot(history['loss'])
plt.xlabel('Época')
plt.ylabel('Pérdida')
plt.title('Curva de Entrenamiento')
plt.show()
Hecho con ❤️ por el equipo de GRNexus
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 grnexus-0.1.2.tar.gz.
File metadata
- Download URL: grnexus-0.1.2.tar.gz
- Upload date:
- Size: 266.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
af662fca07b559047169729adff660603b2a8e8e4b8b72134e83389a91707cac
|
|
| MD5 |
ff4b17ea6960fec0c2cc58471fcdbc5e
|
|
| BLAKE2b-256 |
d37932603963302e531d67d55502ffdcfbaa8cbd0ea329237f6367573e045ce7
|
File details
Details for the file grnexus-0.1.2-py3-none-any.whl.
File metadata
- Download URL: grnexus-0.1.2-py3-none-any.whl
- Upload date:
- Size: 211.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9ef5eb07832b075bf25b5bedd1ba3d08b94fe7b47a3562692599b8056bd33d7e
|
|
| MD5 |
48d75c53ec7ca1fdf787480ddc9fdc8f
|
|
| BLAKE2b-256 |
4adfb96928074e225221d9dccead3254b39e1dc40d27ee0420d80f8a0ca0b78d
|