nnet
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.
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.
En redes modernas se eligen activaciones según estabilidad y desempeño.
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)
El entrenamiento busca minimizar una función de pérdida:
\[ \min_{w,b} \; \mathcal{L}(y, \hat{y}) \]
Pasos conceptuales:
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.
nnetPara un ejemplo introductorio y visual, usaremos:
iris (incluido en R)SpeciesNota:
nnetes una implementación clásica (red pequeña). Para redes profundas se suele usarkeras/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)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…
## 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
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
##
## setosa versicolor virginica
## 14 18 13
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
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
## [1] 0.8444444
Accuracy (test): 0.844
# 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()En proyectos reales, considera:
size a 2, 5 y 10modelo2 <- 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()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
## [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
## [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.
Petal.Length y Petal.Width al
modelo.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
## [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.
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
## [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.
## 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