function fibonacci(n::Int)
a = zeros(Float32, n)
a[1] = 1
a[2] = 1
for i in 3:n
a[i] = a[i-1] + a[i-2]
end
return a
endfibonacci (generic function with 1 method)
Las redes de neuronas artificiales recurrentes (RNN por sus siglas en inglés) son un tipo de redes neuronales diseñadas para trabajar con datos secuenciales. A diferencia de las redes neuronales tradicionales tienen una memoria interna capaz de recordar información de una secuencia temporal de entradas, y utilizar esa información para predecir nuevos valores de la serie o clasificarla. Esto las hace ideales para tareas de predicción de series temporales, traducción de textos o audios, y procesamiento del lenguaje natural entre otras.
En esta práctica veremos cómo funcionan estas redes neuronales y aprenderemos a implementarlas con el paquete Lux de Julia.
Para la realización de esta práctica se requieren los siguientes paquetes:
using CSV # Para la lectura de archivos CSV.
using DataFrames # Para el manejo de datos tabulares.
using GLMakie # Para el dibujo de gráficas.
using Random # Para la generación de números aleatorios.
using Lux # Para la implementación de redes neuronales.
using Zygote # Cálculo automático de derivadas y gradientes.
using Optimisers # Para la optimización de funciones.
using Statistics # Para las funciones de coste.Ejercicio 6.1 La sucesión de Fibonacci es una sucesión recurrente que se define como
\[a_1 = 1,\quad a_2 =1, \quad a_n = a_{n-1} + a_{n-2}\quad \forall n\geq 3.\]
Construir una función para generar la sucesión de Fibonacci.
function fibonacci(n::Int)
a = zeros(Float32, n)
a[1] = 1
a[2] = 1
for i in 3:n
a[i] = a[i-1] + a[i-2]
end
return a
endfibonacci (generic function with 1 method)
Dividir la secuencia de los 30 primeros términos de la sucesión de Fibonacci en subsecuencias de longitud 5. Para cada una de estas subsecuencias, guardar los vectores de los cuatro primeros términos en una matriz de entrada y el último valor en una matriz de salida.
fib = fibonacci(25)
tamaño = 4
n = 25 - tamaño
# La red esperar una entrada con dimensiones (características, tamaño, lote).
X = Array{Float32}(undef, 1, tamaño, n)
Y = Array{Float32}(undef, 1, n)
for i = 1:n
ventana = fib[i:i+tamaño-1]
etiqueta = fib[i+tamaño]
X[1, : , i] .= ventana
Y[1, i] = etiqueta
endDefinir una red neuronal con una neurona recurrente con un estado oculto de tamaño 1, sin término independiente (bias). Inicializar la red con pesos aleatorios.
using Lux, Random
modelo = Chain(
Recurrence(RNNCell(1 => 1, identity; use_bias = false); return_sequence = false)
)
rng = Random.default_rng()
# Semilla aleatoria para reproducibilidad
Random.seed!(rng, 1234)
ps, st = Lux.setup(rng, modelo)((layer_1 = (weight_ih = Float32[-1.7194579;;], weight_hh = Float32[1.174417;;]),), (layer_1 = (rng = TaskLocalRNG(),),))
Definir como función de coste el error cuadrático medio para todas las secuencias del conjunto de entrenamiento.
using Statistics
function coste(ps, st, X, Y)
ŷ, estado = modelo(X, ps, st) # ŷ: (1, batch)
return mean((ŷ .- Y).^2)
end
coste(ps, st, X, Y)6.2632223f9
Entrenar la red neuronal con el algoritmo de optimización Adam tomando una tasa de aprendizaje 0.1. Realizar 200 épocas.
using Optimisers, Zygote
opt = Optimisers.setup(Optimisers.Adam(0.1f0), ps)
nepocas = 200
costes = []
for epoca in 1:nepocas
# gradiente de la red con respecto a los parámetros
gs = first(Zygote.gradient(p -> coste(p, st, X, Y), ps))
opt, ps = Optimisers.update(opt, ps, gs)
push!(costes, coste(ps, st, X, Y))
println("Época $epoca | coste = ", costes[end])
endÉpoca 1 | coste = 5.0130447e9
Época 2 | coste = 4.046983e9
Época 3 | coste = 3.3034327e9
Época 4 | coste = 2.7312077e9
Época 5 | coste = 2.2893059e9
Época 6 | coste = 1.94581e9
Época 7 | coste = 1.6764061e9
Época 8 | coste = 1.4628404e9
Época 9 | coste = 1.2915259e9
Época 10 | coste = 1.1523862e9
Época 11 | coste = 1.0379493e9
Época 12 | coste = 9.4265696e8
Época 13 | coste = 8.623539e8
Época 14 | coste = 7.9391226e8
Época 15 | coste = 7.349589e8
Época 16 | coste = 6.836777e8
Época 17 | coste = 6.386652e8
Época 18 | coste = 5.9882643e8
Época 19 | coste = 5.632986e8
Época 20 | coste = 5.313949e8
Época 21 | coste = 5.0256346e8
Época 22 | coste = 4.7635606e8
Época 23 | coste = 4.5240582e8
Época 24 | coste = 4.3040934e8
Época 25 | coste = 4.101142e8
Época 26 | coste = 3.913082e8
Época 27 | coste = 3.7381197e8
Época 28 | coste = 3.5747312e8
Época 29 | coste = 3.4216106e8
Época 30 | coste = 3.277637e8
Época 31 | coste = 3.141841e8
Época 32 | coste = 3.0133814e8
Época 33 | coste = 2.891527e8
Época 34 | coste = 2.77564e8
Época 35 | coste = 2.665161e8
Época 36 | coste = 2.5595997e8
Época 37 | coste = 2.458527e8
Época 38 | coste = 2.3615629e8
Época 39 | coste = 2.268375e8
Época 40 | coste = 2.1786688e8
Época 41 | coste = 2.0921851e8
Época 42 | coste = 2.0086955e8
Época 43 | coste = 1.927998e8
Época 44 | coste = 1.8499157e8
Época 45 | coste = 1.774291e8
Época 46 | coste = 1.7009869e8
Época 47 | coste = 1.6298826e8
Época 48 | coste = 1.5608707e8
Época 49 | coste = 1.493859e8
Época 50 | coste = 1.4287659e8
Época 51 | coste = 1.3655203e8
Época 52 | coste = 1.3040608e8
Época 53 | coste = 1.24433336e8
Época 54 | coste = 1.1862924e8
Época 55 | coste = 1.1298979e8
Época 56 | coste = 1.0751154e8
Época 57 | coste = 1.02191656e8
Época 58 | coste = 9.70276e7
Época 59 | coste = 9.201727e7
Época 60 | coste = 8.715889e7
Época 61 | coste = 8.245096e7
Época 62 | coste = 7.789219e7
Época 63 | coste = 7.348143e7
Época 64 | coste = 6.9217816e7
Época 65 | coste = 6.510044e7
Época 66 | coste = 6.1128532e7
Época 67 | coste = 5.7301384e7
Época 68 | coste = 5.361825e7
Época 69 | coste = 5.007843e7
Época 70 | coste = 4.6681136e7
Época 71 | coste = 4.3425516e7
Época 72 | coste = 4.0310664e7
Época 73 | coste = 3.7335516e7
Época 74 | coste = 3.449891e7
Época 75 | coste = 3.1799504e7
Época 76 | coste = 2.9235844e7
Época 77 | coste = 2.6806242e7
Época 78 | coste = 2.450881e7
Época 79 | coste = 2.2341502e7
Época 80 | coste = 2.030202e7
Época 81 | coste = 1.838785e7
Época 82 | coste = 1.6596277e7
Época 83 | coste = 1.4924337e7
Época 84 | coste = 1.3368829e7
Época 85 | coste = 1.1926354e7
Época 86 | coste = 1.0593308e7
Época 87 | coste = 9.36583e6
Época 88 | coste = 8.2399175e6
Época 89 | coste = 7.2113615e6
Época 90 | coste = 6.275804e6
Época 91 | coste = 5.428737e6
Época 92 | coste = 4.6655335e6
Época 93 | coste = 3.9814918e6
Época 94 | coste = 3.3718168e6
Época 95 | coste = 2.8316872e6
Época 96 | coste = 2.3562815e6
Época 97 | coste = 1.9407861e6
Época 98 | coste = 1.5804446e6
Época 99 | coste = 1.270575e6
Época 100 | coste = 1.0065965e6
Época 101 | coste = 784080.8
Época 102 | coste = 598748.06
Época 103 | coste = 446494.9
Época 104 | coste = 323433.88
Época 105 | coste = 225888.9
Época 106 | coste = 150422.62
Época 107 | coste = 93842.3
Época 108 | coste = 53206.5
Época 109 | coste = 25830.83
Época 110 | coste = 9288.164
Época 111 | coste = 1405.4087
Época 112 | coste = 258.9841
Época 113 | coste = 4165.221
Época 114 | coste = 11670.322
Época 115 | coste = 21538.016
Época 116 | coste = 32733.744
Época 117 | coste = 44411.508
Época 118 | coste = 55893.332
Época 119 | coste = 66655.56
Época 120 | coste = 76311.76
Época 121 | coste = 84591.56
Época 122 | coste = 91327.59
Época 123 | coste = 96442.8
Época 124 | coste = 99924.69
Época 125 | coste = 101825.52
Época 126 | coste = 102235.44
Época 127 | coste = 101282.234
Época 128 | coste = 99114.875
Época 129 | coste = 95898.414
Época 130 | coste = 91803.37
Época 131 | coste = 86998.22
Época 132 | coste = 81652.52
Época 133 | coste = 75922.914
Época 134 | coste = 69954.91
Época 135 | coste = 63880.133
Época 136 | coste = 57814.195
Época 137 | coste = 51860.23
Época 138 | coste = 46101.43
Época 139 | coste = 40606.44
Época 140 | coste = 35429.72
Época 141 | coste = 30609.598
Época 142 | coste = 26174.066
Época 143 | coste = 22137.898
Época 144 | coste = 18506.016
Época 145 | coste = 15274.37
Época 146 | coste = 12433.482
Época 147 | coste = 9965.116
Época 148 | coste = 7848.189
Época 149 | coste = 6057.5005
Época 150 | coste = 4566.634
Época 151 | coste = 3346.4177
Época 152 | coste = 2367.8516
Época 153 | coste = 1601.9951
Época 154 | coste = 1020.641
Época 155 | coste = 596.68134
Época 156 | coste = 305.06555
Época 157 | coste = 122.30558
Época 158 | coste = 26.97289
Época 159 | coste = 0.021119582
Época 160 | coste = 24.514503
Época 161 | coste = 85.74834
Época 162 | coste = 171.26875
Época 163 | coste = 270.41803
Época 164 | coste = 374.75803
Época 165 | coste = 477.35016
Época 166 | coste = 573.0674
Época 167 | coste = 657.9105
Época 168 | coste = 729.38
Época 169 | coste = 785.9467
Época 170 | coste = 826.7763
Época 171 | coste = 852.2167
Época 172 | coste = 862.56854
Época 173 | coste = 859.2576
Época 174 | coste = 843.40356
Época 175 | coste = 816.80646
Época 176 | coste = 781.19714
Época 177 | coste = 738.0868
Época 178 | coste = 689.4481
Época 179 | coste = 636.91144
Época 180 | coste = 581.95496
Época 181 | coste = 525.9896
Época 182 | coste = 470.28207
Época 183 | coste = 415.8274
Época 184 | coste = 363.58008
Época 185 | coste = 314.0716
Época 186 | coste = 268.0682
Época 187 | coste = 225.7752
Época 188 | coste = 187.4945
Época 189 | coste = 153.36887
Época 190 | coste = 123.32797
Época 191 | coste = 97.2789
Época 192 | coste = 75.056725
Época 193 | coste = 56.43496
Época 194 | coste = 41.149002
Época 195 | coste = 28.87258
Época 196 | coste = 19.250816
Época 197 | coste = 12.009598
Época 198 | coste = 6.7831855
Época 199 | coste = 3.2605946
Época 200 | coste = 1.1491588
Dibujar la evolución de los costes durante el proceso de entrenamiento.
using GLMakie
fig = Figure()
ax = Axis(fig[1, 1], xlabel = "Época", ylabel = "Error cuadrático medio", title = "Evolución del coste")
lines!(ax, costes)
figUsar la red neuronal entrenada para predecir el siguiente término de la serie de Fibonacci a partir de los 4 últimos términos de la serie.
fib = fibonacci(30)
X_test = reshape(fib[end-tamaño : end-1], 1, tamaño, 1)
y_test, _ = modelo(X_test, ps, st)
println("Predicción del término 30: ", y_test[1, 1])
println("Término 30 de la sucesión de Fibonacci: ", fib[end])Predicción del término 30: 831997.2
Término 30 de la sucesión de Fibonacci: 832040.0
Mostrar los pesos de la red neuronal entrenada.
println("Pesos de la entrada de la red neuronal:", ps.layer_1.weight_ih)
println("Pesos del estado de la red neuronal:", ps.layer_1.weight_hh)Pesos de la entrada de la red neuronal:Float32[1.662782;;]
Pesos del estado de la red neuronal:Float32[-0.04483252;;]
Ejercicio 6.2 El fichero stock.csv contiene los precios al cierre de las acciones de una empresas en bolsa de valores de los 300 primeros días del año 2020.
Cargar el conjunto de datos y dibujar la serie temporal.
using CSV, DataFrames, GLMakie
# Cargamos el conjunto de datos en un data frame
df = CSV.read("datos/stock.csv", DataFrame)
# Creamos el gráfico de la evolución
fig = Figure()
ax = Axis(fig[1, 1], xlabel = "Día", ylabel = "Precio de la acción (€)", title = "Evolución del precio de la acción")
lines!(ax, df.dia, df.precio)
figNormalizar el conjunto de datos.
using Statistics
# Simple normalization (z-score)
μ = mean(df.precio)
σ = std(df.precio)
serie = (df.precio .- μ) ./ σ 300-element Vector{Float64}:
-1.2770239698472932
-1.4749432268197025
-1.2481144154580652
-0.8745140202741842
-0.8789616440263692
-1.4460336724304748
-0.9679141190701513
-1.1814000591752272
-1.1591619404142832
-1.054642782237841
-1.1391476335294317
-0.8923045152829369
-1.1035666435119202
⋮
1.4249074596075517
1.5049646871469575
1.031292757538819
1.0379641931671029
1.6472886472170039
0.9112069162297167
1.1469309750957366
1.4782789446338223
1.3137168658028255
1.5027408752708586
1.3759835983334725
1.6450648353409114
Dividir la serie de los precios en secuencias de 50 términos. Para cada una de estas secuencias, guardar los vectores de los 40 primeros términos en una matriz de entrada y el último valor en una matriz de salida.
function crear_secuencias(serie, tamaño)
# Número de ventanas
n = length(serie) - tamaño
# X: (características=1, tamaño=seq_len, lotes=n)
X = Array{Float32}(undef, 1, tamaño, n)
# Y: (etiquetas=1, lotes=n)
Y = Array{Float32}(undef, 1, n)
for i in 1:n
ventana = serie[i : i + tamaño - 1]
etiqueta = serie[i + tamaño]
X[1, :, i] .= ventana
Y[1, i] = etiqueta
end
return X, Y
end
X, Y = crear_secuencias(serie, 50)(Float32[-1.2770239 -1.4749433 … -1.7551435 -1.501629;;; -1.4749433 -1.2481145 … -1.501629 -1.6973244;;; -1.2481145 -0.87451404 … -1.6973244 -1.4393623;;; … ;;; 1.7607031 1.3515216 … 1.478279 1.3137169;;; 1.3515216 1.1980786 … 1.3137169 1.5027409;;; 1.1980786 1.6117077 … 1.5027409 1.3759836], Float32[-1.6973244 -1.4393623 … 1.3759836 1.6450648])
Dividir el conjunto de secuencias en un conjunto de entrenamiento con las 200 primeras secuencias y otro de prueba con las 50 restantes.
Xentrenamiento, Yentrenamiento = X[:, :, 1:200], Y[:, 1:200]
Xtest, Ytest = X[:, :, 201:end], Y[:, 201:end](Float32[0.08839652 0.09062033 … 1.3515216 1.1980786;;; 0.09062033 0.3797159 … 1.1980786 1.6117077;;; 0.3797159 0.29298723 … 1.6117077 1.3248359;;; … ;;; 1.7607031 1.3515216 … 1.478279 1.3137169;;; 1.3515216 1.1980786 … 1.3137169 1.5027409;;; 1.1980786 1.6117077 … 1.5027409 1.3759836], Float32[1.6117077 1.3248359 … 1.3759836 1.6450648])
Definir una red neuronal con una neurona recurrente con un estado oculto de tamaño 64 y una neurona densa de 1 salida. Inicializar la red con pesos aleatorios.
using Lux, Random
modelo = Chain(
Recurrence(RNNCell(1 => 64); return_sequence = false),
Dense(64 => 1)
)
rng = Random.default_rng()
# Semilla aletoria para reproducibilidad
Random.seed!(rng, 1234)
ps, st = Lux.setup(rng, modelo)((layer_1 = (weight_ih = Float32[-0.21493223; 0.14680213; … ; 0.09006676; -0.29187012;;], weight_hh = Float32[0.16710633 -0.10049977 … -0.7339213 -0.44402117; -0.12605162 -0.42469883 … -0.2643636 -0.33319688; … ; -0.2543804 0.052617714 … -0.60686016 -0.4568765; 0.24136081 -0.34361988 … 0.12011717 0.118953556], bias_ih = Float32[-0.0951326, 0.08999576, 0.10426867, 0.11379315, 0.11712587, -0.3674832, -0.018230781, -0.12023171, -0.00048576295, 0.7689639 … -0.06666367, 0.52615803, -0.09323904, -0.015267894, -0.29630774, -0.31387812, -0.2896284, -0.171922, 0.173794, -0.13455431], bias_hh = Float32[0.094174266, -0.32182646, -0.4835172, -0.1707672, -0.11865046, -0.6600209, -0.3815048, -0.3407301, 0.014974058, -0.2173861 … 0.036009803, 0.32550737, -0.016171448, -0.1333059, 0.18780592, -0.085141905, 0.14552, -0.021185711, -0.15243843, -0.029349051]), layer_2 = (weight = Float32[-0.04013527 -0.08779158 … -0.21502617 -0.12662868], bias = Float32[-0.08802833])), (layer_1 = (rng = TaskLocalRNG(),), layer_2 = NamedTuple()))
Definir como función de coste el error cuadrático medio para todas las secuencias del conjunto de entrenamiento.
using Statistics
function coste(ps, st, X, Y)
ŷ, estado = modelo(X, ps, st)
return mean((ŷ .- Y).^2)
end
coste(ps, st, X, Y)2.4999213f0
Entrenar la red neuronal con el algoritmo de optimización Adam tomando una tasa de aprendizaje 0.01. Realizar 300 épocas.
using Optimisers, Zygote
opt = Optimisers.setup(Optimisers.Adam(0.01f0), ps)
nepocas = 100
costes = []
for epoca in 1:nepocas
# gradiente de la red con respecto a los parámetros
gs = first(Zygote.gradient(p -> coste(p, st, Xentrenamiento, Yentrenamiento), ps))
opt, ps = Optimisers.update(opt, ps, gs)
push!(costes, coste(ps, st, Xentrenamiento, Yentrenamiento))
println("Época $epoca | coste = ", costes[end])
endÉpoca 1 | coste = 0.7120536
Época 2 | coste = 0.58144593
Época 3 | coste = 0.86751
Época 4 | coste = 0.9924159
Época 5 | coste = 0.8545057
Época 6 | coste = 0.6759848
Época 7 | coste = 0.5618811
Época 8 | coste = 0.56183267
Época 9 | coste = 0.6390394
Época 10 | coste = 0.7087791
Época 11 | coste = 0.7118296
Época 12 | coste = 0.6544135
Época 13 | coste = 0.5607798
Época 14 | coste = 0.4915699
Época 15 | coste = 0.49867082
Época 16 | coste = 0.5568062
Época 17 | coste = 0.5875028
Época 18 | coste = 0.566299
Época 19 | coste = 0.51768893
Época 20 | coste = 0.4785608
Época 21 | coste = 0.47465625
Época 22 | coste = 0.49831668
Época 23 | coste = 0.517101
Época 24 | coste = 0.5072868
Época 25 | coste = 0.4794915
Época 26 | coste = 0.44851258
Época 27 | coste = 0.4381218
Época 28 | coste = 0.4442783
Época 29 | coste = 0.4462868
Época 30 | coste = 0.4242044
Época 31 | coste = 0.38773945
Época 32 | coste = 0.35950392
Época 33 | coste = 0.34827664
Época 34 | coste = 0.3276915
Época 35 | coste = 0.28721783
Época 36 | coste = 0.25264022
Época 37 | coste = 0.2554782
Época 38 | coste = 0.22638088
Época 39 | coste = 0.20317441
Época 40 | coste = 0.19642892
Época 41 | coste = 0.18331131
Época 42 | coste = 0.16217327
Época 43 | coste = 0.14643814
Época 44 | coste = 0.13728029
Época 45 | coste = 0.13000613
Época 46 | coste = 0.11908289
Época 47 | coste = 0.11113613
Época 48 | coste = 0.11166415
Época 49 | coste = 0.10654645
Época 50 | coste = 0.09456818
Época 51 | coste = 0.08943724
Época 52 | coste = 0.08898305
Época 53 | coste = 0.09065049
Época 54 | coste = 0.089022696
Época 55 | coste = 0.08459871
Época 56 | coste = 0.08418649
Época 57 | coste = 0.08065779
Época 58 | coste = 0.078941554
Época 59 | coste = 0.07779598
Época 60 | coste = 0.0763044
Época 61 | coste = 0.07736759
Época 62 | coste = 0.07613584
Época 63 | coste = 0.074771434
Época 64 | coste = 0.07497347
Época 65 | coste = 0.07299402
Época 66 | coste = 0.07364231
Época 67 | coste = 0.07250167
Época 68 | coste = 0.072929665
Época 69 | coste = 0.07158016
Época 70 | coste = 0.07258179
Época 71 | coste = 0.071268335
Época 72 | coste = 0.071854874
Época 73 | coste = 0.07159285
Época 74 | coste = 0.07038627
Época 75 | coste = 0.07098476
Época 76 | coste = 0.07006505
Época 77 | coste = 0.07028242
Época 78 | coste = 0.07019345
Época 79 | coste = 0.06929663
Época 80 | coste = 0.06949752
Época 81 | coste = 0.06908774
Época 82 | coste = 0.068893895
Época 83 | coste = 0.069023445
Época 84 | coste = 0.0686395
Época 85 | coste = 0.06830147
Época 86 | coste = 0.068120345
Época 87 | coste = 0.06778442
Época 88 | coste = 0.06775368
Época 89 | coste = 0.06764672
Época 90 | coste = 0.06730844
Época 91 | coste = 0.067337014
Época 92 | coste = 0.06719287
Época 93 | coste = 0.06740736
Época 94 | coste = 0.06722504
Época 95 | coste = 0.06719639
Época 96 | coste = 0.06701797
Época 97 | coste = 0.0668914
Época 98 | coste = 0.066815436
Época 99 | coste = 0.06673246
Época 100 | coste = 0.06663198
Dibujar la evolución de los costes durante el proceso de entrenamiento.
using GLMakie
fig = Figure()
ax = Axis(fig[1, 1], xlabel = "Época", ylabel = "Error cuadrático medio", title = "Evolución del coste")
lines!(ax, costes)
figPredecir el precio de las acciones de los próximos 50 días usando el conjunto de prueba.
y_test, _ = modelo(Xtest, ps, st)
predicciones = y_test .* σ .+ μ
fig = Figure()
ax = Axis(fig[1,1], xlabel = "Día", ylabel = "Precio de acción", title = "Predicciones del precio de la acción")
lines!(ax, df.dia, df.precio, label = "Precio real")
lines!(ax, df.dia[251:end], vec(predicciones), label = "Predicción")
axislegend(ax, position = :rb)
fig