1 1. Contexto: ¿Qué es una red neuronal?

Una red neuronal artificial (RNA) es un modelo de aprendizaje automático que busca aprender patrones a partir de datos.
Puede entenderse como una función compuesta por varias transformaciones: capas que combinan variables y aplican no linealidades (activaciones) para representar relaciones complejas.

En términos prácticos, una red neuronal se usa cuando:

En este documento veremos el núcleo conceptual: (lineal + activación) repetido por capas, y cómo esto se entrena ajustando pesos y sesgos.


2 2. La neurona: la unidad básica

Una neurona (en una red feedforward densa) calcula primero una combinación lineal:

\[ z = \sum_{i=1}^{p} w_i x_i + b \]

Luego aplica una función de activación:

\[ a = g(z) \]

Donde: - \(x_i\): variables de entrada (features) - \(w_i\): pesos (importancia de cada feature) - \(b\): sesgo (bias) - \(g(\cdot)\): activación (introduce no linealidad) - \(a\): salida de la neurona

Idea central:
Sin activación (sin \(g\)), aunque apiles varias capas, el modelo sigue siendo esencialmente lineal.
La activación permite modelar fronteras y relaciones no lineales.


3 3. Activaciones comunes (intuición)

En redes modernas se eligen activaciones según estabilidad y desempeño.


4 4. Arquitectura: capas de entrada, ocultas y salida

Una red feedforward típica:

Ejemplos: - Regresión: salida = número (p.ej., precio) - Clasificación binaria: salida = probabilidad (0–1) - Multiclase: salida = vector de probabilidades (softmax)


5 5. Entrenamiento: pérdida + backpropagation (idea)

El entrenamiento busca minimizar una función de pérdida:

\[ \min_{w,b} \; \mathcal{L}(y, \hat{y}) \]

Pasos conceptuales:

  1. Forward pass: con pesos actuales, la red produce \(\hat{y}\)
  2. Cálculo de pérdida: mide qué tan lejos está \(\hat{y}\) de \(y\)
  3. Backpropagation: calcula gradientes (sensibilidad de la pérdida a cada peso)
  4. Actualización: el optimizador ajusta pesos para reducir la pérdida

Una forma simple de actualización es el gradiente descendente:

\[ w \leftarrow w - \eta \frac{\partial \mathcal{L}}{\partial w} \]

donde \(\eta\) es la tasa de aprendizaje.


6 6. Mini-demo en R: red neuronal simple con nnet

Para un ejemplo introductorio y visual, usaremos:

Nota: nnet es una implementación clásica (red pequeña). Para redes profundas se suele usar keras/tensorflow.

# ============================================================
# PAQUETES
# Instala si faltan con install.packages("nombre")
# ============================================================

if (!requireNamespace("nnet", quietly = TRUE)) install.packages("nnet")
if (!requireNamespace("dplyr", quietly = TRUE)) install.packages("dplyr")
if (!requireNamespace("ggplot2", quietly = TRUE)) install.packages("ggplot2")
if (!requireNamespace("tibble", quietly = TRUE)) install.packages("tibble")

library(nnet)
library(dplyr)
library(ggplot2)
library(tibble)

6.1 6.1 Datos y preparación

data("iris")

# Usamos 2 variables para visualizar más fácil
df <- iris %>%
  select(Sepal.Length, Sepal.Width, Species)

glimpse(df)
## Rows: 150
## Columns: 3
## $ Sepal.Length <dbl> 5.1, 4.9, 4.7, 4.6, 5.0, 5.4, 4.6, 5.0, 4.4, 4.9, 5.4, 4.…
## $ Sepal.Width  <dbl> 3.5, 3.0, 3.2, 3.1, 3.6, 3.9, 3.4, 3.4, 2.9, 3.1, 3.7, 3.…
## $ Species      <fct> setosa, setosa, setosa, setosa, setosa, setosa, setosa, s…
head(df, 5)
##   Sepal.Length Sepal.Width Species
## 1          5.1         3.5  setosa
## 2          4.9         3.0  setosa
## 3          4.7         3.2  setosa
## 4          4.6         3.1  setosa
## 5          5.0         3.6  setosa

6.2 6.2 Partición train/test (70/30)

set.seed(123)

n <- nrow(df)
idx_train <- sample(seq_len(n), size = floor(0.7 * n))

train <- df[idx_train, ]
test  <- df[-idx_train, ]

table(train$Species)
## 
##     setosa versicolor  virginica 
##         36         32         37
table(test$Species)
## 
##     setosa versicolor  virginica 
##         14         18         13

6.3 6.3 Entrenamiento de la red

Parámetros clave: - size: neuronas en la capa oculta (capacidad del modelo) - decay: regularización (reduce sobreajuste) - maxit: iteraciones máximas

modelo <- nnet(
  Species ~ Sepal.Length + Sepal.Width,
  data = train,
  size = 5,
  decay = 0.01,
  maxit = 500,
  trace = FALSE
)

modelo
## a 2-5-3 network with 33 weights
## inputs: Sepal.Length Sepal.Width 
## output(s): Species 
## options were - softmax modelling  decay=0.01

6.4 6.4 Evaluación: matriz de confusión y accuracy

pred_class <- predict(modelo, newdata = test, type = "class")

conf <- table(Real = test$Species, Predicho = pred_class)
conf
##             Predicho
## Real         setosa versicolor virginica
##   setosa         14          0         0
##   versicolor      0         12         6
##   virginica       0          1        12
accuracy <- sum(diag(conf)) / sum(conf)
accuracy
## [1] 0.8444444

Accuracy (test): 0.844

6.5 6.5 Visualización: regiones de decisión (aproximadas)

# Construimos una grilla de puntos para predecir en el plano
grid <- expand.grid(
  Sepal.Length = seq(min(df$Sepal.Length), max(df$Sepal.Length), length.out = 200),
  Sepal.Width  = seq(min(df$Sepal.Width),  max(df$Sepal.Width),  length.out = 200)
)

grid$pred <- predict(modelo, newdata = grid, type = "class")

ggplot() +
  geom_tile(data = grid, aes(x = Sepal.Length, y = Sepal.Width, fill = pred), alpha = 0.35) +
  geom_point(data = train, aes(x = Sepal.Length, y = Sepal.Width, color = Species), size = 2) +
  labs(
    title = "Red neuronal simple (nnet): regiones de decisión",
    subtitle = "Fondo = clase predicha | puntos = datos de entrenamiento",
    x = "Sepal.Length",
    y = "Sepal.Width"
  ) +
  theme_minimal()


7 7. Buenas prácticas (resumen)

En proyectos reales, considera:


8 8. Actividad para alumnos (taller corto)

8.1 Actividad A (básica)

  1. Cambia size a 2, 5 y 10
modelo2 <- nnet(
  Species ~ Sepal.Length + Sepal.Width,
  data = train,
  size = 2,
  decay = 0.01,
  maxit = 500,
  trace = FALSE
)

modelo2
## a 2-2-3 network with 15 weights
## inputs: Sepal.Length Sepal.Width 
## output(s): Species 
## options were - softmax modelling  decay=0.01
## size 5
grid_5 <- expand.grid(
  Sepal.Length = seq(min(df$Sepal.Length), max(df$Sepal.Length), length.out = 200),
  Sepal.Width  = seq(min(df$Sepal.Width),  max(df$Sepal.Width),  length.out = 200)
)

grid_5$pred <- predict(modelo2, newdata = grid_5, type = "class")

ggplot() +
  geom_tile(data = grid_5, aes(x = Sepal.Length, y = Sepal.Width, fill = pred), alpha = 0.35) +
  geom_point(data = train, aes(x = Sepal.Length, y = Sepal.Width, color = Species), size = 2) +
  labs(
    title = "Red neuronal simple (nnet): regiones de decisión",
    subtitle = "Fondo = clase predicha | puntos = datos de entrenamiento",
    x = "Sepal.Length",
    y = "Sepal.Width"
  ) +
  theme_minimal()

###
modelo3 <- nnet(
  Species ~ Sepal.Length + Sepal.Width,
  data = train,
  size = 10,
  decay = 0.01,
  maxit = 500,
  trace = FALSE
)

modelo3
## a 2-10-3 network with 63 weights
## inputs: Sepal.Length Sepal.Width 
## output(s): Species 
## options were - softmax modelling  decay=0.01
## size 10
grid10 <- expand.grid(
  Sepal.Length = seq(min(df$Sepal.Length), max(df$Sepal.Length), length.out = 200),
  Sepal.Width  = seq(min(df$Sepal.Width),  max(df$Sepal.Width),  length.out = 200)
)

grid10$pred <- predict(modelo3, newdata = grid, type = "class")

ggplot() +
  geom_tile(data = grid, aes(x = Sepal.Length, y = Sepal.Width, fill = pred), alpha = 0.35) +
  geom_point(data = train, aes(x = Sepal.Length, y = Sepal.Width, color = Species), size = 2) +
  labs(
    title = "Red neuronal simple (nnet): regiones de decisión",
    subtitle = "Fondo = clase predicha | puntos = datos de entrenamiento",
    x = "Sepal.Length",
    y = "Sepal.Width"
  ) +
  theme_minimal()

###
  1. Registra el accuracy y comenta si mejora o empeora.

Accuracy SIZE=2

pred_class2 <- predict(modelo2, newdata = test, type = "class")

conf2 <- table(Real = test$Species, Predicho = pred_class2)
conf2
##             Predicho
## Real         setosa versicolor virginica
##   setosa         14          0         0
##   versicolor      0          9         9
##   virginica       0          1        12
accuracy2 <- sum(diag(conf)) / sum(conf2)
accuracy2
## [1] 0.8444444

Accuracy (test) Size 2: 0.844

Accuracy SIZE=10

pred_class10 <- predict(modelo3, newdata = test, type = "class")

conf10 <- table(Real = test$Species, Predicho = pred_class10)
conf10
##             Predicho
## Real         setosa versicolor virginica
##   setosa         14          0         0
##   versicolor      0         12         6
##   virginica       0          1        12
accuracy10 <- sum(diag(conf)) / sum(conf10)
accuracy10
## [1] 0.8444444

Accuracy (test) Size 10: 0.844

Se observa que el accuracy en ambos modelos (size=2, size=10) es idéntico, sin embargo su matriz de confusion cambia un poco en la cantidad de versicolor predichos correctamente. Se corre el riesgo de sobreajuste con un tamaño más grande. Ambos tienen buenos valores.

8.2 Actividad B (intermedia)

  1. Añade Petal.Length y Petal.Width al modelo.
  2. Re-entrena y compara el accuracy.
library(nnet)
library(dplyr)

# 1) (sépalos + pétalos)
df_B <- iris %>%
  select(Sepal.Length, Sepal.Width, Petal.Length, Petal.Width, Species)

# 2) Usar mismo split para comparar correctamente
train_B <- df_B[idx_train, ]
test_B  <- df_B[-idx_train, ]

# 3) Re-entrenar el modelo con más variables
set.seed(123)
modelo_B <- nnet(
  Species ~ Sepal.Length + Sepal.Width + Petal.Length + Petal.Width,
  data = train_B,
  size = 5,
  decay = 0.01,
  maxit = 500,
  trace = FALSE
)

# 4) Predicción y accuracy
pred_B <- predict(modelo_B, newdata = test_B, type = "class")
conf_B <- table(Real = test_B$Species, Predicho = pred_B)

accuracy_B <- sum(diag(conf_B)) / sum(conf_B)

conf_B
##             Predicho
## Real         setosa versicolor virginica
##   setosa         14          0         0
##   versicolor      0         17         1
##   virginica       0          0        13
accuracy_B
## [1] 0.9777778

El accuracy mejora porque es mayor que el del modelo anterior. Esto se debe a que las variables del pétalo separan mejor las especies, permitiendo al modelo clasificar más casos correctamente.

8.3 Actividad C (reto)

  1. Estandariza las variables numéricas (z-score).
  2. Re-entrena y compara.

Pista (estandarización):

vars <- c("Sepal.Length","Sepal.Width","Petal.Length","Petal.Width")

df2 <- iris %>% select(all_of(vars), Species)
df2[vars] <- scale(df2[vars])

# 1) (sépalos + pétalos)
df_C <- df2 %>%
  select(Sepal.Length, Sepal.Width, Petal.Length, Petal.Width, Species)

# 2) Usar mismo split para comparar correctamente
train_C <- df_C[idx_train, ]
test_C  <- df_C[-idx_train, ]

# 3) Re-entrenar el modelo con más variables
set.seed(123)
modelo_C <- nnet(
  Species ~ Sepal.Length + Sepal.Width + Petal.Length + Petal.Width,
  data = train_C,
  size = 5,
  decay = 0.01,
  maxit = 500,
  trace = FALSE
)

# 4) Predicción y accuracy
pred_C <- predict(modelo_C, newdata = test_C, type = "class")
conf_C <- table(Real = test_C$Species, Predicho = pred_C)

accuracy_C <- sum(diag(conf_C)) / sum(conf_C)


conf_C
##             Predicho
## Real         setosa versicolor virginica
##   setosa         14          0         0
##   versicolor      0         17         1
##   virginica       0          0        13
accuracy_C
## [1] 0.9777778

Accuracy (test) estandarizacion : 0.978

El accuracy tambien cuenta con un valor alto, esto indica que la precision es alta para separar las especies de manera correcta. Sin embargo, el valor es identico a la actividad anterior 0.978 a pesar de la estandarización.

9 9. Reproducibilidad

sessionInfo()
## R version 4.5.2 (2025-10-31 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26200)
## 
## Matrix products: default
##   LAPACK version 3.12.1
## 
## locale:
## [1] LC_COLLATE=Spanish_Peru.utf8  LC_CTYPE=Spanish_Peru.utf8   
## [3] LC_MONETARY=Spanish_Peru.utf8 LC_NUMERIC=C                 
## [5] LC_TIME=Spanish_Peru.utf8    
## 
## time zone: America/Lima
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] tibble_3.3.0  ggplot2_4.0.1 dplyr_1.1.4   nnet_7.3-20  
## 
## loaded via a namespace (and not attached):
##  [1] vctrs_0.6.5        cli_3.6.5          knitr_1.50         rlang_1.1.6       
##  [5] xfun_0.54          generics_0.1.4     S7_0.2.1           jsonlite_2.0.0    
##  [9] labeling_0.4.3     glue_1.8.0         htmltools_0.5.9    sass_0.4.10       
## [13] scales_1.4.0       rmarkdown_2.30     grid_4.5.2         evaluate_1.0.5    
## [17] jquerylib_0.1.4    fastmap_1.2.0      yaml_2.3.11        lifecycle_1.0.4   
## [21] compiler_4.5.2     RColorBrewer_1.1-3 pkgconfig_2.0.3    rstudioapi_0.17.1 
## [25] farver_2.1.2       digest_0.6.39      R6_2.6.1           tidyselect_1.2.1  
## [29] pillar_1.11.1      magrittr_2.0.4     bslib_0.9.0        withr_3.0.2       
## [33] gtable_0.3.6       tools_4.5.2        cachem_1.1.0