Tarea 6: Predicción de una señal

Tarea 6: Predicción de una señal

Información de la Tarea

Estudiante: Andrés Cruz Chipol

Curso: Aprendizaje Automático

Fecha de entrega: 05 de Marzo, 2026

Descripción de la Tarea

Con los datos proporcionados realizar un predictor de la última muestra (predicción del siguiente valor de la señal a partir de las muestras anteriores). Se debe:


Predicción de una señal con MLP

Objetivo

Entrenar una red neuronal tipo MLP (perceptrón multicapa) que, dada una ventana de (n) muestras consecutivas de una señal, predice la siguiente muestra. Una vez entrenado el modelo, se usa en modo autoregresivo: la predicción se toma como la siguiente muestra y se retroalimenta como entrada para predecir el siguiente paso, repitiendo el proceso hasta completar 2000 pasos de señal reconstruida.

Datos

Datos — datos_crudos.txt (abscisa, señal)

Formato: dos columnas separadas por espacios.

0.000000000000000000e+00 0.000000000000000000e+00
1.570796326794896558e-01 1.317531745966325163e+00
3.141592653589793116e-01 2.118033988749894903e+00
4.712388980384689674e-01 2.148785621521231981e+00
6.283185307179586232e-01 1.538841768587627001e+00
7.853981633974482790e-01 7.071067811865474617e-01
9.424777960769379348e-01 1.180339887498949025e-01
1.099557428756427591e+00 2.746527796158948487e-02
1.256637061435917246e+00 3.632712640026802831e-01
1.413716694115406902e+00 8.037885975933173333e-01
1.570796326794896558e+00 1.000000000000000000e+00
1.727875959474386214e+00 8.037885975933183325e-01
1.884955592153875870e+00 3.632712640026806716e-01
2.042035224833365525e+00 2.746527796158892976e-02
2.199114857512855181e+00 1.180339887498946805e-01
2.356194490192344837e+00 7.071067811865464625e-01
2.513274122871834493e+00 1.538841768587626335e+00
2.670353755551324149e+00 2.148785621521232425e+00
2.827433388230813804e+00 2.118033988749894903e+00
2.984513020910303460e+00 1.317531745966325829e+00
3.141592653589793116e+00 1.102182119232617911e-15
3.298672286269282772e+00 -1.317531745966324053e+00
3.455751918948772428e+00 -2.118033988749894458e+00
3.612831551628262083e+00 -2.148785621521233313e+00
3.769911184307751739e+00 -1.538841768587627445e+00
3.926990816987241395e+00 -7.071067811865481278e-01
4.084070449666731051e+00 -1.180339887498953466e-01
4.241150082346220707e+00 -2.746527796158770851e-02
4.398229715025710362e+00 -3.632712640026800055e-01
4.555309347705200018e+00 -8.037885975933179994e-01
4.712388980384689674e+00 -1.000000000000000000e+00
4.869468613064179330e+00 -8.037885975933188876e-01
5.026548245743668986e+00 -3.632712640026810602e-01
5.183627878423158641e+00 -2.746527796158804158e-02
5.340707511102648297e+00 -1.180339887498926821e-01
5.497787143782137953e+00 -7.071067811865464625e-01
5.654866776461627609e+00 -1.538841768587625669e+00
5.811946409141117265e+00 -2.148785621521232425e+00
5.969026041820606920e+00 -2.118033988749894458e+00
6.126105674500096576e+00 -1.317531745966326717e+00
6.283185307179586232e+00 -2.204364238465235822e-15

Se decidió usar 15 muestras de ventana y 12 neuronas en la capa oculta. La señal se duplicó (dos ciclos) para que la red aprenda la transición entre el final y el inicio del periodo. A continuación los pasos realizados, código por código.

Paso 1. Generar datos (ventanas deslizantes)

Se lee datos_crudos.txt, se toma la segunda columna (señal), se duplica para formar dos ciclos y se generan ventanas de 15 muestras con target la siguiente (16ª). Salida: d_tarea5.txt (cada línea: 15 valores + 1 target).

import numpy as np
# Lee los datos crudos y genera ventanas deslizantes
# Señal duplicada para aprender la transición del ciclo
# Ventana de 15 muestras -> predice la 16ava
A = np.loadtxt( "datos_crudos.txt" )
y = A[:, 1]
senial = np.tile( y, 2 )
n = 15
i = 0
m = len( senial ) - n
with open( "d_tarea5.txt", "w" ) as f :
while i < m :
ventana = senial[ i : i + n ]
target = senial[ i + n ]
f.write( " ".join( str(v) for v in ventana ) + " " + str(target) + "\n" )
i += 1
print( "Señal original:", len(y), "puntos" )
print( "Señal duplicada:", len(senial), "puntos" )
print( "d_tarea5.txt generado con ventana de", n, "muestras" )

Paso 2. Entrenar el MLP

Se cargan las ventanas desde d_tarea5.txt, se hace train/test split y se entrena un MLPRegressor con 12 neuronas en la capa oculta, activación tanh y solver lbfgs. Para elegir esta configuración se realizó una búsqueda exhaustiva probando distintas combinaciones de random_state y número de neuronas/capas ocultas; el proceso ejecutó del orden de 7000 comparaciones de modelos y tomó aproximadamente un par de minutos, seleccionando la arquitectura que daba mejor desempeño en entrenamiento y prueba.

import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPRegressor
# Leo los datos
A = np.loadtxt( "d_tarea5.txt" )
print( A.shape )
n = 15
X = A[:, 0:n]
y = A[:, n]
X_entrena, X_prueba, y_entrena, y_prueba = train_test_split( X, y, random_state=21 )
regresor = MLPRegressor( hidden_layer_sizes=(12,), activation='tanh', solver='lbfgs',
random_state=815, max_iter=5000 )
regresor.fit( X_entrena, y_entrena )
e_entrena = regresor.score( X_entrena, y_entrena )
print( "Exactitud entrenamiento:", e_entrena )
e_prueba = regresor.score( X_prueba, y_prueba )
print( "Exactitud prueba:", e_prueba )
print( regresor.coefs_ )
print( regresor.intercepts_ )
print( regresor.out_activation_ )

Salida del script:

Exactitud entrenamiento: 0.9999989814829509
Exactitud prueba: 0.9887118380888489

Paso 3. Modelo (red con pesos fijos)

Se exportan los pesos del MLP entrenado a una red explícita: 15 entradas → 12 neuronas (tanh) → 1 salida (identity). En este paso se definen las matrices de pesos fijos y la función evalua(x) que, dada una ventana de 15 muestras, calcula la predicción de la siguiente muestra.

import numpy as np
#
# 15 características (ventana de 15 muestras)
# 1 salida
# 12 neuronas en la capa oculta
# La red será
# 15 12 1
C2 = np.array([[ 0.29003432, 0.41746049, -0.41867256, 0.3893998 , -0.09424529,
-0.14619631, 0.44204046, -0.06520372, 0.01776102, -0.40287939,
0.02072648, -0.17941415],
[ 0.61650491, -0.1896925 , -0.79743588, -0.06978661, -0.10019431,
-0.10358138, 0.50689693, -0.07539941, -0.2077765 , -0.19283091,
0.16441655, 0.55975249],
[ 0.41508782, -0.0265436 , -0.49549106, 0.12272782, -0.31508212,
0.15343149, -0.04592565, 0.39792991, 0.30323778, -0.18649367,
0.14860112, 0.49316277],
[-0.17147961, -0.34618066, 0.07981664, -0.14417562, -0.35865476,
-0.05862978, 0.46616777, 0.50559741, -0.07649255, 0.18399563,
0.13365243, -0.20347246],
[ 0.27964192, -0.1708882 , 0.17280839, -0.07855425, -0.71869843,
-0.15727863, 0.32913737, 0.02789036, -0.06773085, 0.03967061,
-0.1883208 , -0.3981712 ],
[ 0.29514207, -0.82648058, 0.4437443 , -0.1708265 , -0.35569372,
0.07418537, -0.04394645, 0.55872184, 0.03533008, 0.25230033,
-0.49368765, -0.61093585],
[ 0.40923157, -0.34567637, -0.31564473, -0.21142178, -0.13476684,
-0.29331377, -0.2027102 , -0.02408677, 0.10124833, -0.18580321,
0.25815778, -0.13048778],
[-0.03068666, 0.12010569, -0.02574027, -0.31166878, 0.03152018,
-0.49197128, 0.2175438 , 0.28979682, 0.36262737, -0.38655378,
-0.03821992, 0.29603498],
[ 0.13535222, -0.25211563, 0.13498528, -0.16859933, 0.1409662 ,
-0.28272763, 0.01721792, -0.04941255, 0.29751542, 0.15334287,
0.46616815, -0.35561488],
[ 0.01030758, -0.13279923, -0.09310283, 0.22796515, 0.42070687,
-0.61120394, -0.6443173 , 0.32381778, 0.13572336, 0.27785766,
0.35983483, -0.34181366],
[ 0.56482015, 0.91524428, -0.29237348, -0.10771201, 0.25880541,
-0.30158372, 0.1956239 , -0.15814626, -0.19902201, 0.11911918,
0.7299603 , -0.21214853],
[-0.39189331, 0.49985445, 0.26983156, 0.22913238, 0.24456656,
0.01693252, -0.62597843, 0.06546586, -0.4628089 , -0.10585945,
-0.15202976, 0.00952275],
[-0.51802977, -0.07071823, -0.05500217, -0.25944453, 0.37420089,
0.42010397, -0.12725628, 0.41982527, -0.22370796, -0.2840913 ,
0.2853449 , -0.39758266],
[ 0.18468159, -0.70347063, 0.00197438, 0.63003012, -0.10851216,
0.25375664, 0.09895744, -0.03213104, -0.31708006, 0.53656144,
-0.08197631, 0.097472 ],
[ 0.38387694, -0.31167247, -0.05994645, 0.70684897, 0.1712905 ,
0.18338328, -0.1169708 , -0.87160139, 0.31206742, 0.29161891,
0.7035281 , 0.44105949]])
C3 = np.array([[-0.33279676],
[-0.50380751],
[ 0.37228277],
[ 0.59140774],
[-0.67803344],
[-0.45036283],
[ 0.29880239],
[-0.43662288],
[ 0.28439648],
[ 0.40340646],
[ 0.60159504],
[ 0.40096819]])
vs2 = np.array([ 0.23865528, 0.53511569, -0.51663388, -0.10109733, 0.1209389 ,
0.45575181, -0.04554421, -0.0407963 , 0.46873587, -0.38178092,
-0.1452511 , 0.34376213])
vs3 = np.array([0.27973375])
# identity
def evalua( x ) :
# 1x15 15x12 1x12
S2 = x @ C2 + vs2
O2 = np.tanh( S2 )
#
# 1x12 12x1 1x1
S3 = O2 @ C3 + vs3
return S3

Paso 4. Simular 2000 pasos retroalimentando la salida a la entrada

Se toma la última ventana de 15 muestras de la señal original, se predice la siguiente con modelo3.evalua(vx), se añade la predicción a la ventana (desplazando) y se repite 2000 veces. Se grafica la señal original más la predicción y los primeros 250 pasos.

import numpy as np
import matplotlib.pyplot as plt
import importlib
modelo3 = importlib.import_module( "3_modelo" )
A = np.loadtxt( "datos_crudos.txt" )
y = A[:, 1]
n = 15
vx = np.array( y[-n:] )
pasos = 2000
i = 0
predicciones = []
while i < pasos :
s = modelo3.evalua( vx )
predicciones.append( s[0] )
vx = np.append( vx[1:], s[0] )
i += 1
# Graficar
fig, axs = plt.subplots( 2, 1, figsize=(15, 8) )
axs[0].plot( range( len(y) ), y, 'b', label='Original', linewidth=2 )
axs[0].plot( range( len(y), len(y) + pasos ), predicciones, 'r--',
label=f'Predicción ({pasos} pasos)', alpha=0.9 )
axs[0].set_title( f'Señal original + Predicción (ventana={n}, neuronas=12)' )
axs[0].set_xlabel( 'Paso' )
axs[0].set_ylabel( 'Amplitud' )
axs[0].legend()
axs[0].grid( True, alpha=0.3 )
axs[1].plot( predicciones[:250], color='red' )
axs[1].set_title( 'Primeros 250 pasos de predicción' )
axs[1].set_xlabel( 'Paso' )
axs[1].set_ylabel( 'Amplitud' )
axs[1].grid( True, alpha=0.3 )
plt.tight_layout()
plt.savefig( 'reconstruccion.png' )
print( f"Predicción de {pasos} pasos finalizada. Gráfica guardada en 'reconstruccion.png'" )

Resultados

La gráfica siguiente muestra la señal original (azul), los 2000 pasos reconstruidos por retroalimentación (rojo) y un detalle de los primeros 250 pasos.

Reconstrucción de 2000 pasos de la señal

Conclusión

Se implementó un predictor de la última muestra usando un MLP con ventana de 15 muestras y 12 neuronas en la capa oculta, eligiendo la configuración mediante una búsqueda exhaustiva sobre semillas y arquitecturas. La señal se duplicó para aprender la transición entre ciclos y, una vez entrenado el modelo, se reconstruyeron 2000 pasos retroalimentando la salida a la entrada, obteniendo una continuación coherente de la señal original.